猿问
下载APP

在Linux中创建守护进程

在Linux中我想添加一个无法停止的守护进程,它监视文件系统的变化。如果检测到任何更改,它应该将路径写入启动它的控制台加上换行符。


我已经有文件系统更改代码几乎准备好但我无法弄清楚如何创建一个守护进程。


我的代码来自:http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html


叉子后要做什么?


int main (int argc, char **argv) {


  pid_t pID = fork();

  if (pID == 0)  {              // child

          // Code only executed by child process    

      sIdentifier = "Child Process: ";

    }

    else if (pID < 0) {

        cerr << "Failed to fork" << endl;

        exit(1);

       // Throw exception

    }

    else                                   // parent

    {

      // Code only executed by parent process


      sIdentifier = "Parent Process:";

    }       


    return 0;

}


SMILET
浏览 51回答 3
3回答

月关宝盒

在Linux中,我想添加一个无法停止的守护进程,它监视文件系统的变化。如果检测到任何更改,它应该将路径写入启动它的控制台+换行符。守护进程在后台工作,(通常......)不属于TTY,这就是为什么你不能以你想要的方式使用stdout / stderr。通常,syslog守护程序(syslogd)用于将消息记录到文件(debug,error,...)。除此之外,还有一些必要的步骤来守护进程。如果我没记错,这些步骤是:叉掉父进程和让它终止,如果分叉成功。 - >由于父进程已终止,子进程现在在后台运行。setsid - 创建一个新会话。调用进程成为新会话的领导者和新进程组的进程组负责人。该过程现在与其控制终端(CTTY)分离。捕获信号 - 忽略和/或处理信号。再次fork并让父进程终止以确保您摆脱会话引导进程。(只有会议领导者可能会再次获得TTY。)chdir - 更改守护程序的工作目录。umask - 根据守护程序的需要更改文件模式掩码。close - 关闭可能从父进程继承的所有打开的文件描述符。为您提供一个起点:查看显示基本步骤的骨架代码。此代码现在也可以在GitHub上分叉:Linux守护程序的基本框架/*&nbsp;* daemonize.c&nbsp;* This example daemonizes a process, writes a few log messages,&nbsp;* sleeps 20 seconds and terminates afterwards.&nbsp;*/#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <signal.h>#include <sys/types.h>#include <sys/stat.h>#include <syslog.h>static void skeleton_daemon(){&nbsp; &nbsp; pid_t pid;&nbsp; &nbsp; /* Fork off the parent process */&nbsp; &nbsp; pid = fork();&nbsp; &nbsp; /* An error occurred */&nbsp; &nbsp; if (pid < 0)&nbsp; &nbsp; &nbsp; &nbsp; exit(EXIT_FAILURE);&nbsp; &nbsp; /* Success: Let the parent terminate */&nbsp; &nbsp; if (pid > 0)&nbsp; &nbsp; &nbsp; &nbsp; exit(EXIT_SUCCESS);&nbsp; &nbsp; /* On success: The child process becomes session leader */&nbsp; &nbsp; if (setsid() < 0)&nbsp; &nbsp; &nbsp; &nbsp; exit(EXIT_FAILURE);&nbsp; &nbsp; /* Catch, ignore and handle signals */&nbsp; &nbsp; //TODO: Implement a working signal handler */&nbsp; &nbsp; signal(SIGCHLD, SIG_IGN);&nbsp; &nbsp; signal(SIGHUP, SIG_IGN);&nbsp; &nbsp; /* Fork off for the second time*/&nbsp; &nbsp; pid = fork();&nbsp; &nbsp; /* An error occurred */&nbsp; &nbsp; if (pid < 0)&nbsp; &nbsp; &nbsp; &nbsp; exit(EXIT_FAILURE);&nbsp; &nbsp; /* Success: Let the parent terminate */&nbsp; &nbsp; if (pid > 0)&nbsp; &nbsp; &nbsp; &nbsp; exit(EXIT_SUCCESS);&nbsp; &nbsp; /* Set new file permissions */&nbsp; &nbsp; umask(0);&nbsp; &nbsp; /* Change the working directory to the root directory */&nbsp; &nbsp; /* or another appropriated directory */&nbsp; &nbsp; chdir("/");&nbsp; &nbsp; /* Close all open file descriptors */&nbsp; &nbsp; int x;&nbsp; &nbsp; for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; close (x);&nbsp; &nbsp; }&nbsp; &nbsp; /* Open the log file */&nbsp; &nbsp; openlog ("firstdaemon", LOG_PID, LOG_DAEMON);}int main(){&nbsp; &nbsp; skeleton_daemon();&nbsp; &nbsp; while (1)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; //TODO: Insert daemon code here.&nbsp; &nbsp; &nbsp; &nbsp; syslog (LOG_NOTICE, "First daemon started.");&nbsp; &nbsp; &nbsp; &nbsp; sleep (20);&nbsp; &nbsp; &nbsp; &nbsp; break;&nbsp; &nbsp; }&nbsp; &nbsp; syslog (LOG_NOTICE, "First daemon terminated.");&nbsp; &nbsp; closelog();&nbsp; &nbsp; return EXIT_SUCCESS;}编译代码: gcc -o firstdaemon daemonize.c启动守护进程: ./firstdaemon检查一切是否正常工作: ps -xj | grep firstdaemon输出应该类似于这个:+ ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- +| PPID | PID | PGID | SID | TTY | TPGID | STAT | UID | 时间| CMD |+ ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- +| 1 | 3387 | 3386 | 3386 | ?| -1 | S | 1000 | 0:00 | ./ |+ ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- +你应该看到的是:守护进程没有控制终端(TTY =?)父进程ID(PPID)为1(init进程)该PID!= SID,这意味着我们的进程不是会话组长(因为第二叉())因为PID!= SID,我们的进程无法再次控制TTY阅读系统日志:找到您的syslog文件。我在这里:/var/log/syslog做一个: grep firstdaemon /var/log/syslog输出应该类似于这个:&nbsp; firstdaemon [3387]:第一个守护进程启动。&nbsp; firstdaemon [3387]:第一个守护进程终止。注意: 实际上,您还需要实现信号处理程序并正确设置日志记录(文件,日志级别......)。

潇潇雨雨

man 7 daemon描述了如何非常详细地创建守护进程。我的回答只是摘自本手册。至少有两种类型的守护进程:传统的SysV守护进程(旧式),systemd守护进程(新式)。SysV守护进程如果您对传统的SysV守护程序感兴趣,则应执行以下步骤:关闭除标准输入,输出和错误之外的所有打开文件描述符(即前三个文件描述符0,1,2)。这可确保在守护进程中不会意外传递文件描述符。在Linux上,最好通过迭代来实现/proc/self/fd,其中从文件描述符3迭代到getrlimit()for 返回的值RLIMIT_NOFILE。将所有信号处理程序重置为默认值。最好通过迭代可用信号直到极限_NSIG并重置为SIG_DFL。使用重置信号掩码sigprocmask()。清理环境块,删除或重置可能对守护程序运行时产生负面影响的环境变量。调用fork(),创建后台进程。在孩子中,呼叫setsid()从任何终端分离并创建一个独立的会话。在孩子中,fork()再次呼叫,以确保守护进程再也不能重新获得终端。调用exit()第一个子节点,以便只有第二个子节点(实际的守护进程)保持不变。这确保守护进程重新成为init / PID 1的父级,因为所有守护进程都应该如此。在守护进程中,连接/dev/null到标准输入,输出和错误。在守护进程,重置umask为0,从而使文件模式传递给open(),mkdir()以及诸如此类的直接控制创建文件和目录的访问模式。在守护进程,改变当前目录为根目录(/),以避免该守护进程不由自主块从被卸载安装点。在守护进程中,将守护进程PID(如返回的getpid())写入PID文件,例如/run/foobar.pid(对于假设的守护进程“foobar”),以确保守护进程不能多次启动。这必须以无竞争方式实现,以便仅在先前存储在PID文件中的PID不再存在或属于外部进程的同时验证PID文件时更新PID文件。在守护进程中,如果可能且适用,请删除权限。从守护进程,通知原始进程启动初始化完成。这可以通过未命名的管道或类似的通信通道来实现,该通道在第一个之前创建fork(),因此在原始和守护进程中都可用。请致电exit()原始流程。调用守护程序的进程必须能够依赖于在初始化完成后exit()发生这种情况并且所有外部通信通道都已建立并可访问。注意这个警告:不应使用BSD daemon()函数,因为它仅实现这些步骤的子集。需要提供与SysV系统兼容的守护进程应该实现上面指出的方案。但是,建议通过命令行参数使此行为成为可选和可配置的,以便于调试以及使用systemd简化与系统的集成。请注意,daemon()它不符合POSIX标准。新式守护进程对于新式守护程序,建议执行以下步骤:如果SIGTERM收到,请关闭守护程序并干净地退出。如果SIGHUP收到,请重新加载配置文件(如果适用)。从主守护进程提供正确的退出代码,因为init系统使用它来检测服务错误和问题。建议遵循LSB对SysV init脚本的建议中定义的退出代码方案。如果可能且适用,请通过D-Bus IPC系统公开守护程序的控制接口,并在初始化的最后一步获取总线名称。要在systemd中集成,请提供.service 单元文件,该文件包含有关启动,停止和以其他方式维护守护程序的信息。详情systemd.service(5)请见。尽可能依赖init系统的功能来限制守护进程对文件,服务和其他资源的访问,即在systemd的情况下,依赖systemd的资源限制控制而不是实现自己的,依赖systemd的权限下降代码而不是在守护进程中实现它,类似。请参阅systemd.exec(5)可用控件。如果使用D-Bus,请通过提供D-Bus服务激活配置文件使您的守护程序总线可激活。这有多个优点:您的守护进程可以按需启动; 它可以与其他需要它的守护进程并行启动 - 最大化并行化和启动速度 ; 您的守护程序可以在失败时重新启动而不会丢失任何总线请求,因为总线会对可激活服务的请求进行排队。请参阅下文了解详情。如果你的守护进程通过一个套接字提供给当地其他进程或远程客户提供服务,应当取得插座激活后的方案指出了下面。与D-Bus激活一样,这可以按需启动服务,并且可以改善服务启动的并行化。此外,对于无状态协议(例如syslog,DNS),可以重新启动实现基于套接字的激活的守护程序,而不会丢失单个请求。请参阅下文了解详情。如果适用,守护程序应通过sd_notify(3)接口通知init系统有关启动完成或状态更新的信息。syslog()新样式守护程序可以选择简单地记录到标准错误fprintf(),然后由init系统转发到syslog,而不是使用调用直接记录到系统syslog服务。如果需要日志级别,可以通过在单个日志行前加上字符串(如“<4>”)(对于syslog优先级方案中的日志级别4“WARNING”)进行编码,遵循与Linux内核printk()级别系统类似的样式。有关详细信息,请参阅sd-daemon(3)和systemd.exec(5)。

慕的地10843

你不能在linux中创建一个无法杀死的进程。root用户(uid = 0)可以向进程发送信号,并且有两个信号无法捕获,SIGKILL = 9,SIGSTOP = 19。其他信号(未被捕获时)也可能导致进程终止。您可能需要一个更通用的守护进程函数,您可以在其中指定程序/守护程序的名称,以及运行程序的路径(可能是“/”或“/ tmp”)。您可能还想为stderr和stdout(以及可能使用stdin的控制路径)提供文件。以下是必要的包括:#include <stdio.h>&nbsp; &nbsp; //printf(3)#include <stdlib.h>&nbsp; &nbsp;//exit(3)#include <unistd.h>&nbsp; &nbsp;//fork(3), chdir(3), sysconf(3)#include <signal.h>&nbsp; &nbsp;//signal(3)#include <sys/stat.h> //umask(3)#include <syslog.h>&nbsp; &nbsp;//syslog(3), openlog(3), closelog(3)这是一个更通用的功能,intdaemonize(char* name, char* path, char* outfile, char* errfile, char* infile ){&nbsp; &nbsp; if(!path) { path="/"; }&nbsp; &nbsp; if(!name) { name="medaemon"; }&nbsp; &nbsp; if(!infile) { infile="/dev/null"; }&nbsp; &nbsp; if(!outfile) { outfile="/dev/null"; }&nbsp; &nbsp; if(!errfile) { errfile="/dev/null"; }&nbsp; &nbsp; //printf("%s %s %s %s\n",name,path,outfile,infile);&nbsp; &nbsp; pid_t child;&nbsp; &nbsp; //fork, detach from process group leader&nbsp; &nbsp; if( (child=fork())<0 ) { //failed fork&nbsp; &nbsp; &nbsp; &nbsp; fprintf(stderr,"error: failed fork\n");&nbsp; &nbsp; &nbsp; &nbsp; exit(EXIT_FAILURE);&nbsp; &nbsp; }&nbsp; &nbsp; if (child>0) { //parent&nbsp; &nbsp; &nbsp; &nbsp; exit(EXIT_SUCCESS);&nbsp; &nbsp; }&nbsp; &nbsp; if( setsid()<0 ) { //failed to become session leader&nbsp; &nbsp; &nbsp; &nbsp; fprintf(stderr,"error: failed setsid\n");&nbsp; &nbsp; &nbsp; &nbsp; exit(EXIT_FAILURE);&nbsp; &nbsp; }&nbsp; &nbsp; //catch/ignore signals&nbsp; &nbsp; signal(SIGCHLD,SIG_IGN);&nbsp; &nbsp; signal(SIGHUP,SIG_IGN);&nbsp; &nbsp; //fork second time&nbsp; &nbsp; if ( (child=fork())<0) { //failed fork&nbsp; &nbsp; &nbsp; &nbsp; fprintf(stderr,"error: failed fork\n");&nbsp; &nbsp; &nbsp; &nbsp; exit(EXIT_FAILURE);&nbsp; &nbsp; }&nbsp; &nbsp; if( child>0 ) { //parent&nbsp; &nbsp; &nbsp; &nbsp; exit(EXIT_SUCCESS);&nbsp; &nbsp; }&nbsp; &nbsp; //new file permissions&nbsp; &nbsp; umask(0);&nbsp; &nbsp; //change to path directory&nbsp; &nbsp; chdir(path);&nbsp; &nbsp; //Close all open file descriptors&nbsp; &nbsp; int fd;&nbsp; &nbsp; for( fd=sysconf(_SC_OPEN_MAX); fd>0; --fd )&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; close(fd);&nbsp; &nbsp; }&nbsp; &nbsp; //reopen stdin, stdout, stderr&nbsp; &nbsp; stdin=fopen(infile,"r");&nbsp; &nbsp;//fd=0&nbsp; &nbsp; stdout=fopen(outfile,"w+");&nbsp; //fd=1&nbsp; &nbsp; stderr=fopen(errfile,"w+");&nbsp; //fd=2&nbsp; &nbsp; //open syslog&nbsp; &nbsp; openlog(name,LOG_PID,LOG_DAEMON);&nbsp; &nbsp; return(0);}这是一个示例程序,它成为守护进程,挂起,然后离开。intmain(){&nbsp; &nbsp; int res;&nbsp; &nbsp; int ttl=120;&nbsp; &nbsp; int delay=5;&nbsp; &nbsp; if( (res=daemonize("mydaemon","/tmp",NULL,NULL,NULL)) != 0 ) {&nbsp; &nbsp; &nbsp; &nbsp; fprintf(stderr,"error: daemonize failed\n");&nbsp; &nbsp; &nbsp; &nbsp; exit(EXIT_FAILURE);&nbsp; &nbsp; }&nbsp; &nbsp; while( ttl>0 ) {&nbsp; &nbsp; &nbsp; &nbsp; //daemon code here&nbsp; &nbsp; &nbsp; &nbsp; syslog(LOG_NOTICE,"daemon ttl %d",ttl);&nbsp; &nbsp; &nbsp; &nbsp; sleep(delay);&nbsp; &nbsp; &nbsp; &nbsp; ttl-=delay;&nbsp; &nbsp; }&nbsp; &nbsp; syslog(LOG_NOTICE,"daemon ttl expired");&nbsp; &nbsp; closelog();&nbsp; &nbsp; return(EXIT_SUCCESS);}请注意,SIG_IGN表示捕获并忽略该信号。您可以构建一个可以记录信号接收的信号处理程序,并设置标志(例如标志以指示正常关闭)。
打开App,查看更多内容
随时随地看视频慕课网APP
我要回答