導(dǎo)語(yǔ):這是篇讀書(shū)筆記,每次重讀CSApp都有新的認(rèn)知,尤其是在進(jìn)入了后臺(tái)通道之后才感受到每天和進(jìn)程打交道的感覺(jué)是如此深刻。
0x00 What is Process?
[ system structure ]
- 進(jìn)程(Process)
經(jīng)典定義是一個(gè)執(zhí)行中的程序的實(shí)例,操作系統(tǒng)對(duì)一個(gè)正在運(yùn)行的程序的一種抽象。并發(fā)運(yùn)行,指的是一個(gè)進(jìn)程的指令和另一個(gè)進(jìn)程的指令交錯(cuò)執(zhí)行。操作系統(tǒng)實(shí)現(xiàn)這種交錯(cuò)執(zhí)行的機(jī)制稱(chēng)為上下文切換。
- 線程(Thread)
一個(gè)進(jìn)程可以由多個(gè)線程的執(zhí)行單元組成,每個(gè)線程都運(yùn)行在進(jìn)程的上下文中,并共享同樣的代碼和全局?jǐn)?shù)據(jù)。
- 內(nèi)核(Kernel)
一個(gè)計(jì)算機(jī)程序,用來(lái)管理軟件發(fā)出的數(shù)據(jù)I/O(輸入與輸出)要求,將這些要求轉(zhuǎn)譯為數(shù)據(jù)處理的指令,交由中央處理器(CPU)及計(jì)算機(jī)中其他電子組件進(jìn)行處理,是現(xiàn)代操作系統(tǒng)中最基本的部分。
- 外殼(Shell)
指“為使用者提供使用者界面”的軟件,通常指的是命令行界面的解析器。一般來(lái)說(shuō),這個(gè)詞是指操作系統(tǒng)中提供存取內(nèi)核所提供之服務(wù)的程式。Shell也用于泛指所有為用戶提供操作界面的程序,也就是程序和用戶交互的層面。內(nèi)核不提供交互。
- 搶占(Preemption)
分為非搶占式和搶占式。根據(jù)調(diào)度主體分用戶搶占與內(nèi)核搶占。
非搶占式(Nonpreemptive)——讓進(jìn)程運(yùn)行直到結(jié)束或阻塞的調(diào)度方式。
搶占式(Preemptive)——允許將邏輯上可繼續(xù)運(yùn)行的在運(yùn)行過(guò)程暫停的調(diào)度方式。可防止單一進(jìn)程長(zhǎng)時(shí)間獨(dú)占CPU。
- 異常控制流(ECF,Exceptional Control Flow)
ECF發(fā)生在硬件層,操作系統(tǒng)層,應(yīng)用層。控制轉(zhuǎn)移(control transfer)是指程序計(jì)數(shù)器對(duì)應(yīng)的指令序列的跳轉(zhuǎn),控制轉(zhuǎn)移序列的叫做處理器的控制流(control flow)。
某些如跳轉(zhuǎn)、調(diào)用和返回是為了使得程序?qū)?nèi)部狀態(tài)變化(event)做出反應(yīng)而設(shè)計(jì)的機(jī)制,系統(tǒng)通過(guò)使控制流發(fā)生突變對(duì)發(fā)生各種狀態(tài)變化。
Exceptions
任何情況下,處理器檢測(cè)到event發(fā)生,通過(guò)異常表(exception table)跳轉(zhuǎn)到專(zhuān)門(mén)處理這類(lèi)事件的操作系統(tǒng)子程序(exception handler)。
異步異常由事件產(chǎn)生,同步異常是執(zhí)行一條指令的直接產(chǎn)物。
類(lèi)別包含中斷(異步),陷阱(同步),故障(同步),終止(同步)。
- 中斷——異步發(fā)生,處理器IO設(shè)備信號(hào)的結(jié)果。
- 陷阱——有意的異常。最重要的用途是在用戶程序和內(nèi)核之間提供一個(gè)像過(guò)程一樣的接口,叫做系統(tǒng)調(diào)用。
- 故障——潛在可恢復(fù)的錯(cuò)誤造成的結(jié)果。如果能被修復(fù),則重新執(zhí)行引起故障的指令,否則終止。
- 終止——不可恢復(fù)的致命錯(cuò)誤造成的結(jié)果。
有高達(dá)256種不同的異常類(lèi)型,如出發(fā)錯(cuò)誤(0)、一般保護(hù)故障(13)、缺頁(yè)(14)、機(jī)器檢查(18)、操作系統(tǒng)定義的異常(32-127,129-255)、系統(tǒng)調(diào)用(0x80)。
我們常見(jiàn)的段故障(Segmentation fault),是一般保護(hù)故障(異常13),通常是因?yàn)橐粋€(gè)程序引用了一個(gè)未定義的虛擬存儲(chǔ)器區(qū)域,或者因?yàn)槌绦蛟噲D寫(xiě)一個(gè)只讀的文本段。
[ Examples of popular system calls ]
Processes
- 邏輯控制流(Logical Control Flow)
程序計(jì)數(shù)器PC值的序列叫做邏輯控制流(邏輯流)。PC對(duì)應(yīng)于程序的可執(zhí)行目標(biāo)文件中的指令,或者是包含在運(yùn)行時(shí)動(dòng)態(tài)鏈接到程序的共享對(duì)象中的指令。
邏輯流看起來(lái)就像是在獨(dú)占處理器地執(zhí)行程序,每個(gè)進(jìn)程執(zhí)行邏輯流的一部分然后就被搶占,實(shí)際上處理器通過(guò)上下文保護(hù)好進(jìn)程間的信息,在不同的進(jìn)程中切換。
- 并發(fā)流(Concurrent Flows)
并發(fā)流指邏輯流在執(zhí)行時(shí)間上與另一個(gè)流重疊,多個(gè)就叫并發(fā)(concurrent)。
一個(gè)進(jìn)程和其他進(jìn)程輪流運(yùn)行叫多任務(wù)(multitasking)。
進(jìn)程占有CPU執(zhí)行控制流的每一個(gè)時(shí)間段叫時(shí)間片(time slice)。
多任務(wù)也叫做時(shí)間分片(time slicing)。
如果兩個(gè)流并發(fā)運(yùn)行在不同的處理器或者計(jì)算機(jī),稱(chēng)為并行流(parallel flow)。
- 私有地址空間(Private Address Space)
一般,進(jìn)程間地址空間讀寫(xiě)保護(hù)。進(jìn)程地址空間32位進(jìn)程,代碼段從0x08048000開(kāi)始,64位進(jìn)程從0x00400000開(kāi)始:
[ Process address space ]
- 用戶模式和內(nèi)核模式(User and Kernel Modes)
- 通過(guò)控制寄存器中的模式位(mode bit)描述進(jìn)程當(dāng)前享有的特權(quán)。
- 內(nèi)核模式:(超級(jí)用戶)可執(zhí)行指令集中任何指令,并且可以訪問(wèn)系統(tǒng)中任何存儲(chǔ)器位置。
- 用戶模式:不允許執(zhí)行特權(quán)指令,不允許直接引用地址空間中內(nèi)核區(qū)內(nèi)的代碼和數(shù)據(jù),任何嘗試都會(huì)引發(fā)致命保護(hù)故障。可以通過(guò)系統(tǒng)調(diào)用接口間接訪問(wèn)內(nèi)核代碼和數(shù)據(jù)。
- 上下文切換(Context Switches)
- 內(nèi)核為每個(gè)進(jìn)程維持一個(gè)上下文(context),是內(nèi)核重新啟動(dòng)一個(gè)被搶占的進(jìn)程所需的狀態(tài)。包括:
- 通用目的的寄存器、浮點(diǎn)寄存器、程序計(jì)數(shù)器、用戶棧、狀態(tài)寄存器、內(nèi)核棧和各種內(nèi)核數(shù)據(jù)結(jié)構(gòu)(地址空間的頁(yè)表、有關(guān)當(dāng)前進(jìn)程信息的進(jìn)程表、進(jìn)程已打開(kāi)文件的信息的文件表)
- 內(nèi)核調(diào)度器(scheduler)負(fù)責(zé)調(diào)度進(jìn)程,搶占當(dāng)前進(jìn)程,重新開(kāi)始先前被搶占的進(jìn)程。
0x01 101 Inside Process
Process Control
如何控制進(jìn)程?
?
PID
pid > 0
#include <sys/types.h> // for pid_t #include <unistd.h> ? pid_t getpid(void); // 獲取進(jìn)程ID pid_t getppid(void); // 獲取父進(jìn)程ID
Creating and Terminating Process
從程序角度來(lái)看,進(jìn)程總處于以下三種狀態(tài):
- Running——要么處于CPU執(zhí)行中,要么處于等待被執(zhí)行且最終會(huì)被內(nèi)核調(diào)度。
- Stopped——進(jìn)程被掛起(suspend),且不會(huì)被調(diào)度。當(dāng)收到SIGSTOP、SIGTSTP、SIGTTIN或者SIGTTOU信號(hào)時(shí),進(jìn)程停止,直到收到SIGCONT信號(hào),進(jìn)程再次開(kāi)始運(yùn)行。
- Terminated——進(jìn)程永遠(yuǎn)停止了。三種原因?qū)е陆K止:
- 收到一個(gè)默認(rèn)行為時(shí)終止進(jìn)程的信號(hào);
- 從主程序返回;
- 調(diào)用exit。
#include <sys/types.h> #include <unistd.h> /* 創(chuàng)建子進(jìn)程 * 返回: 子進(jìn)程=0,父進(jìn)程=子進(jìn)程PID,出錯(cuò)=-1 */ pid_t fork(void); ? #include <stdlib.h> void exit(int status);
父進(jìn)程通過(guò)調(diào)用fork創(chuàng)建一個(gè)新的運(yùn)行子進(jìn)程,最大的區(qū)別在于不同的PID。
- fork():一次調(diào)用,返回兩次。
- 在調(diào)用進(jìn)程中(父進(jìn)程),返回子進(jìn)程PID;
- 在新創(chuàng)建的子進(jìn)程中,在子進(jìn)程中返回0。
- 并發(fā)執(zhí)行:父子進(jìn)程是并發(fā)運(yùn)行的獨(dú)立進(jìn)程。
- 相同但是獨(dú)立的地址空間。子進(jìn)程與父進(jìn)程用戶級(jí)虛擬地址空間相同的拷貝,相同的本地變量值、堆、全局變量、以及代碼。如代碼中print出來(lái)不一樣的x。
- 共享文件:任何打開(kāi)文件描述符相同的拷貝,如stdout。
int main() {
pid_t pid;
int x = 1;
?
pid = fork(); // 在此處分裂出了兩條時(shí)間線!
if (pid == 0) {// 子進(jìn)程
printf("child: x=%dn", ++x);
exit(0);
}
// 父進(jìn)程
printf("parent: x=%dn", --x);
exit(0);
?
return 0;
}
out:
parent: x=0
child: x=2
child |————x=2———— father ——————————x=0———— fork exit
Reap Child Process
進(jìn)程終止時(shí),保持位已終止?fàn)顟B(tài),直到被父進(jìn)程回收(reap)。當(dāng)父進(jìn)程回收已終止的子進(jìn)程,內(nèi)核將子進(jìn)程的退出狀態(tài)傳遞給父進(jìn)程,然后拋棄已終止的進(jìn)程,此刻進(jìn)程不復(fù)存在。
僵尸進(jìn)程(zombie):一個(gè)終止了但還未被回收的進(jìn)程。但是如果父進(jìn)程沒(méi)有回收就終止了,則內(nèi)核安排init進(jìn)程(PID=1)回收僵尸進(jìn)程。
#include <sys/types.h> #include <sys/wait.h> ? /* 進(jìn)程可以調(diào)用waitpid等待子進(jìn)程終止或者結(jié)束。 * 默認(rèn)options=0,掛起調(diào)用進(jìn)程,直到它等待集合中的一個(gè)子進(jìn)程終止。如果等待集合中的一個(gè)進(jìn)程在剛調(diào)用的時(shí)刻就已經(jīng)終止了,那么waitpid立即返回。返回已終止的子進(jìn)程PID,并去除該子進(jìn)程。 ? *輸入?yún)?shù)pid: pid>0,等待集合就是一個(gè)單獨(dú)的子進(jìn)程,進(jìn)程ID等于pid。 pid=-1,等待集合是由父進(jìn)程所有的子進(jìn)程組成。 ? *輸入?yún)?shù)options: WNOHANGE:等待集合中任何子進(jìn)程都還沒(méi)有終止,立即返回0;默認(rèn)行為還是掛起調(diào)用進(jìn)程直到子進(jìn)程終止。 WUNTRACED:掛起調(diào)用進(jìn)程執(zhí)行,直到集合中有一個(gè)進(jìn)程終止或停止。返回該進(jìn)程PID。 WNOHANGE|WUNTRACED:立刻返回,0=如果沒(méi)有終止或停止的子進(jìn)程;PID=終止或停止的子進(jìn)程PID。 ? *輸入?yún)?shù)status: WIFEXITED:True=子進(jìn)程是通過(guò)return或者exit終止的; WEXITSTATUS:返回exit狀態(tài),只有WIFEXITED=True時(shí)被定義; WIFSIGNALED:True=子進(jìn)程是因?yàn)橐粋€(gè)未被捕獲的信號(hào)終止的; WTERMSIG:返回導(dǎo)致子進(jìn)程終止信號(hào)量,只有WIFSIGNALED=True被定義; WIFSTOPPED:True=返回的子進(jìn)程是停止的; WSTOPSIG:返回引起子進(jìn)程停止的信號(hào)的數(shù)量,只有WIFSTOPPED=True被定義; ? 返回: 成功=子進(jìn)程PID;if WNOHANG=0; 其他錯(cuò)誤=-1(errno=ECHILD,沒(méi)有子進(jìn)程;errno=EINTR,被一個(gè)信號(hào)中斷) */ pid_t waitpid(pid_t pid, int *status, int options); pid_t wait(int *status); //等價(jià)于waitpid(-1, &status, 0);
Sleep
#include <unistd.h> ? // 返回:seconds left to sleep unsigned int sleep(unsigned int secs); ? // 讓調(diào)用函數(shù)休眠,直到收到一個(gè)信號(hào) // 返回:-1 int pause(void);
Loading and Running Programs
execve函數(shù)在當(dāng)前進(jìn)程的上下文中加載并運(yùn)行一個(gè)新的程序,覆蓋當(dāng)前進(jìn)程的地址空間,但并沒(méi)有創(chuàng)建一個(gè)新進(jìn)程,進(jìn)程PID沒(méi)有改變。
#include <unistd.h> // 返回:成功=不返回;出錯(cuò)=-1 int execve(const char *filename, const char *argv[], const char *envp[]); // 程序主入口: int main(int argc, char **argv, char **envp); int main(int argc, char *argv[], char *envp[]);
Signal
[ linux Signal(`man 7 signal`) ]
信號(hào)傳遞到目的進(jìn)程包括兩個(gè)步驟:1)發(fā)送;2)接收。
- 一個(gè)發(fā)出卻沒(méi)被接收的信號(hào)叫做待處理信號(hào)(Pending Signal)。
- 一個(gè)進(jìn)程有一個(gè)類(lèi)型為k的待處理信號(hào),后面發(fā)送到這個(gè)進(jìn)程的k信號(hào)都會(huì)被丟棄。
- 也可以選擇性阻塞接收某個(gè)信號(hào),信號(hào)被阻塞時(shí)仍可以發(fā)送,但產(chǎn)生的待處理信號(hào)不會(huì)被接收,直到進(jìn)程取消對(duì)這種信號(hào)的阻塞。
- 一個(gè)待處理信號(hào)最多只能被接收一次,內(nèi)核為每個(gè)進(jìn)程在pending位向量中維護(hù)待處理信號(hào)集合,而在blocked位向量中維護(hù)被阻塞的信號(hào)集合。
- 只有接收了k信號(hào),內(nèi)核才會(huì)清除pending中的k位。
Sending Signal
- 每個(gè)進(jìn)程都只屬于一個(gè)進(jìn)程組,進(jìn)程組ID標(biāo)識(shí)。unix所有發(fā)送信號(hào)的機(jī)制都是基于進(jìn)程組(process group)/
#include <unistd.h> ? // 返回:調(diào)用進(jìn)程的進(jìn)程組ID pid_t getpgrp(void); // 返回:成功=1,錯(cuò)誤=-1 int setpgid(pid_t pid, pid_t pgid);
- 用/bin/kill程序發(fā)送信號(hào)
- 發(fā)送信號(hào)9到進(jìn)程15213
- /bin/kill -9 15213
- 發(fā)送信號(hào)9到進(jìn)程組15213中的每個(gè)進(jìn)程。
- /bin/kill -9 -15213
- 從鍵盤(pán)發(fā)送信號(hào)
- unix使用作業(yè)(job)表示對(duì)每一個(gè)命令行執(zhí)行而創(chuàng)建的進(jìn)程,至多一個(gè)前臺(tái)作業(yè)和0個(gè)或多個(gè)后臺(tái)作業(yè)。通過(guò)|unix管道連接起多個(gè)進(jìn)程。
- shell位每個(gè)作業(yè)創(chuàng)建一個(gè)獨(dú)立的進(jìn)程組。進(jìn)程組ID是取自job中父進(jìn)程中的一個(gè)。
- Ctrl + C發(fā)送SIGINT信號(hào)到前臺(tái)進(jìn)程組中的每一個(gè)進(jìn)程,終止前臺(tái)作業(yè)。
[ 前臺(tái)進(jìn)程子進(jìn)程和父進(jìn)程具有相同的進(jìn)程組ID。]
- 用KILL函數(shù)發(fā)送信號(hào)。
#include <signal.h> // 輸入?yún)?shù)pid: // pid>0:發(fā)送SIGKILL給進(jìn)程pid // pid<0:發(fā)送SIGKILL給進(jìn)程組abs(pid) // 返回:成功=0,失敗=-1 int kill(pid_t pid, int sig);
- alarm函數(shù)發(fā)送信號(hào)
#include <unistd.h>
// 發(fā)送SIGALRM給調(diào)用進(jìn)程,如果secs位0,則不會(huì)調(diào)度alarm。任何情況,對(duì)alarm調(diào)用都將取消任何pending alarm,并返回pending alarm在被發(fā)送前還剩下的秒數(shù)。
// 返回:前一次alarm剩余的秒數(shù),0=以前沒(méi)有設(shè)定alarm
unsigned int alarm(unsigned int secs);
?
?
/* 定時(shí)1s觸發(fā)alarm handler,5s結(jié)束 */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
?
?
void handler(int sig) {
static int beeps = 0;
printf("BEEPn");
if (++beeps < 5) {
alarm(1);
} else {
printf("BOOM!n");
exit(0);
}
}
?
?
int main() {
signal(SIGALRM, handler);
alarm(1);
for(;;);
exit(0);
}
Receiving Signals
wtf: 當(dāng)異常處理程序返回時(shí),準(zhǔn)備轉(zhuǎn)移控制權(quán)給進(jìn)程p時(shí),會(huì)檢查非被阻塞的待處理信號(hào)的集合(pending&~blocked) if 集合為空: 進(jìn)程p的邏輯控制流下一跳指令 else: 選擇某個(gè)最小信號(hào)k,強(qiáng)制p接收信號(hào)k goto wtf
每個(gè)信號(hào)類(lèi)型預(yù)定義的默認(rèn)行為(查看Figure8.25):
- 進(jìn)程終止
- 進(jìn)程終止并轉(zhuǎn)存儲(chǔ)器(dump core)
- 進(jìn)程停止直到被SIGCONT信號(hào)重啟
- 進(jìn)程忽略該信號(hào)
#include <signal.h>
?
?
// 定義信號(hào)處理函數(shù)(signal handler)
// 輸入int為信號(hào)量
typedef void (*sighandler_t)(int);
// 輸入函數(shù)sighandler_t:
// handler=SIG_IGN,忽略類(lèi)型為signum的信號(hào);
// handler=SIG_DFL,重置類(lèi)型為signum信號(hào)的行為。
//
// 返回:成功=指向前次處理程序指針,出錯(cuò)=SIG_ERR(不設(shè)置errno)
sighandler_t signal(int signum, sighandler_t handler); // installing the handler
?
?
?
?
/* ctrl-c中斷sleep,并打印睡眠時(shí)間 */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
?
?
void handler(int sig) {} // 改變SIGINT處理函數(shù)
?
?
int snooze(unsigned int sec) {
int left_sleep_sec = sleep(sec);
printf("Slept for %d of %d secs.tUser hits ctrl-c after %d secondsn",
left_sleep_sec, sec, sec-left_sleep_sec);
}
?
?
int main(int argc, char *argv[]) {
if (SIG_ERR == signal(SIGINT, handler)) {
exit(-1);
}
?
?
unsigned int sleep_sec = 0;
if (argc > 1) {
sleep_sec = atoi(argv[1]);
} else {
exit(0);
}
printf("sleep for %d secondsn", sleep_sec);
snooze(sleep_sec);
exit(0);
}
Signal Handling Issues
當(dāng)程序需要捕獲多個(gè)信號(hào)時(shí),問(wèn)題產(chǎn)生了。
- 待處理信號(hào)被阻塞。Unix信號(hào)處理程序通常會(huì)阻塞當(dāng)前處理程序正在處理的類(lèi)型的待處理信號(hào)k。如果另一個(gè)信號(hào)k傳遞到該進(jìn)程,則信號(hào)k將變成待處理,但是不會(huì)被接收,直到處理程序返回。再次檢查發(fā)現(xiàn)仍有待處理信號(hào)k,則再次調(diào)用信號(hào)處理函數(shù)。
- 待處理信號(hào)不會(huì)排隊(duì)等待。任意類(lèi)型最多只有一個(gè)待處理信號(hào)。當(dāng)目的進(jìn)程正在執(zhí)行信號(hào)k的處理程序時(shí)是阻塞的,當(dāng)發(fā)送兩個(gè)信號(hào)k,僅第一個(gè)信號(hào)k會(huì)變成待處理,第二個(gè)則直接被丟棄,不會(huì)排隊(duì)等待。
- 系統(tǒng)調(diào)用可以被中斷。像read、wait和accept調(diào)用過(guò)程會(huì)阻塞進(jìn)程的稱(chēng)謂慢速系統(tǒng)調(diào)用,當(dāng)捕獲到一個(gè)信號(hào)時(shí),被中斷的慢速系統(tǒng)調(diào)用在信號(hào)處理返回時(shí)不再繼續(xù),而是立即返回用戶一個(gè)錯(cuò)誤條件,并將errno設(shè)置為EINTR。(即使sleep被信號(hào)處理捕獲后仍會(huì)返回)
Explicitly Blocking and Unblocking Signals
#include <signal.h> ? ? // how = SIG_BLOCK, blocked=blocked | set // how = SIG_UNBLOCK, blocked=blocked &~ set // how = SIG_SETMASK, blocked = set int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); int sigemptyset(sigset_t *set); // 將每個(gè)信號(hào)添加到set int sigfillset(sigset_t *set); // 添加signum到set int sigaddset(sigset_t *set, int signum); // 從set中刪除signum int sigdelset(sigset_t *set, int signum); //Returns: 0 if OK, −1 on error ? ? int sigismember(const sigset_t *set, int signum); //Returns: 1 if member, 0 if not, −1 on error
Nonlocal Jump
作用允許從一個(gè)深層嵌套的函數(shù)調(diào)用中立即返回。
另一個(gè)作用是使一個(gè)信號(hào)處理程序分支到一個(gè)特殊的位置sigsetjmp/siglongjmp。
#include <setjmp.h>
?
?
int setjmp(jmp_buf env);
int sigsetjmp(sigjmp_buf env, int savesigs);
// Returns: 0 from setjmp, nonzero from longjmps
?
?
void longjmp(jmp_buf env, int retval);
void siglongjmp(sigjmp_buf env, int retval);
// Never returns
?
?
jmp_buf env;
rc=setjmp(env); // 保存當(dāng)前調(diào)用環(huán)境
if(rc == 0) dosomething();
else if (rc == 1) dosomething1(); // 如果
else if (rc == 2) dosomething2();
?
?
int dosomething() {
longjmp(buf,1); // 跳轉(zhuǎn)到setjmp,返回1
// longjmp(buf,2); // 跳轉(zhuǎn)到setjmp,返回2
}
操作進(jìn)程工具
STRACE:打印一個(gè)正在運(yùn)行的程序和它的子程序調(diào)用的每個(gè)系統(tǒng)調(diào)用的軌跡。
PS:列出當(dāng)前系統(tǒng)中的進(jìn)程(包括僵尸進(jìn)程)。
TOP:打印關(guān)于當(dāng)前進(jìn)程資源使用的信息。
PMAP:顯示進(jìn)程的存儲(chǔ)器映射。
/proc:一個(gè)虛擬文件系統(tǒng),以ASCII輸出大量?jī)?nèi)核數(shù)據(jù)結(jié)構(gòu)的內(nèi)容。如cat /proc/loadavg,觀察Linux系統(tǒng)上的當(dāng)前的平均負(fù)載。






