詳細的 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 檔中:
- <key>Disabled</key> 設定項目用來決定工作定義內容是否會被載入 (此項設定的 PCDATA 為 Disabled);其後緊跟著屬於數值原姶的 <true/> 設定用標籤,代表設定內容將不會被載入。
- <key>Label</key> 設定項目用來識別工作,對於 launchd 而言,它必需是唯一的;其後緊跟著 <string> 原始型別類型標籤來設定守護行程或是任務項的名稱 。
- <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 在開機時會完成下列的工作:
- 載入工作定義:掃描所有 daemon 類別的 plist 檔 (存放在 /System/Library/LaunchDaemons 以及 /Library/LaunchDaemons ),然後根據 plist 檔中
<key?>Disabled</key?>
的設定值或是覆寫資料庫 disabled.plist3 的內容 (以 disabled.plist 的設定優先),來決定是否載入 plist 檔中的工作定義。 - 執行工作:執行已經載入的 plist 檔中
<key?>KeepAlive</key?>
的值不為 false 或是
<key?>RunAtLoad</key?>
的值為 true 的守護行程。
而當使用者登入時,一個新的 launchd process 將會啟動;這個 process 和開機時的 launchd process 類似,它將會:
- 載入工作定義,不過掃描的是 agent 類別的檔案夾 (/System/Library/LaunchAgent,/Library/LaunchAgents 和 ~/Library/LaunchAgents)。
- 執行 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" " ;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
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
沒有留言:
張貼留言