找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 3840|回复: 0

【C】教你如何在CentOS下写一个“呆萌”(守护进程Daemon)

[复制链接]
发表于 2017-1-18 18:46:53 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
写“呆萌”,也就是后台守护进程(“Daemon”的谐音是“呆萌”),需要注意以下几点:
1、用户如果用ssh控制他的服务器,然后服务器跑了你的呆萌,当用户断开ssh连接的时候,你的呆萌也能继续运行。
2、你的呆萌应该把运行的过程写入到日志里,而不是输出到屏幕上。
3、保证稳定性,别因为无视了一个signal而挂掉。
4、为了便于管理,你应该在临时文件夹写一个lock文件,里面用文本记录你的PID号,以便于用户用脚本控制你的呆萌进程(典型的,使其能兼容chkconfig的配置方式)

一般来说你的守护进程被直接运行的时候,它的父进程应该是bash,然后bash会一直等待你的进程结束,才会继续执行后面的脚本。
为了能让你的程序不堵住bash的脚本执行过程,一般的做法是启动一个新的进程,然后旧进程退出,让bash继续执行,而新进程则作为呆萌保持执行过程。
这里最常用的函数就是fork()了,它在unistd.h里面有定义,这个函数的作用是:
  • 复制自身进程,新的进程拥有完全一样的内存空间(但它一般并不需要完全复制自身的内存数据,虽然此时新的进程的内存数据和旧进程完全相同)。
  • 如果自己是父进程,返回子进程的PID;如果自己是子进程,返回0;如果分身失败,则返回-1.
调用fork()后,旧进程就可以退出了,然后bash继续运行,与此同时你的新进程也在后台运行。但这还不够,因为此时你的旧进程和新进程都不是一个进程组的组长,因此当你的旧进程被杀的时候,新进程也会死。此时你需要调用setsid()来设置新的进程会话ID,也就是让你的新进程成为一个进程组的组长,这样的话它就真正从父进程独立出来了。

此时新进程需要做的事情有以下几个:
  • 把PID写到lock文件里,便于让用户找到你。
  • 关闭没用的文件描述符,比如stdin,stdout和stderr,或者你把stdout和stderr重定向到你的log,但其实自己fopen一个新的fd比做一次重定向方便得多。
  • 设置信号(signal)处理,将无关信号屏蔽,然后对SIGPIPE、SIGHUP、SIGTERM做出响应。


这里给一份例子,展示一个程序如何进入呆萌模式。
  1. // 需要的头文件
  2. #include<stdio.h>
  3. #include<signal.h>
  4. #include<stdarg.h>
  5. #include<unistd.h>

  6. // 程序信息
  7. #define EXEC_NAME foo
  8. #define LOCK_FILE "/var/tmp/"EXEC_NAME".lock"
  9. #define LOG_FILE EXEC_NAME".log"

  10. static volatile int g_Quit = 0; // 是否退出
  11. static void _signal_handler(int sig);

  12. void Inst_Log(char *format, ...); // 记录log

  13. //==============================================================================
  14. //Func: _Inst_Daemonize
  15. //Desc: Make program run as a daemon
  16. //------------------------------------------------------------------------------
  17. static int _Inst_Daemonize()
  18. {
  19.         int i,lfp;
  20.         char str[10];
  21.        
  22.         if(getppid() == 1)
  23.         {
  24.                 fprintf(stderr, EXEC_NAME" was still running.\n");
  25.                 return 0;
  26.         }
  27.        
  28.         i = fork();
  29.         if(i < 0)
  30.         {
  31.                 // fork error
  32.                 return 0;
  33.         }
  34.         if(i > 0)
  35.         {
  36.                 // Parent exits, child (daemon) continues
  37.                 return 0;
  38.         }
  39.        
  40.         setsid(); // obtain a new process group
  41.        
  42.         fclose(stdin);
  43.         fclose(stdout);
  44.         fclose(stderr);
  45.        
  46.         lfp=open(LOCK_FILE,O_RDWR|O_CREAT,0644);
  47.         if(lfp<0)
  48.         {
  49.                 // Could not open lock file.
  50.                 Inst_Log("Open lock file failed.\n");
  51.                 return 0;
  52.         }
  53.        
  54.         if(lockf(lfp, F_TLOCK, 0) < 0)
  55.         {
  56.                 // can not lock
  57.                 Inst_Log("Lock lock file failed.\n");
  58.                 return 0;
  59.         }
  60.        
  61.         // first instance continues
  62.         sprintf(str,"%d\n",getpid());
  63.         write(lfp,str,strlen(str)); // record pid to lockfile
  64.        
  65.         signal(SIGCHLD,SIG_IGN); // ignore child
  66.         signal(SIGTSTP,SIG_IGN); // ignore tty signals
  67.         signal(SIGTTOU,SIG_IGN);
  68.         signal(SIGTTIN,SIG_IGN);
  69.         signal(SIGPIPE,SIG_IGN);
  70.         signal(SIGHUP,_signal_handler); // catch hangup signal
  71.         signal(SIGTERM,_signal_handler); // catch kill signal
  72.        
  73.         return 1;
  74. }

  75. //==============================================================================
  76. //Func: Inst_Log
  77. //Desc: Print log to a specified file.
  78. //------------------------------------------------------------------------------
  79. void Inst_Log(char *format, ...)
  80. {
  81.         FILE *logfile = NULL;
  82.         va_list ap;
  83.        
  84.         logfile=fopen(LOG_FILE,"a");
  85.         if(logfile)
  86.         {
  87.                 va_start(ap, format);
  88.                 vfprintf(logfile, format, ap);
  89.                 va_end(ap);
  90.                 fclose(logfile);
  91.         }
  92. }

  93. //==============================================================================
  94. //Func: _signal_handler
  95. //Desc: 处理信号
  96. //------------------------------------------------------------------------------
  97. static void _signal_handler(int sig)
  98. {
  99.         switch(sig)
  100.         {
  101.         case SIGHUP:
  102.                 break;
  103.         case SIGTERM:
  104.                 Inst_Log("Terminate signal.\n");
  105.                 g_Quit = 1;
  106.                 break;
  107.         }
  108. }
复制代码
参考资料:
技术上来说,呆萌、服务、进程都有些什么区别?http://askubuntu.com/questions/1 ... service-and-process
为什么呆萌要运行setsid?http://unix.stackexchange.com/qu ... emonizing-a-process


回复

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-12-22 10:17 , Processed in 0.032930 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表