【Linux网络】守护进程

发布时间:2026/7/21 9:32:18

【Linux网络】守护进程 文章目录会话进程组会话终端守护进程setsiddaemon在创建一个网络服务后往往这个服务进程是一直运行的。但是对于大部分进程来说如果退出终端这个终端上创建的所有进程都会退出这就导致进程的生命周期与终端绑定如果终端退出服务就无法继续运行了。为此创建一个服务进程后需要让其不在受到终端的影响就算终端退出进程依然执行这种进程称为守护进程。会话进程组进程组是一个或者多个进程的集合这些进程都有同样的进程组ID(PGID)这个ID和PID类似都是一个正整数可以放在pid_t类型中。往往一个进程组内部的所有进程要共同完成一个功能这些进程之间可以进行通信。而当在Linux中通过管道命令创建多个进程此时Linux认为多个进程是共同完成一个功能的就会把它们放到同一个进程组中。通过管道创建三个sleep进程创建进程后通过ps -ajx查看进行信息可以看到三个经常的PGID都是54844也就是sleep 100这条命令的PID。这说明这三个由管道串联起来的进程属于同一个进程组。而sleep 100这个进程是进程组长。进程组长往往是进程组中的第一个进程其可以在当前进程组中创建新的进程也可以创建一个新的进程组。但是如果进程组长退出了不代表进程组退出了就算进程组长退出了只要进程组中还有进程那么这个进程组依然存在。通过kill杀掉组长进程后剩余的两个进程依然存在并且PGID依然是54844。会话一个Linux系统可以有多个会话每个会话都是一个或多个进程组的集合。创建一个新的会话并在会话中执行三个sleep进程在原先的会话中查看bash进程和sleep进程的信息此处-E bash|sleep表示查找bash或者sleep进程。其中SID表示会话ID可以看到第一个bash的SID 53792第二个bash的SID 54989。这两个bash本身也是进程它们的PID PGID SID。这种PID SID的进程称为话首进程它是会话中的第一个进程。一般而言话首进程都是bash。而创建的三个sleep进程它们的SID 54989与第二个bash相同说明它们属于同一个会话而54989会话中包含四个进程以及两个进程组。而53792会话中只有一个进程和一个进程组。这说明一个会话是一个或多个进程组的集合。当一个话首进程退出这个会话内部的所有进程都会退出。在一个会话中创建三个sleep进程随后把bash话首进程杀掉可以看到不仅仅话首进程退出了三个sleep进程一起退出了。终端一个会话不仅仅包含多个进程组它还需要一个控制终端来接受用户的指令。当创建一个会话会执行以下两步创建一个终端启动一个bash进程(组)终端本质上是一个Linux的文件存在于/dev/pts目录下。当前我创建了两个会话那么就有两个终端这两个终端分别对应0和1这两个文件。如果尝试删除这些文件会被拒绝哪怕你是root用户因为这些文件是由内核管理的。除去两个终端文件还有一个ptmx这个文件是用于创建终端的文件如果这个文件丢失可能就无法创建新的终端了。在一个会话中包含多个进程组这些进程组分为两类前台进程组一个会话只有一个这个进程组独占终端的输入输出流后台进程组一个会话可以有多个无法接收到来自终端的数据在后台运行比如向终端输入ctrl c就是一个中断信号此时这个信号会发送给前台进程导致前台进程退出。但是并不是所有的信号都直接发送给前台进程组因为有一个特殊的bash进程如果某些信号的功能是直接挂断整个会话那么这个信号会发送给bash哪怕bash不是当前的前台进程组。至此可以这样理解Linux中的会话机制一个进程组管理多个进程这些进程往往共同完成一个功能一个会话管理多个进程组当会话退出该会话下的所有进程组都退出终端是会话的对外表现一个会话只有一个进程组可以占用终端刚才提到一个终端本质就是/dev/pts下的一个文件其实与终端交互就是进程在读写这个文件。那么一个进程如何知道要读取/dev/pts下的哪一个文件会话又是如何保证其下的所有进程都使用同一个终端的终端的信息存储在进程的PCB中先前说过会话创建包括一个终端和一个bash而这个过程中会把这个终端的信息存储到bash的PCB中。而在一个会话中创建的所有进程都是bash的后代进程会继承来自bash的PCB也就会继承到PCB中的终端信息从而保证同一会话的所有进程最后都操控同一个终端。守护进程了解Linux的会话机制后就可以谈一谈守护进程的问题了。先前说过当一个会话的话首进程退出那么该会话的所有进程都会退出因此一个守护进程一定不能属于其它会话而是自己创建一个会话自己就是话首进程。setsidsetsid可以创建一个新会话并把自己变成话首进程需要头文件sys/types.h和unistd.h。函数声明pid_tsetsid(void)调用该函数有一个前提该进程不能是进程组长。但是对于一个新创建的进程它自成一个进程组自己就是进程组长如何才能让它不是进程组长尝试以下代码#includeunistd.hintmain(){fork();while(true){}return0;}这个代码中通过fork创建了一个子进程随后父子进程同时陷入死循环。运行代码此时终端被阻塞切换到另一个终端查看test.exe这个进程的信息。此时查询到了两个进程一个是父进程另一个是子进程重点在于它们的GPID都是52746这说明通过fork创建的父子进程属于同一个进程组。而frok创建的子进程会继承来自父进程的所有代码PCB等信息。因此如果想要让一个进程调用setsid创建一个新会话只需要通过fork创建一个子进程子进程继承父进程的所有信息但又不是进程组长可以调用setsid。因此setsid的常见写法如下pid_t idfork();if(id0)exit(0);setsid();这段代码中通过fork创建一个子进程此时父子进程属于同一个进程组父进程是组长。如果id 0说明是父进程父进程直接退出一个进程组中进程组长退出进程组其他进程继续运行不会退出。因此子进程不会退出继承了父进程的所有信息并且还不是进程组长。于是子进程调用setsid自己创建一个会话自己做话首进程至此子进程就是一个守护进程了运行以下代码#includeunistd.h#includesys/types.hintmain(){pid_t idfork();if(id0)exit(0);setsid();while(true){}return0;}这个代码在子进程变为守护进程后执行一个死循环。执行./test.exe后查看bash和test进程的信息发现test.exe这个进程PIDPGIDSID58278并且不和任何一个bash重复。这说明test.exe已经自己创建一个会话自己做话首进程成为了一个守护进程了。但是还有一个问题那就是它的终端没有切断可以看到它的三个标准流都指向/dev/pts/0这就是当前的终端文件。如果当前终端退出那么如果这个守护进程还向终端输出内容就会导致错误因此还要改变它的输出流// 关闭标准输入、输出和错误close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);// 打开/dev/null作为标准输入、输出和错误open(/dev/null,O_RDONLY);open(/dev/null,O_WRONLY);open(/dev/null,O_WRONLY);此处的/dev/null是一个Linux提供的文件它可以接收任何输入但是不论输入什么都会被系统丢弃。因此如果一个程序有输出但是又不希望接收到它的输出时就可以把输出重定向到/dev/null下。daemonsetsid的用法还是有点复杂了为此Linux提供了另一个系统调用daemon它封装了上述所有过程可以快速创建一个守护进程。需要头文件unistd.h。intdaemon(intnochdir,intnoclose)参数nochdir改变工作目录传入0改变工作目录为根目录传入1保持当前工作目录nclose改变输入输出流传入0输入输出流重定向到/dev/null传入1不改变输入输出流原先的代码就可以变成#includeunistd.h#includesys/types.hintmain(){daemon(0,0);while(true){}return0;}

相关新闻