1

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork

 1 year ago
source link: https://blog.51cto.com/xingyuli/5692086
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1.1 基本概念

  • 课本概念:程序的一个执行实例,正在执行的程序等。
  • 内核观点:担当分配系统资源(CPU时间,内存)的实体。

什么是程序的一个执行实例呢,我们在win系统中可以理解为正在运行的一个程序,那程序和进程有什么区别呢?我们可以打开任务管理器查看正在运行的进程。在内核观点中,一个需要系统分配资源(CPU时间,内存)的实体就是一个进程。

我们可以理解为 进程 = 可执行程序+对应的tast_struct(PCB)

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork
[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_03

1.2 描述进程 - PCB 

  • 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
  • 课程上称为PCB(process control block),Linux操作系统下的PCB是:​ ​task_struct​

为什么管理进程要有PCB?

因为操作系统要管理进程,必须先描述再组织,因此每个进程都要有PCB(是操作系统管理描述的结构体类型)

我们可以看看Linux内核源代码(2.6版本),这个结构体非常大!

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_05

task_struct-PCB的一种

  • 在Linux中描述进程的结构体叫做task_struct
  • task_struct是Linux内核的一种数据结构,他会被装到RAM(内存)里并且包含着进程的信息。

1.3 查看进程

1.3.1 第一种方式

我们在Linux下写一个简单的写循环C代码,我们可以使用这条命令系统进程

ps axj
#include <stdio.h>
#include <unistd.h>

int main()
{
while(1)
{
printf("这是一个进程\n");
sleep(1);

}
return 0;
}
[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_09

我们要想查看mytest的进程输入这条指令

ps ajx | grep mytest.c

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_12

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_14

1.3.2 第二种方式

进程信息可以通过/proc系统文件夹查看。proc内有当前系统实时的进程信息!

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_16

我们进入proc文件夹,发现内部有许多蓝色的东西,这些是什么呢?

 这些蓝色的东西叫做进程的pid,每一个进程在系统中都会存在一个唯一的标识符!就如同我们在学校都有一个唯一的学号,这个标识符就是pid!

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_18

我们首先通过下面指令找到我们这段C语言的pid 

ps ajx | head -1 && ps ajx | grep 'mytest' | grep -v grep

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_21

此时我们进入proc文件夹 查找pid为2670的进程,我们说proc下是实时的进程信息,因此我们让C语言程序运行起来时,我们应该可以找到这个路径

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_23

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_25

​编辑当我们终止C程序时,根据实时查找我们应该无法查找到该路径

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_27

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_29

注:每次程序启动时的进程pid可能不同。

1.4 通过系统调用获取进程标识符

  • 进程id(pid)
  • 父进程(ppid)

我们在刚刚查看进程时,我们发现pid前还有一个ppid,那么pid 这些东西都在哪里呢?

这些都是进程的内部属性! 属性是数据,因此这些所有东西都在进程的进程控制块中(PCB)tast_struct结构体中。

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_31

1.4.1 获取进程的pid

我们可以使用man帮助手册查看getpid

man 2 getpid
[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_34

 我们通过C程序查看pid

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(){
while(1)
{
printf("这是一个进程 pid = %d \n",getpid() );
sleep(1);
}
return 0;
}

C程序运行结果 

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_37

系统查看PID

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_39

我们在Linux中要终止一个程序可以使用ctrl+C,我们学习了进程之后,如果一旦获取到进程的pid,我们也可以通过kill命令杀掉该进程(其中-9 为9号信号)

kill -9 [进程pid]
[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_42

1.4.1 ppid

我们首先获取一个ppid,我们使用C程序来打印

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(){
while(1)
{
printf("这是一个进程 pid = %d,ppid = %d \n",getpid(),getppid());
sleep(1);
}
return 0;
}
[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_45

我们也可以也可以通过指令查一下

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_47

我们多次启动该进程发现ppid不发生变化,而pid一直在变

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_49
[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_51

我们使用指令查看一下该进程发现是一个bash。几乎我们在命令行上所执行的所有的指令(你的cmd)都是bash进程的子进程!

ps ajx | head -1 && ps ajx | grep 7670
[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_54

1.5 创建子进程

我们创建进程有很多方法 现在我们知道./运行程序可以创建,我们也可以通过代码创建子进程fork(),首先我们使用man手册看一下fork的使用方法

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_56

fork有两个返回值

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_58

fork函数是用来创建子进程的,它有两个返回值

父进程返回子进程的pid,给子进程返回0

首先我们写一段C程序来验证一下fork函数有两个返回值

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
pid_t id = fork();

printf("hello fork\n");
sleep(1);

return 0;
}

运行这段程序我们发现hello fork打印了两遍,我们明明在程序中只写了一个printf函数,怎么会有两个hello fork打印结果呢?

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_61

再次我们对代码进行修改

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
pid_t id = fork();

printf("hello fork! id = %d\n",id);
sleep(1);

return 0;
}

我们查看这段编译的结果,我们查看id的结果居然都不同!在C语言中怎么可能一个id有两个不同的值呢?这个问题我们暂时回答不了,当我们学习完进程地址空间中再来回答。

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_64

此时我们知道了id为0是子进程,id大于0是父进程,此时我们写一段程序来查看一下

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
pid_t id = fork();

//id:0子进程 >0是父进程
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);
}
}
//binprintf("hello fork! id = %d\n",id);
// sleep(1);
return 0;
}

我们在C语言中,if和else可以同时进行吗,两个while循环可以同时进行吗?

[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_67

我们通过打印这段代码发现 这些都是可能的!!!

结论:

  • fork之后,父进程和子进程会共享代码,一般会执行后续的代码 -- printf为什么会打印两次的问题
  • fork之后,父进程和子进程返回值不同,可以通过不同的返回值,判断,让父子执行不同的代码块!

为什么fork()给父进程返回子进程的pid,给子进程返回0?

在现实生活中,父亲:儿子 = 1:n(n>=1)父亲为了区别儿子,会给儿子起不同的名字。类比到这里就是父进程必须有标识子进程的方案,fork之后,给父进程返回子进程的pid!

子进程最重要的是要知道自己被创建成功了,因为子进程找父进程的成本非常低(直接getppid())

为什么fork()会返回两次?

fork()函数是OS提供的系统调用接口(OS system call),fork之后,OS做了什么?父进程有自己的tast_struct+父进程的代码和数据,创建进程系统多了一个进程,子进程也应该有自己的tast_struct+子进程的代码和数据。

子进程的tast_struct 对象内部的数据从哪里来呢?--》基本是从父进程继承下来的。子进程执行代码,计算数据的,子进程的代码从哪里来呢?--》 子进程和父进程执行同样的代码,父子进程代码共享!而数据要各自独立(比如pid一定不同)

虽然代码相同,但是可以通过不同的返回值,可以执行不同的代码。

(本篇完)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK