image
image

task_struct

进程主要由以下几部分组成:

  • 代码段:编译后形成的一些指令
  • 数据段:程序运行时需要的数据
    • 只读数据段:常量
    • 已初始化数据段:全局变量,静态变量
    • 未初始化数据段(bss):未初始化的全局变量和静态变量
  • 堆栈段:程序运行时动态分配的一些内存
  • PCB:进程信息,状态标识等

Linux 内核中进程用 task_struct结构体表示,称为进程描述符,该结构体相对比较复杂,有几百行代码,记载着该进程相关的所有信息,比如进程地址空间,进程状态,打开的文件等。对内核而言,进程或者线程都称为任务 task。内核将所有进程放入一个双向循环链表结构的任务列表(task list)。

image
image

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct task_struct {
    volatile long state; //进程状态
    struct mm_struct *mm, *active_mm; //内存地址空间
    pid_t pid;
    pid_t tgid;

    struct task_struct __rcu *real_parent; //真正的父进程,fork时记录的
    struct task_struct __rcu *parent; // ptrace后,设置为trace当前进程的进程
    struct list_head children;  //子进程
    struct list_head sibling;	//父进程的子进程,即兄弟进程
    struct task_struct *group_leader; //线程组的领头线程

    char comm[TASK_COMM_LEN];  //进程名,长度上限为16字符
    struct fs_struct *fs;  //文件系统信息
    struct files_struct *files; // 打开的文件

    struct signal_struct *signal;
    struct sighand_struct *sighand;
    struct sigpending pending;

    void *stack;    // 指向内核栈的指针
   ...
}

进程运行在内核态时,需要相应的堆栈信息, 则 linux kernel 为每个进程都提供一个内核栈 kernel stack.

详细版

  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
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
struct task_struct {
	volatile long state;  //说明了该进程是否可以执行,还是可中断等信息
	unsigned long flags;  //Flage 是进程号,在调用fork()时给出
	int sigpending;    //进程上是否有待处理的信号
	mm_segment_t addr_limit; //进程地址空间,区分内核进程与普通进程在内存存放的位置不同
							 //0-0xBFFFFFFF for user-thead
							 //0-0xFFFFFFFF for kernel-thread
							 //调度标志,表示该进程是否需要重新调度,若非0,则当从内核态返回到用户态,会发生调度
	volatile long need_resched;
	int lock_depth;  //锁深度
	long nice;       //进程的基本时间片
					 //进程的调度策略,有三种,实时进程:SCHED_FIFO,SCHED_RR, 分时进程:SCHED_OTHER
	unsigned long policy;
	struct mm_struct *mm; //进程内存管理信息
	int processor;
	//若进程不在任何CPU上运行, cpus_runnable 的值是0,否则是1 这个值在运行队列被锁时更新
	unsigned long cpus_runnable, cpus_allowed;
	struct list_head run_list; //指向运行队列的指针
	unsigned long sleep_time;  //进程的睡眠时间
							   //用于将系统中所有的进程连成一个双向循环链表, 其根是init_task
	struct task_struct *next_task, *prev_task;
	struct mm_struct *active_mm;
	struct list_head local_pages;       //指向本地页面
	unsigned int allocation_order, nr_local_pages;
	struct linux_binfmt *binfmt;  //进程所运行的可执行文件的格式
	int exit_code, exit_signal;
	int pdeath_signal;     //父进程终止是向子进程发送的信号
	unsigned long personality;
	//Linux可以运行由其他UNIX操作系统生成的符合iBCS2标准的程序
	int did_exec : 1;
	pid_t pid;    //进程标识符,用来代表一个进程
	pid_t pgrp;   //进程组标识,表示进程所属的进程组
	pid_t tty_old_pgrp;  //进程控制终端所在的组标识
	pid_t session;  //进程的会话标识
	pid_t tgid;
	int leader;     //表示进程是否为会话主管
	struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
	struct list_head thread_group;   //线程链表
	struct task_struct *pidhash_next; //用于将进程链入HASH表
	struct task_struct **pidhash_pprev;
	wait_queue_head_t wait_chldexit;  //供wait4()使用
	struct completion *vfork_done;  //供vfork() 使用
	unsigned long rt_priority; //实时优先级,用它计算实时进程调度时的weight值

							   //it_real_value,it_real_incr用于REAL定时器,单位为jiffies, 系统根据it_real_value
							   //设置定时器的第一个终止时间. 在定时器到期时,向进程发送SIGALRM信号,同时根据
							   //it_real_incr重置终止时间,it_prof_value,it_prof_incr用于Profile定时器,单位为jiffies。
							   //当进程运行时,不管在何种状态下,每个tick都使it_prof_value值减一,当减到0时,向进程发送
							   //信号SIGPROF,并根据it_prof_incr重置时间.
							   //it_virt_value,it_virt_value用于Virtual定时器,单位为jiffies。当进程运行时,不管在何种
							   //状态下,每个tick都使it_virt_value值减一当减到0时,向进程发送信号SIGVTALRM,根据
							   //it_virt_incr重置初值。
	unsigned long it_real_value, it_prof_value, it_virt_value;
	unsigned long it_real_incr, it_prof_incr, it_virt_value;
	struct timer_list real_timer;   //指向实时定时器的指针
	struct tms times;      //记录进程消耗的时间
	unsigned long start_time;  //进程创建的时间
							   //记录进程在每个CPU上所消耗的用户态时间和核心态时间
	long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];
	//内存缺页和交换信息:
	//min_flt, maj_flt累计进程的次缺页数(Copy on Write页和匿名页)和主缺页数(从映射文件或交换
	//设备读入的页面数); nswap记录进程累计换出的页面数,即写到交换设备上的页面数。
	//cmin_flt, cmaj_flt, cnswap记录本进程为祖先的所有子孙进程的累计次缺页数,主缺页数和换出页面数。
	//在父进程回收终止的子进程时,父进程会将子进程的这些信息累计到自己结构的这些域中
	unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
	int swappable : 1; //表示进程的虚拟地址空间是否允许换出
					   //进程认证信息
					   //uid,gid为运行该进程的用户的用户标识符和组标识符,通常是进程创建者的uid,gid
					   //euid,egid为有效uid,gid
					   //fsuid,fsgid为文件系统uid,gid,这两个ID号通常与有效uid,gid相等,在检查对于文件
					   //系统的访问权限时使用他们。
					   //suid,sgid为备份uid,gid
	uid_t uid, euid, suid, fsuid;
	gid_t gid, egid, sgid, fsgid;
	int ngroups; //记录进程在多少个用户组中
	gid_t groups[NGROUPS]; //记录进程所在的组
						   //进程的权能,分别是有效位集合,继承位集合,允许位集合
	kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
	int keep_capabilities : 1;
	struct user_struct *user;
	struct rlimit rlim[RLIM_NLIMITS];  //与进程相关的资源限制信息
	unsigned short used_math;   //是否使用FPU
	char comm[16];   //进程正在运行的可执行文件名
					 //文件系统信息
	int link_count, total_link_count;

	//NULL if no tty 进程所在的控制终端,如果不需要控制终端,则该指针为空
	struct tty_struct *tty;
	unsigned int locks;
	//进程间通信信息
	struct sem_undo *semundo;  //进程在信号灯上的所有undo操作
	struct sem_queue *semsleeping; //当进程因为信号灯操作而挂起时,他在该队列中记录等待的操作
								   //进程的CPU状态,切换时,要保存到停止进程的task_struct中
	struct thread_struct thread;
	//文件系统信息
	struct fs_struct *fs;
	//打开文件信息
	struct files_struct *files;
	//信号处理函数
	spinlock_t sigmask_lock;
	struct signal_struct *sig; //信号处理函数
	sigset_t blocked;  //进程当前要阻塞的信号,每个信号对应一位
	struct sigpending pending;  //进程上是否有待处理的信号
	unsigned long sas_ss_sp;
	size_t sas_ss_size;
	int(*notifier)(void *priv);
	void *notifier_data;
	sigset_t *notifier_mask;
	u32 parent_exec_id;
	u32 self_exec_id;

	spinlock_t alloc_lock;
	void *journal_info;
};

thread_info

Linux 通过 slab 动态生成 task_struct,那么在栈顶或栈底创建新的结构体 thread_info 即可,其中 task 指向其真正的 task_struct结构体。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
struct thread_info {
	struct task_struct	    *task;		//主要的进程描述符
	struct exec_domain	    *exec_domain;
	__u32			        flags;
	__u32			        status;		// 线程同步flags
	__u32			        cpu;		//当前cpu
	int			            preempt_count;
	mm_segment_t		    addr_limit;
	struct restart_block    restart_block;
	void __user		        *sysenter_return;
	unsigned int		    sig_on_uaccess_error:1;
	unsigned int		    uaccess_err:1;
};

Reference