进程—Linux进程描述符初印象

本文详细介绍了Linux内核中的进程管理,包括进程内核栈结构、线程描述符thread_info、进程描述符task_struct,以及状态、优先级数组、可执行队列结构等关键概念。重点解析了进程描述符的组织和操作,如添加、遍历、判空等,并探讨了如何根据PID查找进程描述符。
摘要由CSDN通过智能技术生成

进程内核栈结构:union task_union

在../include/linux/sched.h中定义了如下一个联合结构用来创建内核栈空间。

//../include/linux/sched.h

union task_union {
      struct thread_info        thread_info;
      unsigned long             stack[THREAD_SIZE/sizeof(long)];
}:


p83 进程内核栈空间

线程描述符:struct thread_info

每一个进程都有一个进程描述符task_struct,且有一个用来定位它的结构thread_info,thread_info位于其进程内核栈中(有些实现没有用到thread_info,而是使用一个寄存器来记录进程描述符的地址),操作系统使用这个结构中的task指针字段找到进程的进程描述符,从而得到执行一个进程所需的全部信息。

//../arch/xtensa/include/asm

struct thread_info {

struct task_struct          *task; //指向当前进程内核栈对应的进程的进程描述符

struct exec_domain          *exec_domain;
__u32                       flags;
__u32                       status;
__u32                       cpu;
int                         preempt_count;
mm_segment_t                addr_limit;
struct restart_block        restart_block;
void                        *sysenter_return;
int                         uaccess_err;

};

内核使用alloc_thread_infofree_thread_info宏分配和释放存储thread_info结构和内核栈的内存区。使用current_thread_info()函数获取当前操作的进程的thread_info 。为了获得当前在CPU上运行的进程的进程描述符指针,内核要调用current宏,该宏本质上等价于current_thread_info()->task。详见【深入理解Linux内核】中文第三版91页。

进程描述符:task_struct

从内核观点看,进程的目的就是担当分配系统资源(CPU 时间、内存等)的实体。为此目的,操作系统为每个进程维持着一个进程描述符。

task_struct 示意图:


p109 进程描述符

源代码

在../include/linux/sched.h中定义了task_struct,其中包含了一个进程所需的全部信息。其结构体实例在内存中的大小一般在1KB以上。

//../include/linux/sched.h
//---------------------------------------------------进程描述符结构定义---------------------------------------------------

struct task_struct
{
//---------------------------------------------------------进程状态------------------------------------------------------------ 
  long                  state;           //任务的运行状态


//---------------------------------------------------------进程标识信息--------------------------------------------------------- 
  pid_t                 pid;  //进程ID
  pid_t                 pgrp; //进程组标识,表示进程所属的进程组,等于进程组的领头进程的pid
  pid_t                 tgid;  //进程所在线程组的ID,等于线程组的领头线程的pid,getpid()系统调用返回tgid值。 
  pid_t                 session; //进程的登录会话标识,等于登录会话领头进程的pid。 
  struct pid            pids[PIDTYPE_MAX]; //PIDTYPE_MAX=4,一共4个hash表。
  char                  comm[TASK_COMM_LEN];  //记录进程的名字,即进程正在运行的可执行文件名 
  int                   leader; //标志,表示进程是否为会话主管(会话领头进程)。



//-------------------------------------------------------进程调度相关信息-------------------------------------------------------
  long                          nice;//进程的初始优先级,范围[-20,+19],默认0,nice值越大优先级越低,分配的时间片
  //可能越少。
  int                           static_prio;//静态优先级。
  int                           prio;//存放调度程序要用到的优先级。
    /*
    0-99 -> Realtime process
    100-140 -> Normal process
  */
  unsigned int                  rt_priority;//实时优先级,默认情况下范围[0,99]
    /*
    0 -> normal
    1-99 -> realtime
    */
  unsigned long                 sleep_avg;//这个字段的值用来支持调度程序对进程的类型(I/O消耗型 or CPU消耗型)进行
  //判断,值越大表示睡眠的时候更多,更趋向于I/O消耗型,反之,更趋向于CPU消耗型。
  unsigned long                 sleep_time;//进程的睡眠时间   
  unsigned int                  time_slice;//进程剩余时间片,当一个任务的时间片用完之后,要根据任务的静态优先级
  //static_prio重新计算时间片。task_timeslice()为给定的任务返回一个新的时间片。对于交互性强的进程,时间片用完之后,它
  //会被再放到活动数组而不是过期数组,该逻辑在scheduler_tick()中实现。
#if defined(CONFIG_SCHEDSTATS)||define(CONFIG_TASK_DELAY_ACCT)  
  unsigned int                  policy;//表示该进程的进程调度策略。调度策略有:
//SCHED_NORMAL 0, 非实时进程, 用基于优先权的轮转法。
//SCHED_FIFO 1, 实时进程, 用先进先出算法。
//SCHED_RR 2, 实时进程, 用基于优先权的轮转法
#endif
  struct list_head              tasks;//任务队列,通过这个寄宿于PCB(task_struct)中的字段构成的双向循环链表将宿主
  //PCB链接起来。
  struct list_head              run_list;//该进程所在的运行队列。这个队列有一个与之对应的优先级k,所有位于这个队列中
  //的进程的优先级都是k,这些k优先级进程之间使用轮转法进行调度。k的取值是0~139。这个位于宿主PCB中的struct list_head类
  //型的run_list字段将构成一个优先级为k的双向循环链表,像一条细细的绳子一样,将所有优先级为k的处于可运行状态的进程的
  //PCB(task_struct)链接起来。
  prio_array_t                  *array; //typedef struct prio_array prio_array_t; 可以说,这个指针包含了操作
  //系统现有的所有按PCB的优先级进行整理了的PCB的信息。 


//---------------------------------------------------------进程链接信息---------------------------------------------------------
  struct task_struct            *real_parent;//指向创建了该进程的进程的进程描述符,如果父进程不再存在,就指向进程
  //1(init)的进程描述符。
  struct task_struct            *parent;//recipient of SIGCHLD, wait4() reports. parent是该进程现在的父进程,
  //有可能是“继父”
  struct list_head              children;//list of my children.  children指的是该进程孩子的链表,使用
  //list_for_each和list_entry,可以得到所有孩子的进程描述符。  
  struct lsit_head              sibling;//linkage in my parent's children list.
//sibling为该进程的兄弟的链表,也就是其父亲的所有孩子的链表。用法与children相似。  
  struct task_struct            *group_leader;//threadgroup leader,主线程描述符
  struct list_head              thread_group;  //线程组链表,也就是该进程所有线程的链表。

//----------------------------------------------------------......------------------------------------------------------------

};

参考
进程描述符结构定义的部分注释
反斜杠作用“\”
Linux3.0.6内核task_struct注释
do {…} while (0) 在宏定义中的作用

进程状态:state


p86 进程的状态

state字段的值通常用一个简单的赋值语句设置。例如:p->state = TASK_RUNNING;,内核也使用 set_task_stateset_current_state 宏,分别设置指定进程的状态和当前执行进程的状态。

//../include/linux/sched.h

#define set_task_state(tsk, state_value)        \
    set_mb((tsk)->state, (state_value))

#define set_current_state(state_value)      \
    set_mb(current->state, (state_value))

set_mb函数确保编译程序或CPU 控制单元不把赋值操作与其他指令混合。混合指令的顺序有时会导致灾难性的后果。

优先级数组:struct prio_array

//../kernel/sched.c

struct prio_array{ 

  int                   nr_active;//各个优先级任务的总数目  
  unsigned 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值