目录
前言:
1. 父子进程创建
2. 进程状态
R(running)状态:
S(sleep)状态:
D(disk sleep)状态:
T(stopped)状态:
X(dead)和Z(zombie)状态:
孤儿进程:
本篇主要讲解关于关于父子进程的生成,以及利用fork生成子进程返回两个不同的返回值的概念,以及不同进程在不同反应下的状态的演示。
操作系统会为我们提供一些系统接口服务于我们的程序,而今天我们又会看到一个新的函数接口用于使用,那就是fork()。
这个函数的在#include
1 #include
2 #include
3 #include
4
5 int main()
6 {
7 pid_t ret = fork();
8 while(1)
9 {
10
11 if(ret == 0)
12 {
13 printf("我是子进程,我的PID是%d,我的PPID是%d,ret = %5d,&ret = %p。\n"
14 ,getpid(),getppid(), ret,&ret);
15 sleep(1);
16 }
17 else
18 {
19 printf("我是父进程,我的PID是%d,我的PPID是%d,ret = %5d,&ret = %p。\n" 20 ,getpid(),getppid(),ret,&ret);
21 sleep(1);
22 }
23 //while(1)
24 //{
25 // printf("hello world\n");
26 // sleep(1);
27 //}
28 }
29
30 return 0;
首先咱们可以看到我们通过fork()生成了一个子进程,由ret来接收它的返回值,但是大伙可能不知道ret其实接收的不只是子进程的返回值,还有当前进程的返回值,请看下图。
可以看到父子进程的ret变量的地址是一模一样的,但是有没有发现这个地址却对应了两个值?子进程的返回值是0,而父进程是一个非0的值。
这很明显不符合我们学习这么长时间代码的规律——一个地址只能存取一个数据。那么这里是系统出BUG了吗?很显然不是的,要是这是一个BUG我也不会为大家讲解了。
那么会出现这个问题的原因其实简单来说就是,我们在程序中获取到的地址,并不是数据存储的真实位置,这是一个伪地址,操作系统会偷偷的给ret找两个存储空间,这一过程被称为写时拷贝,我会在下一篇“进程地址空间”中详细讲解,下面请先看图。
首先我们得知道进程是有独立性的,而进程又是由于内核相关的数据结构 + 代码和数据所构成,那么我们也得保证代码、数据和数据结构也的是独立得才行,所以在同一块地址需要存储两个数据时就不能放在同一块空间,从而出现写时拷贝。
从程序运行图当中,我们还能看到两个东西PID(Process ID)和PPID(Parent Process ID),也就是当前进程的ID和它的父进程ID。通过函数getpid()和getppid()可以得到。
进程ID也就相当于我们得身份证,标明这个进程存在,可以通过这个ID找到它,同时也可以通过这个ID对它进行修改等操作。
我们为了弄明白正在运行得进程时什么意思,我们需要知道进程得不同状态。一个进程可以有几个状态。请先看状态在kernel源码中的定义:
/*
* The task state array is a strange "bitmap" ofreasons to sleep. Thus "running" is zero, and *you can test for combinations of others with simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
R——运行状态,我问大家一个问题,当我们的电脑中有多个软件都被运行着,那么这些软件是否在每一时刻都被CPU运行着呢?
答案是不是,还记得我们的PCB吗,CPU基于PCB去管理这些进程,而管理方式为将这些PCB结构体用特定的数据结构组织起来,例如链表方式,所以CPU在运行这些进程的时候会拿到这些进程的PCB,从而执行程序,那么此时咱们就可以联想到,CPU运行进程其实是拿到PCB,运行一会,中断,拿到下一个PCB在运行,再中断,周而复始,那么运行状态的出现其实是这个进程要么在运行,要么处于运行队列当中。
虽然这样说,但是我们是没有实际感受的,因为我们平时用电脑也没有看到一个程序一会关掉,一会又开始的情况,对此我只能说,CPU的运行速度确实厉害。
不过大家却遇到过另一个问题,那就是电脑卡死的现象,这一现象与我们下一个状态S有关。
在理解S状态之前我们需要先明白阻塞的概念,阻塞顾名思义,也就是当前进程没有继续推进,在等待某种资源获取成功。
那么某种资源是什么呢?举一个简单的例子大家就会懂了:
我们在网上下载游戏或者影视资源时需不需要联网?肯定是需要的,但是在下载的时候,我们将网线给他拔了,又或者是把WIFI给砸了,那么这个时候电脑处于没有网络的状态,他还能继续下载吗?当然是不能的,毕竟没有网嘛,那么此时我们的进程就在等待着网络资源,CPU就不会将他放在运行队列当中,而给它挂上S状态,让它自己去等。
我们运行的进程当中STAT下的S+就是睡眠状态,也可以叫做阻塞状态。不过大家可能有些奇怪,我不是将程序运行起来了吗?网好好的,也没个键盘输入啥的,为什么不是R状态而是S状态呢?有这个疑问的朋友可能忘记了一件事情,那就是将数据打印到显示器上有没有等待显示器准备完毕的资源信息呢?一定是有的咯,所以我们基本看不出来有程序处于R状态,一般都是在S状态。
D状态是S状态的另外一种表现形式,我们也称为不可中断睡眠状态,挂起。这个概念不好理解,我还是以讲故事的形式为大家解释。
我们有CPU,内存,磁盘三个人,某一天磁盘中有程序要运行,然后操作系统就将程序加载到内存当中,然后呢,这个程序要下载100个G的数据进磁盘当中,操作系统说:“好,你下载吧”,然后程序就呆在内存当中挂一个S状态等着。此时,电脑开了很多进程,CPU忙的不可开交,这个时候他就看到了内存里有个程序啥都不干,就在那里呆着。CPU气不过,说:“我都快要忙死了,你还在这里干扰我?去死吧你。”然后这个进程就被CPU杀死了。
这个时候,磁盘内容已经下好了,却发现找不到人来拿了,这就把磁盘急坏了,他又不敢删,但是又不知道应该把这个数据交给谁,问题就出现了。
上面的故事谁错了呢?其实它们都没有错,错的是这个世界。哈哈,开个玩笑,它们确实都没有做错,操作系统可以杀掉进程,这是它的权力,进程在等待资源,也是它的权力,而磁盘人家一直在辛辛苦苦的下载,有什么错?
其实,上面的过程究其原因不就是操作系统把进程给杀掉了,导致磁盘找不到人了吗?所以我们让进程不能被杀掉不就可以了?所以这个时候D状态就出现了。
操作系统看到进程是D状态,它就先获取这个进程的PCB,然后把这个进程退出内存,回到磁盘当中,当磁盘资源下载完毕,又将这个进程重新加载进入内存当中执行。
该状态表示当前停止当前的进程,直到收到继续执行的指令才会再次运行。
下面我以例子在执行:
初始运行状态
输入指令kill -19 27911,暂停父进程。运行状态,只有子进程还在执行。
此时的父进程状态也由S+变为了T。
注意,这里的子进程也由S+变为了S,这里的+表示前台运行,可以直接ctrl+c终止,但是没有+表示后台运行,需要kill杀掉进程才能终止。
X状态是在一个进程结束之后显示的状态,这只是一个返回状态,我们不能在人物列表里面看到这个状态。
而Z状态表示当父进程和子进程都运行着,然后子进程被干掉了,此时子进程的资源还没有被系统回收,这个时候就会处于僵尸状态等待父进程去回收它。如下:
孤儿进程与僵尸状态正好相反,它是父子进程都在运行时,父进程被干掉了,此时的子进程会被init初始进程也就是1号进程收养,用于子进程结束后回收资源。如下:
看到此时的子进程的父进程变化称为了1。
以上就是我对本篇的全部理解,谢谢大伙查看。