一、进程基础知识
1. 进程的定义与特性
**定义**:进程是程序的一次执行过程,是系统资源分配的基本单位
**特性**:
- 动态性:进程是程序的动态执行过程
- 并发性:多个进程可以并发执行
- 独立性:进程拥有独立的地址空间
- 异步性:进程以不可预知的速度向前推进
- 结构性:进程由程序段、数据段和进程控制块(PCB)组成
2. 进程控制块(PCB)
PCB是操作系统管理进程的数据结构,包含:
- 进程标识符(PID)
- 进程状态
- 程序计数器
- CPU寄存器
- CPU调度信息
- 内存管理信息
- 记账信息
- I/O状态信息
二、进程状态与转换
1. 五状态模型
- **创建态(New)**:进程正在被创建
- **就绪态(Ready)**:进程等待被调度
- **运行态(Running)**:进程正在CPU上执行
- **阻塞态(Blocked/Waiting)**:进程等待某事件发生
- **终止态(Terminated)**:进程执行完毕
2. 状态转换触发条件
- **创建→就绪**:进程创建完成,系统资源充足
- **就绪→运行**:调度器选中该进程
- **运行→就绪**:时间片用完或高优先级进程抢占
- **运行→阻塞**:进程请求资源或等待事件
- **阻塞→就绪**:资源可用或等待事件发生
- **运行→终止**:进程正常或异常结束
三、进程控制
1. 进程创建
fork()系统调用
pid_t pid = fork();
- 特点:
- 一次调用,两次返回
- 父进程返回子进程PID,子进程返回0
- 子进程复制父进程的数据空间、堆和栈
- 共享代码段
- 子进程继承父进程的文件描述符、信号处理方式等
exec族系统调用
用于在进程中执行新程序:
- `execl()`、`execlp()`、`execle()`
- `execv()`、`execvp()`、`execve()`
2. 进程终止
- **正常终止**:
- `exit()`函数
- `return`语句(从main函数)
- **异常终止**:
- 接收到未处理的信号
- 段错误或总线错误
3. 进程等待
- **wait()系统调用**:阻塞等待任一子进程结束
- **waitpid()系统调用**:可以等待特定子进程,支持非阻塞模式
4. 特殊进程
- **孤儿进程**:父进程先于子进程结束,子进程被init进程(PID=1)收养
- **僵尸进程**:子进程结束但父进程未调用wait/waitpid回收,占用系统资源
- **守护进程**:在后台运行且不受终端控制的进程,通常以'd'结尾(如httpd)
四、进程调度
1. 调度算法
- **先来先服务(FCFS)**:按进程到达顺序调度
- **最短作业优先(SJF)**:选择执行时间最短的进程
- **优先级调度**:按优先级高低调度
- **时间片轮转(RR)**:每个进程分配一个时间片
- **多级反馈队列**:结合多种调度算法的优点
2. Linux调度器
- **O(1)调度器**:常数时间复杂度
- **完全公平调度器(CFS)**:基于红黑树实现,保证进程公平分配CPU时间
五、进程间通信(IPC)
1. 管道(Pipe)
- **无名管道**:
- 半双工通信
- 只能用于有亲缘关系进程
- 使用`pipe()`系统调用创建
int fd[2];pipe(fd); // fd[0]用于读,fd[1]用于写
- **命名管道(FIFO)**:
- 可用于无亲缘关系进程
- 在文件系统中有路径名
- 使用`mkfifo()`创建
mkfifo("/tmp/myfifo", 0666);
2. 信号(Signal)
- 软中断机制,用于通知进程发生了异步事件
- 常用信号:
- SIGINT (Ctrl+C)
- SIGKILL (无法捕获或忽略)
- SIGSEGV (段错误)
- SIGCHLD (子进程状态改变)
- 信号处理:
- 忽略
- 捕获并处理
- 执行默认操作
3. 消息队列
- 消息的链表,存放在内核中
- 每个消息有特定的类型
- 相关函数:
- `msgget()`:创建或访问消息队列
- `msgsnd()`:发送消息
- `msgrcv()`:接收消息
- `msgctl()`:控制消息队列
4. 共享内存
- 最快的IPC方式
- 多个进程映射同一块物理内存到各自的地址空间
- 相关函数:
- `shmget()`:创建或访问共享内存
- `shmat()`:附加共享内存到进程地址空间
- `shmdt()`:分离共享内存
- `shmctl()`:控制共享内存
5. 信号量(Semaphore)
- 用于进程间同步和互斥
- 解决共享资源访问冲突
- 相关函数:
- `semget()`:创建或访问信号量集
- `semop()`:信号量操作(P/V操作)
- `semctl()`:控制信号量
6. 套接字(Socket)
- 可用于同一主机或不同主机间的进程通信
- 支持多种协议(TCP/UDP等)
- 相关函数:
- `socket()`:创建套接字
- `bind()`:绑定地址
- `listen()`:监听连接
- `accept()`:接受连接
- `connect()`:建立连接
- `send()/recv()`:发送/接收数据
六、线程
1. 线程与进程的区别
- 线程是CPU调度的基本单位
- 同一进程的线程共享代码段、数据段和文件资源
- 每个线程有独立的栈空间和寄存器环境
2. POSIX线程(pthread)
- 创建线程:`pthread_create()`
- 终止线程:`pthread_exit()`
- 等待线程:`pthread_join()`
- 线程同步:
- 互斥锁:`pthread_mutex_t`
- 条件变量:`pthread_cond_t`
- 读写锁:`pthread_rwlock_t`
- 自旋锁:`pthread_spinlock_t`
七、实际应用示例
1. 使用fork创建子进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main() {pid_t pid;printf("进程=%d\n", getpid());pid = fork();if (pid < 0) {perror("fork失败");exit(1);} else if (pid == 0) {// 子进程执行代码printf("子进程:我是子进程,我的PID是=%d\n", getpid());printf("子进程:我的父进程的PID是=%d\n", getppid());exit(0);} else {// 父进程执行代码printf("父进程:我是父进程,我的PID是=%d\n", getpid());printf("父进程:我的子进程的PID是=%d\n", pid);// 等待子进程结束wait(NULL);}return 0;
}
2. 使用管道进行进程间通信
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>int main() {int fd[2];pid_t pid;char buffer[100];// 创建管道if (pipe(fd) == -1) {perror("pipe");exit(1);}pid = fork();if (pid < 0) {perror("fork");exit(1);} else if (pid == 0) { // 子进程close(fd[1]); // 关闭写端read(fd[0], buffer, sizeof(buffer));printf("子进程收到消息: %s\n", buffer);close(fd[0]);exit(0);} else { // 父进程close(fd[0]); // 关闭读端strcpy(buffer, "Hello from parent!");write(fd[1], buffer, strlen(buffer) + 1);close(fd[1]);wait(NULL); // 等待子进程结束}return 0;
}
八、Linux系统调用与库函数
常用系统调用
- `fork()`:创建子进程
- `exec()`:执行新程序
- `wait()/waitpid()`:等待子进程
- `exit()`:终止进程
- `kill()`:发送信号
- `alarm()`:设置定时器
- `sleep()`:进程休眠
- `pause()`:挂起进程直到收到信号
进程信息查看命令
- `ps`:显示进程状态
- `top`:动态显示进程信息
- `pstree`:以树状显示进程
- `kill`:发送信号给进程
- `nice/renice`:调整进程优先级