歡迎您光臨本站 註冊首頁

Solaris內核目錄

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  目錄:

概述
◆ /proc/目錄簡介
◆ procfs的實現
後記

--------------------------------------------------------------------------

概述:

進程文件系統 -- procfs -- 允許像管理文件那樣直接管理內核進程。

進程文件系統,procfs,是一個偽文件系統,提供了內核進程表的文件系統介面,
Jim Mauro 在這裡概要介紹了procfs。

翻譯本文的目的在於編寫64-bit下的SLKM,歡迎對此感興趣的朋友交流。

--------------------------------------------------------------------------

◆ /proc/目錄簡介

進程文件系統,procfs,是一個偽文件系統,它允許對一些非傳統意義上的文件
通過標準文件I/O介面進行訪問。procfs將Solaris內核進程架構進行了抽象,比如當
前系統中所有運行著的進程會在/proc/目錄下有所體現。系統中每個進程對應/proc/
目錄下的一個子目錄,子目錄名即相應進程號(PID),所有進程號子目錄構成了
/proc/目錄的全部內容。

許多提供進程數據和控制點的內核數據結構在/proc//子目錄下有相應反映,
比如,多線程進程中每個LWP的相關數據和控制結構體現在/proc//lwp/
中。 /proc/目錄下的對象不是真實磁碟文件,這些對象位於內核內存中,用戶執行
ls(1)命令顯示/proc/目錄結構時,系統讀取內核內存並返回相應內容。

通過/proc,相對簡便地就可以獲取進程信息,比如進程執行環境、內核資源利
用率。進程式控制制和procfs直接相關,procfs最初的設計目的很簡單,就是為編寫調試
器提供一組介面,現在已經有了相當大的改進。

Solaris系統在/usr/proc/bin/目錄下提供了一組工具從/proc中析取進程信息,
同時可以進行簡單的進程式控制制。可以參看proc(1)手冊頁。進程狀態命令ps(1)也利用
了procfs介面。

下面列舉可以通過/proc文件系統獲取的控制和信息數據,關於這些文件的詳細
信息參看proc(4)手冊頁。

/proc -- procfs的根目錄

/proc/ -- 某一確定進程的根目錄,進程PID正是子目錄名

/proc//as -- 進程地址空間,即struct proc結構中p_as成員。換句話說,進
程地址空間以/proc//as文件的形式展現出來,通過這個偽文件系統介面可以訪
問相應進程地址空間。

struct as * p_as; /* 進程地址空間指針 */

# ls -l /proc/53/as
-rw------- 1 root root 1458176 2月 8 17:34 /proc/53/as

struct proc結構定義在/usr/include/sys/proc.h文件中。

/proc//ctl -- 一個進程式控制制文件。可以只寫打開該文件,然後給相應進程發
送控制信息。可以停止、啟動進程,設置進程停止於某一特殊事件。這演示了procfs
的強大和便捷。進程式控制制、事件跟蹤可以通過打開相應進程的控制文件完成,只需要
寫入期待行為的控制信息。參看proc(4)手冊了解控制信息和控制函數的詳細介紹。

/proc//status -- 進程狀態信息。對應/usr/include/sys/procfs.h文件里定
義的struct pstatus結構。proc(4)手冊頁里也有描述。這個結構中有一個成員

lwpstatus_t pr_lwp; /* status of the representative lwp */

該成員對應一個有代表性的LWP(輕量級進程)。單線程進程只有一個LWP,很容易選定
這個有代表性的LWP。那些多線程進程通常有多個LWPs,一個內核函數遍歷當前進程
的所有LWPs,根據他們的狀態選取這個有代表性的LWP。首先選取正在執行中的LWP,
如果不存在這樣的LWP,按照可運行、休眠、停止的順序選取LWP。

/proc//lstatus -- lwpstatus結構數組,進程中每個LWP對應一個lwpstatus結
構。struct lwpstatus結構定義在/usr/include/sys/procfs.h文件中。

/proc//psinfo -- 類似ps(1)命令提供的進程信息。對應struct psinfo結構,
類似struct pstatus結構,struct psinfo結構中有一個成員

lwpsinfo_t pr_lwp; /* information for representative lwp */

該成員的對應一個有代表性的LWP。

/proc//lpsinfo -- lwpsinfo結構數組,進程中每個LWP對應一個lwpsinfo結構

/proc//map -- 地址空間映射信息,可以用pmap(1)命令顯示這些數據信息。

/proc//rmap -- 進程中保留地址空間段。用pmap -r命令顯示這些數據信息。

/proc//xmap -- 擴展地址空間映射信息。用pmap -x命令顯示這些數據信息。

/proc//cred -- 進程身份驗證信息,對應/usr/include/sys/procfs.h文件中
定義的struct prcred結構。

/proc//sigact -- sigaction結構數組,描述和本進程相關的所有信號設置。
struct sigaction結構定義在/usr/include/sys/signal.h文件中。

/proc//auxv -- auxv_t結構數組,包含進程執行時傳遞給動態鏈接器的初始值。
auxv_t結構定義在/usr/include/sys/auxv.h文件中。

/proc//ldt -- 局部描述符表(LDT),僅存於Intel x86架構。

/proc//usage -- 進程資源利用率的相關數據,對應struct prusage結構,該
結構定義在/usr/include/sys/procfs.h文件中。

/proc//lusage -- prusage結構數組,對應各個LWP資源利用狀況。

/proc//pagedata -- 進程地址空間的另外一種表現方式,可以用於跟蹤頁面級
的引用和修改。參看struct prpageheader結構定義。

/proc//watch -- prwatch結構數組。通過寫控制文件/proc//ctl可以設
置PCWATCH操作,此時建立該文件。允許監視一個或多個地址空間範圍,當訪問這些
被監視頁面時,產生一次陷入。

scz註:這個功能和SoftIce的BPR功能類似,adb支持這種陷入,不知是否利用了
procfs

/proc//cwd -- 到進程當前工作目錄的符號鏈接

/proc//root -- 到進程根目錄的符號鏈接(和上面那個什麼區別)

/proc//fd -- 這是一個子目錄,包含進程打開的文件句柄

/proc//fd/nn -- 對應進程打開的某個確定的文件句柄

/proc//object -- 這是一個子目錄,包含進程相關的可執行文件以及動態鏈接
庫。

/proc//object/nn -- 二進位目標文件。進程對應的可執行文件名為a.out,其
余是進程相關的動態鏈接庫文件。

object目錄提供的信息是進程級的,每個/proc//目錄有一個lwp子目錄,提供
了LWP級的信息:

/proc//lwp -- 這是一個子目錄,包含進程中所有LWPs的信息

/proc//lwp/ -- 這是一個子目錄,包含對應lwpid的LWP信息

/proc//lwp//lwpctl -- 一個控制文件,通過它可以在LWP級上針對每
個LWP發布控制操作

/proc//lwp//lwpstatus -- LWP狀態信息,對應lwpstatus結構,該結
構定義在/usr/include/sys/procfs.h文件中

/proc//lwp//lwpsinfo -- 對應lwpsinfo結構,同樣定義在
/usr/include/sys/procfs.h文件中

/proc//lwp//lwpusage -- LWP資源利用信息,對應prusage結構

/proc//lwp//xregs -- 這個文件是處理器架構相關的,某些平台上可
能沒有這個文件。對於SPARC系統,這個文件對應/usr/include/sys/procfs_isa.h文
件中定義的prxregset結構。

/proc//lwp//gwindows -- 常規寄存器窗口。這個文件僅存於SPARC架
構的系統,描述LWP使用的常規寄存器組(硬體上下文的一部分),對應gwindows結構,
該結構定義在/usr/include/sys/regset.h文件中。

/proc//lwp//asrs -- 輔助寄存器組,僅存於SPARC V9(UltraSPARC)架
構,專為SPARC V9架構定義的一組額外的硬體寄存器,要求sun4u、64-bit內核
(Solaris 7及其後續版本)、64-bit進程。注意,64-bit內核可以運行32-bit進程,
但是32-bit進程沒有這樣一個文件與之對應。

--------------------------------------------------------------------------

◆ procfs的實現

procfs是通過動態可載入內核模塊的方式實現的。系統啟動時自動載入
/kernel/fs/procfs,/etc/vfstab文件中存在預設/proc入口,系統啟動過程中/proc
將被mount上來。mount過程中將調用procfs的prinit()和prmount()函數,它們為
procfs初始化vfs(虛擬文件系統)結構,為根目錄/proc/創建並初始化一個vnode。
vfs結構定義在/usr/include/sys/vfs.h文件中,vnode結構定義在
/usr/include/sys/vnode.h文件中。

/proc目錄所涉及的內核內存空間絕大部分是動態分配的。但是系統支持的最大
進程數(可以通過/etc/system的max_nprocs參數配置)決定了/proc下子目錄插槽數目,
這個是靜態分配初始化的。內核變數procdir是一個指向procent結構數組的指針,
每個procent結構對應一個procfs目錄入口,procent結構數組元素數量源自系統啟動
時初始化的v.v_proc變數的值,也就是系統支持的最大進程數。

scz註:在Solaris Kernel Hacking過程中,應該習慣使用下面這兩種命令,很多未
公開內核數據結構在/usr/include下的頭文件中有相當體現,然後利用nm命
令確認當前內核正在使用這些內核數據結構或者內核函數。

# find /usr/include -name "*" | xargs grep -i "procdir"
# /usr/ccs/bin/nm -x /dev/ksyms | grep -i "|procdir"
[1433] |0x0000104570d0|0x000000000008|OBJT |LOCL |0 |ABS |procdir
# /usr/ccs/bin/nm -x /dev/ksyms | grep -i "|v$"
[8741] |0x00001041e1f4|0x00000000003c|OBJT |GLOB |0 |ABS |v
[6634] |0x00001041e1f4|0x00000000003c|OBJT |GLOB |0 |ABS |v

每個procent結構中pe_proc成員指向對應的proc結構,pe_next成員指向數組的
下一個元素(scz:不但是數組,也形成鏈表)。整個procdir數組由進程PID結構的
pid_prslot成員索引。創建進程時(fork())系統在procdir數組中為之分配一個元素,
參看圖1。

--------------------------------------------------------------------------

+--------+ +---->+---------+<----procdir(procent結構數組)
| proc |<-----------------------|-----| pe_proc |
| | | | pe_next |--+ 0
| p_pidp |----> +------------+ | +---------+ | |
內 +--------+ | pid_prslot |----+ | pe_proc |<-+ |
| ^ | pid_id | | pe_next |--+ |
核 V | +------------+ +---------+ | |
+--------+ | pe_proc |<-+ |
進 | proc | | pe_next |--+ |
| | +---->+---------+ | |
程 | p_pidp |----> +------------+ | | pe_proc |<-+ |
+--------+ | pid_prslot |----+ | pe_next |--+ |
表 | ^ | pid_id | +---------+ | |
V | +------------+ +-----| pe_proc |<-+ |
+--------+ | | pe_next | |
| proc |<-----------------------+ +---------+ |
| | | | |
| p_pidp | | | V
+--------+ | | v.v_proc


圖1. 進程PID結構的pid_prslot成員用於索引procdir數組

--------------------------------------------------------------------------

下面是在我的Sun工作站上找到的相應頭文件內容:

/usr/include/sys/proc.h

struct proc
{
struct proc * p_next; /* active chain link next */
struct proc * p_prev; /* active chain link prev */
struct pid * p_pidp; /* process ID info */
struct vnode * p_trace; /* pointer to primary /proc vnode */
struct vnode * p_plist; /* list of /proc vnodes for process */
}

/* process ID info */
struct pid
{
unsigned int pid_prslot :24;
pid_t pid_id;
};

procfs內核代碼描述了procfs目錄入口的精確格式,這個格式以典型的磁碟文件
系統為模板,內核中每個入口對應一個目錄名。目錄入口包含在目錄中的偏移(第幾
個入口)、長度域以及inode號。/proc下文件對象的inode號源自文件對象類型和進程
號。注意,/proc目錄入口不會出現在目錄名搜索緩存中,根據定義,/proc目錄入口
總是位於物理內存中(不會被扇出?)。

因為procfs是一個文件系統,它構建在虛擬文件系統VFS和Solaris vnode構架之
上,文件系統實例作為一個VFS對象存在,其中的文件通過vnode描述。procfs創建
VFS和vnode結構,通過它們可以針對procfs進行文件系統相關操作,比如mount和
umount,可以針對/proc目錄及文件對象進行open、read和write操作。

除了VFS和vnode結構,procfs實現中主要定義了兩個數據結構用於描述/proc目
錄下文件對象。第一個是prnode結構(/usr/include/sys/proc/prdata.h),描述那些
最終鏈接到vnode的文件系統相關數據。內核UFS實現定義了一個inode做為描述一個
UFS文件的文件系統相關數據結構,類似的,內核procfs實現定義了一個prnode描述
一個procfs文件。/proc目錄下的每個文件有一個vnode和prnode對應。

第二個是prcommon結構,遍布整個/proc目錄結構,換句話說,每個/proc/
和/proc//lwp/目錄本身都對應一個prcommon結構,但是這些目錄下的
文件對象並沒有對應一個prcommon結構,因為訪問這些文件對象時必然與一個確定的
進程或者LWP相關。prcommon結構對這些目錄下的文件對象共性進行抽象。prnode和
prcommon結構定義在/usr/include/sys/proc/prdata.h文件中。參看圖2。

--------------------------------------------------------------------------

/proc
\
\ prnode prcommon
+--+ +--+
+---------------------------------+ | | | |
| | +--+ +--+
| |
| +------------------------------------------------------+
| prnode prcommon | prnode prnode prnode per-process |
lwp +--+ +--+ | as +--+ cred +--+ psinfo +--+ ... file objects |
/ | | | | | | | | | | | |
/ +--+ +--+ | +--+ +--+ +--+ |
/ prnode prcommon +------------------------------------------------------+
+--+ +--+
\ | | | |
\ +--+ +--+
\
+-------------------------------------------------------------------------------+
| prnode prnode prnode prnode per-lwp |
| lwpctl +--+ lwpinfo +--+ lwpstatus +--+ lwpusage +--+ ... file objects |
| | | | | | | | | |
| +--+ +--+ +--+ +--+ |
+-------------------------------------------------------------------------------+

圖2. prnode和prcommon結構定義

--------------------------------------------------------------------------

每個進程有自己的主/proc vnode(就是說這個vnode對應/proc/文件),進程中
每個LWP有自己的vnode對應/proc//lwp/文件,參看圖3。

--------------------------------------------------------------------------

回指到proc結構
<-------------+
prnode prcommon |
+-------------+ +->+-------------+ +->+----------+ procdir |
| proc | | | pr_next ----|--+ | | prc_slot ---->+---------+ |
| structure | /proc | | pr_common --|--|-+ +----------+ | pe_proc --+
| +---------+ | /| | pr_files ---|--|----------------+ | pe_next |
| | | | vnode | | pr_vnode | | | +---------+
一 | | p_trace -------------->+---------+ | | | | pe_proc |
| | p_plist | | | | |vnode | | | | | pe_next |
個 | | | | | | |structure| | | | +---------+
| +---------+ | | | | | | | | | pe_proc |
多 | | +--|-|v_data | | | | | pe_next |
| kthread LWP | /proc | +---------+ | | | +---------+
線 | +---------+ | / +-------------+ | | | pe_proc |
| | | | /lwp/ vnode | | | pe_next |
程 | | t_trace -----+ prnode | | +---------+
| | | | | +->+-------------+<-+ | | pe_proc |
進 | +---------+ | | | | pr_next ----|--+ prcommon | | pe_next |
| | | | | pr_common --|--|--->+----------+| +---------+
程 | kthread LWP | | | | pr_files | | | prc_slot || | |
| +---------+ | | | | pr_vnode | | +----------+| | |
| | | | +-------->+---------+ | | | | |
| | t_trace -----+ | | |vnode | | | |
| | | | | | | |structure| | | |
| +---------+ | | | | | | | | +-->+---+ array of
+-------------+ | +--|-|v_data | | | | | pointers
| | +---------+ | | +---+ to vnodes
| +-------------+ | | | for all files
/proc//lwp | | +---+ within the
/ vnode | prnode | | | directory
| +->+-------------+<-+ .....
| | | pr_next | prcommon | |
| | | pr_common --|------>+----------+ +---+
| | | pr_files | | prc_slot | | |
| | | pr_vnode | +----------+ +---+
+-------->+---------+ |
| | |vnode | |
| | |structure| |
| | | | |
+--|-|v_data | |
| +---------+ |
+-------------+

圖3. 一個多線程進程所涉及結構之間的關聯

--------------------------------------------------------------------------

下面是在我的Sun工作站上找到的相應頭文件內容:

/usr/include/sys/proc/prdata.h

typedef struct prnode
{
vnode_t * pr_next; /* list of all vnodes for process */
prcommon_t * pr_common; /* common data structure */
prcommon_t * pr_pcommon; /*
* process common data structure
* 和上面那個成員什麼區別
*/
vnode_t ** pr_files; /* contained files array (directory) */
vnode_t pr_vnode; /* embedded vnode 這裡不是指針 */
} prnode_t;

/*
* Common file object to which all /proc vnodes for a specific process
* or lwp refer. One for the process, one for each lwp.
*/
typedef struct prcommon
{
int prc_slot; /* process slot number */
} prcommon_t;

/usr/include/sys/vnode.h

/*
* All of the fields in the vnode are read-only once they are initialized
* (created) except for:
* v_flag: protected by v_lock
* v_count: protected by v_lock
* v_pages: file system must keep page list in sync with file size
* v_filocks: protected by flock_lock in flock.c
* v_shrlocks: protected by v_lock
*/
typedef struct vnode
{
caddr_t v_data; /* private data for fs */
} vnode_t;

/usr/include/sys/thread.h

typedef struct _kthread
{
struct vnode * t_trace; /* pointer to /proc lwp vnode */
} kthread_t;

圖3演示了打開一個procfs文件進行讀寫時部分相關procfs數據結構和它們之間
的關聯。注意到一個進程相關的所有vnodes通過prnode結構的pr_next成員鏈接起來。
當引用一個procfs目錄以及目錄下的文件對象時,內核動態創建必要的數據結構支持
這種文件I/O請求,同時也是動態銷毀相關數據結構。無論什麼時候針對procfs目錄
或文件做open(2)請求或者列舉procfs目錄或文件,它們似乎總是在那裡,類似冰箱
里的燈,當你打開冰箱的時候它總是亮著的,但是關上冰箱門之後它事實上關閉著。

通過procfs所能訪問到的數據顯然總是位於內核proc結構以及其他一些數據結構
中,這些數據結構共同構成了Solaris內核中完整的進程模型。應用程序通過procfs
可以獲取進程數據,控制進程執行。這樣做的好處是隱藏了內核進程模型的底層細節,
以一種相對普通的方式析取感興趣的數據、進行進程式控制制。請求發生時建立這種動態
抽象,只要針對特定文件的訪問存在,這種動態抽象就一直保持著。

針對procfs的文件I/O操作遵循傳統方式,打開文件獲取文件句柄,讀寫,關閉
文件句柄。通過vnode開關表機制進行procfs相關vnode操作時,創建並初始化prnode
和prcommon結構,這通常是應用程序文件請求導致的結果。實際的procfs vnode操作
由相關的查找、讀寫函數處理/proc目錄下的對象。

procfs遍歷和讀取請求採用一組函數指針實現,這組函數實現procfs文件類型相
關操作。文件類型分兩層維護。在vnode的v_type成員中,procfs文件類型定義成
VPROC。而prnode結構的pr_type成員定義了這個特定procfs文件的類型。procfs文件
類型直接描述了/proc目錄結構,參看/usr/include/sys/proc/prdata.h文件。

/*
* Node types for /proc files (directories and files contained therein).
*/
typedef enum prnodetype
{
PR_PROCDIR, /* /proc */
PR_PIDDIR, /* /proc/ */
PR_AS, /* /proc//as */
PR_CTL, /* /proc//ctl */
PR_STATUS, /* /proc//status */
PR_LSTATUS, /* /proc//lstatus */
PR_PSINFO, /* /proc//psinfo */
PR_LPSINFO, /* /proc//lpsinfo */
PR_MAP, /* /proc//map */
} prnodetype_t;

打開一個procfs文件時的基本流程如圖4所示。

--------------------------------------------------------------------------

open( "/proc//", O_RDONLY );
| Specific procfs directory object
代| vn_open() lookup functions are invoked
| through the pr_lookup_function[]
碼| +----> lookupxxx() array
| | VOP_LOOKUP() -> prlookup()
流| | index based on type
| | pr_lookup_function +-----------------------+
程| | | pr_lookup_piddir() |\
| | Construct full path name, +-----------------------+ \
| | looking up each element | pr_lookup_lwpdir() | \
| | in the path. +-----------------------+ prgetnode()
| | | pr_lookup_objectdir() | /
| | +-----------------------+ /
| | | | |/
| | ...... | ......
| | |
| +-------------------------------------------+
| VOP_OPEN() -> propen()
V

圖4. 打開一個procfs文件時的基本流程

--------------------------------------------------------------------------

圖4中流程從應用程序開始,針對一個procfs文件做open(2)系統調用。進入
vnode內核層(vn_open()),完成一系列查找以構建目標/proc文件的完整路徑名。通
過vnode層的宏進入文件系統相關操作。在上面的圖例中,VOP_LOOKUP()解析成
procfs的pr_lookup()函數。pr_lookup()完成訪問許可權檢查並根據目錄文件類型調用
相應的procfs函數,比如pr_lookup_piddir()針對/proc/目錄進行查找工作。
每個pr_lookup_xxx()目錄查找函數完成某些目錄類型相關的工作,然後調用
prgetnode()獲取prnode。

prgetnode()為/proc文件創建prnode(其中內嵌了vnode),並初始化prnode和
vnode結構的某些成員。對於/proc/和/proc//lwp/,還會創建
prcommon結構,掛接到prnode結構上,並部分初始化。注意,對於/proc下的目錄文
件,為了正確反映目錄文件類型,vnode類型從VPROC(初始設置)改變成VDIR,表示這
是一個procfs目錄文件。

一旦完整路徑名構建完畢,通過VOP_OPEN()宏進入文件系統相關的open()函數。
procfs的propen()函數完成prnode和vnode結構的其餘初始化以及針對特定文件類型
的訪問測試工作。一旦propen()完成,控制返回到vn_open()。最終一個代表procfs
文件的文件句柄返回給主調者。

讀取一個procfs數據文件(和目錄文件相對)類型打開流程,read()系統調用最終
進入procfs的prread()函數。procfs實現為每個可用文件對象(不同的數據結構)定義
了一個數據文件對象相關的讀函數,比如pr_read_psinfo()、pr_read_pstatus()、
pr_read_lwpsinfo()等等。這些函數指針構成一個數組,以文件類型做下標進行索引,
prread()最終調用了它們。整個流程類似lookup操作。

Solaris 7 的procfs實現是基於64-bit內核的,但是同時支持32-bit和64-bit應
用,在/proc層次結構上提供了32-bit版本的可用數據文件。在64-bit Solaris 7內
核中,描述每個/proc文件對象內容的數據結構同時擁有32-bit版本和64-bit版本,
比如lwpstatus和lwpstatus32、psinfo和psinfo32等等。針對每個32-bit版本的結構
定義,相應pr_read_xxx()函數做了支持32-bit數據模式的編碼。

procfs用戶並不會意識到64-bit內核中多種數據模式實現。調用到prread()時,
它會檢查主調者使用的數據模式,並激活相應數據模式的函數。這裡有一個例外,讀
取/proc//as(地址空間)文件時,主調者必須擁有與/proc//as文件一樣的
數據模式,換句話說,64-bit內核中32-bit應用程序可以讀取另外一個32-bit進程的
AS(地址空間)文件,但是不能讀取另外一個64-bit進程的AS文件。

scz註:我覺得這裡倒不如說,/proc//as本身是擁有單一數據模式的,要麼
32-bit,要麼64-bit,不可得兼。而其他/proc數據文件對象可能同時支持兩
種數據模式。

pr_read_xxxx()函數從內核里讀取相關數據,然後寫入相應的procfs數據結構,
最終返回給主調者。例如,pr_read_psinfo()從目標進程的proc結構、cred結構和as
結構讀取數據,寫入psinfo結構中相應成員。訪問內核數據時靠proc結構的p_lockp
成員確定的互斥鎖進行同步,這樣確保每次只有一個客戶線程能夠訪問per-process
或per-lwp內核數據。

很少需要寫訪問procfs文件。姑且不考慮寫目錄創建數據文件,典型的寫操作就
是為了發出某些控制消息寫進程或LWP控制文件。控制消息(參看proc(1))包括stop/
start消息,信號跟蹤和控制,故障管理,執行控制(比如進入/退出某個系統調用時
暫停)以及地址空間訪問監視。

迄今為止,我們討論的都是用標準系統調用對procfs文件進行I/O操作,目前從
普通應用級程序員編程訪問/proc文件來說這是唯一的辦法。然而另外有一組特定針
對procfs的訪問介面,proc(1)中介紹的/usr/proc/bin/下的命令(隨Solaris分發)
使用了這組介面。這組介面位於libproc.so動態鏈接庫,屬於未公開的介面。Sun公
司正在著手準備關於這組介面的文檔,做為標準Solaris APIs提供出來。圖5展示了
以前討論過的內核中procfs模塊與各層之間的介面關係。

--------------------------------------------------------------------------

+---------------------+--------------------------+
| custom /proc code | /usr/proc/bin/ |
+------------------+ | +-----------------------+
| stdio interfaces | | |\\\\\\\\libproc\\\\\\\\|
+------------------+--+--+--------------+\\\\\\\\|
| system calls |\\\\\\\\| user
-----------------------------------------------------------------
+---------------------------------------+\\\\\\\\| kernel
| vnode layer |\\\\\\\\|
+---------------------------------------+--------+
| procfs |
+------------------------------------------------+

圖5. procfs模塊與各層之間的介面關係

--------------------------------------------------------------------------

圖5演示了多條到達procfs內核常式的路徑。開發者通常通過系統調用進入vnode
層,這是前面過介紹的方式。而proc(1)命令更多構建在libproc.so提供的介面上。
為什麼需要這組動態鏈接庫介面呢,提供一組簡單易用的常式用於應用程序開發,減
少直接使用內核機制帶來的複雜性。控制一個進程的執行,尤其是多線程進程,非常
複雜,需要一組真正屬於API層的編碼介面,而不是內核層的編碼介面。

向控制文件的頭8個位元組(如果是LP64內核,就是頭16個位元組)寫入一個操作碼和
可選的操作數,完成進程式控制制。寫進程式控制制文件的路徑也要經過vnode層,最終調用
了procfs的prwritectl()函數。允許在一次寫調用中向控制文件寫入多個控制消息(
操作碼和操作數),prwritectl()會將一次寫入的多個控制消息分成獨立的操作碼/操
作數對,順序提交給內核的pr_control()函數,pr_control()函數將設置進程或LWP
相應的標誌,以指明控制機制啟動,比如某一事件發生時暫停。控制函數在proc(4)
手冊頁中介紹。

進程/LWP控制的實現與內核中進程/LWP子系統緊密結合,P區、U區、LWP和內核
線程結構中各種域一起協作完成通過procfs進行的進程管理和控制。建立進程式控制制包
括設置標誌和位掩碼欄位,用於跟蹤那些導致進程、線程進入、離開內核的事件,包
括信號、系統調用、故障情形。對應這些事件的進入、離開內核的點定義得比較充分,
為進程狀態改變提供了自然的控制機制。

系統調用、信號和故障分別對應數據類型sysset_t、sigset_t和fltset_t。如果
指定發生某系統調用時暫停,此時尚未從進程讀取提供給該系統調用的參數。如果指
定離開某系統調用時暫停,此時來自系統調用的返回值已經提交給進程。可以指定發
生某種故障時進入內核陷門處理程序。可以指定接收到某個信號時暫停或者從系統調
用、內核陷門處理程序返回,可以通過信號喚醒進程。

可以在進程虛擬地址空間中指定一片區域處在監視中,當針對這片區域進行被監
視類型的操作(比如讀、寫訪問),也就是監視事件發生時,產生一次監視點陷入,典
型地導致進程、LWP暫停,這通過跟蹤FLTWATCH故障或者捕捉非阻塞的SIGTRAP信號實
現。

某些情況下為了析取進程信息、進行進程式控制制,控制進程可能需要目標進程臨時
完成某種特殊的操作。例如,pfiles(1)命令可以列出目標進程打開的每個文件的信
息,這需要目標進程針對每個打開的文件句柄做stat(2)系統調用。運行在Solaris系
統上的進程典型地花費大量時間阻塞在某個系統調用上,為了獲得目標進程的控制權
完成控制進程提交的任務,需要在目標進程阻塞時搶奪CPU,保護當前系統調用狀態,
當控制進程提交的任務完成後恢復保存的系統調用狀態繼續執行目標進程原來的任務。

為了達到這個目的,procfs實現了另外一個代理LWP,而不是使用目標進程中現
有LWP,否則狀態保存、恢復更加複雜。procfs提供了一種機制創建代理LWP(注意
PCAGENT控制消息)。代理LWP創建成功后將是目標進程中唯一可運行LWP,直到它消亡。
目標進程中執行代理LWP以完成控制進程提交的任務,比如在目標進程中執行系統調
用。然後銷毀代理LWP,恢復保存的進程/LWP狀態。proc結構中有一個成員p_agenttp,
指向創建的代理LWP。內核代碼通過檢查該指針判斷目標進程中是否存在代理LWP。

kthread_t * p_agenttp; /* thread ptr for /proc agent lwp */

proc(4)手冊頁介紹了進程式控制制的更多細節。

--------------------------------------------------------------------------

後記:

本篇與<>的一部分,由於很
多東西缺乏內核Hacking經驗和常用術語約定,翻譯得相當牽強,好在可以對照
/usr/include/下的頭文件反覆理解。<<[805-3024] Solaris設備驅動程序編程指南>>
和<<[805-4038] Solaris流編程指南>>是對理解<>很好的補充。
此外可以在comp.unix.programmer和comp.unix.solaris上向Sun開發人員請教。


[火星人 ] Solaris內核目錄已經有531次圍觀

http://coctec.com/docs/unix/show-post-73978.html