在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;
}
Run Code Online (Sandbox Code Playgroud)
Pas*_*rkl 198
在Linux中,我想添加一个无法停止的守护进程,以及监视文件系统更改的守护进程.如果检测到任何更改,它应该将路径写入启动它的控制台+换行符.
守护进程在后台工作,并且(通常......)不属于TTY,这就是为什么你不能以你想要的方式使用stdout/stderr的原因.通常,syslog守护程序(syslogd)用于将消息记录到文件(debug,error,...).
除此之外,还有一些必要的步骤来守护进程.
如果我没记错,这些步骤是:
为您提供一个起点:查看此框架代码,其中显示了基本步骤:
/*
* daemonize.c
* This example daemonizes a process, writes a few log messages,
* sleeps 20 seconds and terminates afterwards.
*/
#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()
{
pid_t pid;
/* Fork off the parent process */
pid = fork();
/* An error occurred */
if (pid < 0)
exit(EXIT_FAILURE);
/* Success: Let the parent terminate */
if (pid > 0)
exit(EXIT_SUCCESS);
/* On success: The child process becomes session leader */
if (setsid() < 0)
exit(EXIT_FAILURE);
/* Catch, ignore and handle signals */
//TODO: Implement a working signal handler */
signal(SIGCHLD, SIG_IGN);
signal(SIGHUP, SIG_IGN);
/* Fork off for the second time*/
pid = fork();
/* An error occurred */
if (pid < 0)
exit(EXIT_FAILURE);
/* Success: Let the parent terminate */
if (pid > 0)
exit(EXIT_SUCCESS);
/* Set new file permissions */
umask(0);
/* Change the working directory to the root directory */
/* or another appropriated directory */
chdir("/");
/* Close all open file descriptors */
int x;
for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)
{
close (x);
}
/* Open the log file */
openlog ("firstdaemon", LOG_PID, LOG_DAEMON);
}
Run Code Online (Sandbox Code Playgroud)
int main()
{
skeleton_daemon();
while (1)
{
//TODO: Insert daemon code here.
syslog (LOG_NOTICE, "First daemon started.");
sleep (20);
break;
}
syslog (LOG_NOTICE, "First daemon terminated.");
closelog();
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)
gcc -o firstdaemon daemonize.c./firstdaemon检查一切是否正常工作: ps -xj | grep firstdaemon
输出应该类似于这个:
+------+------+------+------+-----+-------+------+------+------+-----+ | PPID | PID | PGID | SID | TTY | TPGID | STAT | UID | TIME | CMD | +------+------+------+------+-----+-------+------+------+------+-----+ | 1 | 3387 | 3386 | 3386 | ? | -1 | S | 1000 | 0:00 | ./ | +------+------+------+------+-----+-------+------+------+------+-----+
你应该看到的是:
阅读系统日志:
/var/log/syslog做一个: grep firstdaemon /var/log/syslog
输出应该类似于这个:
firstdaemon[3387]: First daemon started. firstdaemon[3387]: First daemon terminated.
注意:
实际上,您还需要实现信号处理程序并正确设置日志记录(文件,日志级别......).
进一步阅读:
pat*_*eza 29
man 7 daemon描述了如何非常详细地创建守护进程.我的回答只是摘自本手册.
至少有两种类型的守护进程:
- 关闭除标准输入,输出和错误之外的所有打开文件描述符(即前三个文件描述符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简化与系统的集成.
对于新式守护程序,建议执行以下步骤:
- 如果
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).
了解更多阅读整体man 7 daemon.
你不能在linux中创建一个无法杀死的进程.root用户(uid = 0)可以向进程发送信号,并且有两个信号无法捕获,SIGKILL = 9,SIGSTOP = 19.其他信号(未被捕获时)也可能导致进程终止.
您可能需要一个更通用的守护进程函数,您可以在其中指定程序/守护程序的名称,以及运行程序的路径(可能是"/"或"/ tmp").您可能还想为stderr和stdout(以及可能使用stdin的控制路径)提供文件.
以下是必要的包括:
#include <stdio.h> //printf(3)
#include <stdlib.h> //exit(3)
#include <unistd.h> //fork(3), chdir(3), sysconf(3)
#include <signal.h> //signal(3)
#include <sys/stat.h> //umask(3)
#include <syslog.h> //syslog(3), openlog(3), closelog(3)
Run Code Online (Sandbox Code Playgroud)
这是一个更通用的功能,
int
daemonize(char* name, char* path, char* outfile, char* errfile, char* infile )
{
if(!path) { path="/"; }
if(!name) { name="medaemon"; }
if(!infile) { infile="/dev/null"; }
if(!outfile) { outfile="/dev/null"; }
if(!errfile) { errfile="/dev/null"; }
//printf("%s %s %s %s\n",name,path,outfile,infile);
pid_t child;
//fork, detach from process group leader
if( (child=fork())<0 ) { //failed fork
fprintf(stderr,"error: failed fork\n");
exit(EXIT_FAILURE);
}
if (child>0) { //parent
exit(EXIT_SUCCESS);
}
if( setsid()<0 ) { //failed to become session leader
fprintf(stderr,"error: failed setsid\n");
exit(EXIT_FAILURE);
}
//catch/ignore signals
signal(SIGCHLD,SIG_IGN);
signal(SIGHUP,SIG_IGN);
//fork second time
if ( (child=fork())<0) { //failed fork
fprintf(stderr,"error: failed fork\n");
exit(EXIT_FAILURE);
}
if( child>0 ) { //parent
exit(EXIT_SUCCESS);
}
//new file permissions
umask(0);
//change to path directory
chdir(path);
//Close all open file descriptors
int fd;
for( fd=sysconf(_SC_OPEN_MAX); fd>0; --fd )
{
close(fd);
}
//reopen stdin, stdout, stderr
stdin=fopen(infile,"r"); //fd=0
stdout=fopen(outfile,"w+"); //fd=1
stderr=fopen(errfile,"w+"); //fd=2
//open syslog
openlog(name,LOG_PID,LOG_DAEMON);
return(0);
}
Run Code Online (Sandbox Code Playgroud)
这是一个示例程序,它成为守护进程,挂起,然后离开.
int
main()
{
int res;
int ttl=120;
int delay=5;
if( (res=daemonize("mydaemon","/tmp",NULL,NULL,NULL)) != 0 ) {
fprintf(stderr,"error: daemonize failed\n");
exit(EXIT_FAILURE);
}
while( ttl>0 ) {
//daemon code here
syslog(LOG_NOTICE,"daemon ttl %d",ttl);
sleep(delay);
ttl-=delay;
}
syslog(LOG_NOTICE,"daemon ttl expired");
closelog();
return(EXIT_SUCCESS);
}
Run Code Online (Sandbox Code Playgroud)
请注意,SIG_IGN表示捕获并忽略该信号.您可以构建一个可以记录信号接收的信号处理程序,并设置标志(例如标志以指示正常关闭).
尝试使用该daemon功能:
#include <unistd.h>
int daemon(int nochdir, int noclose);
Run Code Online (Sandbox Code Playgroud)
从手册页:
守护进程()函数用于希望从控制终端分离并在后台作为系统守护进程运行的程序.
如果nochdir为零,则daemon()将调用进程的当前工作目录更改为根目录("/"); 否则,当前工作目录保持不变.
如果noclose为零,则守护进程()将标准输入,标准输出和标准错误重定向到/ dev/null; 否则,不会对这些文件描述符进行任何更改.
我可以停止第一个要求"一个无法停止的守护进程......"
不可能是我的朋友; 但是,您可以使用更好的工具,内核模块来实现相同的功能.
http://www.infoq.com/articles/inotify-linux-file-system-event-monitoring
所有守护进程都可以停止.有些人比其他人更容易被阻止.即使与伴侣保持一对守护进程,如果丢失,重新生成伙伴也可以停止.你只需要更加努力地工作.
如果您的应用是以下之一:
{
".sh": "bash",
".py": "python",
".rb": "ruby",
".coffee" : "coffee",
".php": "php",
".pl" : "perl",
".js" : "node"
}
Run Code Online (Sandbox Code Playgroud)
并且您不介意NodeJS依赖项,然后安装NodeJS,然后执行以下操作:
npm install -g pm2
pm2 start yourapp.yourext --name "fred" # where .yourext is one of the above
pm2 start yourapp.yourext -i 0 --name "fred" # run your app on all cores
pm2 list
Run Code Online (Sandbox Code Playgroud)
要使所有应用程序在重新启动时运行(并守护pm2):
pm2 startup
pm2 save
Run Code Online (Sandbox Code Playgroud)
现在你可以:
service pm2 stop|restart|start|status
Run Code Online (Sandbox Code Playgroud)
(还可以轻松地让您监视应用目录中的代码更改,并在发生代码更改时自动重启应用进程)