Linux:进程创建与僵尸孤儿进程

一,进程与PCB

进程典型定义
(1)进程是程序的一次执行。
(2)进程是一个程序及其数据在处理机上顺序执行时所发生的活动。
(3)进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
特征:动态性,并发性,独立性,异步性。
基本状态:

这里写图片描述
PCB
进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
内容分类
(1)标识符:描述本进程的唯一标识符,用来区别其它进程。
(2)状态:任务状态,退出代码,退出信号等。
(3)优先级
(4)程序计数器
(5)内存指针:包括程序代码和进程相关数据的指针,还有和其它进程共享的内存块的指针。
(6)上下文数据
(7)I/O状态信息
(8)记账信息

进程通俗说就是放在内存里的程序。
接下来我们了解一下我们电脑里的进程情况(由于太多只截取了部分)
命令解释: -e表示列出全部进程 -o pid,comm,cmd表示我们需要PID,COMMAND,CMD信息。

这里写图片描述
这里PID:像我们的身份证一样,进程根据PID区别于其他进程
COMMAND:进程名。
CMD:进程所对应的程序以及运行时所带的参数。
像这里的1号进程init就有很重要的作用,后面的知识会有所应用。

进程状态
(1)R运行状态(running):进程或是在运行中,或是在运行队列中。
(2)S睡眠状态(sleeping):进程等待事件完成。
(3)D磁盘休眠状态(Disk sleep):也叫不可中断睡眠状态,在这个状态的进程通常会等待IO结束。
(4)T停止状态(stopped):可通过发送SIGSTOP信号给进程来停止(T)进程。SIGCONT信号让进程继续运行。
(4)X死亡状态(dead):只是一个返回状态,将不会在任务列表中看到这个状态。

二,fork与vfork

fork()系统调用的作用是复制一个进程,当一个进程调用它,完成个就出现两个几乎一摸一样的进程,新进程是子进程,原来的进程是父进程.
pid_t fork(void)若成功,父进程中返回子进程ID,子进程中返回0,若出错返回-1;

#include <unistd.h>  
#include <stdio.h>  
int main (void)  
{  
int count = 0;  
pid_t pid;        /*此时仅有一个进程*/  
pid = fork();      /*此时已经有两个进程在同时运行*/  
if(pid < 0)  
 {  
   printf("error in fork!");  
        exit(1);    /* fork出错退出*/  
     }  
else if(pid==0)  
  printf("I am the child process, the count is %d, my process ID is %d\n",count,getpid());  
   else  
     printf("I am the parent process, the count is %d, my process ID is %d\n",++count,getpid());  
   return 0;  
} 

结果:

[a@localhost ~]$ ./a.out
I am the parent process, the count is 1, my process ID is 2727
[a@localhost ~]$ I am the child process, the count is 0, my process ID is 2728

vfork与fork是有区别的
(1)fork要拷贝父进程的数据段,而vfork不需要完全拷贝父进程的数据段,在子进程没有调用exec或者exit函数之前,子进程会与父进程共享数据段。

#include<stdio.h>
#include<unistd.h>
int main(void)
{
  int count = 1;
  int child;
  printf("Before create son,father count is :%d\n", count);
  child = vfork();
  if(child < 0)
  {
   printf("error in vfork!\n");
   exit(1);
  }
  if(child == 0)
  {
   printf("The is son,pid is:%d and the count is: %d\n",getpid(),++count);
   exit(1);
  }
  else
  {
    printf("After son,This is father,pid is: %d and the count is:%d,the child is:%d\n", getpid(), count, child);
  }
  return 0;
  }

结果:

[a@localhost ~]$ ./a.out
Before create son,father count is :1
The is son, pid is:4846 and the count is: 2
After son,This is father, pid is: 4845 and the count is:2,the child is:4846
[a@localhost ~]$ 

(2)在fork中子进程与父进程的执行顺序不确定,而在vfork中,子进程先运行,父进程挂起,直到子进程调用了exec或者exit函数,父进程才被执行。

#include<stdio.h>
#include<unistd.h>
int main()
{
  int count = 1;
  int child;
  printf("Before create son,the father count is :%d\n",count);
  if(!(child = vfork()))
  {
    int i;
    for(i = 0; i < 100; i++)
       {
          printf("This is son, The i is: %d\n", i);
          if(i ==5)
             exit(1);
       }
       printf("This is son,pid is: %d and the count is :%d\n",getpid(),++count);
       exit(1);
    }
    else
    {
      printf("After son,This is father,pid is:%d and the count is: %d,and the child is: %d\n", getpid(), count, child);
    }
    return 0;
    }

结果:

[a@localhost ~]$ ./a.out
Before create son,the father count is :1
This is son, The i is: 0
This is son, The i is: 1
This is son, The i is: 2
This is son, The i is: 3
This is son, The i is: 4
This is son, The i is: 5
After son,This is father,pid is:5018 and the count is: 1,and the child is: 5019

从结果可以看出父进程是在等子进程执行完毕后才开始执行的。

三,僵尸进程与孤儿进程

僵尸进程:当一个进程完成它的工作终止之后,内核释放该进程的所有资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(进程号,推出状态,运行时间等)所以他的父进程需要调用wait/waitpid系统调用取的子进程的终止状态,释放那段信息。当进程退出但是父进程没有读取到子进程退出的返回代码时,就会产生僵尸进程。
危害:僵尸进程会始终保持在进程表中,如果产生大量的僵尸进程,系统因没有可用的进程号而不能产生新的进程。

来创建一个维持30秒的进程例子

#include<stdio.h>
#include<stdlib.h>
int main()
{
    pid_t id = fork();
    if(id<0)
    {
        perror("fork");
        return 1;
    }
    else if(id>0)
    {
        printf("parent[%d] is sleeping ...\n",getpid());
        sleep(30);
    }
    else
    {
        printf("child[%d] is begin Z...\n",getpid());
        sleep(5);
        exit(EXIT_SUCCESS);
    }
    return 0;
}

这里写图片描述
清除僵尸进程
1)改写父进程,在子进程死后要为它收尸。具体做法是接管SIGCHLD信号。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信 号后,执行 waitpid()函数为子进程收尸。这是基于这样的原理:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,尽管对的默认处理是忽略, 如果想响应这个消息,可以设置一个处理函数。
2)把父进程杀掉。父进程死后,僵尸进程成为”孤儿进程”,过继给1号进程init,init始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失。
避免僵尸进程
1)在一个程序的开始调用函数
signal(SIGCHLD,SIG_IGN);
2)调用fork两次。
3)用waitpid等待子进程返回.

孤儿进程:顾名思义,孤儿即失去了父母,在这里是父进程先退出,子进程就称为“孤儿进程”。
孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
    pid_t id = fork();
    if(id<0)
    {
        perror("fork");
        return 1;
    }
    else if(id==0)
    {
        printf("start:i am child[%d],parent is [%d]\n",getpid(),getpid());
        sleep(10);
        printf("finish:i am child[%d],parent is [%d]\n",getpid(),getppid());
    }
    else
    {
        printf("I am parent, pid:%d\n",getpid());
        sleep(3);
        exit(0);
    }
    return 0;
}

这里写图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值