1. 程式人生 > >linux程序管理與排程:task_struct結構體

linux程序管理與排程:task_struct結構體

程序是處於執行期的程式以及它所管理的資源(如開啟的檔案、掛起的訊號、程序狀態、地址空間等等)的總稱。注意,程式並不是程序,實際上兩個或多個程序不僅有可能執行同一程式,而且還有可能共享地址空間等資源。

Linux核心通過一個被稱為程序描述符的task_struct結構體來管理程序,這個結構體包含了一個程序所需的所有資訊。它定義在include/linux/sched.h檔案中。

談到task_struct結構體,可以說她是linux核心原始碼中最複雜的一個結構體了,成員之多,佔用記憶體之大。

鑑於她的複雜,我們不能簡單的褻瀆,而是要深入“窺探”.

下面來慢慢介紹這些複雜成員

程序狀態

  volatile
long state; /* -1 unrunnable, 0 runnable, >0 stopped */
  • 1
  • 1

state成員的可能取值如下


 /*
  * Task state bitmask. NOTE! These bits are also
  * encoded in fs/proc/array.c: get_task_state().
  *
  * We have two separate sets of flags: task->state
  * is about runnability, while task->exit_state are
  * about the task exiting. Confusing, but this way
  * modifying one set can't modify the other one by
  * mistake.
  */
#define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define __TASK_STOPPED 4 #define __TASK_TRACED 8 /* in tsk->exit_state */ #define EXIT_DEAD 16 #define EXIT_ZOMBIE 32 #define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)
/* in tsk->state again */ #define TASK_DEAD 64 #define TASK_WAKEKILL 128 /** wake on signals that are deadly **/ #define TASK_WAKING 256 #define TASK_PARKED 512 #define TASK_NOLOAD 1024 #define TASK_STATE_MAX 2048 /* Convenience macros for the sake of set_task_state */ #define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE) #define TASK_STOPPED (TASK_WAKEKILL | __TASK_STOPPED) #define TASK_TRACED (TASK_WAKEKILL | __TASK_TRACED)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

5個互斥狀態

state域能夠取5個互為排斥的值(通俗一點就是這五個值任意兩個不能一起使用,只能單獨使用)。系統中的每個程序都必然處於以上所列程序狀態中的一種。

狀態 描述
TASK_RUNNING 表示程序要麼正在執行,要麼正要準備執行(已經就緒),正在等待cpu時間片的排程
TASK_INTERRUPTIBLE 程序因為等待一些條件而被掛起(阻塞)而所處的狀態。這些條件主要包括:硬中斷、資源、一些訊號……,一旦等待的條件成立,程序就會從該狀態(阻塞)迅速轉化成為就緒狀態TASK_RUNNING
TASK_UNINTERRUPTIBLE 意義與TASK_INTERRUPTIBLE類似,除了不能通過接受一個訊號來喚醒以外,對於處於TASK_UNINTERRUPIBLE狀態的程序,哪怕我們傳遞一個訊號或者有一個外部中斷都不能喚醒他們。只有它所等待的資源可用的時候,他才會被喚醒。這個標誌很少用,但是並不代表沒有任何用處,其實他的作用非常大,特別是對於驅動刺探相關的硬體過程很重要,這個刺探過程不能被一些其他的東西給中斷,否則就會讓進城進入不可預測的狀態
TASK_STOPPED 程序被停止執行,當程序接收到SIGSTOP、SIGTTIN、SIGTSTP或者SIGTTOU訊號之後就會進入該狀態
TASK_TRACED 表示程序被debugger等程序監視,程序執行被除錯程式所停止,當一個程序被另外的程序所監視,每一個訊號都會讓進城進入該狀態

2個終止狀態

其實還有兩個附加的程序狀態既可以被新增到state域中,又可以被新增到exit_state域中。只有當程序終止的時候,才會達到這兩種狀態.

/* task state */
int exit_state;
int exit_code, exit_signal;
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
狀態 描述
EXIT_ZOMBIE 程序的執行被終止,但是其父程序還沒有使用wait()等系統呼叫來獲知它的終止資訊,此時程序成為殭屍程序
EXIT_DEAD 程序的最終狀態

而int exit_code, exit_signal;我們會在後面程序介紹

新增睡眠狀態

如前所述,程序狀態 TASK_UNINTERRUPTIBLE 和 TASK_INTERRUPTIBLE 都是睡眠狀態。現在,我們來看看核心如何將程序置為睡眠狀態。

核心如何將程序置為睡眠狀態。

Linux 核心提供了兩種方法將程序置為睡眠狀態。

將程序置為睡眠狀態的普通方法是將程序狀態設定為 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE 並呼叫排程程式的 schedule() 函式。這樣會將程序從 CPU 執行佇列中移除。

  • 如果程序處於可中斷模式的睡眠狀態(通過將其狀態設定為 TASK_INTERRUPTIBLE),那麼可以通過顯式的喚醒呼叫(wakeup_process())或需要處理的訊號來喚醒它。

  • 但是,如果程序處於非可中斷模式的睡眠狀態(通過將其狀態設定為 TASK_UNINTERRUPTIBLE),那麼只能通過顯式的喚醒呼叫將其喚醒。除非萬不得已,否則我們建議您將程序置為可中斷睡眠模式,而不是不可中斷睡眠模式(比如說在裝置 I/O 期間,處理訊號非常困難時)。

當處於可中斷睡眠模式的任務接收到訊號時,它需要處理該訊號(除非它已被屏弊),離開之前正在處理的任務(此處需要清除程式碼),並將 -EINTR 返回給使用者空間。再一次,檢查這些返回程式碼和採取適當操作的工作將由程式設計師完成。

因此,懶惰的程式設計師可能比較喜歡將程序置為不可中斷模式的睡眠狀態,因為訊號不會喚醒這類任務。

但需要注意的一種情況是,對不可中斷睡眠模式的程序的喚醒呼叫可能會由於某些原因不會發生,這會使程序無法被終止,從而最終引發問題,因為惟一的解決方法就是重啟系統。一方面,您需要考慮一些細節,因為不這樣做會在核心端和使用者端引入 bug。另一方面,您可能會生成永遠不會停止的程序(被阻塞且無法終止的程序)。

現在,我們在核心中實現了一種新的睡眠方法

Linux Kernel 2.6.25 引入了一種新的程序睡眠狀態,

狀態 描述
TASK_KILLABLE 當程序處於這種可以終止的新睡眠狀態中,它的執行原理類似於 TASK_UNINTERRUPTIBLE,只不過可以響應致命訊號

它定義如下:

#define TASK_WAKEKILL           128 /** wake on signals that are deadly **/

/* Convenience macros for the sake of set_task_state */
#define TASK_KILLABLE           (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
#define TASK_STOPPED            (TASK_WAKEKILL | __TASK_STOPPED)
#define TASK_TRACED             (TASK_WAKEKILL | __TASK_TRACED)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

換句話說,TASK_UNINTERRUPTIBLE + TASK_WAKEKILL = TASK_KILLABLE。

而TASK_WAKEKILL 用於在接收到致命訊號時喚醒程序

新的睡眠狀態允許 TASK_UNINTERRUPTIBLE 響應致命訊號

程序狀態的切換過程和原因大致如下圖

程序狀態轉換圖

pid_t pid;  
pid_t tgid;  
  • 1
  • 2
  • 1
  • 2

Unix系統通過pid來標識程序,linux把不同的pid與系統中每個程序或輕量級執行緒關聯,而unix程式設計師希望同一組執行緒具有共同的pid,遵照這個標準linux引入執行緒組的概念。一個執行緒組所有執行緒與領頭執行緒具有相同的pid,存入tgid欄位,getpid()返回當前程序的tgid值而不是pid的值。

在CONFIG_BASE_SMALL配置為0的情況下,PID的取值範圍是0到32767,即系統中的程序數最大為32768個。

#define PID_MAX_DEFAULT (CONFIG_BASE_SMALL ? 0x1000 : 0x8000)  
  • 1
  • 1

在Linux系統中,一個執行緒組中的所有執行緒使用和該執行緒組的領頭執行緒(該組中的第一個輕量級程序)相同的PID,並被存放在tgid成員中。只有執行緒組的領頭執行緒的pid成員才會被設定為與tgid相同的值。注意,getpid()系統呼叫返回的是當前程序的tgid值而不是pid值。

程序核心棧

void *stack;  
  • 1
  • 1

核心棧與執行緒描述符

對每個程序,Linux核心都把兩個不同的資料結構緊湊的存放在一個單獨為程序分配的記憶體區域中

  • 一個是核心態的程序堆疊,

  • 另一個是緊挨著程序描述符的小資料結構thread_info,叫做執行緒描述符。

Linux把thread_info(執行緒描述符)和核心態的執行緒堆疊存放在一起,這塊區域通常是8192K(佔兩個頁框),其實地址必須是8192的整數倍。

在linux/arch/x86/include/asm/page_32_types.h中,

#define THREAD_SIZE_ORDER    1
#define THREAD_SIZE        (PAGE_SIZE << THREAD_SIZE_ORDER)
  • 1
  • 2
  • 1
  • 2

出於效率考慮,核心讓這8K空間佔據連續的兩個頁框並讓第一個頁框的起始地址是213的倍數。

核心態的程序訪問處於核心資料段的棧,這個棧不同於使用者態的程序所用的棧。

使用者態程序所用的棧,是在程序線性地址空間中;

而核心棧是當程序從使用者空間進入核心空間時,特權級發生變化,需要切換堆疊,那麼核心空間中使用的就是這個核心棧。因為核心控制路徑使用很少的棧空間,所以只需要幾千個位元組的核心態堆疊。

需要注意的是,核心態堆疊僅用於核心例程,Linux核心另外為中斷提供了單獨的硬中斷棧軟中斷棧

下圖中顯示了在實體記憶體中存放兩種資料結構的方式。執行緒描述符駐留與這個記憶體區的開始,而棧頂末端向下增長。 下圖摘自ULK3,程序核心棧與程序描述符的關係如下圖:

thread_info

但是較新的核心程式碼中,程序描述符task_struct結構中沒有直接指向thread_info結構的指標,而是用一個void指標型別的成員表示,然後通過型別轉換來訪問thread_info結構。

#define task_thread_info(task)  ((struct thread_info *)(task)->stack)
  • 1
  • 1

在這個圖中,esp暫存器是CPU棧指標,用來存放棧頂單元的地址。在80x86系統中,棧起始於頂端,並朝著這個記憶體區開始的方向增長。從使用者態剛切換到核心態以後,程序的核心棧總是空的。因此,esp暫存器指向這個棧的頂端。一旦資料寫入堆疊,esp的值就遞減。

核心棧資料結構描述thread_info和thread_union

thread_info是體系結構相關的,結構的定義在thread_info.h

Linux核心中使用一個聯合體來表示一個程序的執行緒描述符和核心棧:

union thread_union
{
    struct thread_info thread_info;
    unsigned long stack[THREAD_SIZE/sizeof(long)];
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

獲取當前在CPU上正在執行程序的thread_info

下面來說說如何通過esp棧指標來獲取當前在CPU上正在執行程序的thread_info結構。

實際上,上面提到,thread_info結構和核心態堆疊是緊密結合在一起的,佔據兩個頁框的實體記憶體空間。而且,這兩個頁框的起始起始地址是213對齊的。

早期的版本中,不需要對64位處理器的支援,所以,核心通過簡單的遮蔽掉esp的低13位有效位就可以獲得thread_info結構的基地址了。

我們在下面對比了,獲取正在執行的程序的thread_info的實現方式

架構 版本 定義連結 實現方式 思路解析
x86 3.14 return (struct thread_info *)(sp & ~(THREAD_SIZE - 1)); 遮蔽了esp的低十三位,最終得到的是thread_info的地址
x86 3.15 ti = (void *)(this_cpu_read_stable(kernel_stack) + KERNEL_STACK_OFFSET - THREAD_SIZE);
x86 4.1 (struct thread_info *)(current_top_of_stack() - THREAD_SIZE);

早期版本

當前的棧指標(current_stack_pointer == sp)就是esp,

THREAD_SIZE為8K,二進位制的表示為0000 0000 0000 0000 0010 0000 0000 0000。

~(THREAD_SIZE-1)的結果剛好為1111 1111 1111 1111 1110 0000 0000 0000,第十三位是全為零,也就是剛好遮蔽了esp的低十三位,最終得到的是thread_info的地址。

程序最常用的是程序描述符結構task_struct而不是thread_info結構的地址。為了獲取當前CPU上執行程序的task_struct結構,核心提供了current巨集,由於task_struct *task在thread_info的起始位置,該巨集本質上等價於current_thread_info()->task,在include/asm-generic/current.h中定義:

#define get_current() (current_thread_info()->task)
#define current get_current()
  • 1
  • 2
  • 1
  • 2

這個定義是體系結構無關的,當然linux也為各個體系結構定義了更加方便或者快速的current

分配和銷燬thread_info

# if THREAD_SIZE >= PAGE_SIZE
static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
                          int node)
{
    struct page *page = alloc_kmem_pages_node(node, THREADINFO_GFP,
                          THREAD_SIZE_ORDER);

    return page ? page_address(page) : NULL;
}

static inline void free_thread_info(struct thread_info *ti)
{
    free_kmem_pages((unsigned long)ti, THREAD_SIZE_ORDER);
}
# else
static struct kmem_cache *thread_info_cache;

static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
                          int node)
{
    return kmem_cache_alloc_node(thread_info_cache, THREADINFO_GFP, node);
}

static void free_thread_info(struct thread_info *ti)
{
    kmem_cache_free(thread_info_cache, ti);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
架構 版本 定義連結 實現方式 思路解析

程序標記

unsigned int flags; /* per process flags, defined below */  
  • 1
  • 1

反應程序狀態的資訊,但不是執行狀態,用於核心識別程序當前的狀態,以備下一步操作

flags成員的可能取值如下,這些巨集以PF(ProcessFlag)開頭

參見

例如
PF_FORKNOEXEC 程序剛建立,但還沒執行。
PF_SUPERPRIV 超級使用者特權。
PF_DUMPCORE dumped core。
PF_SIGNALED 程序被訊號(signal)殺出。
PF_EXITING 程序開始關閉。

 /*
* Per process flags
*/
#define PF_EXITING      0x00000004      /* getting shut down */
#define PF_EXITPIDONE   0x00000008      /* pi exit done on shut down */
#define PF_VCPU         0x00000010      /* I'm a virtual CPU */
#define PF_WQ_WORKER    0x00000020      /* I'm a workqueue worker */
#define PF_FORKNOEXEC   0x00000040      /* forked but didn't exec */
#define PF_MCE_PROCESS  0x00000080      /* process policy on mce errors */
#define PF_SUPERPRIV    0x00000100      /* used super-user privileges */
#define PF_DUMPCORE     0x00000200      /* dumped core */
#define PF_SIGNALED     0x00000400      /* killed by a signal */
#define PF_MEMALLOC     0x00000800      /* Allocating memory */
#define PF_NPROC_EXCEEDED 0x00001000    /* set_user noticed that RLIMIT_NPROC was exceeded */
#define PF_USED_MATH    0x00002000      /* if unset the fpu must be initialized before use */
#define PF_USED_ASYNC   0x00004000      /* used async_schedule*(), used by module init */
#define PF_NOFREEZE     0x00008000      /* this thread should not be frozen */
#define PF_FROZEN       0x00010000      /* frozen for system suspend */
#define PF_FSTRANS      0x00020000      /* inside a filesystem transaction */
#define PF_KSWAPD       0x00040000      /* I am kswapd */
#define PF_MEMALLOC_NOIO 0x00080000     /* Allocating memory without IO involved */
#define PF_LESS_THROTTLE 0x00100000     /* Throttle me less: I clean memory */
#define PF_KTHREAD      0x00200000      /* I am a kernel thread */
#define PF_RANDOMIZE    0x00400000      /* randomize virtual address space */
#define PF_SWAPWRITE    0x00800000      /* Allowed to write to swap */
#define PF_NO_SETAFFINITY 0x04000000    /* Userland is not allowed to meddle with cpus_allowed */
#define PF_MCE_EARLY    0x08000000      /* Early kill for mce process policy */
#define PF_MUTEX_TESTER 0x20000000      /* Thread belongs to the rt mutex tester */
#define PF_FREEZER_SKIP 0x40000000      /* Freezer should not count it as freezable */
#define PF_SUSPEND_TASK 0x80000000      /* this thread called freeze_processes and should not be frozen */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
/*
 * pointers to (original) parent process, youngest child, younger sibling,
 * older sibling, respectively.  (p->father can be replaced with
 * p->real_parent->pid)
 */
struct task_struct __rcu *real_parent; /* real parent process */
struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
/*
 * children/sibling forms the list of my natural children
 */
struct list_head children;      /* list of my children */
struct list_head sibling;       /* linkage in my parent's children list */
struct task_struct *group_leader;       /* threadgroup leader */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在Linux系統中,所有程序之間都有著直接或間接地聯絡,每個程序都有其父程序,也可能有零個或多個子程序。擁有同一父程序的所有程序具有兄弟關係。

欄位 描述
real_parent 指向其父程序,如果建立它的父程序不再存在,則指向PID為1的init程序
parent 指向其父程序,當它終止時,必須向它的父程序傳送訊號。它的值通常與real_parent相同
children 表示連結串列的頭部,連結串列中的所有元素都是它的子程序
sibling 用於把當前程序插入到兄弟連結串列中
group_leader 指向其所在程序組的領頭程序

Ptrace 提供了一種父程序可以控制子程序執行,並可以檢查和改變它的核心image。

它主要用於實現斷點除錯。一個被跟蹤的程序執行中,直到發生一個訊號。則程序被中止,並且通知其父程序。在程序中止的狀態下,程序的記憶體空間可以被讀寫。父程序還可以使子程序繼續執行,並選擇是否是否忽略引起中止的訊號。

unsigned int ptrace;
 ptraced is the list of tasks this task is using ptrace on.
* This includes both natural children and PTRACE_ATTACH targets.
* p->ptrace_entry is p's link on the p->parent->ptraced list.
*/
struct list_head ptraced;
struct list_head ptrace_entry;

unsigned long ptrace_message;
siginfo_t *last_siginfo; /* For ptrace use.  */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

成員ptrace被設定為0時表示不需要被跟蹤,它的可能取值如下:

/*
 * Ptrace flags
 *
 * The owner ship rules for task->ptrace which holds the ptrace
 * flags is simple.  When a task is running it owns it's task->ptrace
 * flags.  When the a task is stopped the ptracer owns task->ptrace.
 */

#define PT_SEIZED       0x00010000      /* SEIZE used, enable new behavior */
#define PT_PTRACED      0x00000001
#define PT_DTRACE       0x00000002      /* delayed trace (used on m68k, i386) */
#define PT_PTRACE_CAP   0x00000004      /* ptracer can follow suid-exec */

#define PT_OPT_FLAG_SHIFT       3
/* PT_TRACE_* event enable flags */
#define PT_EVENT_FLAG(event)    (1 << (PT_OPT_FLAG_SHIFT + (event)))
#define PT_TRACESYSGOOD         PT_EVENT_FLAG(0)
#define PT_TRACE_FORK           PT_EVENT_FLAG(PTRACE_EVENT_FORK)
#define PT_TRACE_VFORK          PT_EVENT_FLAG(PTRACE_EVENT_VFORK)
#define PT_TRACE_CLONE          PT_EVENT_FLAG(PTRACE_EVENT_CLONE)
#define PT_TRACE_EXEC           PT_EVENT_FLAG(PTRACE_EVENT_EXEC)
#define PT_TRACE_VFORK_DONE     PT_EVENT_FLAG(PTRACE_EVENT_VFORK_DONE)
#define PT_TRACE_EXIT           PT_EVENT_FLAG(PTRACE_EVENT_EXIT)
#define PT_TRACE_SECCOMP        PT_EVENT_FLAG(PTRACE_EVENT_SECCOMP)

#define PT_EXITKILL             (PTRACE_O_EXITKILL << PT_OPT_FLAG_SHIFT)
#define PT_SUSPEND_SECCOMP      (PTRACE_O_SUSPEND_SECCOMP << PT_OPT_FLAG_SHIFT)

/* single stepping state bits (used on ARM and PA-RISC) */
#define PT_SINGLESTEP_BIT       31
#define PT_SINGLESTEP           (1<<PT_SINGLESTEP_BIT)
#define PT_BLOCKSTEP_BIT        30
#define PT_BLOCKSTEP            (1<<PT_BLOCKSTEP_BIT)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

Performance Event是一款隨 Linux 核心程式碼一同釋出和維護的效能診斷工具。這些成員用於幫助PerformanceEvent分析程序的效能問題。

#ifdef CONFIG_PERF_EVENTS
    struct perf_event_context *perf_event_ctxp[perf_nr_task_contexts];
    struct mutex perf_event_mutex;
    struct list_head perf_event_list;
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

程序排程

優先順序

int prio, static_prio, normal_prio;
unsigned int rt_priority;
  • 1
  • 2
  • 1
  • 2
欄位 描述
static_prio 用於儲存靜態優先順序,可以通過nice系統呼叫來進行修改
rt_priority 用於儲存實時優先順序
normal_prio 的值取決於靜態優先順序和排程策略
prio 用於儲存動態優先順序

實時優先順序範圍是0到MAX_RT_PRIO-1(即99),而普通程序的靜態優先順序範圍是從MAX_RT_PRIO到MAX_PRIO-1(即100到139)。值越大靜態優先順序越低。

/*  http://lxr.free-electrons.com/source/include/linux/sched/prio.h#L21  */
#define MAX_USER_RT_PRIO    100
#define MAX_RT_PRIO     MAX_USER_RT_PRIO

/* http://lxr.free-electrons.com/source/include/linux/sched/prio.h#L24  */
#define MAX_PRIO        (MAX_RT_PRIO + 40)
#define DEFAULT_PRIO        (MAX_RT_PRIO + 20)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

排程策略相關欄位

/*  http://lxr.free-electrons.com/source/include/linux/sched.h?v=4.5#L1426  */
unsigned int policy;

/*  http://lxr.free-electrons.com/source/include/linux/sched.h?v=4.5#L1409  */

const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;


cpumask_t cpus_allowed;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
欄位 描述
policy 排程策略
sched_class 排程類
se 普通程序的呼叫實體,每個程序都有其中之一的實體
rt 實時程序的呼叫實體,每個程序都有其中之一的實體
cpus_allowed 用於控制程序可以在哪裡處理器上執行

排程策略

policy表示程序的排程策略,目前主要有以下五種:

/*
* Scheduling policies
*/
#define SCHED_NORMAL            0
#define SCHED_FIFO              1
#define SCHED_RR                2
#define SCHED_BATCH             3
/* SCHED_ISO: reserved but not implemented yet */
#define SCHED_IDLE              5
#define SCHED_DEADLINE          6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
欄位 描述 所在排程器類
SCHED_NORMAL (也叫SCHED_OTHER)用於普通程序,通過CFS排程器實現。SCHED_BATCH用於非互動的處理器消耗型程序。SCHED_IDLE是在系統負載很低時使用 CFS
SCHED_BATCH SCHED_NORMAL普通程序策略的分化版本。採用分時策略,根據動態優先順序(可用nice()API設定),分配 CPU 運算資源。注意:這類程序比上述兩類實時程序優先順序低,換言之,在有實時程序存在時,實時程序優先排程。但針對吞吐量優化 CFS
SCHED_IDLE 優先順序最低,在系統空閒時才跑這類程序(如利用閒散計算機資源跑地外文明搜尋,蛋白質結構分析等任務,是此排程策略的適用者) CFS
SCHED_FIFO 先入先出排程演算法(實時排程策略),相同優先順序的任務先到先服務,高優先順序的任務可以搶佔低優先順序的任務 RT
SCHED_RR 輪流排程演算法(實時排程策略),後 者提供 Roound-Robin 語義,採用時間片,相同優先順序的任務當用完時間片會被放到佇列尾部,以保證公平性,同樣,高優先順序的任務可以搶佔低優先順序的任務。不同要求的實時任務可以根據需要用sched_setscheduler()API 設定策略 RT
SCHED_DEADLINE 新支援的實時程序排程策略,針對突發型計算,且對延遲和完成時間高度敏感的任務適用。基於Earliest Deadline First (EDF) 排程演算法

排程類

sched_class結構體表示排程類,目前核心中有實現以下四種:

extern const struct sched_class stop_sched_class;
extern const struct sched_class dl_sched_class;
extern const struct sched_class rt_sched_class;
extern const struct sched_class fair_sched_class;
extern const struct sched_class idle_sched_class;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5
排程器類 描述
idle_sched_class 每個cup的第一個pid=0執行緒:swapper,是一個靜態執行緒。排程類屬於:idel_sched_class,所以在ps裡面是看不到的。一般執行在開機過程和cpu異常的時候做dump
stop_sched_class 優先順序最高的執行緒,會中斷所有其他執行緒,且不會被其他任務打斷。作用:1.發生在cpu_stop_cpu_callback 進行cpu之間任務migration;2.HOTPLUG_CPU的情況下關閉任務。
rt_sched_class RT,作用:實時執行緒
fair_sched_class CFS(公平),作用:一般常規執行緒

目前系統中,Scheduling Class的優先順序順序為StopTask > RealTime > Fair > IdleTask

開發者可以根據己的設計需求,來把所屬的Task配置到不同的Scheduling Class中.

/*  http://lxr.free-electrons.com/source/include/linux/sched.h?V=4.5#L1453 */
struct mm_struct *mm, *active_mm;
/* per-thread vma caching */
u32 vmacache_seqnum;
struct vm_area_struct *vmacache[VMACACHE_SIZE];
#if defined(SPLIT_RSS_COUNTING)
struct task_rss_stat    rss_stat;
#endif

/*  http://lxr.free-electrons.com/source/include/linux/sched.h?V=4.5#L1484  */
#ifdef CONFIG_COMPAT_BRK
unsigned brk_randomized:1;
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
欄位 描述
mm 程序所擁有的使用者空間記憶體描述符,核心執行緒無的mm為NULL
active_mm active_mm指向程序執行時所使用的記憶體描述符, 對於普通程序而言,這兩個指標變數的值相同。但是核心執行緒kernel thread是沒有程序地址空間的,所以核心執行緒的tsk->mm域是空(NULL)。但是核心必須知道使用者空間包含了什麼,因此它的active_mm成員被初始化為前一個執行程序的active_mm值。
brk_randomized 用來確定對隨機堆記憶體的探測。參見LKML上的介紹
rss_stat 用來記錄緩衝資訊

因此如果當前核心執行緒被排程之前執行的也是另外一個核心執行緒時候,那麼其mm和avtive_mm都是NULL

判斷標誌

int exit_code, exit_signal;
int pdeath_signal;  /*  The signal sent when the parent dies  */
unsigned long jobctl;   /* JOBCTL_*, siglock protected */

/* Used for emulating ABI behavior of previous Linux versions */
unsigned int personality;

/* scheduler bits, serialized by scheduler locks */
unsigned sched_reset_on_fork:1;
unsigned sched_contributes_to_load:1;
unsigned sched_migrated:1;
unsigned :0; /* force alignment to the next boundary */

/* unserialized, strictly 'current' */
unsigned in_execve:1; /* bit to tell LSMs we're in execve */
unsigned in_iowait:1;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
欄位 描述
exit_code 用於設定程序的終止