【Linux】进程 | 控制块pcb | task_struct | 创建子进程fork

lvy¯ 2024-07-03 08:37:02 阅读 85

目录

Ⅰ. 进程的概念(Process)

1. 什么是进程?

2. 多进程管理

3. 进程控制块(PCB)

task_struct 的结构

Ⅱ. 进程查看与管理

1. 使用指令查看进程

​编辑

2. /proc 查看进程信息

​编辑

3. 获取进程 ID

4. 创建子进程

​编辑

原因:fork()的 机制

总结

文章手稿:


xmind:

文章手稿可见文末 

 内容:理解“进程”的概念及其在操作系统中的管理,并探讨进程控制块 (PCB) 的重要性及其结构。本文还将介绍如何查看和管理进程,以及如何通过系统调用创建进程。

Ⅰ. 进程的概念(Process)

1. 什么是进程?

进程是一个运行中的程序。当可执行文件被加载到内存中时,该程序就成为了一个进程。

2. 多进程管理

操作系统中可能同时存在大量的进程吗?of course

操作系统需要管理这些进程,以确保系统资源(如CPU时间、内存等)合理分配。管理进程的本质是对进程数据的管理。

我们需要 先描述再组织。(上一章我们讲过)

所以,当一个程序加载到内存时,操作系统做的不仅仅只是把代码和数据加入到内存,

还要管理进程,创建对应的数据结构。

Linux 操作系统的内核是 C 语言写的,描述时就用到struct啦

3. 进程控制块(PCB)

在操作系统中,用于描述进程的结构体称为进程控制块(PCB)。在Linux中,这种结构体称为 <code>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. 使用指令查看进程

 运行

通过下面这些命令,可以方便地查看和管理系统中的进程

指令 含义
<code>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

若需查看特定进程,可以使用 <code>grep 过滤:

$ ps aux | grep 'mytest' | grep -v grep

相当于Windows 下的任务管理器:

2. /proc 查看进程信息

/proc 是一个虚拟文件系统,包含当前系统的实时进程信息。

<code>$ ls /proc

左边蓝色的就是pid

3. 获取进程 ID

每一个进程在系统中,都会存在一个惟一的标识符--pid

我们可以尝试在proc 目录下找到这个 pid ,发现这个18705 目录

ctrl+c 可以发现进程具有实时性

 接下来我们重启来继续研究一下文件的创建和存储

 对代码进行一些改写

<code>#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:指出进程当前的工作路径

可以通过 <code>getpid() 和 getppid() 系统调用获取当前进程和父进程的 ID。

<code>#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 ,我们还可以这么终止进程

<code>$ kill -9 [pid] # 给这个进程发送9号信号

上面看到的ppid 我们也可以来测试一下

<code>#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. 创建子进程

通过 <code>fork() 系统调用可以创建子进程fork() 有两个返回值:父进程返回子进程的 PID,子进程返回 0。

为什么可以返回两个值呢? 

我们可以来测试一下 

<code>#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);

}

}

}

<code>原因:fork()的 机制

fork() 后,父进程和子进程会共享代码,数据则各自独立。通过不同的返回值,可以让父进程和子进程区分不同的执行流,执行不同的代码块。

具体是怎么区分的可见文末手稿中的图解~ 

总结

进程是操作系统中非常重要的概念。通过进程控制块(PCB)对进程进行描述和管理是操作系统的一项重要职责。通过使用各种工具和系统调用,我们可以方便地查看和管理进程,从而确保系统资源的有效利用。

文章手稿:

 

 



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。