操作系统高频(四)linux基础
1.32位Linux系统的寻址空间是多大?进程能申请的内存有这么大吗?⭐
32位Linux系统的寻址空间是4GB(2^32)大小。
进程实际能够申请的内存大小不会达到整个寻址空间的大小。这是因为Linux内核需要使用一部分地址空间来映射内核空间和其他系统资源,因此用户进程可用的地址空间会受到限制。
通常情况下,32位Linux系统下,用户进程可用的地址空间大小在2GB至3GB之间。
但是,通过使用特殊的技术,比如PAE(Physical Address Extension),可以扩展32位系统的可用内存范围。PAE允许32位处理器处理超过4GB的物理内存,并提供更大的寻址空间,但是单个进程仍然受到32位寻址限制的限制。
2.Linux系统中进程默认栈空间是多大?如何修改?⭐
Linux系统中,进程的默认栈空间大小通常是由操作系统内核设置的,默认情况下是8MB。这个大小在不同的系统中可能会有所不同,但通常在2MB到10MB之间。
如果需要修改进程的栈空间大小,可以使用ulimit命令来进行调整。ulimit命令用于设置或显示进程资源限制。要修改栈空间大小,可以使用以下命令:
ulimit -s <stack_size>
其中,<stack_size>是要设置的栈空间大小,以KB为单位。例如,要将栈空间大小设置为16MB,可以使用以下命令:
ulimit -s 16384
需要注意的是,ulimit命令是针对当前的shell会话生效的,修改只在当前会话中有效。如果需要永久修改栈空间大小,可以将相应命令添加到用户的shell配置文件中,如~/.bashrc或~/.bash_profile。这样,每次启动新的shell时都会生效。
3.Linux系统中调试崩溃问题的流程是?⭐
- 收集崩溃信息:当发生崩溃时,系统通常会生成一些崩溃信息,比如核心转储文件(core dump)或者错误日志。首先,需要收集这些信息,以便后续的调试工作。
- 分析核心转储文件:如果有核心转储文件生成,可以使用调试器(如GDB)来加载核心转储文件,并查看堆栈回溯、变量值等信息。这将有助于确定崩溃发生的位置和原因。
- 查看系统日志:检查系统日志(通常位于/var/log目录下)以获取任何与崩溃相关的信息,如错误消息、警告等。这些信息可能有助于确定崩溃的原因。
- 重现崩溃:如果崩溃问题是可以重现的,尽量复现崩溃,以便在调试时能够更好地观察和分析现象。
- 使用调试器:使用调试器(如GDB)来附加到崩溃的进程或执行崩溃的程序。通过观察程序的执行过程、设置断点、查看变量值等,可以深入分析问题,并找到导致崩溃的具体原因。
- 跟踪代码执行流:通过单步调试、观察变量值、堆栈跟踪等方式,跟踪程序的执行流,找到潜在的缺陷或异常情况。
4.用户空间与内核通信方式有哪些?⭐⭐⭐
- 系统调用:用户空间程序通过系统调用向内核请求执行特定的操作,如读写文件、创建进程等。
- 文件接口:用户空间程序通过文件接口读写文件来与内核进行通信,传递数据和命令。
- procfs:用户空间程序通过读写/proc文件系统的特定文件与内核通信,获取或修改内核状态和信息。
- sysfs:用户空间程序通过读写/sys文件系统中的文件与内核通信,查询或修改设备属性和参数。
- netlink套接字:用户空间程序通过netlink套接字与内核进行双向通信,用于网络配置、管理等任务。
- 共享内存:用户空间程序通过映射共享内存区域来读写数据,实现高效的数据交换。
- 设备文件:用户空间程序通过打开和读写设备文件与内核进行通信,如串口设备、字符设备等。
5.linux下检查内存状态的命令⭐⭐⭐
- free命令:用于查看系统内存的使用情况,包括总内存、已用内存、可用内存等。示例命令:free -h
- top命令:除了显示系统的整体资源使用情况外,还可以查看每个进程的内存使用情况。示例命令:top
- vmstat命令:显示系统的虚拟内存统计信息,包括内存使用情况、交换空间使用情况等。示例命令:vmstat
- /proc/meminfo文件:该文件提供了系统内存的详细信息,可以使用cat或者less命令来查看。示例命令:cat /proc/meminfo
- pmap命令:用于显示进程内存映射的详细信息,包括内存地址、权限、映射的文件等。示例命令:pmap <进程ID>
6.简述自旋锁和互斥锁的使用场景?⭐⭐⭐⭐
自旋锁(Spin Lock)和互斥锁(Mutex Lock)是常见的线程同步机制,它们适用于不同的使用场景:
自旋锁:
- 自旋锁采用了一种忙等待的方式,线程在无法获得锁时不会被阻塞,而是一直循环检查锁的状态,直到获得锁为止。
- 自旋锁适用于锁被占用的时间非常短暂而且线程竞争不激烈的情况。
- 自旋锁不涉及上下文切换,因为线程在等待锁时会忙等待。因此,在多核处理器上,自旋锁可以表现得更好,因为它充分利用了处理器的并行性和线程的空闲时间。
自旋锁的使用场景包括:临界区较短且竞争不激烈的情况,常见于多核处理器、高性能计算和实时系统等。
互斥锁:
- 互斥锁提供了线程的互斥访问,即只有一个线程能够获得锁并执行临界区代码,其他线程需要等待。
- 互斥锁适用于临界区较长或者有激烈线程竞争的情况。
- 互斥锁涉及到线程的阻塞和唤醒操作,当锁被占用时,等待线程会被阻塞,直到锁被释放。
互斥锁的使用场景包括:临界区较长或者需要保证临界区的独占性的情况,常见于共享资源的读写、数据库访问、线程间通信等
7.说说常见信号有哪些,表示什么含义?⭐
1 | SIGHUP | 终端挂起信号,当终端关闭或连接丢失时触发,通常用于通知进程重新加载配置文件 |
2 | SIGINT | 程序中止信号,用于中止前台进程。相当于输出 Ctrl+C 快捷键 |
8 | SIGFPE | 在发生致命的算术运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为 0 等其他所有的算术运算错误 |
9 | SIGKILL | 用来立即结束程序的运行。本信号不能被阻塞、处理和忽略。般用于强制中止进程 |
14 | SIGALRM | 时钟定时信号,计算的是实际的时间或时钟时间。alarm 函数使用该信号 |
15 | SIGTERM | 正常结束进程的信号,kill 命令的默认信号。如果进程已经发生了问题,那么这 个信号是无法正常中止进程的,这时我们才会尝试 SIGKILL 信号,也就是信号 9 |
17 | SIGCHLD | 子进程结束时, 父进程会收到这个信号。 |
18 | SIGCONT | 该信号可以让暂停的进程恢复执行。本信号不能被阻断 |
19 | SIGSTOP | 该信号可以暂停前台进程,相当于输入 Ctrl+Z 快捷键。本信号不能被阻断 |
8. 互斥量能不能在进程中使用?⭐⭐
可以。
互斥量可以在单个进程内的多个线程之间使用,也可以在不同进程间进行共享。在多线程情况下,互斥量被用作互斥锁(Mutex Lock),以确保在临界区资源上的互斥访问。通过加锁和解锁操作,互斥量可以实现线程的安全同步。
在多进程的情况下,可以使用带有命名的互斥量来实现跨进程间的同步和互斥访问。不同进程可以创建或打开同一个命名互斥量对象,并通过对互斥量对象的加锁和解锁操作来实现进程间的互斥和同步。
9. 说说线程池的设计思路,线程池中线程的数量由什么确定?⭐⭐⭐
如何实现一个线程池:
线程池的实现主要包括以下几个步骤:
- 创建一个线程安全的任务队列,充当生产者消费者队列,用于存储待执行的任务。可以使用阻塞队列等线程安全的数据结构实现。
- 初始化固定数量的线程,并让它们进入等待状态,准备处理任务。
- 当任务队列为空时,所有线程阻塞,等待新任务的到来。可以使用条件变量来实现线程的阻塞和唤醒机制。
- 当有任务需要加入队列时,先对队列进行加锁,然后将任务添加到队列中。
- 通过条件变量通知一个阻塞中的线程来处理任务。当任务添加到队列后,使用条件变量唤醒一个线程,该线程从队列中取出任务并执行。
线程池中线程数量的选择与CPU、IO、并行和并发等因素有关。以下是针对不同情况下线程池大小的一些建议:
- CPU密集型应用:如果任务主要是计算密集型,即需要大量的CPU时间,通常将线程池大小设置为CPU核心数量+1,这样可以保持CPU的高利用率。
- IO密集型应用:如果任务主要是IO密集型,即任务在等待IO操作的时间较长,可以增加线程池的大小,一般建议将线程池大小设置为2倍的CPU核心数量加1。这样可以充分利用CPU的计算能力,同时允许一些线程在等待IO操作时进行并发执行。
- 并行度和响应性需求:线程池的大小还受到任务的并行度和对响应性的需求影响。根据公式最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目,线程等待时间所占比例越高,就需要更多的线程来充分利用等待时间;线程CPU时间所占比例越高,就可以减少线程的数量。考虑到任务等待时间和CPU时间的比例,可以调整线程池的大小以提供最佳的性能。
10.请你说说Linux的fork的作用⭐⭐⭐
- fork是一个系统调用,用于创建新的进程。
- 调用fork会生成一个新的子进程,该子进程几乎与父进程完全相同。
- 子进程从父进程继承了代码、数据、堆栈以及其他进程上下文信息。
- 子进程还会拥有父进程打开的文件描述符和其他系统资源。
- fork的返回值不同:在父进程中,返回子进程的进程ID;在子进程中,返回值为0。
- 通过fork,实现了进程的复制和创建,提高了系统的并发性。
- 父进程和子进程在fork之后会同时执行代码,但由于返回值不同,可以进行逻辑分支选择。
总的来说,Linux的fork系统调用可以复制父进程的状态和资源,创建一个独立的子进程,从而实现并发和多进程编程的能力。