进程的各种状态
运行状态(R:runing):运行状态的进程并不意味着此时的进程一定在运行,它表明进程要么在运行,要么在运行队列里。
睡眠状态(S:sleeping):进程在等待着事件完成。
磁盘休眠状态(D:disk sleep):也称不可中断睡眠状态,这个状态的进程通常会等待I/O的结束。
停止状态(T:stopped):可以通过发送SIGSTOP信号给进程来终止停止状态的进程。这个被暂停的进程可以通过发送SIGCONT信号让进程继续运行。
死亡状态(X:dead):这个状态的进程只是一个返回状态,任务列表里看不到这个状态的进程。
僵尸进程(Z:zombie):僵尸进程是一个比较特殊的状态。当进程退出且父进程没有使用wait()读到子进程退出的返回代码时就会产生僵尸进程。僵尸进程会以终止状态继续保持在进程表中,并且一直在等待进程读取退出状态代码。只要进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程就将进入僵尸状态。
僵尸进程
一个进程使用fork()创建子进程后,如果子进程退出,但是父进程并没有wait()或waitpid()来获取子进程的状态信息,那么子进程的描述符就将仍然保存在系统中。此时这种状态的进程就称为僵尸进程。僵尸进程是一个进程必然会经过的过程,这是每个子进程在结束时都要经过的阶段。僵尸进程可以通过ps命令来查询,显示出来的状态为Z(zombie)。
僵尸进程的危害:
我们都知道系统所能使用的进程号是有限的,如果大量的僵尸进程产生,将因为没有可用的进程号而导致系统不能产生新的进程,从而使得进程被init所收养,这样init就会释放所有的僵尸进程所占有的资源,从而结束僵尸进程。
僵尸进程的清除:
(1)外部消灭:通过kill发送SIGTERM或者SIGKILL信号消灭产生僵尸进程的进程,它产生的僵尸进程就变成了孤儿进程,这些孤儿进程会被init进程接管,init进程会wait()这些孤儿进程,释放它们占用的系统进程表中的资源。
(2)子进程退出时向父进程发送SIGCHLD信号,父进程处理此信号,在信号处理函数中调用wait()处理僵尸进程。
(3)fork()两次来清除僵尸进程。原理是将子进程变为孤儿进程,从而使其父进程变为init进程,通过init进程来处理僵尸进程。父进程fork()一个子进程,然后继续工作,子进程再fork()一个子进程,即孙进程后退出,那么孙进程就将被init接管,孙进程结束后,init会回收。不过,子进程的回收还是要自己处理。
孤儿进程
一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)收养,并由init进程对它们完成状态收集工作,由于孤儿进程会被init进程收养,所以孤儿进程不会对系统造成危害。
守护进程
守护进程又称精灵进程,是独立运行于后台、常驻内存的特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。一个守护进程的父进程是init进程。因为它真正的父进程在fork()出子进程后就先于子进程之前退出了,所以它是一个由init接管的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出无论是向标准输出设备stdout还是标准出错设备stderr的输出都被丢到了/dev/null里。守护进程一般用作服务器进程,名称通常以d结尾,如sshd、httpd、syslogd等。
守护进程地特点
(1)运行方式:守护进程是Linux中的后台服务进程,它独立运行于后台、周期性地执行某种任务或等待处理某些发生的事件。
(2)生命周期:守护进程会长时间运行,常常在系统启动时就开始运行,直到系统关闭时才终止。
(3)守护进程不依赖于终端:一般来说,从终端开始运行地进程都会依附于终端,这个终端即为控制终端。当控制终端被关闭时,相应的进程都会被自动关闭。我们平时写进程时,一个死循环程序关闭终端的同时也会关闭我们的程序,但是对于守护进程来说,其生命周期守护需要突破这种限制,它从开始运行,直到整个系统关闭时才会退出,所以守护进程不能依赖于终端。
守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户权限运行,因为它们要使用特殊的端口(1 ~ 1024)或访问某些特殊的资源。
创建守护进程的步骤:实现一个守护进程,其实就是将一个普通进程改造成具有守护进程特征的进程。
参考文章地址:https://blog.csdn.net/mijichui2153/article/details/81394387
(1)fork()创建子进程,父进程exit()退出。
(2)在子进程中调用setsid()创建新会话。
(3)再次fork()一个子进程,父进程exit()退出。
(4)在子进程中调用chdir()让根目录"/"成为子进程的工作目录。
(5)在子进程中调用umask()重设文件权限掩码为0。
(6)在子进程中close()掉不需要的文件描述符。
(7)守护进程退出处理。