进程
进程:一个正在执行的程序。一个进程有一个 PCB(进程控制块)和一个内存指针(代码和数据),在 Linux 中的进程控制块称为 task_struct 。系统用 PCB 来描述进程的基本情况和活动过程,包含:进程ID、状态、优先级、寄存器内容、内存指针、资源分配信息、IO状态等。
bash
#查看所有进程:
ps ajx
#查看指定进程(进程名一般就是运行的程序名):
ps ajx | grep 进程名
#用来显示进程的第一行,也就是表头信息,并显示processname进程的信息,&&可以理解为同时的意思
ps ajx | head -1 && ps axj | grep 进程名
# 获取进程 id
pid_t getpid(void)
#创建进程,若返回值为 0,则为子进程
fork()
# 终止进程:
kill -9 进程pid
进程状态
在操作系统中,进程状态分为:新建、就绪、运行、阻塞、终止。
新建 :进程刚被创建,在这个阶段,操作系统正在为进程分配资源和初始化PCB等信息。
就绪 :进程已经准备好运行,但还未获得CPU资源,在就绪队列中等待被调度。
运行 :进程正在执行指令。
阻塞 :进程正等待某种事件(I/O 操作)而无法继续执行,等待期间,进程不占用CPU资源。
终止:进程正常执行结束,或由操作系统/用户强制终止。
状态转换条件:
新建到就绪 :进程初始化完成,资源分配完成,进入就绪队列等待调度。
就绪到运行 :被调度程序选中。
运行到阻塞 :等待资源(如I/O请求)
阻塞到就绪 :资源可用(如I/O完成)
运行到就绪 :时间片用完或被更高优先级进程抢占。
运行到终止:进程正常执行结束,或由操作系统/用户强制终止
而在Linux中,进程有以下更具体的状态:
R:运行状态,并不一定在运行,也可能在运行队列中。
S:睡眠状态,进程在等待事件完成。
D:磁盘休眠状态,进程通常会等待IO的结束。
T:停止状态,可以通过发送 SIGSTOP 信号给进程来停止进程,这个被暂停的进程可以通过发送 SIGCONT 信号让其继续。
X:死亡状态,这个状态是一个返回状态,在任务列表中看不到。
Z:僵尸进程,已经运行完毕,但是要维持自己的退出信息,在自己的进程 task_struct 会记录自己的退出信息,未来让父进程来进行读取。
此外还有中孤儿进程,即父进程已经终止,但自身还在运行的进程。
进程优先级
进程优先级是指进程获取某种资源的先后顺序,优先级高的进程有优先执行的权利。可以使用命令 ps -l 查看系统进程:

进程创建
cpp
#include <unistd.h>
pid_t fork();
返回值:子进程中返回0,父进程返回子进程id,出错返回-1
进程调用 fork 后,内核需要:
分配新的内存块和内核数据结构给子进程
将父进程部分数据结构内容拷贝至子进程
添加子进程到系统进程列表中
fork 返回,开始调度
cpp
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
printf("Before: pid is %d\n", getpid());
if ((pid = fork()) == -1)
{
perror("fork()");
exit(1); //进程退出方法
}
if (pid == 0) {
// 子进程逻辑
printf("I am child, PID=%d\n", getpid());
} else {
// 父进程逻辑
printf("I am parent, my child PID=%d\n", pid);
}
sleep(1);
return 0;
}
运行结果:
cpp
Before: pid is 665336
I am parent, my child PID=665337
I am child, PID=665337
进程等待
当子进程退出时,它的PCB不会立即被内核释放,而是保留部分信息,等待父进程完成后续内容(收尸),如果父进程不调用 wait,子进程就变成僵尸进程,僵尸进程会占用进程表项,大量僵尸进程就会导致系统无法创建新进程。
cpp
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
返回值:成功返回被等待进程pid,失败返回-1
参数:输出型参数,获取子进程退出状态,不关心则可以设置为 NULL
pid_t waitpid(pid_t pid, int *status, int options);
返回值:成功返回被等待进程pid,失败返回-1
参数:pid 为等待哪个子进程,status获取子进程退出状态,options控制行为的标志位
wait 和 waitpid 作用:阻塞父进程,直到某个子进程结束;获取子进程的退出状态;通知内核彻底释放子进程资源,避免僵尸进程。
进程程序替换
进程程序替换 是指一个正在运行的进程 用另一个可执行程序的内容覆盖自己的地址空间,并从新程序的 main 函数开始执行。 这个过程不会创建新进程(PID不变)。
应用场景:终端输入 ls,fork 一个子进程,子进程通过 exec 替换成 /bin/ls 程序。或 父进程创建子进程,让子进程替换成目标服务程序。
exec函数:
这些函数调用出错则返回-1,成功则加载新的程序从启动代码开始执行,不会返回。
bash
#include <unistd.h>`
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
l------表示参数采用列表
v------表示参数用数组
p------有p则自动搜索环境变量PATH
e------表示自己维护环境变量
execl("/bin/ls", "ls", "-l", "/home", NULL);
第一个参数:可执行文件的完整路径;
第二个及后续参数:传递给新程序的 argv[0], argv[1], ...;
必须以 (char *)NULL 结尾(表示参数列表结束);
例如:
bash
int main()
{
char *const argv[] = {"ps", "-ef", NULL};
char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-ef", NULL);
// 带p的,可以使用环境变量PATH,无需写全路径
execlp("ps", "ps", "-ef", NULL);
// 带e的,需要自己组装环境变量
execle("ps", "ps", "-ef", NULL, envp);
// 带v的
execv("/bin/ps", argv);
// 带p的,可以使用环境变量PATH,无需写全路径
execvp("ps", argv);
// 带e的,需要自己组装环境变量
execve("/bin/ps", argv, envp);
exit(0);
}