什麼是進程?什麼是task_struct?
首先站在用戶的角度來看:進程就是運行中的程序,這是一個比較抽象化的概念。
站在操作系統的角度來看:進程就是操作系統對進程的描述,這個描述信息就是對進程的具象化描述- - -這個描述信息就是進程,這個描述信息就是pcb(process control block)- - -進程控制塊或者進程描述符。在linux下就是task_struct這個結構體。
在創建新進程時,Linux就從系統內存中分配一個task_struct結構,並把它的首地址加入 task 數組中,這個 task 數組是系統中的一個向量數組,該數組的長度默認是 512B,該數組的元素是指向task_struct的。
在《操作系統精髓與設計原理》這本書中對進程作了如下定義:
- 一個正在執行中的程序
- 一個正在計算機上執行的程序實例
- 能分配給處理器並由處理器處理的實體
- 一個具有以下特徵的活動單元:1、一組指令序列的執行。2、一個當前狀態和相關的系統資源集合
那麼 task_struct 中都有什麼內容呢?
- 標識符:描述本進程的唯一標識符,用於區別其他進程
- 狀態:任務狀態,退出信號,退出代碼等
- 優先級:相對於其他進程的優先級
- 程序計數器:程序中即將被執行的下一條指令的地址
- 內存指針:包括程序代碼和進程相關數據的指針,還有和其他進程共享的內存塊的指針
- 上下文數據:進程執行時處理器的寄存器中的數據
- IO狀態信息:包括顯示的 I/O 請求,分配給進程的 I/O 設備和被進程使用的文件列表。
- 記賬信息(審計信息):可能包括處理器時間總和,使用的時鐘數總和,時間限制,記賬號等
- 那麼還有其他的信息如下介紹:
調度數據成員
//進程的狀態,能否執行(-1, 0, >1)
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
volatile這個關鍵字是保持內存可見性,防止代碼過度優化,就是state變量從內存當中讀取數據,而不是將從寄存器中讀取,這也是爲了保證對操作系統狀態實時訪問的穩定性。
/* 狀態取值
192 * Task state bitmask. NOTE! These bits are also
193 * encoded in fs/proc/array.c: get_task_state().
194 *
195 * We have two separate sets of flags: task->state
196 * is about runnability, while task->exit_state are
197 * about the task exiting. Confusing, but this way
198 * modifying one set can't modify the other one by
199 * mistake.
200 */
#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_ZOMBIE 16
#define EXIT_DEAD 32
/* in tsk->state again */
#define TASK_DEAD 64
#define TASK_WAKEKILL 128
#define TASK_WAKING 256
#define TASK_PARKED 512
#define TASK_STATE_MAX 1024
/* 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)
/* Convenience macros for the sake of wake_up */
#define TASK_NORMAL (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)
#define TASK_ALL (TASK_NORMAL | __TASK_STOPPED | __TASK_TRACED)
//用於自定義調度程序行爲
unsigned int flags; /* per process flags, defined below */
/*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_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */
#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 */
unsigned int policy; //該進程的調度策略
調度策略有:
SCHED_OTHER 0 非實時進程,基於優先權的輪轉法(round robin)。
SCHED_FIFO 1 實時進程,用先進先出算法。
SCHED_RR 2 實時進程,用基於優先權的輪轉法。
進程標識符
pid_t pid; //進程標識符
pid_t tgid; //線程組標識符
tgid是用於表示線程組的標識符,它等於主線程的id,主線程的id也就是這個進程的id。
表示進程間親屬關係的成員
struct task_struct __rcu *real_parent; /* real parent process */
struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
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 */
real_parent 指向其父進程,如果父進程不存在,則指向1號 init 進程;
parent 是父進程,是接受SIGCHLD信號的,也就是回收子進程資源的進程;
children 鏈表中都是它的子進程;
sibling 把當前鏈表插入兄弟鏈表中
group_leader 指向線程組的首位。
進程地址空間
struct mm_struct *mm, *active_mm;
struct mm_struct *mm, *active_mm;
#ifdef CONFIG_COMPAT_BRK
unsigned brk_randomized:1;
#endif
#if defined(SPLIT_RSS_COUNTING)
struct task_rss_stat rss_stat;
mm 指向進程所擁有的內存描述符;
active_mm指向進程運行時所使用的內存描述符;
對於普通進程而言,這兩個指針的值相同;
對於內核線程而言,它們不擁有任何內存描述符,所以它們的mm總爲NULL,當內核線程運行時,它的active_mm初始化爲前一個active_mm的值。
信號處理
/* signal handlers */
struct signal_struct *signal;
struct sighand_struct *sighand;
sigset_t blocked, real_blocked;
sigset_t saved_sigmask; /* restored if set_restore_sigmask() was used */
struct sigpending pending;
unsigned long sas_ss_sp;
size_t sas_ss_size;
int (*notifier)(void *priv);
void *notifier_data;
sigset_t *notifier_mask;
signal 指向信號描述符
sighand 指向信號處理程序描述符
blocked 表示被阻塞信號的掩碼
real_blocked 表示臨時掩碼
pending 中存放未決信號(未被處理的信號)
sas_ss_sp 是信號處理程序備用堆棧的地址
sas_ss_size 表示堆棧的大小
設備驅動程序常用notifier指向的函數來阻塞進程的某些信號。(notifier_data是可能用到的數據,notifier_mask是信號的位掩碼)
ptrace系統調用
gdb調試中底層就是使用 ptrace 來完成的。
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. */
#ifdef CONFIG_HAVE_HW_BREAKPOINT
atomic_t ptrace_bp_refcnt;
#endif
其他數據成員
fpu_counter包含使用FPU的連續上下文切換的數量。
(1) unsigned char fpu_counter;
進程描述符使用計數,被置爲2時,表示進程描述符正在被使用而且其相應的進程處於活動狀態。
(2) atomic_t usage;
用於表示獲取大內核鎖的次數,如果進程未獲得過鎖,則置爲-1。
(3) int lock_depth; /* BKL lock depth */
用於管理資源分配以及釋放的自旋鎖。
(4) /* Protection of (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed,
mempolicy */
spinlock_t alloc_lock;
用於調度器統計進程的運行信息。
(5)
#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)
struct sched_info sched_info;
#endif
命名空間
(6)/* namespaces */
struct nsproxy *nsproxy;
死鎖檢測
(7)#ifdef CONFIG_DEBUG_MUTEXES
/* mutex deadlock detection */
struct mutex_waiter *blocked_on;
#endif
內存回收
(8)struct reclaim_state *reclaim_state;
I/O調度器所使用的信息
(9)struct io_context *io_context;
存放塊設備I/O數據流量信息
(10)struct backing_dev_info *backing_dev_info;
PS:本文是基於CentOS Linux release 7.3.1611版本。task_struct中還有很多其他信息,本文沒有講到,可以自行查看。