在内存中的每个进程都有一个属于自己的PID,我们可以通过ps ajx
指令查看进程的基本属性从而查看一个进程的pid,那么这里我们就可以创建一个文件然后往里面写入一个死循环的代码,再创建一个makefile文件在里面实现一些基本的功能,那么这里的操作就如下:
输入make指令并执行新生成的可执行程序就会出现下面的现象:
这时屏幕上就开始不停的打印内容说明我们的进程真正不停的被执行所以这时就可以通过指令ps ajx
查看进程对应的属性:
但是这里显示的进程特别的多,而我们只想查看一个名为myproc的进程,所以这时就可以使用grep指令和管道来查找这里打印出来的内容,这里的指令为:ps ajx| grep 'myproc'
但是这样的查找有一个缺点就是没有显示头部的属性信息,我们无法知道这里的数据代表的是什么意思,所以这里就得将指令改成这样:ps ajx| head -1 && ps ajx|grep 'myproc'
这样显示进程的时候就会将进程的头部信息也显示出来,仔细地观察一下就可以看到myproc进程的pid为26893,但是这样查看进程PID的方法就有点麻烦,所以操作系统就提供了getpid函数这个函数可以直接返回本进程的PID0值(也可以把这里的本进程称之为子进程),并且这个函数没有任何的参数,我们来看看这个函数的介绍:
有了这个函数就可以对之前的文件进行修改,在文件里面调用这个函数并打印这个函数的返回值来显示一个进程的pid,那么修改后的代码如下:
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4 int main()
5 {
6 while(1)
7 {
8 printf("我是一个进程!我的pid为:%d\n",getpid());
9 sleep(1);
10 }
11 return 0;
12 }
重新输入make指令再运行一下可执行程序就会出现下面的现象:
并且跟指令ps ajx| head -1 && ps ajx|grep 'myproc'
显示的值是一样的
那么这就是getpid函数的作用。
getpid的作用是获取子进程的id这个我们都可以理解,当不停的把程序加载进内存的时候子进程的id会不停的发生变化比如说下面的操作:
而getppid的作用就是获取子进程的父进程的id,我们来看看这个函数的介绍:
这个函数没有参数并且该函数的返回值就是父进程的id,所以通过这个函数我们就可以在程序里面获取该进程的父进程的id,那么修改后的代码就如下:
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4 int main()
5 {
6 printf("我是一个进程!我的pid为:%d 我的父进程为:%d",getpid(),getppid());
7 return 0;
8 }
然后不停的执行程序就可以看到下面的现象:
我们发现这里的执行结果就与上面的有一些不一样:子进程的id在不停的发生变化,但是该进程的父进程的id却一直没有发生改变此时父进程的id为32282那这是为什么呢?我们退出服务器重新登录再运行一下这个程序看看结果又是什么样的?
此时父进程的id发生了改变变成了8368,而且我们再创建一个文件
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 int main()
5 {
6 printf("我是另外一个进程我的id为:%d 我的父进程为:%d",getpid(),getppid());
7 }
并生成可执行程序打印这个进程的父进程id时我们依然会发现这个id是8368
那这是为什么呢?这个8368到底是谁啊?那么为了解决这个问题我们可以通过指令
ps ajx| head -1 && ps ajx|grep 8368
来查找一下这个id对应的进程的名字是什么?那么这里的查找结果如下
这个PID对应的进程叫做bash。之前我给大家讲过一个故事说张三特别喜欢如花但是张三的情商特别的低不善于和女生沟通所以张三就找当地一个特别著名的媒婆:王婆,王婆受到了张三的请求跑去找了如花可是如花一个跟别人与子偕手白头到老了,所以王婆也没有办法完成张三的请求可是张三这个人脑子一根筋他非如花不娶而且他的爸爸是村长,他就用着他爸爸的权力强迫王婆完成她的请求,王婆一看这有点进退两难啊,所以王婆就不停的招聘实习生让实习生去跟如花说好话,一旦这个实习生没有完成任务或者放弃任务了王婆就将这个实习生炒鱿鱼并招聘其他的实习生来继续完成这个任务,实习生有没有崩溃实习生有没有完成任务都和王婆本人没有任何的关系,这样王婆既不用与如花直接接触又可以逃避村长和张三的强迫,那么这里的王婆就是bash这里的实习生就是我们在命令行上面执行的各种可执行程序,可执行程序是否运行成功可执行程序是否存在问题都不会影响到bash的稳定,因为这里的程序是以子进程的方式执行,子进程的父进程是bash也就是命令行,我们可以使用kill -9指令来杀死父进程看看有什么什么反应:
我们可以看到当杀死了父进程bash之后就直接断开了连接让我们重新登录,而再次登录的时候就会发现父进程的pid又发生了变化,也就是说我们每次登录机器的时候操作系统都会给我们分配一个新的shell或者说是bash再或者说是命令行,而命令行上启动的进程,一般它的父进程没有特殊情况的话都是bash。
那么这就是父进程希望大家能够理解
我们上面解释了什么是子进程什么是父进程,而fork函数的功能就是在父进程中创建子进程,我们来看看fork函数的介绍:
啊这里的内容就非常的多大家就只用知道几点就行,首先这个fork函数是没有参数的并且该函数有个返回值,学习这个函数的第一步就是如何证明fork函数创建了子进程,我们来看看下面的代码:
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4 int main()
5 {
6 printf("我是一个进程!我的pid为:%d 我的父进程为:%d\n",getpid(),getppid());
7 return 0;
8 }
将其运行一下就可以看到屏幕上只打印了一行语句:
但是此时我们将程序进行修改在打印语句前面调用一次frok函数看看结果会变成什么样:
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4 int main()
5 {
6 fork();
7 printf("我是一个进程!我的pid为:%d 我的父进程为:%d\n",getpid(),getppid());
8 return 0;
9 }
再次运行一下程序就可以看到这里打印出来了两句话,并且这两句话的内容是不一样的
那这就能够说明我们这段程序在执行的过程中创建了一个子进程,并且在子进程中也执行了printf语句,可是这里就有个问题,该程序在创建子进程的时候是从代码一开始创建的就创建了子进程还是将fork函数运行完就创建了子进程的呢?所以为了解决这个问题就可以将文件中的代码进行修改在fork函数之前再添加一个printf函数,那么这里的代码如下:
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4 int main()
5 {
6 printf("hello world\n");
7 fork();
8 printf("我是一个进程!我的pid为:%d 我的父进程为:%d\n",getpid(),getppid());
9 return 0;
10 }
如果hello world被打印了一次就说明父进程是在调用fork函数的时候创建的子进程,如果hello world被打印了两次就说明子程序的创建应该是在父进程刚开始被执行的时候,那么上述代码执行的结果就如下:
这就说明子进程的创建是在调用fork函数的时候,并且当fork函数创建子进程之后会有两个进程来执行fork函数之后的代码所以这里就会打印两次我是一个进程。看了上面的函数介绍我们可以知道一件事就是fork函数会有一个返回值,而且父进程的返回值和子进程的返回值是不一样的,父进程会把子进程的id作为fork函数的返回值,而子进程会把0作为父进程的返回值,比如说下面的代码:
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4 int main()
5 {
6 int i=fork();
7 printf("fork函数的返回值为%d\n",i);
8 return 0;
9 }
这段代码的执行结果为
我们可以看到这里打印了两个数据其中一个为0,那么这个0就是子进程fork函数的返回值,非0就是父进程的fork函数的返回值,这就是fork函数的特性,该函数虽然只调用了一次但是却有两个返回值,子进程的fork永远都返回0父进程的fork永远都返回子进程的pid,所以我们就可以根据父子进程的fork函数的返回值来让父子进程执行接下来的不同的代码比如说下面的代码:
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4 int main()
5 {
6 int i=fork();
7 int a=10;
8 int b=20;
9 if(i!=0)
10 {
11 printf("我是父进程\n");
12 printf("a+b的结果为:%d\n",a+b);
13 }
14 else
15 {
16 printf("我是子进程\n");
17 printf("b-a的结果为:%d\n",b-a);
18 }
19
20 return 0;
21 }
这段代码的运行结果如下:
我们可以看到这里虽然创建了一个子进程但是子进程和父进程却在各自的程序中根据fork函数的返回值执行者不同的代码,那么这就是fork函数的返回值的意义希望大家能够理解。