日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

php守护进程热更新,服务器编程--守护进程

發布時間:2023/12/10 php 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php守护进程热更新,服务器编程--守护进程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

守護(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守护进程热更新,服务器编程--守护进程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。