Linux(操作系统)面经——part2

发布时间:2023年12月19日

1、请你说说进程和线程的区别

1.进程是操作系统资源分配和调度的最小单位,实现操作系统内部的并发;线程是进程的子任务,cpu可以识别、执行的最小单位,实现程序内部的并发。

2.一个进程最少有一个线程或有多个,一个线程只能属于一个进程,线程依赖于进程存在

3.进程有独立的地址空间,线程有自己的堆栈和局部变量,多个线程可以共享同一地址空间。

4.进程的创建、切换、销毁的系统开销都远大于线程。创建或销毁进程时,系统要分配或回收相应的地址空间和io资源。切换时要保存整个cpu环境,还要配置新环境。而线程切换只保留少量寄存器内容。

5.线程间的通信方式很简单,因为它们共享了地址空间。但是要保证互斥、同步的操作保证数据的一致性。

6.一般一个进程挂掉并不会影响别的进程,但是一个线程挂掉,它所在的进程也会挂掉。

2、请你说说线程和协程的区别(了解)

?1. 线程是操作系统的资源,线程的创建、切换、停止等都非常消耗资源,而创建协程不需要调用操作系统的功能,编程语言自身就能完成,所以协程也被称为用户态线程,协程比线程轻量很多;

?2. 线程在多核环境下是能做到真正意义上的并行,而协程是为并发而产生的;

?3. 一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行;

?4. 线程进程都是同步机制,而协程则是异步;

?5. 线程是抢占式,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力;

?6. 操作系统对于线程开辟数量限制在千的级别,而协程可以达到上万的级别。

3、请你说说线程的同步(通信)方式

因为多线程共享了同一片地址空间,所以线程间通信很简单,只需要将数据复制到共享(全局或堆)变量中即可。不过要考虑同步和互斥,保证数据的一致性。用到的技术有:

1.信号:Linux 中使用 pthread_kill() 函数对线程发信号

2.互斥锁、读写锁、自旋锁。

互斥锁mutex——保证同一时间只能有一个线程访问共享资源,当锁被占用时别的线程试图加锁都会进入阻塞状态(释放cpu资源使其从运行进入阻塞状态,当锁被释放时,调度哪个线程取决于内核的调度)。

读写锁rwlock——读共享,写互斥。写模式加锁时,其他不论读写试图加锁都阻塞。读加锁时,读进程不阻塞,写进程阻塞。

自旋锁spinlock——自旋锁上锁受阻时它不会阻塞,而是循环中轮询是否能获得锁,所以它不涉及线程切换所以没有切换开销,但是它霸占了cpu会浪费cpu资源。所以自旋锁适合并行结构(多处理器),或者锁被短时间持有又不希望有线程切换开销的情况。

3.条件变量:条件变量始终与互斥锁一起使用。条件变量以原子的方式阻塞进程直到达到某一特定条件,对条件的判断也是在互斥锁的保护下进行的。

4.信号量:信号量实际是一个非负的整数计算器,用于对公共资源的控制。公共资源增加信号量加1,减少减1。并且只有信号量的值大于0的时候才可访问。

p是对信号量的值进行原子减一,代表获取资源,当信号量的值为0时,p操作会阻塞,意味着资源不可用。

v操作是对信号量的值进行原子加一,代表释放资源,v操作从不阻塞。

4、请你说说互斥锁和自旋锁(参考3)

5、请你说说写时拷贝

fork :在进行 fork 复制进程时,并不马上进行父进程的地址空间的完全拷贝。而是使用了写时拷贝 (Copy-On-Write )技术,即就是让父进程和子进程共享父进程的页面,当父进程或子进程中任意一个 进程试图修改某个页面时,再将其拷贝一份给子进程。这样可以延迟页面拷贝,提高 fork 复制的效 率。另外,通常 Linux 中的新进程都是通过 fork+exec 实现的,如果 fork 后需要执行 exec 那么直接就不需要拷贝了。
这里有一个小细节: fork 之后内核会将子进程排在队列的前面,以让子进程先执行,以免父进程执行导 致写时复制,而后子进程执行 exec 系统调用,因无意义的复制而造成效率的下降。
vfork :除了子进程必须要立刻执行一次对 exec 的系统调用,或者调用 _exit( ) 退出,对 vfork( ) 的成功调 用所产生的结果和fork( ) 是一样的。 vfork( ) 会挂起父进程直到子进程终止或者运行了一个新的可执行文 件的映像。通过这样的方式,vfork( ) 避免了地址空间的按页复制。在这个过程中,父进程和子进程共享 相同的地址空间和页表项。实际上vfork( ) 只完成了一件事:复制内部的内核数据结构。因此,子进程也 就不能修改地址空间中的任何内存。

6、请你说说分段和分页

1、段主要为了内存独立/隔离,有利于共享;

2、页与物理内存映射,解决碎片问题,提升效率;

3、找到段,找到页表起始地址,找到页帧号,得到物理地址

7、请你介绍一下 I/O 多路复用

io复用使得一个程序能监听多个文件描述符,提高程序的性能。

Linux系统实现io复用的技术有select,poll,epoll

8、说一说 select 、poll、epoll

1. 用户态将文件描述符传入内核的方式
2.
select :创建 3 个文件描述符集并拷贝到内核中,分别监听读、写、异常动作。这里受到单个进程可以打开的fd 数量限制,默认是 1024
poll :将传入的 struct pollfd 结构体数组拷贝到内核中进行监听。
epoll :执行 epoll_create 会在内核的高速 cache 区中建立一颗红黑树以及就绪链表 ( 该链表存储已经就绪 的文件描述符) 。接着用户执行的 epoll_ctl 函数添加文件描述符会在红黑树上增加相应的结点。
内核态检测文件描述符读写状态的方式
select :采用轮询方式,遍历所有 fd ,最后返回一个描述符读写操作是否就绪的 mask 掩码,根据这个掩码给fd_set 赋值。 poll :同样采用轮询方式,查询每个 fd 的状态,如果就绪则在等待队列中加入一项并继续遍历。epoll :采用回调机制。在执行 epoll_ctl add 操作时,不仅将文件描述符放到红黑树上,而且也注册了回调函数,内核在检测到某文件描述符可读/ 可写时会调用回调函数,该回调函数将文件描述符放在就绪链表中。
3. 找到就绪的文件描述符并传递给用户态的方式
select :将之前传入的 fd_set 拷贝传出到用户态并返回就绪的文件描述符总数。用户态并不知道是哪些文件描述符处于就绪态,需要遍历来判断。
poll :将之前传入的 fd 数组拷贝传出用户态并返回就绪的文件描述符总数。用户态并不知道是哪些文件描述符处于就绪态,需要遍历来判断。
epoll epoll_wait 只用观察就绪链表中有无数据即可,最后将链表的数据返回给数组并返回就绪的数量。内核将就绪的文件描述符放在传入的数组中,所以只用遍历依次处理即可。这里返回的文件描述符是通过mmap 让内核和用户空间共享同一块内存实现传递的,减少了不必要的拷贝。
4. 重复监听的处理方式
select :将新的监听文件描述符集合拷贝传入内核中,继续以上步骤。
poll :将新的 struct pollfd 结构体数组拷贝传入内核中,继续以上步骤。
epoll :无需重新构建红黑树,直接沿用已存在的即可。
epoll 更高效的原因
1. select poll 的动作基本一致,只是 poll 采用链表来进行文件描述符的存储,而 select 采用 fd 标注位来存放,所以select 会受到最大连接数的限制,而 poll 不会。
2. select poll epoll 虽然都会返回就绪的文件描述符数量。但是 select poll 并不会明确指出是哪些文件描述符就绪,而epoll 会。造成的区别就是,系统调用返回后,调用 select poll 的程序需要遍历监听的整个文件描述符找到是谁处于就绪,而epoll 则直接处理即可。
3. select poll 都需要将有关文件描述符的数据结构拷贝进内核,最后再拷贝出来。而 epoll 创建的有关文件描述符的数据结构本身就存于内核态中,系统调用返回时利用mmap() 文件映射内存加速与内核空间的消息传递:即epoll 使用 mmap 减少复制开销。
4. select poll 采用轮询的方式来检查文件描述符是否处于就绪态,而 epoll 采用回调机制。造成的结果就是,随着fd 的增加, select poll 的效率会线性降低,而 epoll 不会受到太大影响,除非活跃的socket很多。
5. epoll 的边缘触发模式效率高,系统不会充斥大量不关心的就绪文件描述符虽然 epoll 的性能最好,但是在连接数少并且连接都十分活跃的情况下,select poll 的性能可能比 epoll 好,毕竟 epoll 的通知机制需要很多函数回调。

9、请你说一说虚拟内存与物理内存

1. 物理内存 以前,还没有虚拟内存概念的时候,程序寻址用的都是物理地址。程序能寻址的范围是有限的,这取决于 CPU 的地址线条数。比如在 32 位平台下,寻址的范围是 2^32 也就是 4G。并且这是固定的,如果没有虚拟内存,且每次开启一个进程都给 4G 物理内存,就可能会出现很多问题: - 因为物理内存是有限的,当有多个进程要执行的时候,都要给 4G 内存,很显然内存不够,这很快就分配完了,于是没有得到分配资源的进程就只能等待。当一个进程执行完了以后,再将等待的进程装入内存。这种频繁的装入内存的操作效率很低 - 由于指令都是直接访问物理内存的,那么任何进程都可以修改其他进程的数据,甚至会修改内核地址空间的数据,这是不安全的

2. 虚拟内存 由于物理内存有很多问题,所以出现了虚拟内存。虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。

10、请你介绍一下死锁,产生的必要条件,产生的原因,怎么预防死锁

?死锁:两个或几个进程在运行时由于资源争夺处于互相等待的过程,如无外力干涉,它们都不会有改变。

产生的原因:系统资源不足、资源分配不合理、进程运行推进方式不合理

必要条件:

互斥条件——一个资源每次只能给一个进程使用

请求与保持条件——一个进程在请求资源时,对已获得的资源保持不变

不可剥夺条件——对于已获取资源的进程,不可强行剥夺其资源

循环等待条件——若干进程处于一种头尾相接的循环等待的过程

11、请你说说条件变量

条件变量是线程同步的一种方式,这种方式下有两种状态的线程:等待条件变量成立而挂起的线程和条件变量成立。为了避免竞争,所以条件变量总是和互斥锁一起使用。

条件变量可以以原子的方式阻塞线程,直到满足条件变量为真为止。

条件变量为假时线程阻塞并以原子方式释放等待条件变化的互斥锁。当另一个线程条件改变时,该线程可能会向相关原子变量发出信号,于是几个处于等待的线程将会唤醒,获取互斥锁,然后评估条件。

文章来源:https://blog.csdn.net/qq_60012598/article/details/134957306
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。