守护进程deamon
是一个后台进程,无需用户输入就能运行,用来在系统后台提供某种服务。
常见的守护进程有Wbe服务器、邮件服务器以及数据库服务器等等。守护进程不能控制终端,所以任何的输入和输出都需要做特殊处理。
创建守护进程的过程并不复杂,首先执行fork(),将父进程退出。
一个守护进程是从shell脚本或者命令启动,守护进程和应用程序不同的地方是守护进程不是交互式的,它在后台运行,不控制终端。
守护进程既不需要从标准输入设备读入信息,也不需要从标准输出设备输出信息。
下一步是在子进程中调用setside(),取消进程和任何控制终端的关联。
下一步是让根目录成为子进程的当前工作目录(可选项)chdir("/")(如果将工作目录设置为根目录的,在通过通道实现对守护进程输入命令的时候要注意读管道的路劲是绝对路径而不能写相对路径)
因为任何进程如果它的当前目录在一个被安装的文件系统上,那么就会妨碍这个文件系统被卸载。
接下来执行umask(0),将进程的umask设置为0断开与所继承的文件的权限上的联系,这样可以使守护进程建立的程序其用户可以正常使用。
最后关闭进程的任何不必要的文件描述符。close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);
具体关闭哪些根据守护进程的需要和要求。
相关的函数有:
setsid()创建一个新会话和一个新进程组,然后守护进程成为新会话的会话领导,以及新进程组的进程组领导。setsid()调用还保证新会话没有控制终端
如果调用该函数的进程已经是一个进程组的领导进程,函数调用失败返回-1,并设置errno,成功则返回新会话ID。
chdir()函数,int chadir(const char *pathname),成功返回0,失败返回-1.
umask()函数,mode_t umask(mode_t mask);
创建守护进程代码:
void setdaemon()
{
pid_t pid, sid;
pid = fork();
if (pid < 0)
{
printf("fork failed\n");
exit(EXIT_FAILURE);
}
if(pid > 0)
{
exit(EXIT_SUCCESS);//in the parent
}
if((sid = setsid()) < 0)
{
printf("setsid failed\n");
exit(EXIT_FAILURE);
}
if((chdir("/")) < 0)
{
printf("chdir failed\n");
exit(EXIT_FAILURE);
}
umask(0);
//close(STDIN_FILENO);//if close stdin,then daemon_console failed
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
一旦系统调用setsid,它就不再有控制终端。可以通过syslog提供服务,记录守护进程的各种输出信息
openlog函数打开日志,syslog写入日志,closelog关闭日志。
#include <syslog.h> void openlog(const char *ident, int option,int facility); void syslog(int priority, const char*format, ...); void closelog(void);
openlog函数发起到系统日志服务器的连接,参数
ident是要向每个消息加入的字符串,典型的情况
是要设置成程序的名称。
参数option是下面一个或多个值的“或”
名称 | 含义 |
LOG_CONS | 如果系统日志服务器不能用,写入控制台 |
LOG_NDELAY | 立即打开连接,正常情况下,直到发送第一 条消息才打开连接 |
LOG_PERROR | 打印输出到stderr |
LOG_PID | 每条消息中包含进程 PID |
参数facitity指定程序发送消息的类型。
名称 | 含义 |
LOG_AUTHPRIV | 安全授权消息 |
LOG_CRON | 时钟守护进程: cron和at |
LOG_DAEMON | 其他系统守护进程 |
LOG_KERN | 内核消息 |
LOG_LPR | 打印机子系统 |
LOG_MAIL | 邮件子系统 |
LOG_USER | 默认 |
参数priority指定消息的重要性。
名称 | 含义 |
LOG_EMERG | 系统不能使用 |
LOG_ALERT | 立即采取措施 |
LOG_CRIT | 紧急事件 |
LOG_ERR | 出错条件 |
LOG_WARNING | 警告条件 |
LOG_NOTICE | 正常但重大事件 |
LOG_INFO | 信息消息 |
LOG_DEBUG | 调试信息 |
syslog代码例子:
syslog(LOG_INFO, "my daemin is OK");
严格的说, openlog和closelog是可选的,因为函数syslog在首次使用的时候自动打开日志文件。linux系统上日志文件通常是/var/log/messages
要和一个守护进程通信,你要向它发送信号,让它以某种方式相应。例如:强行要求一个守护进程重新读取它的配置文件,或者改变守护进程的行为,或者指示守护进程结束运行。
使用信号与守护进程通信。守护进程中添加信号捕捉函数
void catch_Signal(int Sign)
{
switch(Sign)
{
case SIGTERM:
exit(EXIT_SUCCESS);
}
}
通过shell脚本结束守护进程。
#!/bin/sh
WHOAMI=`whoami`
PID=`ps -u $WHOAMI | grep abc | awk '{print $1}'`
if (test "$PID" != "") then
kill $PID
fi
注意:脚本文件的名称不要有‘ abc’字样,不然执行ps的时候会把脚本名称也print出来
使用FIFO与守护进程通信。
读FIFO
void readfifo()
{
int len = ;
char buf[];
memset(buf, , sizeof(buf));
int fd = open("/home/test/1/fifo1",O_RDONLY);
while ((len = read(fd, buf, sizeof(buf)))> )
{
printf("%s\n", buf);
}
close(fd);
return;
}
写FIFO
void writefifo()
{
int len = ;
char buf[];
memset(buf, , sizeof(buf));
int fd = open("/home/test/1/fifo1",O_WRONLY);
scanf("%s", buf);
write(fd, buf, sizeof(buf));
close(fd);
return;
}
如何让后台运行的daemon程序向屏幕打印信息。
daemon.c
1、执行mkfifo fifo1命令,创建一个名为fifo1的管道文件。
2、编译daemon.c,执行程序,关闭当前控制台终端窗口,让程序在后台运行。
3、打开一个新的终端窗口,通过ps aux找到进程PID
4、执行 kill –s 2 PID tty > fifo1
如何确保只启动一个守护进程的实例。
#!/bin/sh
WHOAMI=`whoami`
PID=`ps -u $WHOAMI | grep abc | awk '{print $1}'`
if (test "$PID" = "") then
./abc
fi
注意:脚本文件的名称不要有‘ abc’字样,不然执行ps的时候会把脚本名称也print出来