Process Management 進(jìn)程管理·

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)閉步驟:

  1. 以注冊的相反順序調(diào)用使用atexit()或on_exit()注冊的任何函數(shù)。
  2. 刷新所有打開的標(biāo)準(zhǔn)I/O stream舷夺。
  3. 刪除使用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;
}
atexit()測試結(jié)果

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;
}
fork之后return 1
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;
}
fork之后調(diào)用·abort()

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è):

  1. real
  2. effective
  3. saved
  4. 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秧廉。


sessions, process groups, processes和控制終端之間的關(guān)系

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)程:

  1. 調(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。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疟游,一起剝皮案震驚了整個(gè)濱河市呼畸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌颁虐,老刑警劉巖蛮原,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異聪廉,居然都是意外死亡瞬痘,警方通過查閱死者的電腦和手機(jī)故慈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來框全,“玉大人察绷,你說我怎么就攤上這事〗虮纾” “怎么了拆撼?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長喘沿。 經(jīng)常有香客問我闸度,道長,這世上最難降的妖魔是什么蚜印? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任莺禁,我火速辦了婚禮,結(jié)果婚禮上窄赋,老公的妹妹穿的比我還像新娘哟冬。我一直安慰自己,他們只是感情好忆绰,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布浩峡。 她就那樣靜靜地躺著,像睡著了一般错敢。 火紅的嫁衣襯著肌膚如雪翰灾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天稚茅,我揣著相機(jī)與錄音纸淮,去河邊找鬼。 笑死峰锁,一個(gè)胖子當(dāng)著我的面吹牛萎馅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播虹蒋,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼糜芳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了魄衅?” 一聲冷哼從身側(cè)響起峭竣,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎晃虫,沒想到半個(gè)月后皆撩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年扛吞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呻惕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡滥比,死狀恐怖亚脆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情盲泛,我是刑警寧澤濒持,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站寺滚,受9級(jí)特大地震影響柑营,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜村视,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一官套、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蓖议,春花似錦虏杰、人聲如沸讥蟆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘸彤。三九已至修然,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間质况,已是汗流浹背愕宋。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留结榄,地道東北人中贝。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像臼朗,于是被迫代替她去往敵國和親邻寿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容