Programs, Progresses, and Threads
一個(gè)二進(jìn)制文件是位于一個(gè)諸如磁盤的存儲(chǔ)介質(zhì)上被編譯的,可執(zhí)行代碼。通俗來說残拐,我們可以用術(shù)語program,大型重要的二進(jìn)制文件我們也可以稱為應(yīng)用。
一個(gè)progress是一個(gè)正在運(yùn)行的program。
一個(gè)progress包括加載到內(nèi)存中的二進(jìn)制image丐枉,但也包含更多:虛擬化內(nèi)存的實(shí)例、內(nèi)核資源(如打開的文件)掘托、安全上下文(如關(guān)聯(lián)用戶)瘦锹、 一個(gè)或多個(gè)thread。
一個(gè)thread是進(jìn)程內(nèi)部活動(dòng)的單位闪盔。每個(gè)線程都有自己的虛擬處理器弯院,其中包括堆棧、處理器狀態(tài)(如寄存器)和指令指針泪掀。
在單個(gè)線程的進(jìn)程中听绳,進(jìn)程就是線程。有一個(gè)虛擬化內(nèi)存實(shí)例和一個(gè)虛擬化處理器异赫。在多線程進(jìn)程中椅挣,有多個(gè)線程。 虛擬內(nèi)存與進(jìn)程相關(guān)聯(lián)塔拳,線程都共享相同的內(nèi)存地址空間鼠证。
Process ID 進(jìn)程ID
每個(gè)進(jìn)程由一個(gè)唯一的標(biāo)識(shí)符表示,即進(jìn)程ID(通晨恳郑縮短為pid)量九。
The Progress Hierarchy 進(jìn)程的層級(jí)
產(chǎn)生新進(jìn)程的進(jìn)程稱為父進(jìn)程;新進(jìn)程稱為子進(jìn)程孕荠。
每個(gè)進(jìn)程都是從另一個(gè)進(jìn)程派生出來的(當(dāng)然娩鹉,init進(jìn)程除外)攻谁。因此,每個(gè)子進(jìn)程都有父進(jìn)程弯予。此關(guān)系記錄在每個(gè)進(jìn)程的父進(jìn)程ID(ppid)中戚宦, 它是子進(jìn)程的父進(jìn)程的pid。
每個(gè)子進(jìn)程繼承其父進(jìn)程的用戶和組所有權(quán)锈嫩。
pid_t
以編程方式上來講受楼,進(jìn)程ID由類型表示,該類型在頭文件<sys/ypes.h>中定義呼寸。在Linux上艳汽,pid_t通常是int類型的。
獲取進(jìn)程ID和父進(jìn)程ID
getpid()系統(tǒng)調(diào)用返回調(diào)用進(jìn)程的進(jìn)程ID:
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
getppid()系統(tǒng)調(diào)用返回調(diào)用進(jìn)程的父進(jìn)程的進(jìn)程ID:
#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);
兩個(gè)調(diào)用都不會(huì)返回錯(cuò)誤对雪。
//usage
printf("My pid=%jd\n", (intmax_t)getpid());
printf("Parent's pid=%jd\n", (intmax_t)getppid());
運(yùn)行一個(gè)新的進(jìn)程
一個(gè)系統(tǒng)調(diào)用將二進(jìn)制程序加載到內(nèi)存中河狐,替換以前地址空間的內(nèi)容,并開始執(zhí)行新程序瑟捣。這被稱為執(zhí)行一個(gè)新的程序馋艺,功能上是由exec系統(tǒng)調(diào)用家族提供的。
使用不同的系統(tǒng)調(diào)用來創(chuàng)建一個(gè)新進(jìn)程迈套。通常捐祠,新進(jìn)程會(huì)立即執(zhí)行新程序。
創(chuàng)建一個(gè)新過程的行為叫做forking桑李,這個(gè)功能由系統(tǒng)調(diào)用fork()提供踱蛀。
首先,要在新進(jìn)程中執(zhí)行一個(gè)新程序贵白,就需要fork來創(chuàng)建一個(gè)新進(jìn)程率拒,然后exec將一個(gè)新二進(jìn)制文件加載到該進(jìn)程中。
exec函數(shù)系列
沒有單一的exec函數(shù)戒洼;相反俏橘,有一個(gè)基于單個(gè)系統(tǒng)調(diào)用的exec函數(shù)系列允华。
讓我們首先看看其中最簡單的調(diào)用execl():
#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int ret;
ret = execl("bin/vi", "vi", NULL);
if(ret == -1)
perror("execl");
int ret;
ret = execl("/bin/vi", "vi", "/home/kidd/hooks.txt", NULL);
if(ret == -1)
perror("execl")
The rest of the family
#include <unistd.h>
int execlp (const char *file,
const char *arg,
...);
int execle (const char *path,
const char *arg,
...,
char * const envp[]);
int execv (const char *path, char *const argv[]);
int execvp (const char *file, char *const argv[]);
int execve (const char *filename,
char *const argv[],
char *const envp[]);
l和v描述參數(shù)是通過列表·還是通過數(shù)組(vector)提供的圈浇。
p代表系統(tǒng)的PATH被用來搜索要執(zhí)行的文件。
e代表一個(gè)新的環(huán)境也被提供給新的進(jìn)程靴寂。
//execvp() sample
int ret;
ret = execvp("vi", "vi", "/home/kidd/hooks.txt", NULL);
if(ret == -1)
perror("execvp");
const char *args[] = { "vi", "/home/kidd/hooks.txt", NULL };
int ret;
ret = execv ("/bin/vi", args);
if (ret == ?1)
perror ("execvp");
在Linux中磷蜀,只有EXEC家族的一個(gè)成員是一個(gè)系統(tǒng)調(diào)用。其余的是圍繞系統(tǒng)調(diào)用的C庫中的封裝百炬。因?yàn)榍ё內(nèi)f化的系統(tǒng)調(diào)用很難實(shí)現(xiàn)褐隆,而且 用戶路徑的概念只存在于用戶空間中,惟一的系統(tǒng)調(diào)用選項(xiàng)是execve()剖踊。系統(tǒng)調(diào)用原型與用戶調(diào)用相同庶弃。也就是說其他(execl衫贬,execle,execlp歇攻,execv固惯,execvp)都是調(diào)用execve的庫函數(shù)。
fork()系統(tǒng)調(diào)用
運(yùn)行與當(dāng)前image相同的新進(jìn)程可以通過fork()系統(tǒng)調(diào)用創(chuàng)建
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void)
在子節(jié)點(diǎn)中缴守,成功調(diào)用fork()將返回0葬毫。在父類中,fork()返回子節(jié)點(diǎn)的pid屡穗。
//Usage Sample
pid_t pid;
pid = fork();
if(pid>0)
printf("I am the parent of pid = %d!\n", pid);
else if(!pid)
printf("I am the child!\n");
else if(pid == -1)
perror("fork");
下面的示例生成一個(gè)運(yùn)行二進(jìn)制/bin/winlass的新進(jìn)程
pid_t pid;
pid = fork ();
if (pid == ?1)
perror ("fork");
/* the child ... */
if (!pid) {
const char *args[] = { "windlass", NULL };
int ret;
ret = execv ("/bin/windlass", args);
if (ret == ?1) {
perror ("execv");
exit (EXIT_FAILURE);
}
}
Copy-on-write
Copy-on-write 是一種懶散的優(yōu)化策略贴捡,旨在減少復(fù)制資源的開銷。
前提很簡單:如果多個(gè)使用者請(qǐng)求對(duì)自己的資源副本進(jìn)行讀取訪問村砂,則不需要復(fù)制資源副本烂斋。
如果使用者確實(shí)試圖修改其資源的副本,此時(shí)該資源被透明地復(fù)制础废,并且該副本被給予修改的使用者源祈。消費(fèi)者然后可以修改其資源的副本,而其他使用者則繼續(xù)共享原始的色迂、未更改的版本香缺。
這就是名字的由來:復(fù)制僅在寫入時(shí)發(fā)生。
Terminating a Process 結(jié)束一個(gè)進(jìn)程
POSIX和C89都定義了終止當(dāng)前進(jìn)程的標(biāo)準(zhǔn)函數(shù):
#include <stdlib.h>
void exit(int status);
具體來說歇僧,Status&0377返回給父程序图张。
EXIT_SUCCESS和EXIT_FAILURE被定義為表示成功和失敗的可移植方式。在linux上诈悍,0通常表示成功祸轮;一個(gè)非零的值,如1或?1, 表示失敗侥钳。
//一個(gè)成功的exit
exit(EXIT_SUCCESS);
在終止進(jìn)程之前适袜,C庫執(zhí)行以下關(guān)閉步驟:
- 以注冊的相反順序調(diào)用使用atexit()或on_exit()注冊的任何函數(shù)。
- 刷新所有打開的標(biāo)準(zhǔn)I/O stream舷夺。
- 刪除使用tmpfile()函數(shù)創(chuàng)建的所有臨時(shí)文件苦酱。
當(dāng)進(jìn)程退出時(shí),內(nèi)核將清理它代表進(jìn)程創(chuàng)建的不再使用的所有資源给猾。這包括但不限于已分配的內(nèi)存疫萤、打開的文件、 和SystemV信號(hào)量敢伸。清理后扯饶,內(nèi)核將銷毀進(jìn)程并通知父級(jí)其子進(jìn)程的終止。
其他關(guān)閉進(jìn)程的方法
結(jié)束程序的經(jīng)典方法不是通過顯式的系統(tǒng)調(diào)用,而是簡單地“falling off the end” of the program尾序。
C/C++中就是指從main函數(shù)返回钓丰。
顯式返回退出狀態(tài)是一個(gè)很好的編碼實(shí)踐,可以通過exit()或者從main()return一個(gè)值每币。
注意斑粱,成功的返回是EXIT(0),或者是main()的return 0脯爪。
如果進(jìn)程被發(fā)送信號(hào)则北,其默認(rèn)操作是終止該進(jìn)程,則該進(jìn)程也可以終止痕慢。這些信號(hào)包括SIGTERM和SIGKILL尚揣。
結(jié)束程序執(zhí)行的最后一種方法是引起內(nèi)核的憤怒。內(nèi)核可以終止用于執(zhí)行非法指令的進(jìn)程掖举,比如說導(dǎo)致段錯(cuò)誤快骗,消耗完內(nèi)存,消耗更多允許的資源塔次,等等方篮。
atexit()
atexit()庫調(diào)用,用于注冊在進(jìn)程終止時(shí)調(diào)用的函數(shù)励负。
#include <stdlib.h>
int atexit(void(*function)(void));
如果進(jìn)程通過信號(hào)終止藕溅,則不調(diào)用已注冊的函數(shù)。
當(dāng)進(jìn)程通過exit()或從main()返回終止時(shí)已注冊的函數(shù)運(yùn)行继榆。
POSIX標(biāo)準(zhǔn)要求atexit()至少支持ATEXIT_MAX個(gè)·注冊函數(shù)巾表,并且該值至少為32。通過sysconf()和_SC_ATEXIT_MAX可以得到精確的最大值略吨。
long atexit_max;
atexit_max = sysconf(_SC_ATEXIT_MAX);
printf("atexit_max=%ld\n", atexit_max);
On success, atexit() returns 0. On error, it returns ?1.
//Sample
void out(void)
{
long atexit_max;
atexit_max = sysconf(_SC_ATEXIT_MAX);
printf("atexit() succeeded! atexit_max=%ld\n",
atexit_max);
}
int atexitSample() {
if(atexit(out)){
fprintf(stderr, "atexit() failed!\n");
}
return 0;
}
int main(int argc, char*argv[])
{
atexitSample();
printf("Alex is cool!\n");
return 0;
}
on_exit()
SunOS 4定義了自己的與atexport()等價(jià)的內(nèi)容集币,Linux的glibc支持它:
#include <stdlib.h>
int on_exit (void (*function)(int, void *), void *arg);
這個(gè)函數(shù)的工作原理與atexit()相同,但是注冊函數(shù)的原型是不同的:
void my_function (int status, void *arg);
建議應(yīng)該使用符合標(biāo)準(zhǔn)的atexit()翠忠。
SIGCHLD
當(dāng)進(jìn)程終止時(shí)鞠苟,內(nèi)核將信號(hào)SIGCHLD發(fā)送給父進(jìn)程。默認(rèn)情況下秽之,此信號(hào)將被忽略当娱,父級(jí)不采取任何操作。進(jìn)程可以選擇處理此信號(hào)政溃,可以通過使用signal()或Sigaction()系統(tǒng)調(diào)用函數(shù)趾访。
Waiting for Terminated Child Processes 等待子進(jìn)程結(jié)束
Unix的原始設(shè)計(jì)者決定态秧,當(dāng)子進(jìn)程在其父進(jìn)程之前死亡時(shí)董虱,內(nèi)核應(yīng)該將子進(jìn)程置于一個(gè)特殊的進(jìn)程狀態(tài)。處于這種狀態(tài)的進(jìn)程稱為僵尸。只有一些包含潛在有用數(shù)據(jù)的基本內(nèi)核數(shù)據(jù)結(jié)構(gòu)的基本框架被保留下來愤诱。處于此狀態(tài)的進(jìn)程等待其父進(jìn)程查詢其狀態(tài)云头。 (一種稱為等待僵尸進(jìn)程的過程)。只有在父進(jìn)程獲得有關(guān)已終止子進(jìn)程的保留信息之后淫半,進(jìn)程才會(huì)正式退出溃槐。
Linux內(nèi)核提供了幾個(gè)接口,用于獲取關(guān)于被結(jié)束子類的信息科吭。POSIX定義的最簡單的此類接口是wait():
#include <sys/types.h>
#include <sys.wait.h>
pid_t wait(int *status);
對(duì)wauit()的調(diào)用返回終止的子進(jìn)程的PID或在錯(cuò)誤時(shí)返回-1昏滴。
如果沒有子項(xiàng)終止,則調(diào)用將阻塞对人,直到子項(xiàng)終止谣殊。
所以早收到SIGCHILD時(shí)再調(diào)用wait()將不會(huì)阻塞。
status指針包含有關(guān)子進(jìn)程的信息牺弄。
由于POSIX允許實(shí)現(xiàn)根據(jù)它們認(rèn)為合適的情況定義狀態(tài)中的位姻几,因此標(biāo)準(zhǔn)提供了一系列宏來解釋參數(shù):
#include <sys/wait.h>
int WIFEXITED (status);
int WIFSIGNALED (status);
int WIFSTOPPED (status);
int WIFCONTINUED (status);
int WEXITSTATUS (status);
int WTERMSIG (status);
int WSTOPSIG (status);
int WCOREDUMP (status);
- 如果進(jìn)程正常終止,return或者exit, 則WIFEXITED返回true势告。在本例中蛇捌,宏WEXITSTATUS提供了傳遞給_EXIT()的低階八位。
- 如果一個(gè)信號(hào)導(dǎo)致進(jìn)程終止咱台,WIFSIGNALED返回true络拌。
在這種情況下,WTERMSIG返回導(dǎo)致終止的信號(hào)數(shù)目回溺,如果進(jìn)程響應(yīng)于接收到信號(hào)而產(chǎn)生了core dump盒音,則WCOREDUMP返回true。
WIFSTOPPED和WIFCONTINUED返回true(如果進(jìn)程已停止或繼續(xù))馅而,當(dāng)前正在通過ptrace()系統(tǒng)調(diào)用進(jìn)行跟蹤祥诽。只有在實(shí)現(xiàn)調(diào)試器時(shí)才會(huì)應(yīng)用。
如果WIFSTOPPED為真瓮恭,則WSTOPSIG提供停止進(jìn)程的信號(hào)雄坪。
int main(int argc, char*argv[])
{
int status;
pid_t pid;
if(!fork()){
return 1;
}
pid = wait(&status);
if(pid == -1){
perror("wait");
}
printf("pid=%d\n", pid);
if(WIFEXITED(status)){
printf("Normal termination with exit status=%d\n",
WEXITSTATUS(status));
}
if(WIFSIGNALED(status)){
printf("Killed by signal=%d%s\n",
WTERMSIG(status),
WCOREDUMP(status)?"(dumped core)": "");
}
if(WIFSTOPPED(status)){
printf("Stopped by signal = %d\n",
WSTOPSIG(status));
}
if(WIFCONTINUED(status)){
printf("Continued\n");
}
return 0;
}
int main(int argc, char*argv[])
{
int status;
pid_t pid;
if(!fork()){
abort();
}
pid = wait(&status);
if(pid == -1){
perror("wait");
}
printf("pid=%d\n", pid);
if(WIFEXITED(status)){
printf("Normal termination with exit status=%d\n",
WEXITSTATUS(status));
}
if(WIFSIGNALED(status)){
printf("Killed by signal=%d%s\n",
WTERMSIG(status),
WCOREDUMP(status)?"(dumped core)": "");
}
if(WIFSTOPPED(status)){
printf("Stopped by signal = %d\n",
WSTOPSIG(status));
}
if(WIFCONTINUED(status)){
printf("Continued\n");
}
return 0;
}
Waiting for Specific Process 等待指定的進(jìn)程
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int &status, int options);
pid參數(shù)指定要等待的進(jìn)程。它的值可以分為4個(gè)組:
- < -1
等待進(jìn)程組ID等于該值絕對(duì)值的任何子進(jìn)程屯蹦。例如维哈,傳遞?500等待進(jìn)程組500中的任何進(jìn)程。 - -1
等待任何子進(jìn)程登澜。這是與wait()相同的行為阔挠。 - 0
等待與調(diào)用進(jìn)程屬于同一進(jìn)程組的任何子進(jìn)程。 - > 0
等待任何子進(jìn)程脑蠕,其pid恰好是所提供的值购撼。
status參數(shù)和wait()是一樣的跪削。
options參數(shù)是以下選項(xiàng)中的零或多個(gè)的二進(jìn)制OR
- WNOHANG
不阻塞,如果沒有匹配的子進(jìn)程已經(jīng)終止(或停止或繼續(xù)),則立即返回。 - WUNTRACED
如果設(shè)置了WUNTRACED屎鳍,則會(huì)設(shè)置返回狀態(tài)參數(shù)中的WIFSTOPPED位,即使調(diào)用進(jìn)程沒有跟蹤子進(jìn)程毫玖。此標(biāo)志允許實(shí)現(xiàn)更一般的作業(yè)控制,比如說在shell中凌盯。 - WCONTINUED
如果設(shè)置付枫,即使調(diào)用進(jìn)程沒有跟蹤子進(jìn)程,也會(huì)設(shè)置返回狀態(tài)參數(shù)中的WIFCONTINUED位驰怎。與WUNTRACED一樣励背,此標(biāo)志對(duì)于實(shí)現(xiàn)shell非常有用。
成功后砸西,waitpid()返回狀態(tài)已更改狀態(tài)的進(jìn)程的pid叶眉。如果指定了WNOHANG,并且指定的子進(jìn)程·或多個(gè)子進(jìn)程尚未更改狀態(tài)芹枷,那么返回0衅疙。如果·發(fā)生錯(cuò)誤,返回?1鸳慈。
int status;
pid_t pid;
pid = waitpid (1742, &status, WNOHANG);
if (pid == ?1)
perror ("waitpid");
else {
printf ("pid=%d\n", pid);
if (WIFEXITED (status))
printf ("Normal termination with exit status=%d\n",
WEXITSTATUS (status));
if (WIFSIGNALED (status))
printf ("Killed by signal=%d%s\n",
WTERMSIG (status),
WCOREDUMP (status) ? " (dumped core)" : "");
}
wait(&status) == waitpid(-1 &status, 0)
Even more Waiting Versatility
#include <sys/wait.h>
int waitid(idtype_t idtype, id_t id,
siginfo_t *infop, int options);
waitid()用于等待和獲取有關(guān)子進(jìn)程的狀態(tài)更改(終止饱溢、停止、繼續(xù))的信息
idtype和id參數(shù)指定要等待的子對(duì)象走芋。
idtype可以由如下的值:
- P_PID
等待一個(gè)pid與參數(shù)id匹配的子進(jìn)程绩郎。 - P_GIP
等待一個(gè)進(jìn)程組ID與參數(shù)id匹配的子進(jìn)程。
-P_ALL
等待任意的子進(jìn)程翁逞,id值忽略肋杖。
參數(shù)options可以值下列值的二進(jìn)制OR形式組成:
- WEXITED
調(diào)用將等待已終止的子進(jìn)程(由id和idtype確定)。 - WSTOPPED
該調(diào)用將等待響應(yīng)接收signal而停止執(zhí)行的子進(jìn)程挖函。 - WCONTINUED
調(diào)用將等待那些在接收到信號(hào)后繼續(xù)執(zhí)行的子進(jìn)程状植。 - WNOHANG
調(diào)用永遠(yuǎn)不會(huì)阻塞,但是如果沒有匹配的子進(jìn)程(已經(jīng)終止怨喘,或停止津畸,或繼續(xù)),則會(huì)立即返回 必怜。 - WNOWAIT
調(diào)用不會(huì)從僵尸狀態(tài)中刪除匹配進(jìn)程肉拓。這一過程今后可能會(huì)等待。
在成功地等待一個(gè)子進(jìn)程時(shí)梳庆,wawtid()將填寫infop參數(shù)暖途,該參數(shù)必須指向有效的siginfo_t類型卑惜。這其中如下的參數(shù)會(huì)被填寫: - si_pid 子進(jìn)程的pid
- si_uid 子進(jìn)程的uid
- si_code
設(shè)置為CLD_EXECED、CLD_KELD丧肴、CLD_STEST或CLD_CONJ續(xù)残揉,分別響應(yīng)子進(jìn)程終止胧后、通過信號(hào)死亡芋浮、通過信號(hào)停止或繼續(xù)通過信號(hào)。 - si_signo
設(shè)置為SIGCHLD - si_status
如果si_code是CLD_EXTEXT壳快,則該字段是子進(jìn)程的退出代碼纸巷。否則,此字段是發(fā)送給導(dǎo)致狀態(tài)更改的子進(jìn)程的信號(hào)的代碼眶痰。
成功后瘤旨,watid()返回0。如果出現(xiàn)錯(cuò)誤竖伯,witid()將返回?1
Launching ans Waiting for a New Process
如果一個(gè)進(jìn)程生成一個(gè)子進(jìn)程只是為了立即等待它的終止存哲,那么使用這個(gè)接口是有意義的:
#define _XOPEN_SOURDE
#include <stdlib.h>
int system(const char *command);
使用system()運(yùn)行簡單的實(shí)用程序或shell腳本是很常見的。
通常七婴,目標(biāo)是簡單地獲得它的返回值祟偷。
command參數(shù)是參數(shù)/bin/sh -c的后綴。
在成功的情況下打厘,返回值是wait()提供的命令的返回狀態(tài), 通過WEXITSTATUS狀態(tài)獲取已執(zhí)行命令的退出代碼修肠。
如果調(diào)用/bin/sh本身失敗,則WEXITSTATUS給出的值與Exit(127)返回的值相同户盯。
如果出現(xiàn)錯(cuò)誤嵌施,則調(diào)用返回?1。
如果命令為NULL莽鸭,如果shell/bin/sh可用吗伤,system()返回一個(gè)非零值,否則返回0硫眨。
do{
int ret;
ret = system ("pidof rudderd");
if (WIFSIGNALED (ret) &&
(WTERMSIG (ret) == SIGINT ||
WTERMSIG (ret) == SIGQUIT))
break; /* or otherwise handle */
}while(1);
Zombies
Users and Groups
軟件開發(fā)中的最佳實(shí)踐鼓勵(lì)最低特權(quán)原則牲芋,這意味著流程應(yīng)該以盡可能最低的權(quán)限執(zhí)行。
Real, Effective, and Saved User and Group IDs
事實(shí)上捺球,與進(jìn)程關(guān)聯(lián)的user ID不是一個(gè)缸浦,而是四個(gè):
- real
- effective
- saved
- filesystem
real user ID是最初運(yùn)行進(jìn)程的用戶的uid。也就是父進(jìn)程的uid氮兵。
effective user ID 是進(jìn)程當(dāng)前使用的uid裂逐。
saved used ID是進(jìn)程的遠(yuǎn)離啊的effective user ID。
Changing the Effective User or Group ID
Linux提供了兩個(gè)POSIX授權(quán)的函數(shù)泣栈,用于設(shè)置當(dāng)前正在執(zhí)行的進(jìn)程的effective 用戶ID和組ID:
#include <sys/types.h>
#include <unistd.h>
int seteuid (uid_t euid);
int setegid (gid_t egid);
seteuid() returns 0. On failure, it returns ?1卜高。
Support for Saved User IDs
Obtaining the User and Group IDs
這兩個(gè)系統(tǒng)調(diào)用返回real 的用戶和組ID弥姻,
#include <unistd.h>
#include <sys/types.h>
uid_t getuid (void);
gid_t getgid (void);
這兩個(gè)系統(tǒng)調(diào)用分別返回effective用戶ID和組ID。
#include <unistd.h>
#include <sys/types.h>
uid_t geteuid (void);
gid_t getegid (void);
Sessions and Process Group
每個(gè)進(jìn)程是一個(gè)進(jìn)程組的成員,
進(jìn)程組的主要屬性是可以向組中的所有進(jìn)程發(fā)送信號(hào):單個(gè)操作可以終止掺涛、停止或繼續(xù)同一進(jìn)程組中的所有進(jìn)程庭敦。
每個(gè)進(jìn)程組由進(jìn)程組ID(pgid)標(biāo)識(shí),并具有一個(gè)process group leader薪缆。
進(jìn)程組ID等于進(jìn)程leader的pid秧廉。
Session System Calls
shell在登錄時(shí)創(chuàng)建新會(huì)話。他們通過一個(gè)特殊的系統(tǒng)調(diào)用來做到這一點(diǎn)拣帽,這使得創(chuàng)建一個(gè)新會(huì)話變得很容易:
#include <unistd.h>
pid_t setsid (void);
換句話說疼电,setsid()在新session中創(chuàng)建一個(gè)新的progress group,并使調(diào)用的過程成為兩者的領(lǐng)導(dǎo)者减拭。
確保任何給定進(jìn)程不是進(jìn)程組領(lǐng)導(dǎo)的最簡單方法是分叉蔽豺,讓父進(jìn)程終止,并讓子進(jìn)程執(zhí)行setsid()拧粪。例如:
pid_t pid;
pid = fork ();
if (pid == ?1) {
perror ("fork");
return ?1;
} else if (pid != 0)
exit (EXIT_SUCCESS);
if (setsid () == ?1) {
perror ("setsid");
return ?1;
}
獲取當(dāng)前session ID(雖然不太有用)也是可以的:
#define _XOPEN_SOURCE 500
#include <unistd.h>
pid_t getsid (pid_t pid);
如果PID參數(shù)為0修陡,則調(diào)用返回調(diào)用進(jìn)程的會(huì)話ID。
getsid()的使用不常見可霎,主要用于診斷目的:
pid_t sid;
sid = getsid (0);
if (sid == ?1)
perror ("getsid"); /* should not be possible */
else
printf ("My session id=%d\n", sid);
Process Group System Calls
調(diào)用setpgid()將進(jìn)程pid的進(jìn)程組ID設(shè)置為pgid
#define _XOPEN_SOURCE 500
#include <unistd.h>
int setpgid (pid_t pid, pid_t pgid);
如果pid參數(shù)為0魄鸦,則使用當(dāng)前進(jìn)程。如果pgid為0啥纸,則使用pid標(biāo)識(shí)的進(jìn)程ID作為進(jìn)程組ID号杏。
與會(huì)話一樣,獲取進(jìn)程的進(jìn)程組ID也是可能的斯棒,盡管用處不大:
#define _XOPEN_SOURCE 500
#include <unistd.h>
pid_t getpgid (pid_t pid);
如果PID為0時(shí)盾致,則使用當(dāng)前進(jìn)程的進(jìn)程組ID。
與getsid()一樣荣暮,使用主要用于診斷目的:
pid_t pgid;
pgid = getpgid (0);
if (pgid == ?1)
perror ("getpgid"); /* should not be possible */
else
printf ("My process group id=%d\n", pgid);
Daemons 守護(hù)進(jìn)程
守護(hù)進(jìn)程是在后臺(tái)運(yùn)行的進(jìn)程庭惜,而不是連接到任何控制終端。
守護(hù)進(jìn)程有兩個(gè)一般要求:它必須作為init的子程序運(yùn)行穗酥,并且不能連接到終端护赊。
通常,程序執(zhí)行以下步驟成為守護(hù)進(jìn)程:
- 調(diào)用fork()砾跃。這將創(chuàng)建一個(gè)新進(jìn)程骏啰,它將成為守護(hù)進(jìn)程。
2.在父進(jìn)程中抽高,調(diào)用exit()判耕。這確保了父進(jìn)程的父父進(jìn)程(守護(hù)進(jìn)程的祖父母)確信它的子進(jìn)程終止,守護(hù)進(jìn)程的父進(jìn)程不再運(yùn)行翘骂,并且守護(hù)進(jìn)程不是進(jìn)程組的領(lǐng)導(dǎo)壁熄。
3.調(diào)用setsid()帚豪,為守護(hù)進(jìn)程提供一個(gè)新的進(jìn)程組和會(huì)話,這兩個(gè)進(jìn)程組和會(huì)話都將其作為領(lǐng)導(dǎo)者草丧。這也確保了進(jìn)程沒有相關(guān)的控制終端(因?yàn)檫M(jìn)程剛剛創(chuàng)建了一個(gè)新會(huì)話狸臣,并且不會(huì)分配一個(gè)會(huì)話)。
4.通過chdir()將工作目錄更改為根目錄昌执。這是因?yàn)槔^承的工作目錄可以在文件系統(tǒng)上的任何位置烛亦。守護(hù)進(jìn)程傾向于在系統(tǒng)正常運(yùn)行期間運(yùn)行,您不想讓某個(gè)隨機(jī)目錄處于打開狀態(tài)仙蚜,從而防止廣告 管理員從卸載包含該目錄的文件系統(tǒng)此洲。
5.關(guān)閉所有文件描述符厂汗。您不希望繼承打開的文件描述符委粉,并且在不知情的情況下將其保持為打開狀態(tài)。
6.打開文件描述符 0娶桦、1和2(標(biāo)準(zhǔn)輸入贾节、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤)并將它們重定向到/dev/null。
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fs.h>
int main (void) {
pid_t pid;
int i;
/* create new process */
pid = fork ();
if (pid == ?1)
return ?1;
else if (pid != 0)
exit (EXIT_SUCCESS);
/* create new session and process group */
if (setsid () == ?1)
return ?1;
/* set the working directory to the root directory */
if (chdir ("/") == ?1)
return ?1;
/* close all open files--NR_OPEN is overkill, but works */
for (i = 0; i < NR_OPEN; i++)
close (i);
/* redirect fd's 0,1,2 to /dev/null */
open ("/dev/null", O_RDWR); /* stdin */
dup (0); /* stdout */
dup (0); /* stderror */
/* do its daemon thing... */
return 0;
}
大多數(shù)Unix系統(tǒng)在其C庫中提供了一個(gè)守護(hù)進(jìn)程()函數(shù)衷畦,使這些步驟自動(dòng)化栗涂,將繁瑣的操作變成簡單的操作:
#include <unistd.h>
int daemon (int nochdir, int noclose);
如果nochdir為非零,守護(hù)進(jìn)程將不會(huì)將其工作目錄更改為根目錄祈争。如果noclose為非零斤程,守護(hù)進(jìn)程將不會(huì)關(guān)閉所有打開的文件描述符。如果下列情況下菩混,這些選項(xiàng)是有用的 父進(jìn)程已經(jīng)設(shè)置了忿墅。然而,通常情況下沮峡,這兩個(gè)參數(shù)的值都是0疚脐。
成功后,調(diào)用返回0邢疙。如果失敗棍弄,調(diào)用將返回?1。