Linux 内核(二)
进程管理
进程的定义
- 进程:程序关于某数据集合的一次运行活动,是系统进行资源分配和调度的独立单位。
PCB(进程控制块)
- 操作系统管理和控制进程的数据结构,具体实现是task_struct。
- 包括以下信息:
- 进程标识与关系信息:
- PID:非负整数,进程/线程的唯一标识,一般情况为顺序编号。
- TGID:非负整数,线程组的唯一标识,主线程上等于PID。
- PGID:非负整数,同一组进程的标识,子进程上会继承父进程的PGID。
- SID:非负整数,会话领导进程的PID,如终端shell的PID。
- 进程状态信息:
- state:进程状态枚举,生命周期中所处的阶段。
- exit_state:进程结束状态,正常退出还是异常。
- flags:标志位,记录多个布尔类型的状态。
- 调度信息:优先级、调度策略、调度参数等。
- 内存管理信息:如页表、内存空间布局等。
- CPU上下文信息:如寄存器状态。
- 进程资源信息:如打开文件、信号量、共享内存等。
- 信号处理信息:如待处理信号队列、信号处理函数等。
进程状态
- 抽象看包括:
- 运行状态:进程正在CPU上运行。
- 就绪状态:进程已经具备运行条件,只是等待CPU调度。
- 阻塞状态:进程在等待某个事件发生(如I/O操作、获取资源等)。
- 创建状态:进程正在被创建。
- 结束状态:进程已经执行完毕,占有资源将被回收。
进程状态转化图:
进程的创建
fork
- fork(分叉):创建一个与原进程几乎完全相同的进程,新进程被称为原进程的子进程。
- 写时复制:子进程复制了父进程的task_struct,虚拟内存指向与父进程相同的物理内存,只有当子进程对虚拟内存进行写操作时,才会将父进程的虚拟内存复制一份,并修改其内容。
clone
- clone(克隆):选择性地继承父进程的资源,可以选择与父进程共享一个虚拟空间,从而创造线程。
进程上下文切换
- 进程地址空间切换:
- 页表基址寄存器:设置为新进程的一级页表地址。
- TLB:TLB条目记录进程的ASID(地址空间标识),减少切换空间时的TLB失效。
- 处理器状态切换:
- CPU寄存器会保存在PCB的CPU上下文结构中,切换时快速恢复。
进程调度
- 定义:从就绪进程中挑选进程分配CPU资源。
- 目标:
- 公平性:所有进程都能得到合理时间。
- 效率:最大限度利用CPU,减少空闲。
- 响应时间:确保交互式进程能及时响应。
- 策略:
- 先来先服务:按到达就绪队列的先后顺序调度。
- 短进程优先:优先调度执行时间短的。
- 时间片轮转:按时间片获得时间,强制切换。
- 优先级调度:按nice值,越小优先级越高。
- CFS(完全公平调度)
- Linux内核广泛采用的基于虚拟运行时间的公平调度模型。
- 使用红黑树动态维护进程优先级。
- 每次调度时选择红黑树最左边(虚拟运行时间最小)的进程执行。
- 虚拟运行时间=实际运行时间/权重,nice值越小权重越大。
进程间通信
信号
- 信号:信号是进程间通信和控制的一种方式,用于通知接收进程某个事件已经发生。
- 信号编号:唯一标识,通过kill -l查看所有信号。
- 信号类型:与信号编号一一对应的逻辑名称。如:
- SIGINT:中断进程,来自终端如Ctrl+C。
- SIGKILL:立即杀死进程,不能被捕获或忽略。
- SIGTERM:正常结束进程,可以被捕获或忽略。
- SIGQUIT:退出并生成core文件,可以被捕获或忽略。
- SIGSTOP:暂停进程,不能被捕获或忽略。
- SIGCONT:继续运行暂停的进程,不能被捕获或忽略。
- SIGUSR1/SIGUSR2:用户自定义信号。
- 信号处理方式:每个信号都有默认处理方式。如:
- 终止进程:如SIGKILL
- 忽略信号:如SIGCHLD,通知父进程有子进程退出。
- 产生core文件:如SIGQUIT,core是核心转储文件,包含内存和寄存器的内容。
- 自定义处理函数:通过signal或sigaction注册处理函数。
- 信号传递机制:
- kill命令行:如kill -SIGKILL PID。
- kill函数:传入PID和信号类型。
- raise函数:向自身进程发送信号。
- sigqueue:向进程发送信号,带参数,搭配sigaction使用。
管道
- 在内核中创建一个缓冲区,多个进程通过读写该缓冲区实现通信。
- 特点:
- 数据只能单向流动。
- 管道没有数据时,读取的进程会阻塞。
- 管道放满数据时,写入的进程会阻塞。
- 管道容量受内核限制。
- 打开管道的所有进程终止时,管道自动消失。
- 匿名管道:
- 只能用于具有亲缘关系的进程间通信。
- 命令行管道符“|”表示前面进程的输出作为后面进程的输入。
- 命名管道:
- 通过mkfifo函数创建,需指定管道文件路径和读写权限。
共享内存
- 共享内存:多个进程将同一块物理内存映射到自己的虚拟空间实现数据共享。
- 特点:
- 速度最快。
- 生命周期随内核。
- 不带同步和互斥。
消息队列
- 消息队列:由内核提供的支持多进程访问的队列。
- 特点:
- 生命周期随内核。
- 自带同步和互斥。
信号量
- 信号量:用于多进程对共享资源进行互斥访问。
- 本质:通过计数器控制对共享资源的访问次数。
- 原理:内核的计数器、等待队列、自旋锁。
套接字
- 套接字:用于相同或不同主机上进程之间的通信。
- 原理:通过TCP或UDP协议进行通信。
网络管理
- 网络协议栈
- linux 内核网络协议栈只涉及l2/l3/l4层。
- 接收时,从l2网络设备驱动程序,传递到l3,再到l4。
- 发送时相反。
- 通信过程
- TCP 和 UDP 协议通信都是通过操作系统的 Socket 来实现。
- 服务端调用 socket()函数,指定网络协议和传输协议创建 Socket。
- 服务端调用 bind()函数,将 Socket 绑定到 IP 地址和端口号。
- 可能存在多个网卡,所以需要指定 IP 地址。
- 服务端调用 listen()函数,将 Socket 设置为监听模式,等待客户端连接。
- 可以通过 netstat 命令查看端口号是否被监听。
- 服务端调用 accept()函数,从内核获取客户端连接,如果没有连接则阻塞。
- 客户端调用 socket()函数,指定网络协议和传输协议创建 Socket。
- 客户端调用 connect()函数,传入 IP 地址和端口号,向服务端发起连接请求。
- 客户端与服务端通过三次握手建立TCP 连接。
- 双方通过read()和write()函数读写数据。