什么是进程?
进程是一个正在运行的程序,是加载到内存中的程序实体。
进程与程序相比,具有动态属性。
进程先描述后组织,即进程是先通过进程控制块(PCB)描述,再由操作系统组织和管理的。
PCB 概念 (进程控制块)
PCB(进程控制块)是操作系统中用来描述进程的基本结构,用来存储进程的所有属性。
struct task_struct {
// 该进程的所有属性
// 该进程对应的代码地址和属性地址
};
task_struct
是内核结构体,表示一个内核对象,它将代码和数据与具体的进程关联起来。内核通过 task_struct
管理进程,即通过 PCB 将进程信息与内存中的代码关联,实现进程的组织和调度。
Linux 查看进程的方式
常见的系统进程查看方式
在 Linux 中,命令行上的进程的父进程通常是 bash
。可以通过系统调用函数来查看进程:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
while(1) {
printf("%d\n", getpid());
}
return 0;
}
常见的用于查看父进程的系统调用是 getppid()
:
printf("%d\n", getppid());
另外,通过查看 /proc
文件目录也可以查看当前进程的详细信息。
fork 初识
fork()
是创建子进程的系统调用,返回两个值:父进程返回子进程的 PID,子进程返回 0。父子进程共享代码,但数据独立,各自拥有自己的空间。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
fork();
printf("%d -- | -- %d\n", getpid(), getppid());
return 0;
}
运行效果如下:
chen@chen:~$ ./test
13977 -- | -- 13667
13978 -- | -- 13977
其中 13667 是 bash
进程。
进程状态
进程的状态包括:运行、新建、就绪、挂起、阻塞、等待、停止、死亡。
进程的状态会影响操作系统的调度和管理。一个 CPU 只有一个运行队列,所有进程都需要排队等待 CPU 资源。
static const char * const task_state_array[] = {
"R (running)", // 运行状态
"S (sleeping)", // 浅度睡眠
"D (disk sleep)", // 深度睡眠
"T (stopped)", // 停止
"t (tracing stop)", // 跟踪状态
"X (dead)", // 死亡
"Z (zombie)" // 僵尸状态
};
运行状态:被调度并处于运行队列的进程。
阻塞状态:等待外设响应的进程。
挂起状态:进程数据与代码被交换到外部设备,暂时被挂起。
深度睡眠的进程无法被操作系统直接杀掉,除非断电或等待系统自动恢复。
使用命令 ps axj | head -1 && ps -axj | grep bash
可以查看进程状态。
chen@chen:~$ ps axj | head -1 && ps -axj | grep bash
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
15572 15707 15706 15572 pts/0 15706 S+ 1000 0:00 bash
S+
中的 +
表示前台进程,若无 +
则表示后台进程。
僵尸状态 (Z):子进程退出后,父进程未读取其状态,子进程进入僵尸状态。
孤儿进程:父进程退出后,孤儿进程被 init
进程(PID 1)收养。
进程的优先级
什么是优先级?
进程优先级用于决定任务的调度顺序,某些任务需要优先执行。
Linux 的优先级机制
在 Linux 中,进程的优先级由两个数值决定:PRI 和 NI。PRI 是基本优先级,NI 是 Nice 值。
优先级 = 老的优先级 + NICE
Nice 值的取值范围是 [-20, 19],Nice 值越低,优先级越高。可以使用 top
命令,按 R
键修改优先级。
进程的特性
竞争性:根据优先级高效完成任务。
独立性:多个进程独享资源,互不干扰。
并行:多个进程在多个 CPU 上同时运行。
并发:在一个 CPU 上通过进程切换,在同一段时间内推动多个进程。
进程切换
进程切换时需要保存和恢复进程的上下文。操作系统会在不同进程之间进行切换,保证每个进程的上下文信息正确保存和恢复。