Linux 系统编程(一) —— 基本概念

内核

操作系统内核是指管理和分配计算机资源(CPU、RAM 和设备)的核心层软件。

职责

  • 进程调度: 内核进程调度程序决定了哪些进程可以获得 CPU,每个进程能够获得使用多长时间。
  • 创建和终止进程
  • 内存管理:采用虚拟内存管理机制来在进程之间公平、高效的共享物理内存(RAM)这一有限资源
  • 提供了文件系统:内核在磁盘之上提供了文件系统,允许对文件执行创建、获取、更新以及删除等操作。
  • 对设备的访问
  • 联网
  • 提供系统调用应用编程接口(API)

内核态和用户态

现代处理器架构一般允许 CPU 至少在两种状态下运行,即:用户态和内核态(也成为监管态)。执行硬件指令可使 CPU 在两种状态来回切换。对应的,将虚拟内存区域划分为用户空间部分或内核空间部分。

在用户态下运行时,CPU 只能访问被标记为用户空间的内存。当运行于核心态,CPU 可以访问两种空间内存。

利用硬件设计,将操作系统置于内核管理硬件,确保用户进程不能访问内核指令和数据结构,也无法执行不利于系统运行的操作。

shell

shell: 用户读取用户输入的命令,并执行相应的程序以响应命令,也可以成为命令解释器。

shell 的设计不仅仅是用于人机交互,对 shell 脚本进行解释也是其用途之一。

用户和组

系统对每个用户的身份做唯一标识,用户可隶属多个用户组

用户

系统的每个用户都拥有唯一的登录名和与之对应的整数型用户 ID(UID),还用组 ID,主目录(用户登录后初始的目录),登录 shell(执行以解释用户命令的程序名称)

将用户分为多个组,每个用户可以同时属于同各族,每个组对应系统组文件/etc/group 中的一行记录

目录、链接、文件

内核维护着一套单根目录结构,以放置系统的所有文件,这一目录层级的根基就是名为’/‘ 的根目录。所有的文件和目录都是根目录的“子孙”

Linux 层级目录

文件类型

在文件系统内,会对文件类型进行标记,表明种类。包括:普通数据文件、设备、管道、套接字、目录以及符号链接

“文件”常用来指导任意类型的文件,不仅仅指普通文件。

路径和链接

目录是一种特殊的文件,内容采用表格形式,数据项包括文件名以及对应文件的引用。“文件名+引用”的组合被称为链接。 每个文件都可以有多条链接,因而也可以有多个名称,在相同或不同的目录中出现。

目录可包括文件或其他目录的链接。路径间的链接建立如上图所示的目录层级。

文件的所有权和权限

每个文件都有一个与之相关的用户 ID 和组 ID,分别定义文件的属主和属组。系统根据文件的所有权来判定用户对文件的访问权限。

系统根据3类用户( 属主:文件的用户, 属组:与文件组相匹配的属组成员用户以及其他用户)分别设置3种权限(共计9种权限位)

文件权限

如图,每行文件记录的头部记录的文件的权限。

  • 第一位表文件类型, d 表示目录, - 表示文件
  • 2-4位表所有者权限, 5-7表组用户权限,8-10表其他用户权限
  • 每三位对应 r => 读权限, w => 写权限, x => 执行权限

文件 I/O 模型

UNIX 系统I/O 模型最为显著的特性之一是其 I/O 通用性概念。也就是说,同一套系统调用所执行 I/O 操作,可施之于所有文件类型,包括设备文件,应用程序发起的 I/O 请求,内核会将其转化为相应的文件系统操作,或者设备驱动程序操作,以此来执行针对目标文件或设备的 I/O 操作。简而言之,采用这些系统调用的程序能够处理任何类型的文件。

文件描述符

I/O 系统调用使用文件描述符(往往很小的非负整数)来指代打开的文件,获取文件描述符的常用手法是调用 open(), 在参数中指定 I/O 操作目标文件的路径名。

进程

进程是具有一定独立功能的程序在某个数据集合上的执行过程,是系统进行资源分配和调度的独立单位。简而言之,进程是正在执行的程序实例。

执行程序时,内核会将程序代码载入虚拟内存,为程序变量分配空间,建立内核记账数据结构,记录与进程有关的各种信息(比如,进程 ID、用户 ID、组 ID 以及终止状态等)

在内核看来,进程是一个实体,内核必须在它们之间共享各种计算机资源。对于像内存这种受限资源来说,内核一开始为进程分配一定数量的资源,并在进程生命周期内,统筹该进程和整个系统对资源的需求,对这一分配进行调整。

进程的内存布局

逻辑上将一个进程划分为以下几个部分,也成为段。

  • 文本:程序的指令
  • 数据:程序使用的静态变量
  • 堆:程序可以从该区域动态分配额外内存
  • 栈:随函数调用、返回而增减的一片内存,用于为局部变量和函数调用链接信息分配存储空间。

创建进程和执行程序

父进程通过系统调用 fork()函数来创建一个子进程。内核通过对父进程的复制来创建子进程,子进程获得资源后可自行修改并不会影响父进程。

子进程要么去执行与父进程共享代码段中的另一组不同函数,或者,更为常见的情况是使用系统调用 execve()去加载并执行一个全新程序。

进程ID 和父进程ID

每一个进程都一个唯一的整数型进程标志符PID, 此外,每一个进程还具有一个父进程标识符 PPID 属性,用以标识请求内核创建自己的进程。

进程终止和终止状态

可使用以下两种方式来终止进程:

  1. 进程使用exit()系统调用(或相关 exit()库函数), 请求退出;
  2. 向进程传递信号,将其“杀死”

无论哪种方式退出,进程都会生成“终止状态”, 第一种方式进程会指明自己的终止状态,第二种方式则会根据导致进程“死亡”的信号类型来设置进程的终止状态。

进程的用户和组标识符(凭证)

每个进程都有一组与之相关的用户 ID(UID)和组ID(GID), 通过这两个 ID 来确定对受保护资源的访问权限

特权进程

特权进程是指有效用户 ID为0的进程。通常有内核所施加的权限限制对此类进程无效。由某一个进程创建的进程,也可以是特权进程。

init 进程

系统引导时,内核会创建一个名为 init 的特殊进程,即“所有进程之父”, 该进程相应程序文件为/sbin/init。系统的所有进程不是有 init(使用 fork())“亲自”创建,就是有其后代进程创建。init 进程进程号总为1,且总是以超级用户权限执行,谁都不能杀死 init 进程,只有关闭系统才能终止进程。

init 进程的主要任务使创建并监控系统运行所需的一系列进程。

守护进程

守护进程指的是具有特殊用途的进程,系统创建和处理此类进程的方式与其他进程相同,但具有以下独有特征:

  • “长存”, 守护进程通常在系统引导时启动,直至系统关闭前,会一直“健在”。
  • 守护进程在后台运行,且无控制终端供其读取或写入数据。
坚持原创技术分享