本文參考了:linux
task_struct
,用來保存task 狀態。linux 內核中有一個包含全部 task 的鏈表,把全部的 task_struct 連起來。git
如圖所示:github
struct 定義:docker
struct list_head tasks;
複製代碼
看一下每一個task_struct
包含了哪些重要的字段。bash
和任務 ID 相關的字段有下面這些:數據結構
pid_t pid;
pid_t tgid;
struct task_struct *group_leader;
複製代碼
這三個字段的具體含義爲:工具
任何一個進程,若是隻有主線程,那 pid 是本身,tgid 是本身,group_leader 指向的仍是本身。spa
可是,若是一個進程建立了其餘線程,那就會有所變化了。線程有本身的 pid,tgid 就是進程的主線程的 pid,group_leader 指向的就是進程的主線程。操作系統
有了 tgid 以後,咱們就能夠判斷一個 task 是線程仍是進程了。線程
那麼區分是進程仍是線程有什麼用呢?考慮下面幾個場景:
ps
命令ps
默認展現的是全部進程的列表,而不是把全部的線程都列出來,那會顯得很亂沒有重點。
kill -9
信號?假如說咱們給某個進程中的一個線程發送了退出信號(好比kill -9
),那麼咱們不該該只退出這個線程,而是退出整個進程(至於爲何請看下文)。因此就須要某種方式,可以獲取該線程所在進程中全部線程的 pid。
從一個進程中殺死某一個線程是很是危險的操做。 好比說某個 thread正在進行分配內存的工做,這時候它會hold 內存分配器的 lock。若是你把它強制殺死了,這個鎖就永遠不會釋放,那麼其餘的 thread 也會中止。因此須要主進程的協助,來優雅地退出全部的線程。
上圖來源於 這個so 上的問答。
不信的話,咱們能夠來作一個實驗。
下圖顯示的是htop
工具,白色的表示進程,綠色的表示線程。能夠看到每一個線程確實都有一個惟一的 PID。
如今讓咱們來給圖中標記的PID 爲21656
的code-server
線程發送kill -9
信號,而後發現,整個進程都退出了:
上圖中,code-server
這個 docker 容器進程在一分鐘前退出了。
源代碼地址:github.com/torvalds/li…
/* Signal handlers: */
struct signal_struct *signal;
struct sighand_struct *sighand;
sigset_t blocked;
sigset_t real_blocked;
sigset_t saved_sigmask;
struct sigpending pending;
unsigned long sas_ss_sp;
size_t sas_ss_size;
unsigned int sas_ss_flags;
複製代碼
注意這裏的struct signal_struct *signal;
指向了一個signal
struct。這個struct 中還有一個struct sigpending pending;
。前面提到過須要區分線程和進程,這裏也能夠看出一點端倪。第一個是線程組共享的,一個是本任務的。
一個 task 的任務狀態一共能夠取下面的這些值:
/* Used in tsk->state: */
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define __TASK_STOPPED 4
#define __TASK_TRACED 8
/* Used in tsk->exit_state: */
#define EXIT_DEAD 16
#define EXIT_ZOMBIE 32
#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)
/* Used in tsk->state again: */
#define TASK_DEAD 64
#define TASK_WAKEKILL 128
#define TASK_WAKING 256
#define TASK_PARKED 512
#define TASK_NOLOAD 1024
#define TASK_NEW 2048
#define TASK_STATE_MAX 4096
複製代碼
個人公衆號:全棧不存在的