进程管理(1):task_struct结构体说明

本篇笔记详细讲述task_struct结构体的基本内容,涉及的代码基于Linux5.9.8版本。

1. 进程和线程

关于进程和线程的概念,这里不赘述。需要了解的是在Linux内核中,进程通常被叫做任务,所以进程控制块(PCB)也被命名为struct task_struct。而Linux中是没有明确的线程的概念的,被称为轻量级进程,和进程使用相同的PCB结构,内核使用clone()来创建线程。

进程和线程的区别在于进程拥有独立的资源空间,而线程则共享进程的资源空间,通常一个进程可以拥有多个线程。

2. task_struct

Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程,这个结构体包含了一个进程所需的所有信息。

该结构体存放在叫做任务列表的双向循环列表中。如下图所示:
进程管理(1):task_struct结构体说明_第1张图片

即系统中每一个进程/线程的PCB都由该双向链表进行管理。

2.1 结构体定义

其定义在==/include/linux/sched.h==文件中。

太长了,略去~~~


3. 成员解析

3.1 进程状态

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

Linux kernel中有两类状态,其中state表示进程的运行状态,exit_state表示进程的退出状态,看起来很令人疑惑,但是这样的好处是但我们修改其中一类时,不会影响到另一类的状态。

状态值是bitmask,即有些状态可能是其他多种状态的或操作。

state成员可能的取值如下:

/* 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
#define TASK_STATE_MAX			0x1000

/* Convenience macros for the sake of set_current_state: */
#define TASK_KILLABLE			(TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
#define TASK_STOPPED			(TASK_WAKEKILL | __TASK_STOPPED)
#define TASK_TRACED			(TASK_WAKEKILL | __TASK_TRACED)

#define TASK_IDLE			        (TASK_UNINTERRUPTIBLE | TASK_NOLOAD)

/* Convenience macros for the sake of wake_up(): */
#define TASK_NORMAL			(TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)

/* get_task_state(): */
#define TASK_REPORT			(TASK_RUNNING | TASK_INTERRUPTIBLE | \
                                             TASK_UNINTERRUPTIBLE | __TASK_STOPPED | \
					          __TASK_TRACED | EXIT_DEAD | EXIT_ZOMBIE | \
					          TASK_PARKED)

下面我们介绍几个常用的取值:

状态 描述
TASK_RUNNING 表示进程正在执行或者处于准备执行的状态
TASK_INTERRUPTIBLE 进程因为等待某些条件处于阻塞(挂起的状态),一旦等待的条件成立,进程便会从该状态转化成就绪状态
TASK_UNINTERRUPTIBLE 意思与TASK_INTERRUPTIBLE类似,但是我们传递任意信号等不能唤醒他们,只有它所等待的资源可用的时候,他才会被唤醒。
TASK_STOPPED 进程被停止执行
TASK_TRACED 进程被debugger等进程所监视。
EXIT_ZOMBIE 进程的执行被终止,但是其父进程还没有使用wait()等系统调用来获知它的终止信息,此时进程成为僵尸进程
EXIT_DEAD 进程被杀死,即进程的最终状态。
TASK_KILLABLE 当进程处于这种可以终止的新睡眠状态中,它的运行原理类似于 TASK_UNINTERRUPTIBLE,只不过可以响应致命信号

3.2 进程标识符

pid_t pid;     //进程的标识符
pid_t tgid;    //线程组标识符

pid是进程唯一标识符(包括线程),它的引入是为了区别每个进程;tgid是线程组id,更像是真正意义上的进程id,共享资源的进程集合共享同一tgid

3.3 进程标记符

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

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 */

常用状态介绍:

状态 描述
PF_FORKNOEXEC 表示进程刚被创建,但还没有执行
PF_SUPERPRIV 表示进程拥有超级用户特权
PF_SIGNALED 表示进程被信号杀出
PF_EXITING 表示进程开始关闭

3.4 表示进程亲属关系的成员

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 指向当前操作系统执行进程的父进程,如果父进程不存在,指向pid为1的init进程
paren 指向当前进程的父进程,当当前进程终止时,需要向它发送wait4()的信号
children 位于链表的头部,链表的所有元素都是children的子进程
group_leader 指向进程组的领头进程

进程间亲属关系如下图所示:
进程管理(1):task_struct结构体说明_第2张图片

进程间通过多个双向循环链表组织

  • parent指向父进程
  • sibling指向兄弟进程
  • children指向子进程
  • sibling.next指向进程的下一个兄弟进程的进程描述符sibling成员,若其后没有其他兄弟进程,则指向父进程;而sibling.prev指向进程的上一个兄弟进程,若其之前没有兄弟进程,则指向父进程。
  • children.next指向父进程的第一个子进程的sibling成员(而不是children成员!),而children.prev却指向父进程的最后一个子进程的sibling成员。

__rcu说明

该关键字定义在include/linux/compiler_types.h文件中。

# define __rcu		__attribute__((noderef, address_space(__rcu)))

RCU代表的是 “read, copy, update”。它是一种算法,允许多个读者访问数据,并且同时允许修改者,删除者能够进行操作。
如果内核使用 CONFIG_SPARSE_RCU_POINTER 的编译, __rcu 就会被定义为上面的定义。

3.5 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.
* 'ptrace_entry' is this task's link on the p->parent->ptraced list.
*/
struct list_head		ptraced;
struct list_head		ptrace_entry;

Ptrace提供了一种父进程,它可以被用来控制子进程的运行,常被用来进行断点调试,当它被设置为0时表示不需要追踪。

#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<
#define PT_BLOCKSTEP_BIT        		30
#define PT_BLOCKSTEP            		(1<

3.6 进程调度

3.6.1 优先级

int			prio;
int			static_prio;
int			normal_prio;
unsigned int	rt_priority;
成员 描述
static_prio 用来保存静态优先级,可以调用nice系统直接来修改取值范围为100~139
rt_priority 用来保存实时优先级,取值范围为0~99
prio 用来保存动态优先级
normal_prio 它的值取决于静态优先级和调度策略

实时优先级和静态优先级的取值范围中,值越大,优先级越低

3.6.2 进程地址空间

进程都拥有自己的资源,这些资源指的就是进程的地址空间,每个进程都有着自己的地址空间,在task_struct中,有关进程地址空间的定义如下:

struct mm_struct		*mm;
struct mm_struct		*active_mm;
/* Per-thread vma caching: */
struct vmacache		vmacache;
struct task_rss_stat	rss_stat;
成员 描述
mm 进程所拥有的内存空间描述符,对于内核线程的mm为NULL
active_mm 指进程运行时所使用的进程描述符
rss_stat 被用来记录缓冲信息

如果当前内核线程被调度之前运行的也是另外一个内核线程时候,那么其mm和avtive_mm都是NULL。

3.7 CPU编号信息

#ifdef CONFIG_SMP
	int				on_cpu;
	struct __call_single_node	wake_entry;
#ifdef CONFIG_THREAD_INFO_IN_TASK
	/* Current CPU: */
	unsigned int			cpu;
#endif
	unsigned int			wakee_flips;
	unsigned long			wakee_flip_decay_ts;
	struct task_struct		*last_wakee;
	int				recent_used_cpu;
	int				wake_cpu;
#endif

on_cpu实际上是一个锁,目的是通过拥有未锁定的运行队列来避免高延迟,当它为0时,可以将任务移到另一个CPU中。

cpu字段指定在其上执行进程的CPU的编号。

参考链接

https://blog.csdn.net/bit_clearoff/article/details/54292300
__attribute__如何使用的记录

你可能感兴趣的:(Linux,#,进程管理)