程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> (七) 一起學 Unix 環境高級編程(APUE) 之 進程關系 和 守護進程,高級編程apue

(七) 一起學 Unix 環境高級編程(APUE) 之 進程關系 和 守護進程,高級編程apue

編輯:關於C語言

(七) 一起學 Unix 環境高級編程(APUE) 之 進程關系 和 守護進程,高級編程apue


.

.

.

.

.

目錄

(一) 一起學 Unix 環境高級編程(APUE) 之 標准IO

(二) 一起學 Unix 環境高級編程(APUE) 之 文件 IO

(三) 一起學 Unix 環境高級編程(APUE) 之 文件和目錄

(四) 一起學 Unix 環境高級編程(APUE) 之 系統數據文件和信息

(五) 一起學 Unix 環境高級編程(APUE) 之 進程環境

(六) 一起學 Unix 環境高級編程(APUE) 之 進程控制

(七) 一起學 Unix 環境高級編程(APUE) 之 進程關系 和 守護進程

 

 

進程關系是《APUE》第三版的第九章,這章的內容對我們來說通常意義不大,因為它終篇貫穿著一個概念,叫做“終端”,而現在已經幾乎無法見到真正的終端了。

但是不了解這章就無法解釋第13章(守護進程)的內容,大家要了解終端和進程之間關系才能了解守護進程是個什麼東西。

進程關系與守護進程這兩章內容不多,而且有諸多關聯,所以我們把這兩章的內容放到一起討論。

先來看看幾個概念。

1.終端

真正意義上的終端是“笨設備”,只能接收命令的輸入並返回結果。你問它 1+1=? 它也不知道,它只能把你的問題傳給計算機,再把計算機返回的結果顯示給你。

它出現在計算機既昂貴又龐大的年代。那時候的計算機昂貴到了只有一部分公司買得起、另一部分公司買不起,而且有些公司只能買一台,買第二台就要破產了的程度。

所以這麼昂貴的設備如果只能給一個人使用太浪費了,於是為了讓計算機可以被多人使用,就出現了終端這種設備。

 

2.會話(Session)

一次成功的終端登錄就是一個會話。現在一次 shell 的成功登錄,相當於那時候終端的成功登錄。會話相當於是進程組的容器,它能承載一個或多個進程組。

 

3.進程組

進程組用來承載進程,一個進程組中有一個或多個進程,它是一個或多個進程的集合(也可以看作是容器)。一個進程不但擁有唯一的 PID,同時也屬於一個進程組。

如何產生一個進程組呢?很簡單:

1 # 使用管道可以用一條命令產生一個進程組。
2 ls | more

 

進程組分為前台進程組和後台進程組。

一個會話中只能有一個前台進程組,也可以沒有前台進程組。

終端設備(如鍵盤)只能與前台進程通訊,不能與後台進程通訊,根據約定,如果終端設備與一個後台進程關聯,就會殺掉這個後台進程。

什麼是前台進程組呢?比如你正在使用 tar 命令進行打包的時候是無法再輸入其它命令的。如果 tar 命令執行的時間很長,我們就會在命令後面添加一個 & 參數,把它放到後台去運行。

ps(1) 命令的 SID(Session ID)列 就是程序運行的會話 ID。

進程是先出現的,後來人們發現進程可以拆分為多個小任務分別執行,於是便出現了線程的概念,這個到後面線程的章節會詳細討論。

如今進程已經退化為容器了,它的存在就是為了承載線程。PID 看似是進程號,實際上是線程在消耗它。

進程和線程只是我們的說法,內核中只能看到線程,內核所謂的進程管理其實就是線程管理,內核永遠以線程為單位執行任務。

 

總結來說:會話用來承載進程組,進程組用來承載進程,進程用來承載線程。

 

第九章了解這幾個概念就差不多了,還記得我們前面提到的 myshell 嗎,用 fork(2) + exec(3) + wait(2) 來實現一個可以執行外部命令的 shell。如果你想實現一個支持內部命令的 shell 那麼可以仔細學習一下第九章的內容,shell 內部命令處理的主要知識點都在第九章。我們這裡就不對第九章討論得那麼詳細了,感興趣的小伙伴可以自己看看書,有什麼疑問可以加入博客上方標注的郵件列表討論。

4.setsid(2)

1 setsid - create session and set process group ID
2 
3 #include <unistd.h>
4 
5 pid_t setsid(void);

創建一個會話並設置進程組的ID。這個函數是我們在第 9 章最有價值的函數,沒有這個函數,我們後面就無法創建守護進程。

調用者不能是進程組組長,調用者在調用之後自動變為新進程組組長,並且脫離控制終端,進程 ID 將被設為進程組 ID 和會話 ID,所以守護進程通常 PID、PGID、SID 是相同的。通常的用法是父進程 fork(2) 一個子進程,然後子進程調用 setsid(2) 將自己變成守護進程,父進程退出即可。

 

5.守護進程

守護進程的栗子我就不寫了,因為《APUE》第三版 P375 圖13-1 已經有詳細的代碼了,我針對書上的栗子總結一下。

守護進程的特點:

1)脫離控制終端,ps(1) axj tty 為問號(?)。

2)是進程組的 leader,也就是 PID 和 PGID 相同。

3)通常沒有父進程,由 1 號  init 接管。

4)創建了一個新會話,是 session 的 leader,所以 PID 與 SID 相同。

使用 ps(1) axj 命令查看,PID、PGID、SID 相同的進程就是守護進程。

守護進程也可以使用標准輸出,但是不符合常理了,因為守護進程沒有控制終端,所以守護進程一般會關閉或重定向標准輸入輸出流。

 寫守護進程的時候我們會切換工作路徑,把它切換到一個一定會存在的路徑,比如 /。因為假設你的守護進程是在一個可卸載設備(如U盤)上被啟動的,如果不修改工作路徑,該設備無法被卸載。

調用 umask(2) 是為了將文件模式創建掩碼設置為一個已知值,因為通過繼承得來的掩碼可能會被設置為拒絕某些權限,如果守護進程中需要這些權限則要設置它。

對於書上的栗子,有兩點要吐槽:

1)SIGHUP 信號用於通知服務進程軟重啟,比如修改了某服務的配置文件之後可以通過給服務進程發 SIGHUP 信號使它重新讀取配置文件,所以如果沒有特殊要求不必忽略該信號。

2)如果沒有特殊要求,不必關閉所有的文件描述符,僅關閉標准輸入、標准輸出和標注錯誤即可。

 

6.系統日志

守護進程不應使用標准輸出,那麼當守護進程需要記錄一些事件或者是錯誤的時候怎麼辦呢?那就要采用系統日志了。

系統日志一般保存在 /var/log/ 目錄下,但是這個目錄下的日志文件權限幾乎都是只有 root 才能讀寫,那麼普通用戶的日志如何寫入呢?這就需要借助系統日志函數來寫日志了。

root 用戶授權給 syslogd 服務專門寫日志,然後其它程序都需要通過封裝好的一系列函數調用 syslogd 服務來記錄日志。這樣就提高了日志的安全性了,可以防止日志文件被非法篡改。

1 closelog, openlog, syslog - send messages to the system logger
2 
3 #include <syslog.h>
4 
5 void openlog(const char *ident, int option, int facility);
6 void syslog(int priority, const char *format, ...);
7 void closelog(void);

openlog(3) 函數並非是打開日志文件,而是與 syslogd 服務建立鏈接,表示當前進程要寫日志。

參數列表:

  ident:表明自己的身份,由程序員自行指定,寫什麼都行。

  option:要在日志中附加什麼內容,多個選項用按位或鏈接。LOG_PID 是附加 PID,這個是最常用的。

  facility:消息來源。一般只能指定一個。

消息來源 含義 LOG_CRON 消息來自定時任務 LOG_DAEMON 消息來自守護進程 LOG_FTP 消息來自 FTP 服務 LOG_KERN 消息來自內核 LOG_USER 默認,常規用戶級別消息

表1 facility 參數的常見需選項

syslog(3) 函數用於提交日志內容,參數列表:

  priority:優先級。詳見下表:

級別 含義 LOG_EMERG 「嚴重」導致系統不可用的問題 LOG_ALERT 「嚴重」必須立即處理的情況 LOG_CRIT 「嚴重」臨界條件 LOG_ERR 「嚴重」錯誤 LOG_WARNING 警告 LOG_NOTICE 正常 LOG_INFO 信息 LOG_DEBUG 調試

表2 日志優先級

以 LOG_ERR 為分界線,如果遇到了程序無法繼續運行的問題,要報 LOG_ERR 以上的級別(包括 LOG_ERR)。

如果遇到的問題不會影響程序繼續運行,報 LOG_ERR 以下級別的就可以了。

日志太多肯定對磁盤空間的要求就比較高,而且無用的日志太多會影響日志審計。日志文件中會記錄哪些級別的日志是在配置文件中配置的,默認的情況是 LOG_DEBUG 以上級別的日志都會被記錄。

  format:類似於 printf(3) 函數的格式化字符串。注意不要使用轉義字符 \n,否則日志中會記錄一個字符串"\n"而不是記錄一個換行符。

  ...:format 中占位符的參數。

closelog(3) 表示日志寫入結束。

 

7.單實例守護進程

有些守護進程需要在同一時間只能有一個實例在運行,它們稱為單實例守護進程。它們在啟動的時候會在 /var/run 下面創建一個進程鎖文件,守護進程啟動的時候會先判斷這個鎖文件是否存在,如果已存在就報錯並退出,如果不存在就繼續運行並創建一個鎖文件,退出的時候再刪除它。

守護進程如果想要開機自動啟動,可以配置到自動啟動腳本中:/etc/rc.d/rc.local

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved