/proc/<pid>/cmdline 的意外非空編碼
我正在解析
/proc/pid/cmdline
我的 Linux 系統(Ubuntu 16.04)上的多個程序的值,並發現雖然大多數條目都是空編碼的,正如預期的那樣,但至少有一個使用空格作為分隔符,這是我發現的意外。從proc(5) 的文件中,我沒有看到任何跡象表明這應該發生。在任何情況下,我應該將空格作為分隔符而不是空值?如果是這樣,我在哪裡可以找到描述該行為的文件?
行為
這是我在嘗試為 chromium-browser 程序之一設置 cmdline 時看到的(注意空格字元用於分隔值):
user@host:~$ cat /proc/2721/cmdline /usr/lib/chromium-browser/chromium-browser --type=gpu-process --field-trial-handle=2073283832741738928,4790986738309707242,131072 --gpu-preferences=GAAAAAAAAAAAAQAAAQAAAAAAAAAAAGAA --gpu-vendor-id=0x15ad --gpu-device-id=0x0405 --gpu-driver-vendor=Mesa --gpu-driver-version=17.2.8 --gpu-driver-date --service-request-channel-token=3778166CAD6E96F44A7268DF1AB1DD53
我希望看到這樣的東西(空值作為分隔符),這是我從系統上的其他程序中看到的:
~$ cat /proc/354/cmdline vmware-vmblock-fuse/run/vmblock-fuse-orw,subtype=vmware-vmblock,default_permissions,allow_other,dev,suid
至少一個使用空格作為分隔符
不正確。
如果您查看 FreeBSD/TrueOS 上偽文件的末尾,您會遇到與 Chromium 完全相同的行為,您會發現
␀
. 這是␀ 終止的。這都是一個單一的論點。Chromium 在 a 之後覆蓋其參數
fork()
,以便在ps
. 它正在使用setproctitle()
庫函式。這是 BSD C 庫的一部分。它不是 GNU C 庫的一部分。在 GNU C 平台上,Chromium 使用自己的asetproctitle()
直接覆蓋argv
數據。
setproctitle()
事實上,這不是這項工作的正確工具,因為它不允許設置多個參數字元串。它將格式化的“標題”設置為第 0 個參數,並將參數計數設置為 1。所有內容都通過庫函式作為一個參數進行編組。這不是唯一的問題
setproctitle()
。FreeBSD/OpenBSD/NetBSD C 庫版本也有任意 2KiB 的限制,直接繼承自舊的 BSDsendmail
程序(在 FreeBSD 案例中庫函式最初是從該程序中提取的),這對於 Chromium 經常設置的命令行來說太短了到。而且 Chromium 自己的和 FreeBSD/OpenBSD/NetBSD C 庫版本都有額外的功能,格式字元串是一個空指針,Chromium 不使用這些功能(但具有諷刺意味的是,setproctitle()
儘管如此,它必須在自己的實現中處理)。用更少的程式碼可以做得更好。FreeBSD/TrueOS 上的底層系統呼叫是庫函式在構造參數數據後呼叫以完成工作的
sysctl()
函式,以CTL_KERN
、KERN_PROC
、KERN_PROC_ARGS
和程序 ID 作為地址。這可以接受多個以 ␀ 結尾的字元串。我為使用它的工具集編寫了一個相當簡單的setprocargv()
函式。外部 空白 setprocargv ( size_t argc, 常量字元 * argv[] ) { #if 定義(__FreeBSD__) || 定義(__DragonFly__) std::string s; for (size_t c(0); c < argc; ++c) { if (!argv[c]) 中斷; s += argv[c]; s += '\0'; } 常量 int oid[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ARGS, getpid() }; sysctl(oid, sizeof oid/sizeof *oid, 0, 0, s.data(), s.length()); #elif 定義(__OpenBSD__) ...
(OpenBSD/NetBSD 以 FreeBSD/TrueOS 過去的舊方式做事,
ps_strings
在應用程序記憶體中有一個結構,但它仍然sysctl()
是使用的底層系統呼叫,用於查找該結構的位置。)% /package/admin/nosh/command/exec 前台暫停 \; 真的 & [1] 30318 % hexdump -C /proc/30318/cmdline 00000000 66 6f 72 65 67 72 6f 75 6e 64 00 70 61 75 73 65 |foreground.pause| 00000010 00 3b 00 74 72 75 65 00 |.;. 真。 00000018 % hexdump -C /proc/30319/cmdline 00000000 70 61 75 73 65 00 |暫停。| 00000006 %
因為
setproctitle()
是錯誤的工作工具,Chromium 正在獲取新argv
成員並構造一個由 ␠ 分隔的長字元串,作為單個參數傳遞給setproctitle()
.for (size_t i = 1; i < command_line->argv().size(); ++i) { if (!title.empty()) 標題+=“”; 標題 += 命令行->argv()[i]; } // 如果我們自己在上面添加了 argv[0],則禁用在 argv[0] 前添加 '-'。 setproctitle(have_argv0 ? "-%s" : "%s", title.c_str());
如您所見,Chromium本身已經將新的參數向量作為一系列以 ␀ 結尾的字元串。它通過一個中間庫層傳遞它,該層需要將它們全部打包成一個字元串,其中實際的系統呼叫級別仍然根據 ␀ 終止的字元串的參數向量進行操作。
因此,您正在目睹的行為,其中 Chromium 將其更改的參數向量作為一個單一參數呈現給系統。
也許您可以說服 Chromium 的作者採用類似
setprocargv()
. ☺進一步閱讀
- 彼得·韋姆 (1995-12-16)。
setproctitle
. FreeBSD 庫函式手冊。自由BSD。