Linux 核心101:程式資料結構

長江CJ發表於2019-04-27

本文參考了:

基本概念清單

  • Linux 裡面,程式和執行緒到了核心,統一都叫做任務(Task)。
  • 每個 task 都有一個資料介面task_struct,用來儲存task 狀態。

任務列表

linux 核心中有一個包含所有 task 的連結串列,把所有的 task_struct 連起來。

如圖所示:

Linux 核心101:程式資料結構

task_struct

struct 定義:

struct list_head		tasks;
複製程式碼

看一下每個task_struct包含了哪些重要的欄位。

任務 ID

和任務 ID 相關的欄位有下面這些:

pid_t pid;
pid_t tgid;
struct task_struct *group_leader; 
複製程式碼

這三個欄位的具體含義為:

  • pid : 每個 task 都有一個 pid,是唯一的,不管是程式還是執行緒。
  • tgid: 指向主執行緒的 pid
  • group_leader: 指向程式的主執行緒

任何一個程式,如果只有主執行緒,那 pid 是自己,tgid 是自己,group_leader 指向的還是自己。

但是,如果一個程式建立了其他執行緒,那就會有所變化了。執行緒有自己的 pid,tgid 就是程式的主執行緒的 pid,group_leader 指向的就是程式的主執行緒。

有了 tgid 之後,我們就可以判斷一個 task 是執行緒還是程式了。

那麼區分是程式還是執行緒有什麼用呢?考慮下面幾個場景:

  • ps命令

ps預設展示的是所有程式的列表,而不是把所有的執行緒都列出來,那會顯得很亂沒有重點。

  • 給執行緒傳送kill -9訊號?

假如說我們給某個程式中的一個執行緒傳送了退出訊號(比如kill -9),那麼我們不應該只退出這個執行緒,而是退出整個程式(至於為什麼請看下文)。所以就需要某種方式,能夠獲取該執行緒所在程式中所有執行緒的 pid。

從一個程式中殺死某一個執行緒是非常危險的操作。 比如說某個 thread正在進行分配記憶體的工作,這時候它會hold 記憶體分配器的 lock。如果你把它強制殺死了,這個鎖就永遠不會釋放,那麼其他的 thread 也會停止。所以需要主程式的協助,來優雅地退出所有的執行緒。

Linux 核心101:程式資料結構

Linux 核心101:程式資料結構

上圖來源於 這個so 上的問答

不信的話,我們可以來做一個實驗。

下圖顯示的是htop工具,白色的表示程式綠色的表示執行緒。可以看到每個執行緒確實都有一個唯一的 PID。

Linux 核心101:程式資料結構

現在讓我們來給圖中標記的PID 為21656code-server執行緒傳送kill -9訊號,然後發現,整個程式都退出了:

Linux 核心101:程式資料結構

上圖中,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;
複製程式碼
  • blocked : 被阻塞暫不處理
  • pending : 等待處理
  • sighand : 哪個訊號正在被處理

注意這裡的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

複製程式碼

Linux 核心101:程式資料結構

總結一下

Linux 核心101:程式資料結構

Linux 核心101:程式資料結構

我的公眾號:全棧不存在的

Linux 核心101:程式資料結構

相關文章