关于进程(僵尸/孤儿)

进程创建:

进程创建的一般过程:
1、给新进程分配一个标识符,在内核中分配一个PCB
2、复制父进程的环境
3、分配资源(程序,数据,堆栈等)
4、复制父进程地址空间中的内容(写时拷贝)
5、将进程置成就绪状态,放入就绪队列


fork()函数—用来创建一个子进程

#include


上面代码中涉及两个函数:

pid_t getpid() //获取自己的进程id
pid_t getppid() // 获取父进程的进程id


子进程会继承父进程哪些内容

  • 用户号UID和用户组号GID
  • 环境
  • 进程上下文(当前运行状态的信息)
  • 进程堆栈(设置为只读,写时拷贝)
  • 内存信息
  • 打开的文件描述符
  • 信号控制设定
  • 进程调度优先级(nice值,由nice函数设定,值越小,优先级越高)
  • 当前工作目录
  • 根路径
  • 控制终端
  • 进程组号(父子进程同属一个进程组,但可以人为设置)
  • 文件方式创建屏蔽字(umask)
  • 资源限制

子进程不会继承父进程哪些内容,即子进程独有的内容

  • 进程标识符PID
  • 父进程的id子进程也不继承
  • 父进程的锁子进程子进程不继承
  • 父进程未决的信号子进程会进行清除
  • 不继承异步输入和输出

**注意:**gdb调试多进程时会有所阻碍,gdb只能跟踪一个进程(默认跟踪父进程),因此要想让gdb跟踪子进程需要对其进行设置
set follow-fork-mode child //代表跟踪子进程
set follow-fork-mode parent //代表跟踪父进程


僵尸进程与孤儿进程

僵尸进程

子进程退出,父进程还在运行,父进程未读取到子进程的退出信息,这时,子进程就是一个僵尸进程。僵尸进程会以终止状态一直在进程表中存在,并一直等待父进程读取自己的终止信息代码。
在UNIX术语中,一个已经终止,但是其父进程尚未对其进行善后处理(获取终止子进程的相关信息,释放它仍占用的资源)的进程被称为僵死进程。

每个进程退出时,内核释放该进程的所有资源,比如分配的内存、打开的文件描述符,但还会保留一些极少的信息,如pid、进程退出状态、运行时间等来供父进程使用父进程一般会调用wait/waitpid来进行一些收尾工作。

使用SIGKILL也不能杀死僵尸进程,因为僵尸进程是已经死掉的进程,他不能接收任何信号。

僵尸进程的形成:

  • 子进程先结束, 若父进程未处理子进程的退出状态,在父进程退出之前,子进程 一直处于僵尸状态。
  • 父进程调用了wait/waitpid(阻塞调用保证子进程在父进程结束前结束)来等待子进程,当执行到wait/waitpid时,父进程会进入睡眠状态,知道子进程结束对其回收后才会返回。若子进程结束,父进程还未执行到wait/waitpid,在此之前子进程也一直处于僵尸状态。

僵尸进程的危害:
进程的退出状态一直被维持下去,保留信息不会释放,进程号就一直被占用。而系统所能使用的进程号是有限的,若有大量的僵尸进程,则导致系统没有可用的进程号而导致不能创建新进程。(少量的僵尸进程不会对系统性能有影响)

僵尸进程解决方法

  • 信号机制
    子进程结束时,会向父进程发送SIGCHLD信号,Linux默认忽略该信号
    我们注册该信号,并在信号处理函数中调用wait
  • fork两次
    父进程先创建一个子进程,然后立刻调用wait等待子进程的退出,儿子进程再fork一次,创建一个孙子进程,让孙子进程来完成一些任务,而儿子进程创建完孙子进程后直接退出,父进程此时刚好回收儿子进程,此时,孙子进程的父进程已经死亡,该孙子进程就变成一个孤儿进程,被Init进程领养,Init进程会循环等待,当孙子进程执行完任务就会被回收。这就相当于将执行任务的进程的等待转交给了一号进程来完成。

孤儿进程

孤儿进程是指父进程在子进程之前结束(exit或return),这时子进程被称为孤儿进程,孤儿进程会被1号进程(init进程)领养。而init进程会循环的wait等待它已经退出的子进程。因此孤儿进程并不会有什么危害。

猜你喜欢

转载自blog.csdn.net/shidantong/article/details/81195153