目录
Ⅰ. 进程的概念(Process)
1. 什么是进程?
2. 多进程管理
3. 进程控制块(PCB)
task_struct 的结构
Ⅱ. 进程查看与管理
1. 使用指令查看进程
2. /proc 查看进程信息
3. 获取进程 ID
4. 创建子进程
原因:fork()的 机制
总结
文章手稿:
内容:理解“进程”的概念及其在操作系统中的管理,并探讨进程控制块 (PCB) 的重要性及其结构。本文还将介绍如何查看和管理进程,以及如何通过系统调用创建进程。
文章手稿可见文末
Ⅰ. 进程的概念(Process)
1. 什么是进程?
进程是一个运行中的程序。当可执行文件被加载到内存中时,该程序就成为了一个进程。
2. 多进程管理
操作系统中可能同时存在大量的进程吗?of course
操作系统需要管理这些进程,以确保系统资源(如CPU时间、内存等)合理分配。管理进程的本质是对进程数据的管理。
我们需要 先描述再组织。(上一章我们讲过)
所以,当一个程序加载到内存时,操作系统做的不仅仅只是把代码和数据加入到内存,
还要管理进程,创建对应的数据结构。
Linux 操作系统的内核是 C 语言写的,描述时就用到struct啦
3. 进程控制块(PCB)
在操作系统中,用于描述进程的结构体称为进程控制块(PCB)。在Linux中,这种结构体称为 task_struct
。
task_struct
的结构
struct task_struct {
volatile long state;
void *stack;
atomic_t usage;
unsigned int flags;
unsigned int ptrace;
unsigned long ptrace_message;
siginfo_t *last_siginfo;
int lock_depth;
// ... 其他属性
};
task_struct
包含进程的所有属性数据,如进程状态、优先级、程序计数器、内存指针、上下文数据、I/O状态信息和记账信息等。
操作系统对进程的管理,最终变成了对链表的增删查改。
什么是进程?目前为止我们可以总结成:进程 = 可执行程序 + 该进程对应的内核数据结构
操作系统不相信任何人的,不会直接暴露自己的任何数据结构,代码逻辑,其他数据相关的细节。
想做系统是通过 系统调用 的方式,对外提供接口服务的。
下面我们将来学习一些系统接口
Ⅱ. 进程查看与管理
1. 使用指令查看进程
运行
通过下面这些命令,可以方便地查看和管理系统中的进程。
指令 | 含义 |
---|---|
ps a | 显示现行终端机下的所有程序,包括其他用户的程序 |
ps -A | 显示所有程序 |
ps c | 列出程序时,显示每个程序真正的指令名称 |
ps -e | 显示所有程序 |
ps e | 列出程序时,显示每个程序所使用的环境变量 |
ps f | 用ASCII字符显示树状结构,表达程序间的相互关系 |
ps -H | 显示树状结构,表示程序间的相互关系 |
ps -N | 显示所有的程序,除了执行ps指令终端机下的程序之外 |
ps s | 采用程序信号的格式显示程序状况 |
ps S | 列出程序时,包括已中断的子程序资料 |
ps -t <终端机编号> | 指定终端机编号,并列出该终端机的程序状况 |
ps u | 以用户为主的格式来显示程序状况 |
ps x | 显示所有程序,不以终端机来区分 |
ps -l | 显示详细PID信息 |
通过指令如 ps
和 top
可以查看系统中的进程信息。例如,使用 ps aux
可以显示系统中所有的进程:
$ ps aux
若需查看特定进程,可以使用 grep
过滤:
$ ps aux | grep 'mytest' | grep -v grep
相当于Windows 下的任务管理器:
2. /proc 查看进程信息
/proc 是一个虚拟文件系统,包含当前系统的实时进程信息。
$ ls /proc
左边蓝色的就是pid
3. 获取进程 ID
每一个进程在系统中,都会存在一个惟一的标识符--pid
我们可以尝试在proc 目录下找到这个 pid ,发现这个18705 目录
ctrl+c 可以发现进程具有实时性
接下来我们重启来继续研究一下文件的创建和存储
对代码进行一些改写
#include <stdio.h>
#include <unistd.h>
int main(void) {
FILE* fp = fopen("log.txt", "w"); // 若不存在就创建之
while (1) {
printf("I am m a process!\n");
sleep(1);
}
}
C语言专栏中讲到过,fopen 后面如果不带路径,那么会默认在当前路径。
所谓的当前路径,其本质 —— 当前进程所在的路径
进程会自己维护,进程会知道自己的工作路径在哪里:
- exe:指出进程对应的可执行程序的磁盘文件
- cwd:指出进程当前的工作路径
可以通过 getpid()
和 getppid()
系统调用获取当前进程和父进程的 ID。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void) {
printf("PID: %d, PPID: %d\n", getpid(), getppid());
return 0;
}
运行可以看到:
除了ctrl+c ,我们还可以这么终止进程
$ kill -9 [pid] # 给这个进程发送9号信号
上面看到的ppid 我们也可以来测试一下
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void) {
while (1) {
printf("I am m a process! , pid: %d, ppid: %d\n",getpid(), getppid());
sleep(1);
}
}
4. 创建子进程
通过 fork()
系统调用可以创建子进程。fork()
有两个返回值:父进程返回子进程的 PID,子进程返回 0。
为什么可以返回两个值呢?
我们可以来测试一下
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void) {
pid_t id = fork();
if (id == 0) {
// 子进程
while (1) {
printf("我是子进程,我的pid: %d,我的父进程是 %d\n", getpid(), getppid());
sleep(1);
}
} else {
// 父进程
while (1) {
printf("我是父进程,我的pid: %d,我的父进程是 %d\n", getpid(), getppid());
sleep(1);
}
}
}
原因:fork()的
机制
fork()
后,父进程和子进程会共享代码,数据则各自独立。通过不同的返回值,可以让父进程和子进程区分不同的执行流,执行不同的代码块。
具体是怎么区分的可见文末手稿中的图解~
总结
进程是操作系统中非常重要的概念。通过进程控制块(PCB)对进程进行描述和管理是操作系统的一项重要职责。通过使用各种工具和系统调用,我们可以方便地查看和管理进程,从而确保系统资源的有效利用。
文章手稿: