成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

深入理解Linux進程管理

系統 Linux
進程是程序的執行過程。程序是靜態的,是存在于外存之中的,電腦關機后依然存在。進程是動態的,是存在于內存之中的,是程序的執行過程,電腦關機后就不存在進程了。進程的內容來源于程序,進程的啟動過程就是把程序從外存加載到內存的過程。

一、進程基本概念

進程是計算機里面最重要的概念之一。操作系統的目的就是為了運行進程。那么到底什么是進程,操作系統又是如何實現進程和管理進程的呢?

1.1 進程與程序

進程是程序的執行過程。程序是靜態的,是存在于外存之中的,電腦關機后依然存在。進程是動態的,是存在于內存之中的,是程序的執行過程,電腦關機后就不存在進程了。進程的內容來源于程序,進程的啟動過程就是把程序從外存加載到內存的過程。程序文件是有格式的,UNIX-Like操作系統的通用程序文件格式是ELF。程序文件是從源碼文件編譯過來的,源碼文件很多是用C或者C++書寫的。關于編譯系統,請參看《深入理解編譯系統》,關于C和C++,請參看《深入理解C與C++》。

1.2 進程與線程

進程是操作系統分配和管理系統資源的基本單位。進程本來也是程序執行的基本單位,但是自從有了線程之后就不是了。現在線程是程序執行的基本單位,代表一個執行流,一個進程可以有多個執行流。最初的時候,一個進程就只有一個執行流,也就是主線程,此時進程就是線程,線程就是進程。當程序需要多個執行流的時候,采取的都是多進程的方式。但是創建一個新進程是一個很耗費資源的事情,而且多個進程之間還要進行進程間通信也很費事。于是人們便想到了開發進程內并發機制,也就是在一個進程內能同時存在多個執行流(線程)。不同的人設計的進程內并發機制并不相同。按照線程的管理是否實現在內核里,進程內并發機制可以分為兩大類,分別是內核級線程(內核級線程也被叫做輕量級進程)和用戶級線程,注意這兩個名詞都帶個級,它們是進程內并發機制的兩個子類,并不是具體的線程。內核級線程下的線程,按照運行主體是在內核空間還是在用戶空間可以分為內核線程和用戶線程。用戶級線程下的線程,按照運行主體是在內核空間還是在用戶空間也可以分為內核線程和用戶線程,但是由于用戶級線程實現在用戶空間,所以它的線程不可能存在于內核空間。內核級線程下的用戶線程一般被叫做用戶線程,簡稱線程。用戶級線程下的用戶線程如果再叫用戶線程或者線程就會產生混淆,于是就被叫做協程或者纖程。如下圖所示: 

圖片

這兩種實現多線程的方法各有優缺點。在用戶空間實現的話,優點是簡單,不用改內核,只需要實現一個庫就行了,創建線程開銷小,缺點是線程之間做不到真并發,一個線程阻塞就會阻塞同一進程的所有其它線程。在內核空間實現的話,缺點是麻煩,需要改內核,創建線程開銷大,但是優點是能做到真并發,一個進程的多個線程可以同時在多個CPU上運行,能充分利用CPU。當然這兩者并不是對立的,它們可以同時實現,一個進程可以有多個內核級線程,一個內核級線程又可以有多個用戶級線程,編程者可以靈活選擇使用哪種多線程方式。

1.3 進程與內核

進程與內核在同一個虛擬地址空間中,但是在不同的子空間,進程是在用戶空間,內核是在內核空間。整個系統只有一個內核空間,但是卻有很多用戶空間,不過當前用戶空間永遠只有一個(對于一個CPU來說)。雖然內核空間和用戶空間在同一個空間中,但是它們的權限并不相同。內核空間處于特權模式,用戶空間處于非特權模式。內核可以隨意訪問和操作用戶空間,但是用戶空間對內核空間卻是看得見摸不著。內核空間可以做很多特權操作,用戶空間沒有權限做,但是有些時候又需要做,所以內核為用戶空間開了一個口子,就是系統調用,用戶空間可以通過系統調用來請求內核的服務。關于系統調用請參看《深入理解Linux系統調用與API》。

下面我們用一張圖來總結內核和進程之間的關系: 

圖片

這個圖是在講進程調度的時候畫的,但是用在這里表示進程和內核的關系也很合適。

1.4 進程與內存

對于內核來說,內存是有虛擬內存和物理內存之分的。但是對于進程來說,這些都是透明的,進程只需要知道自己獨占一個用戶空間的內存就可以了,它不知道也不需要知道自己是否運行在虛擬內存上。如果非要說進程知道物理內存和虛擬內存,那么進程也只能分配和管理虛擬內存,它沒法分配管理物理內存,因為物理內存對它來說是透明的。內核在合適的時候會為進程分配相應的物理內存,保證進程在訪問內存的時候一定會有對應的物理內存,但是進程對此毫不知情,也管不了。

進程需要內存的時候可以通過系統調用brk、sbrk、mmap來向內核申請分配虛擬內存。但是直接使用系統調用來分配管理內存顯然很麻煩效率也低,為此libc向進程提供了malloc庫,malloc提供了malloc、free等幾個接口供進程使用。這樣進程需要內存的時候就可以直接使用malloc去分配內存,使用完了就用free去釋放內存,不用考慮分配效率、內存碎片等問題了。目前比較流行的malloc庫有ptmalloc、jemalloc、scudo等。

1.5 進程運行狀態

很多操作系統的書籍上都會講進程的運行狀態,有的講的是三態,有的講的是五態。其實兩者并不矛盾,三態只有進程運行時的狀態,五態把進程的新建和死亡狀態也算上去了,如下圖所示: 

圖片

進程剛創建之后處于新建態,但是新建態不是持久狀態,它會立馬轉變為就緒狀態。然后進程就會一直處于就緒、執行、阻塞三態的循環之中。就緒態會由于進程調度而轉為執行態;執行態會由于時間片耗盡而轉為就緒態,也會由于等待某個事件而轉為阻塞態;阻塞態會由于某個事件的發生而轉為就緒態。最后進程可能會由于主動退出或者發生異常而死亡。死亡態也不是一個持久態,進程死亡之后就不存在了。

1.6 進程親緣關系

所有進程都通過父子關系連接而構成一顆親緣樹,這顆樹的樹根是init進程(pid1)。Init進程是第一個用戶空間進程,所有的用戶空間進程都是init進程的子孫進程。Init進程的父進程是零號進程,零號進程是在代碼中通過硬編碼創建的,其它所有的進程都是通過fork創建的。這里為什么叫做零號進程呢?因為零號進程的職責發生過變化,在系統剛啟動的時候,零號進程是BSP(bootstrapprocess),start_kernel函數就是在零號進程中運行的。當系統初始化完成的時候,零號進程退化為了idle進程。當我們只強調零號進程的身份而不關心它的職責的時候,就叫它零號進程。當后面我們強調它的idle職責的時候,就叫它idle進程。

零號進程有兩個親兒子,除了init之外,還有一個是kthreadd(pid2)。Kthreadd是一個內核線程,它是所有其它內核線程的父進程。內核線程比較特殊的點在于它只運行在內核空間,所以所有的內核線程都可以看做是同一個進程下的線程,因為內核空間只有一個。但是每個內核線程在邏輯意義上又是一個獨立的進程,它們執行獨立的任務,有著獨立的進程人格。所以當我們說一個內核線程的時候,心里也要明白它是一個單獨的進程,是一個只有主線程的單線程進程。

我們來畫一下進程的親緣關系: 

圖片

進程除了父子這種血緣關系之外,還存在著家族關系。一個是大家族關系,會話組(session),一個是小家族關系,進程組(process

group)。會話組的產生來源于早期的大型計算機,當時一個公司或者一個科研單位只能買得起一臺大型機。然后每個人都通過一個終端連接到這個大型機,用自己的用戶名和密碼登錄上去。每個用戶都有自己的用戶id,一個用戶運行的所有的程序構成了一個會話組。有了會話組的概念,就可以方便我們把一個用戶運行的所有進程作為一個整體進行管理。進程組的產生來源于命令行操作的作業管理。什么是作業管理呢?就是把一行命令的執行整體作為一個作業。一行命令的執行不一定只有一個進程,比如命令ps -ef | grepbash,就有兩個進程,我們需要有個概念把這兩個進程作為一個整體來處理,這個概念就是進程組。有了進程組的概念,作業管理就比較方便了,比如Ctrl+C就是給當前正在執行的命令(進程組)發信號,進程組中的每個進程都會收到信號。

一個進程誕生的時候默認繼承父進程的會話組和進程組,但是進程可以通過系統調用(setsid,setpgrp)創建新的會話組或者進程組。會話組的第一個進程叫做這個會話組的組長,進程組的第一個進程叫做這個進程組的組長,會話組的id等于會話組組長的pid,進程組的id等于進程組組長的pid。一個進程只有當它不是某個進程組組長的時候,它才可以調用setpgrp創建新的進程組,同時它也成為了這個新建的進程組的組長。這個也很好理解,只有臣子造反當皇帝,哪有皇帝自己造自己的反重新創建一個朝代的。同理,只有不是會話組組長的進程才能通過setsid創建新的會話組,并成為這個會話組組長。而且在這個新的會話組里也不能沒有進程組啊,于是還會創建一個進程組,這個會話組組長還會成為這個新建的進程組的組長,這也要求了這個進程之前不能是進程組組長。所以只有既不是進程組組長又不是會話組組長的進程才能創建新的會話組。

任何一個進程,它必然屬于某個進程組,而且只能同時屬于一個進程組。任何一個進程,它必然屬于某個會話組,而且只能屬于一個會話組。任何一個進程組,它的所有進程必須都屬于同一個會話組。一個進程所屬的會話組只有兩種來源,要么是繼承而來的,要么是自己創建的,進程是不能轉會話組的。不過一個進程是可以轉進程組的,但是只能在同一個會話組中的進程組之間轉。因此我們可以得出一個結論,一個會話組的所有進程肯定都是其會話組組長的子孫進程,一個進程組的所有進程一般情況下都是其進程組組長的子孫進程。

我們來畫一下進程的家族關系: 


圖片

二、進程的實現

明白了進程的基本概念之后,我們來看一看Linux是怎么實現進程的。按照標準的操作系統理論,進程是資源分配的單位,線程是程序執行的單位,內核里用進程控制塊(PCB Process Control Block)來管理進程,用線程控制塊(TCB Thread Control Block)來管理線程。那么Linux是按照這個邏輯來實現進程的嗎?我們來看一下。

2.1 基本原理

Linux內核并不是按照標準的操作系統理論來實現進程的,在內核里找不到典型的進程控制塊和線程控制塊。內核里只有一個task_struct結構體,初學內核的人會很疑惑這是代表進程還是代表線程呢。之所以會這樣,是由于歷史原因造成的。Linux最開始的時候是不支持多線程的,也可以認為此時一個進程只能有一個線程就是主線程,因此線程就是進程,進程就是線程。所以最初的時候,task_struct既代表進程又代表線程,因為進程和線程沒有區別。但是后來Linux也要支持多線程了,我們在1.2節中討論過,多線程的實現方法可以在內核實現,也可以在用戶空間實現,也可以同時實現,Linux選擇的是在內核實現。為了最大限度地利用已有的代碼,盡量不對代碼做大的改動,Linux選擇的方法是:task_struct既是線程又是進程的代理。注意這句話,task_struct既是線程又是進程的代理(不是進程本身)。Linux并沒有設計單獨的進程結構體,而是用task_struct作為進程的代理,這是因為進程是資源分配的單位,線程是程序執行的單位,同一個進程的所有線程共享相同的資源,因此我們讓同一個進程下的所有線程(task_struct)都指向相同的資源不就可以了嘛。線程在執行的時候會通過task_struct里面的指針訪問資源,同一個進程下的線程自然就會訪問到相同的資源,而且這么做還有很大的靈活性。

我們下面再來強調一下這句話,以加深對這句話的理解。

task_struct既是線程又是進程的代理(不是進程本身)。 

2.2 進程結構體

當我們明白了task_struct既是線程又是進程的代理之后,再來理解task_struct就容易多了。task_struct的字段由兩部分組成,一部分是線程相關的,一部分是進程相關的,線程相關的一般是直接內嵌其它數據,進程相關的一般是用指針指向其它數據。線程代表的是執行流,所以task_struct的線程相關部分是和執行有關的,進程代表的是資源分配,所以task_struct的進程相關部分是和資源有關的。我們可以想一下和執行有關的都有哪些,和資源有關的都哪些?可以很輕松地想到,和執行有關的肯定是進程調度相關的數據啊(進程調度雖然叫進程調度,但實際上調度的是線程)。和資源相關的,最重要的首先肯定是虛擬內存啊,其次是文件系統。

下面我們來看一下task_struct的定義: linux-src/include/linux/sched.h

struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
struct thread_info thread_info;
#endif
unsigned int __state;
void *stack;
unsigned int flags;
int on_cpu;
unsigned int cpu;
int recent_used_cpu;
int wake_cpu;
int on_rq;
int prio;
int static_prio;
int normal_prio;
unsigned int rt_priority;
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;
struct sched_dl_entity dl;
unsigned int policy;
int nr_cpus_allowed;
cpumask_t cpus_mask;
struct sched_info sched_info;
struct list_head tasks;

struct mm_struct *mm;
struct mm_struct *active_mm;

struct vmacache vmacache;

int exit_state;
int exit_code;
int exit_signal;

pid_t pid;
pid_t tgid;

struct task_struct __rcu *real_parent;
struct task_struct __rcu *parent;
struct list_head children;
struct list_head sibling;
struct task_struct *group_leader;

unsigned long nvcsw;
unsigned long nivcsw;

u64 start_time;
u64 start_boottime;

unsigned long min_flt;
unsigned long maj_flt;

char comm[TASK_COMM_LEN];

struct fs_struct *fs;
struct files_struct *files;

struct signal_struct *signal;
struct sighand_struct __rcu *sighand;
sigset_t blocked;
sigset_t real_blocked;
sigset_t saved_sigmask;
struct sigpending pending;

struct thread_struct thread;
};

這個結構體定義有700多行,本文把一些暫時用不到的都刪除了,現在還有70多行,我們來看一下大概都有哪些內容。先看和進程相關的,首先最重要的是虛擬內存空間信息mm、active_mm,這兩個都是指針,對于用戶線程來說兩個指針的值永遠都是相同的,同一個進程的所有線程都指向相同的mm,這個值就表明了同一個進程的線程都在同一個用戶空間。其次比較重要的是文件管理相關的兩個字段fs和files,也都是指針,fs代表的是文件系統掛載相關的,這個不僅是同進程的所有線程都相同,而且整個系統默認的值都一樣,除非使用了mount 命名空間,files代表的是打開的文件資源,這個是同進程的所有線程都相同。然后我們再來看一下信號相關的,信號有的數據是進程全局的,有的是線程私有的,信號的處理是進程全局的,所以signal、sighand兩個字段都是指針,同進程的所有線程都指向同一個結構體,信號掩碼是線程私有的,所以blocked直接是內嵌數據。進程相關的數據基本就這些,下面我們來看一下線程相關的數據。首先是進程的運行退出狀態,有幾個字段,__state、on_cpu、cpu、exit_state、exit_code、exit_signal。然后是和線程調度相關的幾個字段,有和優先級相關的rt_priority、static_prio、normal_prio、prio,有和調度信息統計相關的兩個結構體,se、sched_info。還有兩個非常重要的字段我們下一節講。

2.3 進程標識符

task_struct里面有兩個重要的字段pid、tgid。我們在用戶空間的時候也有pid、tid,那么用戶空間的pid是不是就是內核的pid呢,那tgid又是啥呢。很多初學內核的人會認為用戶空間的pid就是內核的pid,剛開始我也是這么認為的,給我的內核學習帶來了很大的困擾。實際上用戶空間的tid是內核空間pid,用戶空間的pid是內核空間的tgid,內核空間的tgid是內核里主線程的pid。為什么會這樣呢?主要還是前面講的問題,task_struct既是線程又是進程的代理,沒有單獨的進程結構體。當進程創建時,也就是進程的第一個線程創建時,會為task_struct分配一個pid,就是主線程的tid,然后進程的pid也就是字段tgid會被賦值為主線程的tid。此后再創建的線程都會繼承父線程的tgid,所以在每個線程中都能直接獲取進程的pid。

我們在這里畫個圖總結一下進程與線程的關系、pid與tgid之間的關系: 

Linux里面雖然沒有進程結構體,但是所有tgid相同、虛擬內存等資源相同的線程構成一個虛擬的進程結構體。創建進程的第一個線程(task_struct)就是同時在創建進程,其對應的mm_struct、files_struct、signal_struct等資源都會被創建出來。創建進程的第二個線程那就是純粹地創建線程了。

2.4 進程的狀態

進程的狀態在Linux中是如何表示的呢?task_struct中有兩個字段用來表示進程的狀態,__state和exit_state,前者是總體狀態,后者是進程在死亡時的兩個子狀態。

我們來看一下代碼中的定義: linux-src/include/linux/sched.h

/* Used in tsk->state: */
#define TASK_RUNNING 0x0000
#define TASK_INTERRUPTIBLE 0x0001
#define TASK_UNINTERRUPTIBLE 0x0002
#define __TASK_STOPPED 0x0004
#define __TASK_TRACED 0x0008
/* Used in tsk->exit_state: */
#define EXIT_DEAD 0x0010
#define EXIT_ZOMBIE 0x0020
#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)
/* Used in tsk->state again: */
#define TASK_PARKED 0x0040
#define TASK_DEAD 0x0080
#define TASK_WAKEKILL 0x0100
#define TASK_WAKING 0x0200
#define TASK_NOLOAD 0x0400
#define TASK_NEW 0x0800

其中TASK_RUNNING代表的是Runnable和Running狀態。在Linux中不是用flag直接區分Runnable和Running狀態的,它們都用TASK_RUNNING表示,區分它們的方法是進程是否在運行隊列的當前進程字段上。Blocked狀態有兩種表示,TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE,它們的區別是前者在睡眠時能被信號喚醒,后者不能被信號喚醒。表示死亡的狀態是TASK_DEAD,它有兩個子狀態EXIT_ZOMBIE、EXIT_DEAD,這兩個狀態在3.6中講解。


二、進程的實現

明白了進程的基本概念之后,我們來看一看Linux是怎么實現進程的。按照標準的操作系統理論,進程是資源分配的單位,線程是程序執行的單位,內核里用進程控制塊(PCB Process Control Block)來管理進程,用線程控制塊(TCB Thread Control Block)來管理線程。那么Linux是按照這個邏輯來實現進程的嗎?我們來看一下。

2.1 基本原理

Linux內核并不是按照標準的操作系統理論來實現進程的,在內核里找不到典型的進程控制塊和線程控制塊。內核里只有一個task_struct結構體,初學內核的人會很疑惑這是代表進程還是代表線程呢。之所以會這樣,是由于歷史原因造成的。Linux最開始的時候是不支持多線程的,也可以認為此時一個進程只能有一個線程就是主線程,因此線程就是進程,進程就是線程。所以最初的時候,task_struct既代表進程又代表線程,因為進程和線程沒有區別。但是后來Linux也要支持多線程了,我們在1.2節中討論過,多線程的實現方法可以在內核實現,也可以在用戶空間實現,也可以同時實現,Linux選擇的是在內核實現。為了最大限度地利用已有的代碼,盡量不對代碼做大的改動,Linux選擇的方法是:task_struct既是線程又是進程的代理。注意這句話,task_struct既是線程又是進程的代理(不是進程本身)。Linux并沒有設計單獨的進程結構體,而是用task_struct作為進程的代理,這是因為進程是資源分配的單位,線程是程序執行的單位,同一個進程的所有線程共享相同的資源,因此我們讓同一個進程下的所有線程(task_struct)都指向相同的資源不就可以了嘛。線程在執行的時候會通過task_struct里面的指針訪問資源,同一個進程下的線程自然就會訪問到相同的資源,而且這么做還有很大的靈活性。

我們下面再來強調一下這句話,以加深對這句話的理解。

task_struct既是線程又是進程的代理(不是進程本身)。 

2.2 進程結構體

當我們明白了task_struct既是線程又是進程的代理之后,再來理解task_struct就容易多了。task_struct的字段由兩部分組成,一部分是線程相關的,一部分是進程相關的,線程相關的一般是直接內嵌其它數據,進程相關的一般是用指針指向其它數據。線程代表的是執行流,所以task_struct的線程相關部分是和執行有關的,進程代表的是資源分配,所以task_struct的進程相關部分是和資源有關的。我們可以想一下和執行有關的都有哪些,和資源有關的都哪些?可以很輕松地想到,和執行有關的肯定是進程調度相關的數據啊(進程調度雖然叫進程調度,但實際上調度的是線程)。和資源相關的,最重要的首先肯定是虛擬內存啊,其次是文件系統。

下面我們來看一下task_struct的定義: linux-src/include/linux/sched.h

struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
struct thread_info thread_info;
#endif
unsigned int __state;
void *stack;
unsigned int flags;
int on_cpu;
unsigned int cpu;
int recent_used_cpu;
int wake_cpu;
int on_rq;
int prio;
int static_prio;
int normal_prio;
unsigned int rt_priority;
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;
struct sched_dl_entity dl;
unsigned int policy;
int nr_cpus_allowed;
cpumask_t cpus_mask;
struct sched_info sched_info;
struct list_head tasks;

struct mm_struct *mm;
struct mm_struct *active_mm;

struct vmacache vmacache;

int exit_state;
int exit_code;
int exit_signal;

pid_t pid;
pid_t tgid;

struct task_struct __rcu *real_parent;
struct task_struct __rcu *parent;
struct list_head children;
struct list_head sibling;
struct task_struct *group_leader;

unsigned long nvcsw;
unsigned long nivcsw;

u64 start_time;
u64 start_boottime;

unsigned long min_flt;
unsigned long maj_flt;

char comm[TASK_COMM_LEN];

struct fs_struct *fs;
struct files_struct *files;

struct signal_struct *signal;
struct sighand_struct __rcu *sighand;
sigset_t blocked;
sigset_t real_blocked;
sigset_t saved_sigmask;
struct sigpending pending;

struct thread_struct thread;
};

這個結構體定義有700多行,本文把一些暫時用不到的都刪除了,現在還有70多行,我們來看一下大概都有哪些內容。先看和進程相關的,首先最重要的是虛擬內存空間信息mm、active_mm,這兩個都是指針,對于用戶線程來說兩個指針的值永遠都是相同的,同一個進程的所有線程都指向相同的mm,這個值就表明了同一個進程的線程都在同一個用戶空間。其次比較重要的是文件管理相關的兩個字段fs和files,也都是指針,fs代表的是文件系統掛載相關的,這個不僅是同進程的所有線程都相同,而且整個系統默認的值都一樣,除非使用了mount 命名空間,files代表的是打開的文件資源,這個是同進程的所有線程都相同。然后我們再來看一下信號相關的,信號有的數據是進程全局的,有的是線程私有的,信號的處理是進程全局的,所以signal、sighand兩個字段都是指針,同進程的所有線程都指向同一個結構體,信號掩碼是線程私有的,所以blocked直接是內嵌數據。進程相關的數據基本就這些,下面我們來看一下線程相關的數據。首先是進程的運行退出狀態,有幾個字段,__state、on_cpu、cpu、exit_state、exit_code、exit_signal。然后是和線程調度相關的幾個字段,有和優先級相關的rt_priority、static_prio、normal_prio、prio,有和調度信息統計相關的兩個結構體,se、sched_info。還有兩個非常重要的字段我們下一節講。

2.3 進程標識符

task_struct里面有兩個重要的字段pid、tgid。我們在用戶空間的時候也有pid、tid,那么用戶空間的pid是不是就是內核的pid呢,那tgid又是啥呢。很多初學內核的人會認為用戶空間的pid就是內核的pid,剛開始我也是這么認為的,給我的內核學習帶來了很大的困擾。實際上用戶空間的tid是內核空間pid,用戶空間的pid是內核空間的tgid,內核空間的tgid是內核里主線程的pid。為什么會這樣呢?主要還是前面講的問題,task_struct既是線程又是進程的代理,沒有單獨的進程結構體。當進程創建時,也就是進程的第一個線程創建時,會為task_struct分配一個pid,就是主線程的tid,然后進程的pid也就是字段tgid會被賦值為主線程的tid。此后再創建的線程都會繼承父線程的tgid,所以在每個線程中都能直接獲取進程的pid。

我們在這里畫個圖總結一下進程與線程的關系、pid與tgid之間的關系: 

Linux里面雖然沒有進程結構體,但是所有tgid相同、虛擬內存等資源相同的線程構成一個虛擬的進程結構體。創建進程的第一個線程(task_struct)就是同時在創建進程,其對應的mm_struct、files_struct、signal_struct等資源都會被創建出來。創建進程的第二個線程那就是純粹地創建線程了。

2.4 進程的狀態

進程的狀態在Linux中是如何表示的呢?task_struct中有兩個字段用來表示進程的狀態,__state和exit_state,前者是總體狀態,后者是進程在死亡時的兩個子狀態。

我們來看一下代碼中的定義: linux-src/include/linux/sched.h

/* Used in tsk->state: */
#define TASK_RUNNING 0x0000
#define TASK_INTERRUPTIBLE 0x0001
#define TASK_UNINTERRUPTIBLE 0x0002
#define __TASK_STOPPED 0x0004
#define __TASK_TRACED 0x0008
/* Used in tsk->exit_state: */
#define EXIT_DEAD 0x0010
#define EXIT_ZOMBIE 0x0020
#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)
/* Used in tsk->state again: */
#define TASK_PARKED 0x0040
#define TASK_DEAD 0x0080
#define TASK_WAKEKILL 0x0100
#define TASK_WAKING 0x0200
#define TASK_NOLOAD 0x0400
#define TASK_NEW 0x0800

其中TASK_RUNNING代表的是Runnable和Running狀態。在Linux中不是用flag直接區分Runnable和Running狀態的,它們都用TASK_RUNNING表示,區分它們的方法是進程是否在運行隊列的當前進程字段上。Blocked狀態有兩種表示,TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE,它們的區別是前者在睡眠時能被信號喚醒,后者不能被信號喚醒。表示死亡的狀態是TASK_DEAD,它有兩個子狀態EXIT_ZOMBIE、EXIT_DEAD,這兩個狀態在3.6中講解。

責任編輯:武曉燕 來源: Linux閱碼場
相關推薦

2013-06-20 10:25:56

2021-07-26 07:47:36

數據庫

2021-07-20 08:02:41

Linux進程睡眠

2023-02-10 08:11:43

Linux系統調用

2020-09-28 08:44:17

Linux內核

2025-06-16 05:10:00

2021-08-31 10:32:11

LinuxPage Cache命令

2010-06-01 15:25:27

JavaCLASSPATH

2016-12-08 15:36:59

HashMap數據結構hash函數

2020-07-21 08:26:08

SpringSecurity過濾器

2021-05-19 07:56:26

Linux內核搶占

2017-01-12 19:34:58

2023-10-19 11:12:15

Netty代碼

2021-02-17 11:25:33

前端JavaScriptthis

2009-09-25 09:14:35

Hibernate日志

2013-09-22 14:57:19

AtWood

2020-09-23 10:00:26

Redis數據庫命令

2019-06-25 10:32:19

UDP編程通信

2017-01-10 08:48:21

2024-02-21 21:14:20

編程語言開發Golang
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲视频免费观看 | 欧美久久久网站 | 精品影院 | 成人日韩 | 欧美午夜精品 | 久久久www成人免费无遮挡大片 | 国产精品久久a | 亚洲成人精品视频 | 人人干人人干人人 | 国产亚洲一区在线 | www.操com| 久久国产精品一区二区三区 | 精品毛片在线观看 | 精品www | 九九伦理片 | 成人精品久久日伦片大全免费 | julia中文字幕久久一区二区 | 特级丰满少妇一级aaaa爱毛片 | 羞羞的视频免费看 | 国产精品亚洲第一 | 夜夜爽夜夜操 | 国内精品久久精品 | 久久久久久黄 | 婷婷福利| 欧美日本在线观看 | 欧美一级欧美三级在线观看 | 理论片免费在线观看 | 亚洲一区视频在线 | 一级毛片免费看 | 亚洲一页 | 色在线免费 | 一级片av | 韩日一区二区三区 | 亚洲一区二区三区四区五区中文 | 国产伦精品| 精品国产网 | 国产精品久久精品 | 亚洲不卡在线观看 | 午夜免费小视频 | 在线中文字幕av | 亚洲一区av |