守护(Daemon)进程又叫作“精灵进程”,虽然守护进程这个名字更为常用,但是个人感觉还是精灵进程较为机灵可爱些。服务器进程一般都是守护进程,这类进程的一个显著特点就是无交互地在后台进程。注意:这里所说的无交互并不是说真的不能和这类进程打交道,不能控制其运行,那样他们还能提供什么服务?而是说不能通过传统的终端用类似shell的交互方式控制其运行。

那么怎么创建守护进程呢?咋们就边看代码边讲解。

1

2  #include 3  #include 4  #include 5  #include 6  #include 7  #include 8  #include 9  #include 10  #include 11  #include 12  #include 13  #include 14  #include 15

1-15行: 加载必要的头文件,其实这些头文件并不是随意罗列的,而是当需要时再添加,具体方法是需要调用某个库函数或者系统调用时,用man查找它被定义的头文件的路径,然后添加之。如果在编译的时候显示某个函数没有被定义的错误,这时也可以用man查找所需的头文件之所在。有的时候甚至需要用grep到/usr/include目录下面查找变量或者函数的定义。

16  void daemonize(const char *prgname, ...)

17  {

18          va_list args;

19          char buf[512];

20          int pid, i;

21          struct sigaction act, oldact;

22          struct rlimit lim;

23

24          /* Detach controlling terminal */

25          if ((pid = fork()) < 0)

26                  exit(1);

27          else if (pid > 0)

28                  _exit(0);

29          setsid();

30

25-29行:从当前进程fork出一个子进程,然后当前进程退出。如果当前进程是shell从前台启动的的话,当当前进程退出的时候,子进程将变成孤儿进程,接着自动被启动进程(init)收养,当然它所在的进程组也将从前台转为后台。调用完setsid()之后,子进程将创建一个新的会话和进程组,sid和gpid都是子进程的pid,因为子进程已经和当前进程不属于一个会话,那么与会话相关联的控制终端也不复存在。如果你足够细心,你可能注意到了这段代码中用了两个进程退出函数exit和_exit,为什么要如此呢?_exit并不会执行由atexit或者on_exit注册的进程退出回调函数,除此之外,它和exit并没有区别。假设用户在调用daemonize把当前进程守护化之前注册过进程退出回调函数,如果fork成功而当前进程通过调用exit退出,那么回调函数将被执行,而这时执行回调函数也许是不当的,因为子进程并没有退出,当子进程退出的时候也许还将执行一遍回调函数。exit和_exit的选用正是为了保证进程退出回调函数被且尽被执行一次。以下对exit和_exit的选用也是基于此目的,遇到时将不再赘述。事实上,daemonize函数应该尽早调用,最好不要再其前面做太多的非必要操作,类似注册进程退出回调函数的举动应该尽量避免。

31          /* Avoid owning controlling terminal again */

32          memset(&act, 0, sizeof(act));

33          act.sa_handler = SIG_IGN;

34          sigemptyset(&act.sa_mask);

35          sigaction(SIGHUP, &act, &oldact);

36          if ((pid = fork()) < 0)

37                  exit(1);

38          else if (pid > 0)

39                  _exit(0);

40          /* Wait for the death of it's parent. */

41          while (getppid() != 1)

42                  ;

43          sigaction(SIGHUP, &oldact, NULL);

44

31-43行:这段代码的意义何在呢?有些UNIX操作系统(如SVR4)的会话首进程打开一个终端设备时,如果其所在会话组并没有控制终端,那么这个终端设备将自动成为这个会话组的控制终端。通过这次的fork而产生的孙子进程因为不是会话首进程,也就失去了为此会话设置控制终端的能力。当会话首进程退出的时候可能向其所在会话组的所有进程发送SIGHUP信号,而SIGHUP信号的默认处理函数是结束进程。为了防止孙子进程因此意外结束,忽略SIGHUP信号直到子进程退出,孙子进程被启动进程(init)收养。我查看了Linux内核的相关代码,发现只有当进程被SIGSTP终止时才会被发送SIGHUP和SIGCONT信号,所以此段关于信号的处理部分在Linux下是无效的,也许其他操作系统行为有异,姑且加之。

45          /* Deal with file operations */

46          umask(0);

47          if (chdir("/") < 0)

48                          exit(1);

49          if (getrlimit(RLIMIT_NOFILE, &lim) < 0)

50                  exit(1);

51          if (lim.rlim_cur == RLIM_INFINITY)

52                  lim.rlim_cur = 1024;

53          for (i = 0; i < lim.rlim_cur; i ++) {

54                  if (close(i) < 0 && errno != EBADF)

55                          exit(1);

56          }

57          if (open("/dev/null", O_RDWR) < 0

58                          || dup(0) < 0

59                          || dup(0) < 0)

60                  exit(1);

61

45-60行:设置文件掩码为0,改变当前工作目录到系统根目录,关闭所有打开的文件描述符,并把标准输入、标准输出和标准错误输出重定向到空设备(/dev/null),使他们保持沉默。

62          /* Ignore all traditional signals */

63          for (i = 1; i < 32; i ++)

64                  sigaction(i, &act, NULL);

65

62-65行:忽略所有的传统信号,当然SIGKILL信号是无法忽略的,所以我也没有检查返回值。按照设计惯例:SIGHUP用来热更新系统配置;SIGTERM用来结束进程,这个信号一般是需要捕捉并处理的,不然被SIGKILL强制杀死的滋味可不好受哦。(修正:62-65行的操作最好免除,因为大部分信号是不希望被忽略的,如SEGV)。

66          /* Initialize the log file */

67          va_start(args, prgname);

68          vsnprintf(buf, sizeof(buf), prgname, args);

69          va_end(args);

70          openlog(buf, LOG_CONS | LOG_PID, LOG_DAEMON);

71  }

72

66-71行:一般的服务器都需要用日志(log)记录守护进程的状态等信息以备分析和调试之用,这段代码就是打开到系统日志服务器(syslogd)的连接,并设置记录守护进程的进程名和pid。

至此,进程的守护化就顺利完成了。

服务器程序一般都具有排他性,换句话说就是一个操作系统上只允许有一个守护进程实例存在。以下代码实现了这个功能:

96 int uniqued(const char *prgname)

97 {

98         char buf[512];

99         int fd, retval = -1;

100

101         assert(prgname != NULL);

102         snprintf(buf, sizeof(buf), "/var/run/%s.pid", prgname);

103         if ((fd = open(buf, O_RDWR | O_CREAT)) < 0)

104                 goto out;

105         if (flock(fd, LOCK_EX | LOCK_NB) < 0) {

106                 if (errno == EWOULDBLOCK)

107                         retval = 0;

108                 else

109                         unlink(buf);

110                 goto err;

111         }

112         if (ftruncate(fd, 0) < 0)

113                 goto err;

114         snprintf(buf, sizeof(buf), "%ld\n", (long)getpid());

115         if (write(fd, buf, strlen(buf)) != strlen(buf))

116                 goto err;

117         retval = fd;

118

119 out:

120         return retval;

121

122 err:

123         while (close(fd) < 0 && errno == EINTR)

124                 ;

125         goto out;

126 }

遵从惯例,记录有守护进程进程号的文件被放在/var/run/目录下,并被命名为:守护进程名.pid。函数uniqued利用排他文件锁保证了守护进程实例的单一性。

php守护进程热更新,服务器编程--守护进程相关推荐

  1. linux进程热更新 go,Golang热更新原理

    Linux进程间通信方式 首先,进程之间为什么要进行通信呢?主要有以下应用场景: 数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间. 共享数据:多个进程想要操作共 ...

  2. Linux高性能服务器编程:进程池和线程池原理及应用(有图有代码有真相!!!)

    一.问题引入 在前面编写多进程.多线程服务器时通过动态创建子进程和子线程来实现并发服务器,这样做有以下缺点: 1)动态创建进程.线程将会比较耗费时间,将导致较慢的客户响应. 2)动态创建的子进程只为一 ...

  3. python程序更新实现_Python 软件热更新

    Python 软件热更新 本篇文章涉及技术知识如下: Redis threading 多线程 PyQt5 importlib 热更新 场景 咱们在平时运行一些长时间都会一直运行的软件(如:某些云同步软 ...

  4. 【热更新】游戏热更新方案

    游戏热更新方案 热更新演化 热更新方案 [1] 进程切换 1.1 利用fork.exec切换 1.2 利用网关切换 1.3 微服务 - 进程切换注意要点 [2] 动态库替换 [3] 脚本语言热更新 热 ...

  5. native react 更新机制_react-native热更新全方位讲解

    最近在研究热更新技术,看了网上各个大佬的博客,整体流程上总是卡壳.跳了几天坑,刚刚终于把简单的热更新流程跑通,现在也正在一边学习更新,一边整理资料,在此篇博客上记录操作流程,希望我的实践能帮助各位同行 ...

  6. Cordova App 热更新 超详细教程

    前言: Cordova热更新的一些要点: 1.在不用重新安装App的情况下,更新你的代码.可以越过应用商店的审核步骤. 2.涉及的插件依赖发生变化时,无法使用热更新,需要去应用商店下载最新版本安装. ...

  7. webpack配置及热更新原理

    1:配置webpack 1:新建一个vue项目: 进入目录,在终端创建: vue create 项目名: 2:创建新文件 对应的文件目录src下新建存放css文件index.css,项目的入口文件ma ...

  8. 数据系统服务器更新是什么,云更新服务器是啥意思

    云更新服务器是啥意思 内容精选 换一换 企业主机安全在控制台提供总览页面,包括云主机的防护状态.当前开启防护的云主机最近24小时的风险统计.最近一周风险趋势和最近一周TOP5风险的云服务器,帮助您实时 ...

  9. cocos2dx lua 热更新

    文章新地址 原理   每次登陆游戏利用cocos的assetManager从服务器拉去当前最新的两个文件. 一个是version.mainifest,一个project.mainifest. 这两个文 ...

最新文章

  1. xman的思维导图快捷键_一次性入门大纲笔记神器“幕布”,支持一键生成思维导图...
  2. 【实战】用机器学习来提升你的用户增长(二)
  3. Linux shell控制台改变显示前缀
  4. php会员权限分析,PHP会员权限控制-很详细(转)
  5. SAP Spartacus home页面的layout,template,section和slots
  6. Mol. Biol. Evol. | 中科院动物所揭示石山叶猴适应喀斯特环境的遗传机制
  7. python元类使用场景_Python元类使用简介
  8. 177. Nth Highest Salary
  9. Git Tag 使用
  10. vitualbox更改安装的位置不放在系统盘
  11. 打印机出现另存为xps_共享打印机打印出现另存为*.xps,现把打印机驱动改成打印机的型号,可客户机还是打...
  12. 微信小程序登录注册界面
  13. jade---模板项目
  14. java 地铁线路_个人项目-地铁出行路线规划(Java代码实现)
  15. 2021 USGS Landsat 8 批量下载教程
  16. 专访雪球网技术团队:用Node.js做前端的类SOA架构
  17. 基于SSM+VUE的交通事故案例库系统(前后端分离)
  18. 阿里总参谋长曾鸣:区块链中没有绝对的“去中心化”
  19. 屏幕尺寸大全和UI设计规范
  20. cad立体图怎么旋转看图_cad布局中图形怎么旋转

热门文章

  1. Leetcode刷题(4)罗马数字转整数
  2. PS教程第三课:PS界面
  3. [css] 怎么使用css选择空链接?
  4. [js] 在设置keyup监听事件后按F5刷新和按浏览器中刷新键刷新有什么区别?
  5. 前端学习(1685):前端系列实战课程之设置难度
  6. 前端学习(392):京东制作页面1京东项目项目介绍
  7. 第一百五十一期:最新计算机技能需求排名出炉:Python仅排第三,第一你猜得到吗?
  8. 第一百二十三期:免费在线制图神器!不上水印支持中文版,GitHub标星已破1万2
  9. 医疗:pacs(3)
  10. vue-cli 4.x 配置 htmlWebpackPlugin.options.title