简要记录整理下。

内核的职责

  • 进程调度:Linux 属于抢占式多任务操作系统,由内核按照一定规则分配调度CPU给程序使用。
  • 内存管理:物理内存属于有限资源,需要内核公平、高效的在进程间共享这一资源。Linux也采用了虚拟内存管理机制,有两方面优势:
    • 进程与进程之间,进程与内核之间彼此隔离。
    • 只需将进程的一部分保持在内存中(虚拟内存管理以“页”为单位,每个程序任一时刻只需部分分页驻留物理内存中,未使用的页保存在交换区,仅在需要的时候载入物理内存中),此举能降低每个进程的内存需求量。能在RAM中加载更多的进程,使得在任一时刻,CPU中都至少一个进程可以执行,充分利用CPU资源。
  • 提供文件系统: 允许对文件创建、获取、更新、以及删除操作。
  • 创建和终止进程: 内核可将新程序载入内存,并为其提供所需的资源,进程执行完毕,内核还要保证
  • 对设备的访问: 提供访问外设的接口,并管理外设资源。
  • 联网
  • 提供系统调用应用编程接口(API):进程可通过系统调用请求内核去执行各种任务。

内核态和用户态

之前一直对这两个名词不太明白,如书中所述,这是须由硬件支持的功能。执行硬件指令可以在内核态和用户态之间切换,只有在内核态才能执行一些特定操作。这里引入了CPU Rings术语,Ring通常是由硬件强制的,Intel cpu支持4个Ring级别。Linux只用了其中两个:Ring0 拥有最高权限,用户进程跑在Ring3。Ring1和2并没有在Linux中使用。若一个用户进程想要执行更高权限操作,它必须将它的意图从Ring3传到Ring0,Ring0层即内核对它的参数进行检查,然后执行一系列操作,即进行了一次系统调用。 参考:https://stackoverflow.com/questions/6710040/cpu-privilege-rings-why-rings-1-and-2-arent-used https://zh.wikipedia.org/zh-hans/%E5%88%86%E7%BA%A7%E4%BF%9D%E6%8A%A4%E5%9F%9F

用户和组

系统密码文件/etc/passwd为每个用户都定义有一行记录:

1
2
3
4
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin

冒号分割的几项分别是

  • 登录名
  • 密码:x 表示加密后的密码保存在/etc/shadow)
  • 用户ID
  • 组ID
  • User ID info:关于此用户的其他额外注释信息,比如用户全名、电话号码等
  • 主目录:用户登录后所居于的初始目录
  • 登录shell:执行以解释用户命令的程序名称

一个用户可以同时属于多个组,每个用户组都对应着/etc/group中的一行记录.

1
2
3
4
5
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:syslog,x

每一项分别为:

  • 组名
  • 组密码
  • 组ID
  • 用户列表:隶属于改组的用户登录名列表

进程

进程是正在执行的程序实例,执行程序时,内核会把程序载入虚拟内核,为程序变量分配空间,建立内核记账数据结构,以记录与进程有关的各种信息如进程ID、用户ID、组ID以及终止状态。

进程的内存布局

  • 文本: 程序的指令
  • 数据: 程序使用的静态变量
  • 堆: 程序可从该区域动态分配额外内存
  • 栈: 用于为局部变量和函数调用链接信息分配存储空间

创建和执行程序

  • 系统调用fork()可以创建一个新进程,内核通过复制父进程来创建子进程,内存中标记为只读的程序文本段由父子进程共享,子进程从父进程处继承的数据段、栈段及堆段的副本后,可以修改,不影响父进程。
  • execve() 会去加载并执行一个全新的程序,execve()会销毁现有的文本段,数据段、栈段及堆段,并根据新程序的代码,创建新段来替换它们。

进程终止

  • 使用_exit()系统调用退出。
  • 向进程传递信号,将其“杀死”。 无论以何种方式终止,进程都会生成“终止状态”,一个非负小整数,可供父进程的wait()系统调用检测。第一种情况下终止状态可以通过传给_exit()参数指定,第二种情况会根据信号类型设置终止状态。终止状态为0表示进程“功成身退”,非0表示有错误发生。 大多数shell会将嗯前一执行程序的终止状态保存于shell变量$?中。

资源限制

可以使用系统调用getrlimit获取或设定资源使用限制,setrlimit设置资源使用限制,由fork()创建的新进程,会继承其父进程对资源的限制的设置。 具体可调整的资源见:https://linux.die.net/man/2/setrlimit

内存映射

调用系统函数mmap()的进程会在其虚拟地址空间中创建一个新的内存映射。 有两种:

  • 文件映射:将文件的部分内容映射入调用进程的虚拟内存,映射一旦完成,对文件映射内容的访问则转换成对对应内存融内容的访问,映射页面会按需从文件中加载。
  • 匿名映射,无文件与之对应,其映射页面会初始化为0。

进程间通信机制:

  • 信号 用来表示事件发生

  • 管道 及shell中 “|” 操作符和FIFO 管道的特点:

    • 只能单向通信
    • 只能血缘关系的进程进行通信
    • 依赖于文件系统
    • 生命周期随进程
    • 面向字节流的服务
    • 管道内部提供了同步机制 命名管道 FIFO:FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中。命名管道是一个设备文件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。
  • 套接字

  • 文件锁定

  • 消息队列,用于在进程间交换消息(数据包)

  • 信号量(semaphore),用来同步进程动作

  • 共享内存

进程组

  • 进程组中的每个进程都具有相同的进程组标识符,,其实际为进程组组长的进程ID。内核可对进程中的所有成员执行各种动作,尤其是信号的传递。

会话

  • 会话由多个进程组构成,会话是由其中的进程建立的,该进程叫做会话的领导进程(session leader)。会话领导进程的PID成为识别会话的SID(session ID)。会话中的每个进程组称为一个工作(job)。会话可以有一个进程组成为会话的前台工作(foreground),而其他的进程组是后台工作(background)。每个会话可以连接一个控制终端(control terminal)。当控制终端有输入输出时,都传递给该会话的前台进程组。由终端产生的信号,比如CTRL+Z, CTRL+\,会传递到前台进程组。

会话的意义在于将多个工作囊括在一个终端,并取其中的一个工作作为前台,来直接接收该终端的输入输出以及终端信号。 其他工作在后台运行。