wait()
#include<sys/wait.h>
pid_t wait(int *status);
複製代碼
- 阻塞直到一個子進程終止
- status非空則子進程終止信息經過status指向的int變量返回
- 內核爲父進程下全部子進程的運行總量追加進程CPU時間和資源使用數據
- 終止子進程ID做爲返回
- 若是
wait()
返回-1,可能的緣由是無等待子進程,此時errno會被置位ECHILD
侷限性linux
- 沒法等待某個特定子進程完成
- 沒法非阻塞等待
- 只能檢測終止,對於中止或其餘情況沒法檢測
waitpid()
#include<sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
複製代碼
- status 和 返回值和
wait()
一致- pid>0,爲等待進程的pid
- pid=0,爲父進程同一進程組的全部子進程
- pid<-1,取絕對值
- pid=-1,爲任意子進程,
wait(&status) eq waitpid(-1, &status, 0)
- option = WUNTRACED:除了子進程終止信息外,還返回因信號而中止的進程信息
- option = WCONTINUED:返回收到SIGCONT信號而恢復執行的已中止進程的狀態信息
- option = WNOHANG:指定子進程未發生改變馬上返回不會阻塞,若是無匹配pid的子進程,
waitpid()
報錯,錯誤號設置爲ECHILD- 克隆子進程會有option擴展,見28章
<sys/wait.h>
定義了一組宏對status處理shell
- WIFEXITED(status):子進程正常退出返回true
- WIFSIGNALED(status):經過信號殺掉子進程爲true,WTERMSIG(status)返回信號編號,WCOREDUMP(status)true代表生成核心轉儲文件
- WIFSTOPPED(status):因信號中止返回true, WSTOPSIG(status)返回信號編號
- WIFCONTINUED(status):因受到SIGCONT恢復執行返回true
若是在函數中調用_exit(EXIT_SUCCESS)父進程會認爲子進程正常終止編程
// 函數應當以下,wait才能正常捕捉是信號致使終止
void handler(int sig){
/*func body*/
signal(sig, SIG_DFL);
raise(sig);
}
複製代碼
waittid()
,wait3()
,wait4()
waittid()
更精準的控制,略
孤兒進程: 父進程終止後,init進程接管子進程,
getppid()
返回1 殭屍進程: 在父進程調用wait()
以前,子進程就已經終止bash
- 系統容許父進程在以後某一時刻執行
wait()
,肯定子進程爲什麼- 內核經過將子進程轉化爲殭屍進程處理此狀況,內核將釋放子進程大部分資源,僅保留內核進程表中的一條記錄(包含子進程ID,終止狀態,資源使用數據)
- 沒法經過信號殺死殭屍進程,即便SIGKILL也不行
- 父進程調用
wait()
,或者父進程未調用後退出,init接管後自動調用wait()
,從系統移除殭屍進程
在一個進程終止或者中止時,將SIGCHLD信號發送給其父進程函數
若是每次收到SIGCHLD的時調用
wait()
由於在調用時對同一信號(SIGCHLD)要麼忽略要麼阻塞,阻塞的話標準信號之會保留一次,可能會遺漏SIGCHLD信號。工具
一個比較醜的解決殭屍進程的方法(調用前有殭屍進程產生不能解決)性能
while(waitpid(-1, NULL, WNOHANG) > 0)
continue;
複製代碼
一些特殊spa
- 一般子進程中止也會向父進程發送SIGCHLD
- 經過
sigaction()
調用,傳入SA_NOCLDSTOP,可取消這個行爲- 雖然默認SIGCHLD是忽略
- 若是顯示設置SIGCHLD爲SIG_IGN,以後不會把符合條件的子進程其轉化爲殭屍進程,而是直接刪除(有些UNIX實現可能會把以前殭屍進程給刪除)
- 經過
sigaction()
調用,傳入SA_NOCLDWAIT,也可終止殭屍進程的產生,可是不保證是否發送SIGCHLD信號
#include<unistd.h>
int execve(const char *pathname, char *const argv[], char *const envp[]) // envp 成員字符串格式爲name=value 複製代碼
- 若是pathname文件設置了set-user-id權限位,會將有效用戶ID設爲文件屬主ID
- 不論有沒set-user-id,均會將當前進程的有效用戶id覆蓋已保存的set-user-id
默認由
exec()
調用程序所打開的全部文件描述符在exec()
執行過程當中都會保持打開線程
$ ls /tmp > dit.txt
複製代碼
execlp()
複製代碼
- 調用fork()建立子進程
- 子shell進程以描述符1打開dir.txt用於輸出,打開的方式可能以下:
- 子shell關閉描述符1,隨機打開文件dir.txt,由於open老是取最小值,標準輸入0此時已經打開
- 打開dir.txt 若是獲取的描述符不是標準輸出,shell會調用dup2強制將標準輸出複製爲新描述符的副本
dup2(fd, STDOUT_FILENO)
, 以後再close(fd)
- 子進程執行ls, 將結果輸出到標準輸出
shell有內建命令如
cd
,不會調用fork()
或者exec()
unix
執行時關閉標誌(FD_CLOEXEC)
- 庫函數應該老是爲其打開文件設置執行時關閉標誌
- 若是設置了執行時關閉
- 成功執行
exec()
時, 會自動關閉此描述符- 若是調用
exec()
失敗,文件描述符會保持打開
exec()
略
system()
#include<stdlib.h>
int system(const char *command);
system(cmd);
//等同於
execl("/bin/sh", "sh", "-c", cmd, (char *) NULL);
複製代碼
#define _BSD_SOURCE
#include<unistd.h>
int acct(const char *acctfile) // 特權進程能夠調用, acctfile 一般是路徑字符串/var/log/pacct 或 /usr/account/pacct的指針,若取消記帳功能,accfile爲NULL便可 複製代碼
clone()
#define _GNU_SOURCE
#include<sched.h>
int clone(int (*func)(void *), void *child_stack, int flags, void *func_arg, ...)
複製代碼
- flags 由高位和低位組成
- 低位保存子進程終止時發個父進程的信號, 爲0則不發送,若是子進程是被信號終止,則仍是發SIGCHLD給父進程
- 高位
![]()
進程和線程都是KSE
略略略結合線程
- 共享數據
- 建立線程比建立進程快10倍多(經過
clone()
)
類型 | 描述 |
---|---|
pthread_t | 線程ID |
pthread_mutex_t | 互斥對象 |
pthread_mutexattr_t | 互斥屬性對象 |
pthread_cond_t | 條件變量 |
pthread_condattr_t | 條件變量的屬性對象 |
pthread_key_t | 線程持有數據的鍵 |
pthread_once_t | 一次性初始化控制上下文 |
pthread_attr_t | 線程的屬性對象 |
在線程中
errno
被標記成宏,展開後是返回一個可修改左值的函數
編譯Pthread API程序
設置
cc -pthread
#include<pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start) (void *), void *arg);
複製代碼
- 新線程經過調用
start(arg)
開始start
的返回值不該等於PTHREAD_CANCELED,會被pthread_join
捕獲?,不該在線程棧中分配start
的返回值pthread
參數保存線程的惟一標識attr
指定了新線程的各類屬性
start
函數執行return並返回返回值- 線程調用
pthread_exit()
- 調用
pthread_cancel()
取消線程- 任意線程調用了
exit()
,或主線程執行了return 語句,致使進程中全部線程當即終止
#include<pthread.h>
void pthread_exit(void *retval);
複製代碼
retval
指定了線程的返回值,同start
返回值同樣不該再線程棧中分配- 若主線程調用
pthread_exit
則其餘線程繼續運行
# include<pthread.h>
pthread_t pthread_self(void);
int pthread_equal(pthread_t t1, pthread_t t2);
複製代碼
linux中線程ID全部進程中都惟一,其餘不必定
#include<pthread.h>
int pthread_join(pthread_t thread, void **retval);
// 等待thread標誌的線程終止,若是已經終止馬上返回
// 傳入以前鏈接過的thread 會致使未定義結果
複製代碼
pthread_join
接waitpid
比較
- 線程之間沒有層級關係,ex:線程A 建立B,B建立C,A能夠
pthread_join
C, C能夠pthread_join
A,fork
以後只有父進程能wait子進程- 沒法鏈接任意進程,已不能以非阻塞方式鏈接
若是線程既沒有分離也沒有鏈接,會產生殭屍線程
- 不關心線程的返回,但願在線程終止時自動清理而且移除
- 一旦線程detach,不能再join
- 其餘線程調用了
exit()
或者主線程執行return,遭分離的線程仍是會受到影響
#inlcude<pthread.h>
int pthread_detach(pthread_t thread);
複製代碼
靜態分配互斥量
#include<pthread.h>
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
複製代碼
- 若是發起
pthread_mutex_lock()
的進程已然將目標互斥量鎖定
- 線程死鎖(linux默認)
- 返回EDEADLK錯誤
pthread_mutex_trylock()
不會阻塞,失敗會返回EBUSYpthread_mutex_timedlock()
能夠傳參數abstime, 超時後返回ETIMEOUT
- 加鎖減鎖的操做比之間代碼邏輯操做小的多時,性能影響較小,比文件鎖,信號量加減鎖性能要高
- 互斥量實現採用了機器語言級原子操做,內存中執行
- 相同順序對互斥量加鎖
- 使用
pthread_mutex_lock()
鎖定第一個,以後用pthread_mutex_trylock()
鎖定以後,若是鎖定失敗,釋放全部互斥量在重來,效率較低
動態初始化互斥量
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t * attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
void pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
複製代碼
必須動態初始化的case
- 互斥量動態分配於堆中
- 互斥量在棧中自動分配
- 初始化由靜態分配,不使用默認屬性的互斥量
- 動態分配的必須destory,在堆free或是函數棧消失前釋放
互斥量的類型(pthread_mutexattr_t)
- PTHREAD_MUTEX_NORMAL 不具備死鎖檢測,對本身鎖定互斥量加鎖則死鎖,對未鎖定或其餘線程鎖定的互斥量解鎖會致使不肯定結果(Liunx上都會成功)
- PTHREAD_MUTEX_ERRORCHECK 以上三種狀況都會犯回錯誤,但運行比前者慢,可做爲調試工具
- PTHREAD_MUTEX_RECURSIVE 同一線程每次加鎖鎖計數器會加一,解鎖到0其餘線程才能加鎖,對於後兩種異常,解鎖都會失敗
容許一個線程就某個共享資源的狀態變化通知其餘線程,並讓其餘線程等待這一通知
靜態分配條件變量
#include<pthread.h>
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//喚醒任意單個線程
int pthread_cond_signal(pthread_cond_t *cond);
//喚醒全部線程
int pthread_cond_broadcast(pthread_cond_t *cond);
//解鎖互斥量mutex -> 阻塞調用線程,直到另外一線程cond發出型號->從新鎖定mutex (若是在2->3中發現mutex被鎖了,會再次休眠)
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
複製代碼
pthread_cond_wait
的操做
- 解鎖互斥量mutex
- 阻塞調用線程,直到另外一線程cond發出信號
- 從新鎖定mutex
若是在2->3中發現mutex被鎖了,會再次休眠
阻塞時其餘線程得到CPU使用權,而不會盲輪訓之類的浪費資源
#include<pthread.h>
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
s = pthread_mutex_lock(&mtx);
while (...){
s = pthread_cond_wait(&cond, &mtx);
}
s = pthread_mutex_unlock(&mtx);
複製代碼
之因此while
- 其餘線程搶先
- 寬鬆原則
- 虛假喚醒
動態分配條件變量
#include<pthread.h>
int pthread_cond_init(pthread_cond_t *mutex, const pthread_condattr_t * attr);
int pthread_cond_destroy(pthread_cond_t *mutex);
複製代碼