2017年11月9日 星期四

macOS 的啟動守護行程 (Launch Daemon):launchd

修訂日期:2019/09/18

詳細的 launchd 介紹可以參考

macOS1 自 10.4 開始使用 launchd 這一個啟動守護行程 (Launch Daemon) 來管理系統中的程序 (Processes)、應用程式 (Applications) 及腳本 (Scripts),以及進行工作排程。Launchd 透過屬性列表檔案 (Property List File)2 來描述執行內容 (工作定義),使用 launchctl 命令來進行操作。

打開活動監視器,並且將顯示方式改為依階層排列,可以發現在最上層的 kernel_task  (PID 為 0) 下只有 launchd  (PID 為 1) 這個 Process,而其它的 Processes 則會在 launchd 下面 (為  launchd 的子程序或是孫程序)。

將 "活動監視器" 的顯示方式改為 "依階層排列"


 launchd 的 PID 為 1 為 kernel_task 的唯一子程序,其他的程序則為 launchd 的子或是孫程序



透過 pstree (使用 Homebrew 安裝) 可以知道其它的 Processes 為 launchd 的子程序或孫程序 



註1:為 Apple 桌機和筆電內建的作業系統,2011 年前稱為 Mac OS X (Mac OS X Lion 10.7 之前的版本),2012 年 (OS X Mountain Lion 10.8) - 2015 (OS X El Capitan 10.11) 年稱為 OS X,2016 (macOS Sierra 10.12) 年起改稱為 macOS。

註2:屬性列表檔案是副檔名為 plist 的 XML 格式檔案。

屬性列表 (Property List) 檔案簡介


MacOS的服務可以分為系統層級 (System-Wide) 以及使用者層級 (Per-User) 兩種。系統層級的服務被稱為是守護行程 (Daemon),會在開機時載入;使用者層級的服務則被稱為任務項 (Agent),會在使用者登入時才載入。
負責儲存服務工作定義 (Job Definition) 的檔案被稱為屬性列表 (Property List) 檔案 (簡稱為 plist 檔),分別存放在下列的檔案夾中:


檔案夾描述
~/Library/LaunchAgents由用戶自己定義的任務項 (Agents provided by the user.)
/Library/LaunchAgents由管理員為用戶定義的任務項 (Agents provided by the administrator.)
/Library/LaunchDaemons由管理員定義的系統守護行程 (System daemons provided by the administrator.)
/System/Library/LaunchAgents由 Apple 為用戶定義的任務項 (Agents provided by Apple.)
/System/Library/LaunchDaemons由 Apple 定義的系統守護行程 (System daemons provided by Apple.)


plist 檔是 XML 格式的檔案,標籤 (Tags) 的文件類型定義 (Document Type Definition, DTD) 在 http://www.apple.com/DTDs/PropertyList-1.0.dtd;標籤可以分為集合 (Collections)、原始 (Primitive) 型別、以及數值原姶 (Numerical primitives) 型別等不同的類型。

plist 檔首先由 <plist> 標籤來說明其版本,所有的設定內容都會定義在一組集合類型的<dict> 標籤內。每一項的設定內容都由屬於集合類型的鍵值標籤 <key> 開始,加上被解析字元資料 (Parsed Character Data, PCDATA) 的設定項目,以及鍵值標籤的結束標籤 <key/>,在其後則是相對應的設定用標籤。

下面為存放在 /System/Library/LaunchDaemons 檔案夾中的  tftp.plist 檔的設定內容:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" 
    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
 <key>Disabled</key>
 <true/>
 <key>Label</key>
 <string>com.apple.tftpd</string>
 <key>ProgramArguments</key>
 <array>
  <string>/usr/libexec/tftpd</string>
  <string>-i</string>
  <string>/private/tftpboot</string>
 </array>
 <key>inetdCompatibility</key>
 <dict>
  <key>Wait</key>
  <true/>
 </dict>
 <key>InitGroups</key>
 <true/>
 <key>Sockets</key>
 <dict>
  <key>Listeners</key>
  <dict>
   <key>SockServiceName</key>
   <string>tftp</string>
   <key>SockType</key>
   <string>dgram</string>
  </dict>
 </dict>
</dict>
</plist>


在  tftp.plist 檔中:

  1. <key>Disabled</key> 設定項目用來決定工作定義內容是否會被載入 (此項設定的 PCDATA 為 Disabled);其後緊跟著屬於數值原姶的 <true/> 設定用標籤,代表設定內容將不會被載入。
  2. <key>Label</key> 設定項目用來識別工作,對於 launchd 而言,它必需是唯一的;其後緊跟著 <string> 原始型別類型標籤來設定守護行程或是任務項的名稱 。
  3. <key>ProgramArguments</key> 設定項目指定要執行的程式的路徑以及參數,因為需要設定較多的內容,因此使用 <array> 集合類型標籤來設定多個參數,而每個參數則會使用 <string> 標籤。


如果使用 Homebrew 安裝伺服器軟體,並且將其設定為  daemon,則其 plist 檔會儲存在 /Library/LaunchDaemons 檔案夾中。例如,在安裝完 Apache Web Server,並將其設定為 daemon 後,在 /Library/LaunchDaemons 檔案夾會新增下列內容的  homebrew.mxcl.httpd.plist 檔:

<?xml version="1.0" encoding="UTF-8"??>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" 
    "http://www.apple.com/DTDs/PropertyList-1.0.dtd"?>
<plist version="1.0"?>
<dict?>
  <key?>Label</key?>
  <string?>homebrew.mxcl.httpd</string?>
  <key?>ProgramArguments</key?>
  <array?>
    <string?>/usr/local/opt/httpd/bin/httpd</string?>
    <string?>-D</string?>
    <string?>FOREGROUND</string?>
  </array?>
  <key?>RunAtLoad</key?>
  <true/?>
</dict?>
</plist?>


守護行程以及任務項的啟動



macOS 在開機時,首先會載入作業系統核心 (OS Kernel) ,核心載入完成後就執行 launchd 來載入其它的 daemons。launchd 在開機時會完成下列的工作:

  1. 載入工作定義:掃描所有 daemon 類別的  plist 檔 (存放在 /System/Library/LaunchDaemons 以及 /Library/LaunchDaemons ),然後根據 plist 檔中
    <key?>Disabled</key?>
    的設定值或是覆寫資料庫 disabled.plist3  的內容 (以 disabled.plist 的設定優先),來決定是否載入 plist 檔中的工作定義。
  2. 執行工作:執行已經載入的 plist 檔中
    <key?>KeepAlive</key?>
    的值不為 false 或是
    <key?>RunAtLoad</key?>
    的值為 true 的守護行程。

而當使用者登入時,一個新的 launchd process 將會啟動;這個 process 和開機時的 launchd process 類似,它將會:

  1. 載入工作定義,不過掃描的是 agent 類別的檔案夾 (/System/Library/LaunchAgent,/Library/LaunchAgents 和 ~/Library/LaunchAgents)。
  2. 執行 plist 檔中  <key?>KeepAlive</key?>  的值不為 false 或是 <key?>RunAtLoad</key?> 的值為 true 的任務項。



註3. disabled.plist 為覆寫資料庫 (Override Database) 存放在 /var/db/com.apple.xpc.launchd/ 檔案夾中;而較舊版本的 macOS 使用的是 overrides.plist,存放在 /var/db/launchd.db/com.apple.launchd/ 檔案夾中。覆寫資料庫只可以藉由 launchctl 來覆寫裡面的資料。

一個覆寫資料庫 disabled.plist 的內容可能如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" &quot
    ;http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
 <key>com.apple.AppleFileServer</key>
 <true/>
 <key>com.apple.ManagedClientAgent.enrollagent</key>
 <true/>
 <key>com.apple.screensharing</key>
 <true/>
 <key>com.apple.ftpd</key>
 <true/>
 <key>com.apple.usbmuxd</key>
 <false/>
 <key>com.apple.smbd</key>
 <true/>
 <key>com.apple.mrt</key>
 <true/>
 <key>com.teamviewer.service</key>
 <false/>
 <key>com.apple.stackshot</key>
 <false/>
 <key>org.apache.httpd</key>
 <true/>
 <key>com.apple.backupd-auto</key>
 <false/>
 <key>com.apple.pacemaker</key>
 <true/>
 <key>homebrew.mxcl.httpd</key>
 <false/>
</dict>
</plist>

Launchctl 命令簡介


(適用於 OSX 10.10 Yosemite以及之後的版本,更早之前版本請參考中的 "Operation")

Launchctl 命令是用來控制 Launchd 的命令,它有十分豐富的子命令,可以讓使用者列出目前的工作 (list)、載入工作 (load)、卸載工作 (unload)、執行工作 (start)、中止工作 (stop) 等。通常套件都會提供執行與中止的控制方式,所以本文不介紹 launchctl 的 start 及 stop 功能。


1) 列出目前載入的工作:
sudo launchctl list



會列出:

  • PID:數值為執行中的工作的 PID,- 則表示工作目前沒有執行。
  • 狀態 :0 代表工作成功結束,正值為發生錯誤,負值表示工作因為收到訊號而中止。
  • 工作的標頭等。


sudo launchctl list 也可以加上工作的標頭,查詢工作的詳細狀態,例如:
sudo launchctl list homebrew.mxcl.httpd



2) 載入工作:

sudo launchctl load /Library/LaunchDaemons/homebrew.mxcl.httpd.plist

在載入工作時,如果 plist 檔中  KeepAlive  的值不為 false 或是 RunAtLoad 的值為 true,服務同時會被執行 (產生新的程序)。

而如果工作的 plist 檔中的 Disabled 鍵值為 true 或是覆寫資料庫 (disabled.plist) 中工作的鍵值為 true,會出現下面服務停用的訊息,無法載入工作:

/Library/LaunchDaemons/homebrew.mxcl.httpd.plist: Service is disabled

此時要用下列的指令載入工作

sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.httpd.plist

(使用 -w 參數代表要覆寫 disabled.plist 中工作的鍵值為 false。)

3) 缷載並停止工作:

sudo launchctl unload /Library/LaunchDaemons/homebrew.mxcl.httpd.plist

由於設定為自動執行的守護行程 (任務項) 會在啟動 (登入) 時會自動載入,上面的操作是讓工作從目前的 Launchd 中缷載,並不會影響到 plist 檔跟 disabled.plist 的設定。如果要缷載 守護行程 (任務項) ,並且讓其不會在重新開機後自動啟動,則需要使用下面的指令 (使用 -w 參數覆寫 disabled.plist):

sudo launchctl unload -w /Library/LaunchDaemons/homebrew.mxcl.httpd.plist





沒有留言: