1. 进程
1.1 基本概念
- 课本概念:程序的一个执行实例,正在执行的程序等。
- 内核观点:担当分配系统资源(CPU时间,内存)的实体。
什么是程序的一个执行实例呢,我们在win系统中可以理解为正在运行的一个程序,那程序和进程有什么区别呢?我们可以打开任务管理器查看正在运行的进程。在内核观点中,一个需要系统分配资源(CPU时间,内存)的实体就是一个进程。
我们可以理解为 进程 = 可执行程序+对应的tast_struct(PCB)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f6942345451.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_02](http://img.558idc.com/uploadfile/allimg/centos/c800a6d63ec2538db8c94802939cf104f3be48.gif)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_03](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8fa122959490.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_04](http://img.558idc.com/uploadfile/allimg/centos/19a0e48260346e7a00f388ab01a829f0d3ce13.gif)
1.2 描述进程 - PCB
- 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
- 课程上称为PCB(process control block),Linux操作系统下的PCB是:task_struct
为什么管理进程要有PCB?
因为操作系统要管理进程,必须先描述再组织,因此每个进程都要有PCB(是操作系统管理描述的结构体类型)
我们可以看看Linux内核源代码(2.6版本),这个结构体非常大!
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_05](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f68e5482403.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_06](http://img.558idc.com/uploadfile/allimg/centos/f4d2088454eb06e471f450a4840c24d4f513b9.gif)
task_struct-PCB的一种
- 在Linux中描述进程的结构体叫做task_struct
- task_struct是Linux内核的一种数据结构,他会被装到RAM(内存)里并且包含着进程的信息。
1.3 查看进程
1.3.1 第一种方式
我们在Linux下写一个简单的写循环C代码,我们可以使用这条命令系统进程
ps axj![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_07](http://img.558idc.com/uploadfile/allimg/centos/147d97d374eaba5c76507494a9dd055e2334c1.gif)
C程序
#include <stdio.h>#include <unistd.h>
int main()
{
while(1)
{
printf("这是一个进程\n");
sleep(1);
}
return 0;
}
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_08](http://img.558idc.com/uploadfile/allimg/centos/36a600c591c1496c063386afb34b2252f30e0d.gif)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_09](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f7340c17013.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_10](http://img.558idc.com/uploadfile/allimg/centos/1616e9c42841775b7db82310b84eb2e54242a2.gif)
我们要想查看mytest的进程输入这条指令
ps ajx | grep mytest.c![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_11](http://img.558idc.com/uploadfile/allimg/centos/e21521f10eec7ef49a537837d7b3005933277e.gif)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_12](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f759ee37173.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_13](http://img.558idc.com/uploadfile/allimg/centos/d9be81156badadb73803792aa1c5996fc69907.gif)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_14](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f73b8d88098.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_15](http://img.558idc.com/uploadfile/allimg/centos/c3b456537a19e703c09186c8f8158bef8f8ae7.gif)
1.3.2 第二种方式
进程信息可以通过/proc系统文件夹查看。proc内有当前系统实时的进程信息!
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_16](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8fa779792228.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_17](http://img.558idc.com/uploadfile/allimg/centos/d3a4aae34b613fbbcaf571b2e269ff1cbcdf43.gif)
我们进入proc文件夹,发现内部有许多蓝色的东西,这些是什么呢?
这些蓝色的东西叫做进程的pid,每一个进程在系统中都会存在一个唯一的标识符!就如同我们在学校都有一个唯一的学号,这个标识符就是pid!
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_18](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f8cf8f40860.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_19](http://img.558idc.com/uploadfile/allimg/centos/d617c582477043016c6419e1c8a3a9c830c462.gif)
我们首先通过下面指令找到我们这段C语言的pid
ps ajx | head -1 && ps ajx | grep 'mytest' | grep -v grep![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_20](http://img.558idc.com/uploadfile/allimg/centos/b299c2919dcfa48cd0f076b6a3a1c8f634e263.gif)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_21](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f82c9e10913.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_22](http://img.558idc.com/uploadfile/allimg/centos/87402de69cf9e2b68099603f4bc579687b4312.gif)
此时我们进入proc文件夹 查找pid为2670的进程,我们说proc下是实时的进程信息,因此我们让C语言程序运行起来时,我们应该可以找到这个路径
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_23](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f78a7a70823.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_24](http://img.558idc.com/uploadfile/allimg/centos/480cda618ab156836c023412894a5f4384996e.gif)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_25](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f63b9494942.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_26](http://img.558idc.com/uploadfile/allimg/centos/4326c8c7861523e384765509361835f3ddb20b.gif)
编辑当我们终止C程序时,根据实时查找我们应该无法查找到该路径
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_27](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f92e8a36684.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_28](http://img.558idc.com/uploadfile/allimg/centos/45c0b738756066ea2dc1434a5e15d779b75156.gif)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_29](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f8a0f077094.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_30](http://img.558idc.com/uploadfile/allimg/centos/f36ccc309a05e42446748216ccd1b0b9ba6539.gif)
注:每次程序启动时的进程pid可能不同。
1.4 通过系统调用获取进程标识符
- 进程id(pid)
- 父进程(ppid)
我们在刚刚查看进程时,我们发现pid前还有一个ppid,那么pid 这些东西都在哪里呢?
这些都是进程的内部属性! 属性是数据,因此这些所有东西都在进程的进程控制块中(PCB)tast_struct结构体中。
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_31](http://img.558idc.com/uploadfile/allimg/centos/20120919_63293cef2f2f099420.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_32](http://img.558idc.com/uploadfile/allimg/centos/013dc0948abb4e15035605f5661999d129ec57.gif)
1.4.1 获取进程的pid
我们可以使用man帮助手册查看getpid
man 2 getpid![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_33](http://img.558idc.com/uploadfile/allimg/centos/83ea67680b33329e66c21778c3b8caf00a2be2.gif)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_34](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f60fce89255.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_35](http://img.558idc.com/uploadfile/allimg/centos/024e9d102e1ebb21270315f0cab2907de886e3.gif)
我们通过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;
}
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_36](http://img.558idc.com/uploadfile/allimg/centos/37076141320e9a309b98042e78602dfc35469b.gif)
C程序运行结果
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_37](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f70ee173473.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_38](http://img.558idc.com/uploadfile/allimg/centos/74483d136c83cc13e7c028e2f5f3ff17776d7d.gif)
系统查看PID
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_39](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f558be16116.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_40](http://img.558idc.com/uploadfile/allimg/centos/56ebfa0795e8efaff665010508f04d9e5a3189.gif)
我们在Linux中要终止一个程序可以使用ctrl+C,我们学习了进程之后,如果一旦获取到进程的pid,我们也可以通过kill命令杀掉该进程(其中-9 为9号信号)
kill -9 [进程pid]![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_41](http://img.558idc.com/uploadfile/allimg/centos/472415087fb4b27bd8d362a92fc7c25ab56cd2.gif)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_42](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f7462985022.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_43](http://img.558idc.com/uploadfile/allimg/centos/e2f76bb6782e86ce633546c8f54657c106a4dc.gif)
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_44](http://img.558idc.com/uploadfile/allimg/centos/351e6826488fca7154b711da954093539fec35.gif)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_45](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f6914476058.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_46](http://img.558idc.com/uploadfile/allimg/centos/d160f08926e7a653e1a868c2ff10765cf90bf0.gif)
我们也可以也可以通过指令查一下
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_47](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f725f029728.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_48](http://img.558idc.com/uploadfile/allimg/centos/737f031055aecefb37825177bf2d7b1d7950de.gif)
我们多次启动该进程发现ppid不发生变化,而pid一直在变
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_49](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f8813982536.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_50](http://img.558idc.com/uploadfile/allimg/centos/b67526585df7510a07c272c399ecef74d93794.gif)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_51](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f6aa0412117.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_52](http://img.558idc.com/uploadfile/allimg/centos/c8d948f975816c90902264042758ea7abccabc.gif)
我们使用指令查看一下该进程发现是一个bash。几乎我们在命令行上所执行的所有的指令(你的cmd)都是bash进程的子进程!
ps ajx | head -1 && ps ajx | grep 7670![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_53](http://img.558idc.com/uploadfile/allimg/centos/15e9aa21746a7a09778475784c56358600a5a0.gif)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_54](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f6857e93682.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_55](http://img.558idc.com/uploadfile/allimg/centos/a81c1b008528af957ea02573c609e2fb0eb040.gif)
1.5 创建子进程
我们创建进程有很多方法 现在我们知道./运行程序可以创建,我们也可以通过代码创建子进程fork(),首先我们使用man手册看一下fork的使用方法
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_子进程_56](http://img.558idc.com/uploadfile/allimg/centos/32dbb1e066660a6537d7337188a3701e1797ca.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_57](http://img.558idc.com/uploadfile/allimg/centos/92c9e7602094ff804ce704d8603373bf49d7e2.gif)
fork有两个返回值
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_58](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8fa65ee87772.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_59](http://img.558idc.com/uploadfile/allimg/centos/a35c0a3577c6df2c81f8919493cba4850753b6.gif)
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;
}
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_60](http://img.558idc.com/uploadfile/allimg/centos/07d925207ef840b8eb9019153c641087ace86a.gif)
运行这段程序我们发现hello fork打印了两遍,我们明明在程序中只写了一个printf函数,怎么会有两个hello fork打印结果呢?
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_61](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f77a7193280.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_62](http://img.558idc.com/uploadfile/allimg/centos/d110df590bea7b0e9e4213f58614769ced99b1.gif)
再次我们对代码进行修改
#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;
}
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_63](http://img.558idc.com/uploadfile/allimg/centos/26003d966cce2845021009ca474d88390fa70f.gif)
我们查看这段编译的结果,我们查看id的结果居然都不同!在C语言中怎么可能一个id有两个不同的值呢?这个问题我们暂时回答不了,当我们学习完进程地址空间中再来回答。
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_64](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8f838ab97552.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_65](http://img.558idc.com/uploadfile/allimg/centos/a1d936163bb215dfa91201237976da320caa44.gif)
此时我们知道了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;
}
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_66](http://img.558idc.com/uploadfile/allimg/centos/61f823a03f340c66dae7842898d4b24af78ed4.gif)
我们在C语言中,if和else可以同时进行吗,两个while循环可以同时进行吗?
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_fork_67](http://img.558idc.com/uploadfile/allimg/centos/20120743_63293c8fbbacd54363.png)
![[ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork_父进程_68](http://img.558idc.com/uploadfile/allimg/centos/7402be113421d7dc9fb30987439edb5746970a.gif)
我们通过打印这段代码发现 这些都是可能的!!!
结论:
- 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一定不同)
虽然代码相同,但是可以通过不同的返回值,可以执行不同的代码。
(本篇完)
