学习书籍:
- 《深入理解linux内核》
记录重点、还未掌握、个人理解以及有意思的
书本对应linux版本的源码下载地址: http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/v2.6/
本章多是一些基本概念的补全,在操作系统的基本知识层面,后面会探究其原理实现
主要差异见书: P9, 这里记一下重点:
clone()
系统调用处理(你敢想象clone
命令居然不在POSIX
标准内?),而其他类Unix都是基于内核线程(一个能够被内核独立调度的执行环境)类Unix操作系统不给用户程序直接访问物理硬件的权利,在其中加入了一层软件集合,也就是操作系统或者内核
随之而来的就是对引入两种不同的执行模式:用户程序的非特权模式和内核的特权模式,也就是问题所述的两种状态
两种体系结构:
微内核程序的优劣可以总结为:
Linux的设计:
目标是达到微内核的许多优点并不影响效率(俗话说就是通吃)
做法:
Linux提供了模块的概念,模块是一个目标文件,其可以在Linux内核运行时链接加载也可以解除链接(模块是块砖,哪用往哪搬)
模块的目标代码是一组函数构成的,这些函数代码逻辑实现了内核上层的功能,例如文件系统、驱动程序等等
注意,模块与微内核的上层实现操作系统功能的进程不同,其不是进程,相反其类似于其他静态链接的内核函数
总结:就是替换了微内核的系统进程为一系列按需链接的模块静态代码
优点:
结论:并不会
一个文件被打开后,系统调用会创建一个被打开文件的文件对象并返回文件描述符,一个文件对象可能由同一个进程的多个文件描述符描述
多个进程打开同一个文件,Unix文件系统会给每个进程分配单独的文件对象和单独的文件描述符,并且不提供任何形式的同步机制
但是当前会在上层提供文件被打开的提示(
.swap
),如果写入的话也会提示“在读取到现在此文件已经被修改是否覆盖写入“
如果想要同步可以通过flock()系统调用实现进程在整个文件或者部分文件上的I/O
操作实施同步
不管是read还是write默认都是顺序访问,会用文件指针将本次的访问的结束位置保存用于下一次的继续顺序访问
文件指针保存在哪?=> 放在打开的文件对象中
不会改变,只是特权模式的改变
一个进程在某一个时刻只能处于内核模式和用户模式二者之一
参考内核的体系结构
内核本身不是一个进程而是进程的管理者,其可以包含一些内核进线程或特权进程,因为这些进程/线程始终在内核态运行伴随着系统关闭而关闭,所以其运行依赖的内核程序属于内核,所以其也属于内核
简单来说就是:内核不单单是一个内核态进程,而是系统进程的管理者,其也包含了管理所需要的一些内核线程或特权进程
以一个系统调用为例子:
补充:
在等待,准确的说在不同事件的等待队列中(内核提供的),内核通过进程描述符的队列实现这些等待不同事件的idle进程
可重入性表示内核提供了多个进程同时进入内核态执行的条件,目前所有的Unix系统都是支持的
保证可重入性的主要问题/挑战在哪?
内核如何去实现这样的可重入性呢?=> 内核同步
内核控制路径(kernel control path
) 表示内核处理系统调用、异常、中断的指令序列(个人理解就是一段程序整个生命周期的执行路线,这个程序也包括内核程序)
内核控制路径的交错:当用户态进程发生系统调用时,内核会通过控制路径证实执行下去无法立即完成要等待,所以此时就会发生进程切换,内核又开始执行其他的控制路径
内核控制路径的嵌套:当一个进程(启用了中断内核路径)正在执行当前的内核控制路径时此时发生了中断,CPU会优先响应中断开始执行另一个指令序列,执行完毕后恢复第一个控制路径继续执行,这样嵌套的整体过程在同一个进程上下文,并且中断处理的时间会算在该进程的系统CPU运算总时间中(很神奇,这并不是这个进程该干的活)
每个内核控制路径都引用它自己的私有内核栈
所以,进程系统调用、CPU处理中断、异常、进程优先级抢占时内核都会切换到另一个的控制路径即另一个的一段指令序列
可以将内核想象成为一个服务器,硬件中断、多个用户态进程的系统调用、异常都可以看作是对服务器的请求,都需要内核来处理,所以内核不是按顺序执行的而是交错运行的,并且要保证同步安全
几乎差不多,都是通过底层的原子性操作支持实现上层同步,方式包括锁、信号量等等,只不过在内核代码设计中可能要加上内核前缀 : ),例如内核信号量
内核将IPC看作一个资源来实现,类似于文件,IPC资源是不变的需要创建者手动释放
fork的父子进程结构中有两个指针,分别指向本进程的父进程与子进程,如此产生联系
我们都知道父进程不合适的处理子进程的SIGCHLD
话会导致子进程一直占用资源导致僵尸进程,这个信号是子进程在调用_exit()
系统调用时,内核会释放该进程的所有资源并发送此信号给其父进程(如果你想问怎么找到父进程的,请看上一个问题)
说到僵尸进程,那么孤儿进程又是如何产生的呢?
当一个进程调用_exit()
系统调用的时候会除了通知父进程(如果有),还是将其所有现有自己子进程的进程描述符,改为指向init
系统特殊进程,成为他的孩子,从而成为孤儿
如字面意思,其表示一系列进程的组合,可以抽象为“作业(Job
)”,类似于数据库中理解的事务
每个进程描述符中有一个进程组ID(PGID
)的字段,对于一个进程组来说其ID就是领头进程的PID
新创建的进程默认会被插入其父亲的进程组中
登陆会话(login session
)
补充来自:
- https://www.cnblogs.com/sparkdev/p/12146305.html
一般在linux中就是shell session
, 就是已经开始会话的那个终端进程以及其所有子进程
一个进程组的进程必须在同一个会话中
一个会话组的所有进程都有相同的SID
, 该值就是领头进程的PID
一个会话中可能会有多个进程组并且同时处于活跃状态,那么同一时刻只能有一个组在前台(意味着改组可以访问终端),其他组在后台,可以通过bg/fg内部命令切换
前台后台的意义?
- 一个进程组或者说Job在前台就代表着终端的所有输入(包括信号等)全部交给其处理,其输出也直接发送到控制终端上
- 一般来说一个终端对应一个
session
,每新打开一个终端就是新开启了一个session
进程、进程组、session
之间的关系:
可以说session
是进程组的集合
Unix操作系统都采用了请求调页(demand paging
)的内存分配策略,也就是在页还没有加载到内存时就先开始执行,随后导致异常,MMU内存管理单元开始处理加载内存
类似的延迟处理也在调用malloc
分配堆空间的时候,调用时只是修改了进程的堆内存区大小,等到真正使用的时候产生异常再分配页框
同样的fork的CoW机制也是如此,被复制的子进程拥有同样于父进程的页映射(指向同一个物理页框),并且这些页框都标记为只读,一旦写入就会发生异常错误,随后初始化新的页框