alin的学习之路: 面试题 操作系统相关
alin的学习之路: 面试题 操作系统相关
-
进程与线程的区别
-
进程是资源分配的最小单位,线程是程序执行的最小单位。
-
进程有自己独立的地址空间,线程共享地址空间
-
进程:无名管道、有名管道、信号、共享内存、消息队列
线程:互斥量、读写锁、自旋锁、线程信号、条件变量、信号量 -
线程有四种状态:1.新生状态 2.可运行状态 3. 被阻塞状态 4.死亡状态
-
-
多进程与多线程区别,应用场景
-
多进程要使用
fork()
函数进行创建,多线程要使用pthread_create()
函数进行创建 -
开销方面:每个进程都有独立的代码和数据空间(程序上下文),进程之间切换开销大;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
-
所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)
-
内存分配:系统为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源
-
包含关系:线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程
-
-
volatile和原子变量的区别
-
proc文件系统
-
自旋锁与普通锁的区别
1.读写锁: 有三个状态:读加锁状态、写加锁状态、不加锁状态。
读写锁可以支持高并发,其中一个原因就是 只能允许一个线程占用写状态锁,但是多个线程可以共享读状态锁。当写状态锁被占用的时候,后续想要写状态的线程就会被阻塞。所以读写锁非常适合那些 读状态 远远多于 写状态的应用场景。
特点:
1.多个读线程可以同时进行读
2.写线程必须互斥,不能同时读同时写。
3.写线程优先于读线程,先写加锁线程之后才能读加锁线程。
2.互斥锁:访问共享资源时,允许一个线程对资源区进行操作,其他线程即为堵塞睡眠状态,直到被唤醒。
特点:一次只能一个线程拥有互斥锁,其他线程只有等待。
3.自旋锁:
自旋锁是一种特殊的互斥锁,当资源被加锁后,其他想要访问的线程不会被阻塞而是陷入循环等待状态,循环检查资源持有者是否已经释放了资源,这样的好处是节省了线程从休眠到被唤醒的开销,但同时也会一直占据CPU的资源。所以自旋锁适合多核CPU、共享资源区被加锁的时间短的情况。但是还有一个问题是当自旋锁递归调用的时候会造成死锁现象。所以慎重使用自旋锁。
4.乐观锁、悲观锁:一种思想:当线程去访问数据的时候,如果认为其他线程 不会修改数据,那么就不会对数据资源上锁,但是在更新数据的时候会去判断以下其他线程是否修改了数据。通过版本来判断,如果数据被修改了就拒绝更新,这就是乐观锁,即不上锁。悲观锁反之,它会对数据加锁,保证数据的正确性。
特点:以上两种锁多用于数据库,当读操作远远大于写操作的时候,乐观锁的效率会很高,会增加数据库的吞吐量。 -
虚拟内存
- mmap 将一个文件映射到进程的虚拟地址空间,实现了文件磁盘地址与进程虚拟地址的一一对应关系,实现这样的关系之后,进程就可以通过指针的方式去读写这一段内存,避免了通过write/read 函数,(write/read函数会形成多次拷贝,内核压力过大)。相反,内也可以通过映射直接修改数据,从而实现不同进程之间对该内存空间的通信,这也就是进程之间共享内存的通信方式。
- mmap 优势:
- 对文件的读取跨过了页缓存,减少了数据的拷贝次数,提高了文件的读取效率
- 共享内存通信方式提供了一种不管什么进程都可以通信的方式,父子进程等都可以。
-
进程的内存分布
-
-
栈内存为什么由系统自动分配和释放
- 栈内存空间一般较小,在程序中的局部变量和全局变量声明是直接由系统赋予内存空间,但是当某些变量所需内存空间较大,则有程序员手动申请和释放空间,而这些空间一般为堆内存。
-
守护进程如何创建
- 创建子进程,父进程退出(必须)
- 所有工作在子进程中进行形式上脱离了控制终端
- 在子进程中创建新会话(必须)
- setsid()函数
- 使子进程完全独立出来,脱离控制
- 改变当前目录为根目录(不是必须)
- chdir()函数
- 防止占用可卸载的文件系统
- 也可以换成其它路径
- 重设文件权限掩码(不是必须)
- umask()函数
- 防止继承的文件创建屏蔽字拒绝某些权限
- 增加守护进程灵活性
- 关闭文件描述符(不是必须)
- 继承的打开文件不会用到,浪费系统资源,无法卸载
- 开始执行守护进程核心工作(必须)
守护进程退出处理程序模型
- 创建子进程,父进程退出(必须)
-
进程间的通信方式及其区别,应用场景
-
匿名管道pipe:使用简单,父子进程间的简单通信可以使用。
-
命名管道fifo:使用fifo文件,可以在不相干的两个进程之间进行通信。
-
消息队列:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
-
信号signal:传递的信息少。
-
共享存储映射mmap:通信的数据映射到磁盘文件中,可以反复使用。
-
套接字socket:多用于网络通信。分为本地套接字和网络套接字。
-
共享内存:通过多个进程共享同一块内存中的数据从而达到通信的目的,是速度最快的通信方式,需要配合信号量机制实现进程同步。
共享内存的缺点:基于内存,只能在同一主机内使用,不能应用与分布式系统当中。
-
-
死锁条件和解除
-
死锁:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
原理:当一组进程或者线程都在等待某个事件的产生,而且只有该组进程/线程才能出发该事件,这就称这组进程、线程发生了死锁。死锁产生的四个必要条件:
1.互斥,某种资源只允许一个进程访问,另一个进程想要访问该资源必须等待阻塞。
2.占有并等待,当某个进程本身占有资源,同时它又在等待其他线程释放的某种其他的资源,这就造成了这两个线程出现相互等待的现象。
3.不可抢占,产生第2条的条件之一就是两个进程之间不可抢占被别人占据的资源,也即是互斥条件
4.循环等待,存在一个进程链,使得每一个进程都占有下一个进程所需要的至少一种资源
-
处理死锁的思路
-
预防死锁
破坏死锁的四个必要条件中的一个或多个来预防死锁。
-
避免死锁
和预防死锁的区别就是,在资源动态分配过程中,用某种方式防止系统进入不安全的状态。
-
检测死锁
运行时出现死锁,能及时发现死锁,把程序解脱出来
-
解除死锁
发生死锁后,解脱进程,通常撤销进程,回收资源,再分配给正处于阻塞状态的进程。
-
-
预防死锁的方法
-
破坏请求和保持条件
-
破坏不可抢占条件
-
破坏循环等待条件
-
-
-
进程调度方式
-
先来先服务算法:FCFS ,按照顺序执行
-
短作业优先算法:SJF 按照任务时间,先执行时间短的
-
最高响应比优先法:以上两种的综合算法,提高效率
-
时间片轮转法:采用剥夺方式,每个进程分配一个固定的时间段,按照队列中的顺序执行
-
多级反馈队列:UNIX采用这种算法。算
-
-
对编译连接的理解
-
编译是从源文件检查语法错误并生成汇编文件的过程。
-
连接:寻找需要使用的库的代码并连接,并且为程序生成一个启动代码。
-
-
共享内存实现原理
-
共享内存可以说是最有用的进程之间的通信方式,两个进程之间的共享内存的意思是,同一块的物理内存被映射到进程A/B各自的进程地址空间,A/B都可以随时看到共享内存中数据的更新。
Linux中,通过吧一块内存分别映射到不同的进程空间中实现进程间通信,实际中需要联合同步、互斥机制来一起完成。 -
共享内存实现机制 :
一、mmap机制:在磁盘上建立一个文件,每个进程存储器中,单独开辟一个空间来映射
保存到实际硬盘,实际并没有反映到主存上
优点:存储量大
缺点:读取和写入速度比较慢
mmap() 是一个系统调用函数,本质是一种进程虚拟内存的映射方法,可以将一个文件、一段物理内存或者其它对象映射到进程的虚拟内存地址空间。实现这样的映射关系后,进程就可以采用指针的方式来读写操作这一段内存,进而完成对文件的操作,而不必再调用 read/write 等系统调用函数了。 -
常规的文件操作为了提高读写效率和保护磁盘,使用了页缓存机制。这样造成读文件时需要先将文件页从磁盘拷贝到页缓存中,由于页缓存处在内核空间,不能被用户进程直接寻址,所以还需要将页缓存中数据页再次拷贝到内存对应的用户空间中。这样,通过了两次数据拷贝过程,才能完成进程对文件内容的获取任务。写操作也是一样,待写入的 Buffer 在内核空间不能直接访问,必须要先拷贝至内核空间对应的主存,再写回磁盘中(延迟写回),也是需要两次数据拷贝。
共享内存通过零拷贝技术,直接将磁盘文件拷贝到内核缓存中,然后进程通过共享内存直接读取,整个过程只有一次拷贝,所以从效率上来说是比传统读写高的。 -
二、shm机制:每个进程的共享内存都直接映射到实际物理存储器上
shm保存到物理存储器(主存),实际的存储量直接反映到主存上
优点:进程间访问速度比磁盘快
缺点:存储量不能非常大
-
-
僵尸进程是什么,如何处理
- 子进程先于父进程结束,父进程还没有结束后的自动回收子进程,并且父进程没有手动对子进程进行回收,此时该子进程被称为僵尸进程。
- 处理方法:注册一个SIGCHLD信号处理函数,当收到SIGCHLD信号时说明子进程的状态发生了改变,通常是子进程结束,此时执行
waitpid()
回到掉子进程即可。
-
自旋锁在单cpu与多cpu下的使用
-
Linux上的自旋锁有三种实现:
- 在单cpu,不可抢占内核中,自旋锁为空操作。
- 在单cpu,可抢占内核中,自旋锁实现为“禁止内核抢占”,并不实现“自旋”。
- 在多cpu,可抢占内核中,自旋锁实现为“禁止内核抢占” + “自旋”。
-
自旋锁在单核处理器中容易造成CPU的瘫痪
-
-
用户态与内核态
-
用户态是在用户内存空间,内核态是在内核的内存空间
-
-
3G-4G大部分是共享的,是内核态的地址空间。这里存放整个内核的代码和所有的内核模块以及内核所维护的数据。
计算机系统中通常运行两种程序,一种是系统程序,一种是应用程序。
即用户态和内核态,但是在程序运行通常会在这两种状态中切换。为了区分操作级别,X86 cpu架构采用了 0 - 3 四种特权阶级,其中 0 级为最高特权。
特权指令:对内存空间的访问范围基本不受限制,不仅能访问用户存储空间,也能访问系统存储空间,特权指令只允许操作系统使用,不允许应用程序使用,否则会引起系统混乱。
非特权指令:一般应用程序所使用的都是非特权指令,它只能完成一般性的操作和任务,不能对系统中的硬件和软件直接进行访问,其对内存的访问范围也局限于用户空间。 -
用户态切换到内核态的唯一途径——>中断/异常/陷入
内核态切换到用户态的途径——>设置程序状态字
当进行I/O操作的时候,由内核态到用户态的拷贝转移是极其耗费 cpu 资源的,零拷贝是追求的目标。
-
-
Linux 的文件存储系统
- 最小单位是扇区,每个扇区512字节,
- 按块读取,每个块有八个扇区,即 4Kb,
- inode 即索引节点,其中包含了文件的大小,创建等基本信息