分享

linux守护进程 编写要点

 心不留意外尘 2016-05-10
http://blog.csdn.net/gpengtao/article/details/7889728
2012
   守护进程(Daemon)是运行在后台的一种特殊进程,脱离于终端。它在执行过程中产生的信息也不会在终端上显示。守护进程周期性地执行某种任务或等待处理某些事件的发生。Linux的大多数服务器就是用守护进程实现的。
编写要点:(最后有完整示例)
1.屏蔽一些有关终端操作的信号。这是为了防止在守护进程还没有正常运行起来前,受到终端干扰退出或者挂起。代码:
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGHUP, SIG_IGN);
2.在后台运行。为避免挂起控制终端将其放入后台执行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。代码:
if( (pid = fork()) > 0)
    exit(0);    //父进程,结束父进程,子进程继续
3.脱离控制终端和进程组。代码:
setsid();  //刚才fork的子进程调用
调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
4.禁止进程重新打开控制终端。现在,进程已经成为无终端的会话组长,并且该会话没有控制终端,但会话组长可以重新申请打开一个控制终端。所以要让进程不是会话组长,来禁止进程重新打开控制终端。代码:
if( (pid = fork()) > 0)
   exit(0);  //之前的子进程结束,子子进程继续运行
此时的进程组id、会话id是之前子进程的pid,现在程序运行的是子子进程。虽然刚才的会话组长结束了,但是它pid还在,因为进程组、会话的生命期结束消失是在这个组、会话中所有进程都结束时才发生的。
5.关闭打开的文件描述符。进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。代码:
#define MAX_FILENO 1024
for(i = 0; i < MAX_FILENO; ++i)
  close(i);
程序能打开的文件最大个数,在头文件<linux/limits.h>中 NR_OPEN 指定,查看并使用此值。
6.改变当前工作目录。进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。写运行日志的进程将工作目录改变到特定目录如/tmp。代码:
chdir("/");
7.重设文件创建掩码。进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除。代码:
umask(0);
8.处理SIGCHLD信号。对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN来解决问题。代码:
signal(SIGCHLD, SIG_IGN);
上面说了这么多都是守护进程的编写要点,值得一提的是系统已经给我们提供了API让进程直接成为守护进程:

#include <unistd.h>

int daemon(int nochdir, int noclose);  //使进程成为守护进程

//0,则改变工作目录到“/”; 0,则重定向stdin、stdout、stderr到“/dev/null”

此外,守护进程可以与日志守护进程建立联系,将日志信息写入日志文件。用到的函数:
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
//打开当前程序与日志守护进程之间的联系。
ident,是一个标记,ident所表示的字符串将固定地加在每行日志的前面以标识这个日志,通常就写成当前程序的名称以作标记。
option,一般是下列选项值取“与”运算(使用“|”表示,如“LOG_CONS | LOG_PID”)的结果:
  LOG_CONS:如果送到system logger时发生问题,直接写入系统终端;
  LOG_NDELAY:立即开启连接,通常连接是在第一次写入消息时才打开的;
    LOG_NOWAIT: Don't wait for child processes that may have  been  created while logging the message.  
               (The GNU C library does not create a child process, so this option has no effect on Linux.)
    LOG_ODELAY:The converse of LOG_NDELAY; opening of the connection is delayed until syslog() is called.
               (This is the default,and need not be specified.)
  LOG_PERROR:将消息也同时送到stderr设备;
  LOG_PID:将进程PID含入所有消息中。
facility,指明记录日志的程序的类型,它主要具有如下几类日志类型:
  LOG_AUTH :安全/授权消息
  LOG_AUTHPRIV:安全/授权消息(私有)
  LOG_CRON:时间守护进程(cron和at)专用
  LOG_DAEMON:其它系统守护进程
  LOG_KERN:核心消息
  LOG_LOCAL0到LOG_LOCAL7:系统保留
  LOG_LPR:printer子系统
  LOG_MAIL:mail子系统
  LOG_NEWS:USENET新闻子系统
  LOG_SYSLOG:syslogd进程内部所产生的消息
  LOG_USER(缺省):一般使用者缺省使用消息
  LOG_UUCP:UUCP子系统
  LOG_FTP:FTP子系统使用
void syslog(int priority, const char *format, ...);
//产生一条日志信息,然后由日志守护程序将其发送到各日志文件。
level priority,紧急级别:
    LOG_EMERG:紧急状况
  LOG_ALERT:高优先级问题,比如说数据库崩溃等,必须要立即采取反应行动
  LOG_CRIT:重要状况发生,比如硬件故障
  LOG_ERR:错误发生
  LOG_WARNING:警告发生
  LOG_NOTICE:一般状况,需要引起注意
  LOG_INFO:信息状况
  LOG_DEBUG:调试消息
第二个参数是消息及其格式,之后是格式对应的参数,如同C语言里面printf输出函数一样使用。
  在实际使用中,如果我们的程序要使用系统日志功能,只需要在程序启动时使用openlog函数来连接syslogd程序,后面随时用syslog函数写日志就行了。
void closelog(void);
//关闭日志。虽然这个函数简单,但必不可少,因为日志也是资源,只打开不管可能会造成内存不足。
守护程序完整示例:
  1. #include <unistd.h>  
  2. #include <signal.h>  
  3. #include <fcntl.h>  
  4. #include <sys/types.h>  
  5. #include <sys/stat.h>  
  6. #include <sys/syslog.h>  
  7. #include <sys/param.h>  
  8. #include <stdio.h>  
  9. #include <stdlib.h>  
  10. #include <time.h>  
  11. #define MAX_FILENO 1024  
  12. int init_daemon()  
  13. {  
  14.     int pid;  
  15.     int i;  
  16.     signal(SIGTTOU,SIG_IGN);  
  17.     signal(SIGTTIN,SIG_IGN);  
  18.     signal(SIGTSTP,SIG_IGN);  
  19.     signal(SIGHUP ,SIG_IGN);  
  20.     if( (pid = fork()) > 0)  
  21.     {  
  22.         exit(EXIT_SUCCESS);  
  23.     }  
  24.     else if(pid< 0)  
  25.     {  
  26.         perror("fork");  
  27.         exit(EXIT_FAILURE);  
  28.     }  
  29.     setsid();  
  30.     if( (pid = fork()) > 0)  
  31.     {  
  32.         exit(EXIT_SUCCESS);  
  33.     }  
  34.     else if(pid< 0)  
  35.     {  
  36.         perror("fork");  
  37.         exit(EXIT_FAILURE);  
  38.     }  
  39.     for(i = 0; i < MAX_FILENO; ++i)  
  40.     {  
  41.         close(i);  
  42.     }  
  43.     open("/dev/null", O_RDONLY);  
  44.     open("/dev/null", O_RDWR);  
  45.     open("/dev/null", O_RDWR);  
  46.     chdir("/tmp");  
  47.     umask(0);  
  48.     signal(SIGCHLD,SIG_IGN);  
  49.     return 0;  
  50. }  
  51. int main(int argc,char *argv[])  
  52. {  
  53.     init_daemon();  
  54.     openlog("MyLog", LOG_PID, LOG_KERN);  
  55.     while(1)  
  56.     {  
  57.         sleep(1);  
  58.         syslog(LOG_INFO,"This is my log : %s\n",argv[0]);  
  59.     }  
  60. }  
此时,ps -axj,显示:          
PPID   PID  PGID   SID  TTY   TPGID STAT   UID   TIME  COMMAND
 1    4613  4612   4612  ?     -1    S     1000   0:00  ./main
打开/var/log/messages 文件:
MyLog[4613]: This is my log : ./main





                                    

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多