【摘要】本文主要讲述何为守护进程,以及如何自编或者利用现有程序将其伪装成守护进程。
通常,当我们写一个守护进程时,需要按照如下步骤来写:
创建一个子进程,父进程随即退出
fork()函数用来创建一个子进程;
在父进程退出后,后续所有步骤在子进程中进行。
在子进程中创建新会话
setsid()函数用来创建一个新的会话;改变子进程当前工作目录,一般设为根目录
chdir()函数用来为子进程重新分配工作目录;重设文件权限掩码
umask()函数用来更改子进程的默认掩码;关闭文件描述符
close()函数用来关闭文件描述符指定的文件;
从父进程继承的打开文件及默认打开的0、1、2在守护进程中一般不会用到,而且又浪费系统资源,所以一般要关闭他们。
getdtablesize()函数返回当前打开的文件项数;
守护进程功能实现(无限循环)
代码实例:
#include
#include
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
int i, fd, len;
char *buf = "守护进程运行中.\n";
len = strlen(buf)+1;
pid = fork(); //1.1 创建子进程
if (pid < 0) {
printf("fork error!");
exit(1);
}
if (pid>0) // 1.2父进程退出
exit(0);
setsid(); // 2.在子进程中创建新会话。
chdir("/"); // 3.设置工作目录为根目录
umask(0); // 4.设置权限掩码
for(i=0; i<getdtablesize(); i++) //5.关闭用不到的文件描述符
close(i);
//6.守护进程功能实现
while(1) { // 死循环表征它将一直运行
fd = open("/var/log/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600);
if(fd < 0) {
printf("Open file failed!\n");
exit(1);
}
write(fd, buf, len); // 将buf写到fd中
close(fd);
sleep(10);
}
printf("error: Never run here!\n");
return 0;
}
cat /var/log/daemon.log查看进程运行情况;也可以通过ps -axjf | grep 程序名查看。由于守护进程脱离了控制终端,因此,不能像其它普通进程一样将信息输出到控制终端来通知程序员。所以,只有使用syslog服务,将程序中的出错信息输入到系统日志文件中,例如/var/log/messages。
syslog是Linux的系统日志管理服务,通过守护进程syslogd来维护。该服务在启动时会读配置文件/etc/syslog.conf,该文件决定了不同类别的消息会发往何处。例如,紧急消息发到控制台上直接显示,警告消息则发到记录文件中。
该机制为程序员提供了3个接口函数,分别为openlog()、syslog()、closelog():
openlog()
void openlog(char *ident, int option, int facility);syslog()
void syslog(int priority, char *format, ...);closelog()
void closelog(void);重写上节代码:
#include
#include
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
int i, fd, len;
char *buf = "守护进程运行中.\n";
len = strlen(buf)+1;
pid = fork(); //1.1 创建子进程
if (pid < 0) {
printf("fork error!");
exit(1);
}
if (pid>0) // 1.2父进程退出
exit(0);
/*打开系统日志服务连接*/
openlog("daemon_syslog", LOG_PID, LOG_DAEMON);
if ((sid = setsid()) < 0 ) // 2.在子进程中创建新会话。
{
syslog(LOG_ERR, "%s\n", "setsid");
exit(1);
}
if ((sid = chdir("/")) < 0) // 3.设置工作目录为根目录
{
syslog(LOG_ERR, "%s\n", "chdir");
exit(1);
}
umask(0); // 4.设置权限掩码
for(i=0; i<getdtablesize(); i++) //5.关闭用不到的文件描述符
close(i);
//6.守护进程功能实现
while(1) // 死循环表征它将一直运行
{
if((fd = open("/var/log/daemon.log",\
O_CREAT|O_WRONLY|O_APPEND,0600)) < 0)
{
syslog(LOG_ERR, "open");
exit(1);
}
write(fd, buf, len); // 将buf写到fd中
close(fd);
sleep(10);
}
closelog();
exit(1);
}
意思就是希望某一个程序能够在当前会话被关闭后,照样能够运行,方法也很简单,就是利用nohup命令。
例如,我想运行一个命令sleep 1000,正常情况下,该命令在终端执行后,如果关闭该终端,命令随之结束,而不会等到1000秒之后。
通过执行:nohup sleep 1000 &命令后,查看sleep的运行情况如下:

关闭当前会话窗口,再重新打开,执行ps axjf | grep sleep命令发现它任然在执行中。
