当前位置: 首页 > Linux

LinuxDaemon介绍及C++实现

时间:2023-04-06 18:42:15 Linux

1.Daemon简介Daemon是一个长生命周期的进程。它们往往在系统开机加载时启动(如果daemon进程需要随系统自动启动,需要在/etc/init.d目录下放置相应的启动脚本,或者使用systemctl来控制,以及还有一些其他方法比如supervisor等,读者可以自行上网搜索相关用法),只有在系统关闭时才会终止。由于它们没有控制终端,因此据说它们在后台运行。根据守护进程的启动和管理方式,守护进程可以分为两类:可以独立启动(standalone)的守护进程和由超级守护进程(superdaemon)管理的守护进程。standalone:可以独立启动的守护进程。这种守护进程在启动后会一直占用内存和系统资源。最大的优点是响应速度快。多用于随时可以接受远程请求的服务,如WWW守护进程(httpd)、FTP守护进程(vsftpd)等。超级守护进程:由专门的守护进程统一管理。这种服务在需要的时候由一个统一的守护进程唤醒。当没有远程请求时,这些服务不会启动。当有远程请求时,superdaemon唤醒相应的服务。当远程请求结束时,被唤醒的服务关闭并释放系统资源。早期的superdaemon是inetd,后来被xinetd取代。superdaemon本身就是一个独立的服务,因为它需要管理其他后续的服务,所以它本身当然需要常驻内存。2.守护进程创建步骤执行一次fork(),之后父进程退出,子进程继续执行。(结果是守护进程成为init进程的子进程。)这一步有两个原因:假设守护进程是从命令行启动的,父进程的终止将被shell检测到,这将display调出另一个shell提示符,并保持子进程在后台运行。子进程保证不被称为进程组组长进程,因为它继承了父进程的进程组ID,有自己唯一的进程ID,而且这个进程ID和继承的进程组ID是不同的,所以下一步可以成功执行。子进程调用setsid()来打开一个新会话并释放它与控制终端之间的所有关联。结果是子进程:(a)成为新会话的领导者,(b)成为新进程组的领导者,以及(c)没有控制终端。如果守护进程从未打开过终端设备,则无需担心守护进程再次请求控制终端。如果一个终端设备可以在守护进程后面打开,则必须采取措施确保该设备不会成为控制终端。这可以通过两种方式实现:在可能适用于终端设备的所有open()调用中指定O_NOCTTY标志。或者更简单地说,在setsid()调用之后执行第二个fork(),让父进程再次退出并让孙进程继续。这确保子进程永远不会成为会话领导者,因此该进程永远不会根据SystemV获取终端的规则重新请求控制终端。(额外的fork()调用不会造成任何伤害。)清除进程的umask以确保守护进程在创建文件和目录时具有所需的权限。改变进程的当前工作目录,通常是根目录(/)。这是必要的,因为守护进程通常会运行到系统关闭为止。如果守护进程的当前工作目录是不包含/的文件系统,则无法卸载该文件系统。或者守护进程可以将工作目录更改为完成任务的目录,或者在配置文件中定义一个目录,只要包含该目录的文件系统永远不会被卸载。关闭守护进程从其父进程继承的所有打开的文件描述符。(守护进程可能需要使继承的文件描述保持打开状态,因此此步骤是可选的或可更改的。)这样做有几个原因。由于守护进程失去了控制终端并在后台运行,因此守护进程保持文件描述符0(标准输入)、1(标准输出)和2(标准错误)打开是没有意义的,因为它们指向控制终端。终端。此外,不可能卸载托管由长时间运行的守护进程打开的文件的文件系统。因此,通常的做法是关闭所有无用的打开的文件描述符,因为文件描述符是一种有限的资源。关闭文件描述符0、1和2后,守护进程通常会打开/dev/null并使用dup2()(或类似函数)使所有这些描述符都指向该设备。这样做有两个原因:它确保守护进程对库函数的调用在这些描述符上执行I/O不会意外失败。它可以防止守护进程稍后打开描述符为1或2的文件,因为库函数会将数据作为标准输出和标准错误写入这些描述符(从而破坏原始数据)。3.C++实现将下面的示例代码直接粘贴到文件中,例如这里的文件名为example_daemon.cpp,直接用g++编译。#生成名为example_daemon的可执行文件$g++-oexample_daemonexample_daemon.cpp$./example_daemon#可以看到example_daemon进程在后台运行,其父进程ID为1$ps-ef|grepexample_daemonlvnux178891014:37?00:00:00./example_daemon示例代码:#include#include#include#include#include#includeboolstart_daemon(){intfd;switch(fork()){case-1:printf("fork()失败\n");返回假;情况0:中断;默认值:退出(0);}/*pid_tsetsid(void);该进程调用setsid()来创建一个新会话。如果调用这个函数的进程不是一个进程组的leader,那么这个函数会创建一个新的session,结果是:1.这个进程成为新session的sessionleader(对话的进度)。此进程是此新会话中的唯一进程。2、本进程成为新进程组的leader进程。新的进程组ID是调用进程的进程ID。3.这个进程没有控制终端。如果辅助进程在调用setsid之前有一个控制终端,则该关联也会被破坏。如果调用进程已经是进程组的领导者,则此函数返回错误。为确保不是这种情况,通常先调用fork(),然后导致父进程终止,而子进程继续执行。因为子进程继承了父进程的进程组ID,而子进程的进程ID是新分配的,两者不能相等,这样就保证了子进程不是进程组的组长。*/if(setsid()==-1){printf("setsid()失败\n");返回假;}switch(fork()){case-1:printf("fork()failed\n");返回假;情况0:中断;默认值:退出(0);}掩码(0);目录(“/”);长maxfd;如果((maxfd=sysconf(_SC_OPEN_MAX))!=-1){for(fd=0;fdSTDERR_FILENO){if(close(fd)==-1){printf("close()失败\n");返回假;}}returntrue;}intmain(intargc,char**argv){start_daemon();while(true){睡眠(100);}return0;}4.参考文献《Linux_UNIX系统编程手册》《UNIX环境高级编程》