標籤:blog http 使用 strong 檔案 os
進程ID
每一個進程都有一個唯一的進程ID。幾個特殊進程:
- 0號進程是核心進程,一般是調度進程swapper。
- 1號進程init,是使用者進程(以root許可權執行/sbin/init),負責初始化。
- 幾個重要函數:getpid(進程ID)/getppid(父進程ID)/getuid(進程真有使用者ID)/geteuid(進程有效使用者ID)/getgid(進程真有使用者組ID)/getegid(進程有效使用者組ID)。
fork/exec/wait常式
fork家族函數用於建立子進程(父子進程關係下節詳細介紹),子進程往往調用exec家族函數運行新程式(fork+exec操作在有些系統中被稱為spawn孵化),而wait家族函數用於擷取子進程終止狀態。
system函數使用/bin/sh運行命令,下面是使用fork/exec/wait實現的簡單版本號碼
#include<sys/wait.h>#include<errno.h>#include<unistd.h>int system(constchar *cmdstring) /* version without signal handling */{ pid_t pid; int status; if (cmdstring == NULL) return(1); /* always a command processor withUNIX */ if ((pid = fork()) < 0) { status = -1; } else if (pid == 0) { /* fork傳回值為0,表示是在子進程中*/ execl("/bin/sh", "sh","-c", cmdstring, (char *)0); _exit(127); /* execl error */ } else { /* 在父進程中,fork返回子進程pid */ while (waitpid(pid, &status, 0) < 0) { if (errno != EINTR) { status = -1; /* error other than EINTR fromwaitpid() */ break; } } } return(status);}
- fork函數一次調用,在父進程和自己成中兩次返回,由於父進程能夠fork多個子進程,所以設計成在父進程中返回子進程pid,而在子進程返回0,由於子進程能夠通過getpid和getppid擷取自身pid和父進程pid。常見應用情境是在網路編程中,父進程while迴圈監聽使用者請求,當接收到使用者請求,fork出子進程進行處理。註:linux系統中,fork通過clone系統調用實現。
- waipid函數使用參數0等待指定子進程返回,wait函數家族包含wait(等待任一子進程返回)/waitpid(等待指定子進程&組返回,並能通過第三個參數設定堵塞選項)/waittid(進一步擴充,能擷取導致進程終止的訊號資訊等)/wait3(還能夠返回進程使用的資源)/wait4(其它wait函數的入口)。
- exec家族函數的作用是替換掉當前進程上下文(text/data/heap/stack等),運行新的程式(不會建立新進程)。exec家族函數包含execl/execlp/execle/execv/execvp/execve,各個函數主要差別在參數上,當中l表示是列表形式,v表示是指標數組形式,e表示環境變數,p表示命令參數是相對路徑,會在PATH路徑中搜尋。
父子進程
子進程和父進程共用僅僅讀的text段,針對bss段、對、棧,現代作業系統使用COW(copy-on-write)技術,僅僅有發生改變時,才會拷貝對應的記憶體頁。
父子進程關係
子進程會繼承父進程的大量屬性,當中一些重要屬性包含:真實/有效使用者資訊,進程組/會話資訊,工作資料夾,環境變數,資源限制等。
父子進程最明顯的差別是:子進程的tms時間統計資訊被清零,子進程不會繼承檔案鎖,未決鬧鐘&訊號等(興許章節討論)。
子進程和父進程返回先後順序是不確定的,假設使用者程式對父子進程運行順序有依賴,須要自行處理,比方使用訊號實現等待通知機制等。
- 核心為每一個正在終止的進程保留了少量資訊(pid,終止狀態,CPU時間等),便於父進程擷取其終止狀態。
- 假設子進程在父進程之前結束,而父進程沒有wait,子進程會變成殭屍進程。
- 假設父進程先結束,子進程的父進程會變成init進程(pid為1),所以假設要避免殭屍進程的產生,能夠兩次調用fork,即在子進程中再次調用fork,然後退出。這樣第二個fork出來的進程因為其父進程退出,所以被init進程接管。
檔案分享權限設定
子進程會dup父進程開啟的檔案描寫敘述符(共用檔案描寫敘述符close-on-exec標記),包含標準輸出、輸入和錯誤輸出。
,父子進程共用file tableentry,位置位移量一致,所以要父子進程讀寫同一檔案時要注意同步。
設定進程使用者ID
之前提到,子進程會繼承父進程的uid和euid(有效使用者ID),能夠調用setuid(setgid)改動進程使用者(組)。
- 假設是root使用者調用,會同一時候改動進程的uid、euid和備份euid(saved set-user-id)。登陸後,由login(root進程)設定使用者ID。
- 非root使用者僅僅能改動euid,並且僅僅能改動成之前的uid或者備份euid,否則出錯。
- 假設自進程運行exec方法,並且運行程式的set-user-ID位被設定,那麼euid被設定被運行程式屬主ID。
- 備份euid複製euid。正常情況下,uid=euid=備份euid。
- 程式編寫遵循“最小許可權“模型,若且唯若程式須要高許可權時,才調用setuid提升許可權,操作完之後再調用setuid恢複許可權。
其它進程相關函數
- 進程審計:acct開啟進程審計功能,系統記錄已終止進程的統計資訊,包含使用者ID,啟動時間,CPU時間等。Linux系統審計記錄儲存在/var/log/account/pacct,須要用fread讀取acct結構體資訊。
- 進程調度:進程調整nice值來設定執行優先順序(你越nice,你的優先順序越低,人艱不拆。。),相關函數:nice/getpriority/setpriority
- 進程時間:調用clock_t times(structtms *buf )函數,當中返回值為時鐘時間,而tms結構體被下面內容填充:
struct tms { clock_t tms_utime; /* user CPU time */ clock_t tms_stime; /* system CPU time */ clock_t tms_cutime; /* user CPU time, terminated children */ clock_t tms_cstime; /* system CPU time, terminated children */};
注意:相關時間已經通過每秒滴答數(_SC_CLK_TCK)轉化成了秒數,但它是從過去任一時間開始統計的,所以其絕對值無意義。一般分別在進程開始和結束調用times,再計算之間的時間差。