linux下的进程结构

Linux进程表示

​ 在Linux中使用task_struct表示一个进程描述符,该结构体包含了一个进程的所有信息

// tags/v4.18 - include/linux/sched.h
struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
	/*
	 * For reasons of header soup (see current_thread_info()), this
	 * must be the first element of task_struct.
	 */
	struct thread_info		thread_info;
#endif
	/* -1 unrunnable, 0 runnable, >0 stopped: */
	volatile long			state;

	/*
	 * This begins the randomizable portion of task_struct. Only
	 * scheduling-critical items should be added above here.
	 */
	randomized_struct_fields_start
	void				*stack;

...

	/*
	 * New fields for task_struct should be added above here, so that
	 * they are included in the randomized portion of task_struct.
	 */
	randomized_struct_fields_end

	/* CPU-specific state of this task: */
	struct thread_struct		thread;

	/*
	 * WARNING: on x86, 'thread_struct' contains a variable-sized
	 * structure.  It *MUST* be at the end of 'task_struct'.
	 *
	 * Do not put anything below here!
	 */
};

​ 在4.13内核中引入了stucture randomization技术来随机化task_struct大部分结构体成员布局,用来防止结构体偏移来覆盖特定敏感字段(函数指针)的内核攻击,Randomizing structure layout介绍了具体实现原理。

​ stack指向线程或进程的内核栈,每个进程或线程对应唯一的结构体,在Linux中进程其实就是具有特定共享资源的线程组的主线程。

Current宏

​ current宏用于查找一个指向当前正在运行进程的task_struct的指针,不同硬件体系架构实现不一样:

  • 将该指针保存在一个单独的寄存器里面直接访问,如ia64、sparc64
  • 对于寄存器不多的体系,通常会使用栈寄存器esp来保存thread_info结构如arm。
  • 大多数体系用寄存器存放thread_info结构指针,间接通过thread_info来访问,如alpha、sparc32
  • x86每个CPU都有一个特定于该CPU的私有数据区域,保存各种信息,其中就有指向stack_struct的指针

通用的 current 宏定义在 include/asm-generic/current.h

// tags/v4.18 - include/asm-generic/current.h
#define get_current() (current_thread_info()->task)
#define current get_current()

直接通过current_thread_info()->task来获取当前进程,这样的话需要在thread_info中定义一个指向thread_struct的指针。X86体系的current宏如下

// tags/v4.18 - arch/x86/include/asm/current.h
DECLARE_PER_CPU(struct task_struct *, current_task);

static __always_inline struct task_struct *get_current(void)
{
	return this_cpu_read_stable(current_task);
}

#define current get_current()

thread_info

task_struct 结构里面通过 CONFIG_THREAD_INFO_IN_TASK 宏来控制是否有 thread_info 成员结构.

//include/linux/thread_info.h
 /*
 * For CONFIG_THREAD_INFO_IN_TASK kernels we need <asm/current.h> for the
 * definition of current, but for !CONFIG_THREAD_INFO_IN_TASK kernels,
 * including <asm/current.h> can cause a circular dependency on some platforms.
 */
#include <asm/current.h>
#define current_thread_info() ((struct thread_info *)current)
#endif   

定义了thread_info结构会直接返回current的指针作为thread_info的地址,如果没有定义的话,取决于各个体系<asm/thread_info.h>,直接用单独的及存储器来存放比较简单。

THREAD_SIZE 表示进程内核栈的大小,这边是2个页(8KB),实际上就是 task_struct 结构中的 stack 大小。
current_thread_info 宏把栈指针的后 13 个有效位屏蔽掉,用来计算 thread_info 的偏移。

thread_info 和内核栈共享这块内存地址。

                                       arm

          +  +--------------------+
          |  |                    |
          |  |       stack        |
          |  +--------------------+ <--+ sp
          |  |                    |
          |  |         +          |            +--------------------+
          |  |         |          |            |                    |
          |  |         |          |            |                    |
THREAD_SIZE  |         v          |            |                    |
          |  |                    |            |                    |
          |  |                    |            |                    |
          |  |                    |            |                    |
          |  |                    |       +----+       *stack       |
          |  |                    |       |    | struct task_struct |
          |  |                    |   +------->+--------------------+ <--+ current
          |  |       *task        +---+   |
          |  | struct thread_info |       |
          +  +--------------------+<------+
这幅图展示了arm架构current宏代码实现如下
              
// tags/v4.18 - include/linux/sched.h
union thread_union {
#ifndef CONFIG_ARCH_TASK_STRUCT_ON_STACK
	struct task_struct task;
#endif
#ifndef CONFIG_THREAD_INFO_IN_TASK
	struct thread_info thread_info;
#endif
	unsigned long stack[THREAD_SIZE/sizeof(long)];
};

​ 当进程从用户态切换到内核态,这时候 sp 栈寄存器指向内核栈底,然后保存硬件上下文,将 thread_info 和内核栈放在一起,就可以通过 sp 寄存器获取当前正在CPU上运行的进程。

​ 实际上 thread_info 结构和具体的体系架构有关,保存了为实现进入、退出内核态的特定体系结构的汇编代码段所需要访问的部分进程的数据,所以大多数体系采用了独立寄存器或栈寄存器来保存 thread_info 地址。

State

​ Linux进程的几种状态在include/Linux/sched.h定义了

// tags/v4.18 - 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->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

​ 通常使用ps、top命令可以看到前5种状态,系统中每个进程都必然处于五种状态的一种

  • TASK_RUNING:表示进程可运行的状态,正在运行,或者在运行队列等待;
  • TASK_INTERRUPTIBLE表示进程正在处于可睡眠中(阻塞),等待某些条件,该状态可被信号打断;
  • TASK_UNINTERRUPTIBLE:不可中断睡眠,无法被外部信号唤醒;
  • _TASK_STOPPED:进程停止状态,通常是发生在进程接受到SIGSTOP、SIGTSTP等信号时;
  • _TASK_TRACED:表示被其他进程追踪的进程。

PID&tgid

​ 内核通过一个唯一的进程标识PID来标识每个task_struct,处于某个线程组中的所有进程都有一个统一的线程组ID(TGID)。如果进程没有使用线程,则其PID和TGID相同.所以,我们在使用 getpid 系统调用的时候,返回的其实是 tgid 的值。

​ pid 和 tgid 都是 pid_t 类型,该类型是由各体系分别定义的 __kernel_pid_t,通常是一个 int 类型。

// tags/v4.18 - include/linux/types.h
typedef __kernel_pid_t      pid_t

// tags/v4.18 - include/uapi/asm-generic/posix_types.h
#ifndef __kernel_pid_t
typedef int		__kernel_pid_t;
#endif

parent&children&sibling

​ Linux的进程之间是有家族关系的,每个进程都有一个父进程,相应的,每个进程也可以拥有零个或多个子进程,有同一个父进程的多个子进程之间称为兄弟(sibling)进程。

// tags/v4.18 - include/linux/sched.h
struct task_struct {
...
	/* Real parent process: */
	struct task_struct __rcu	*real_parent;

	/* Recipient of SIGCHLD, wait4() reports: */
	struct task_struct __rcu	*parent;

	/*
	 * Children/sibling form the list of natural children:
	 */
	struct list_head		children;
	struct list_head		sibling;
...
}
  • real_parent 指向创建该进程的父进程描述符,如果父进程不在存在,就指向进程 1(init)的描述符;

  • parent 指向该进程当前的父进程,通常与 real_parent 一致,但在 ptrace() 系统调用时会不一致;

  • children 指向该进程创建的子进程链表的头部;

  • sibling 指向兄弟进程链表

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值