进程控制块:PCB之task_struct

2019-07-14 09:06发布

进程就是程序动态运行的实例,是承担分配系统资源的实体
进程信息被存放在一个叫进程控制块的数据结构中,将其称之为PCB。而Linux操作系统下的task_struct是PCB的一种,task_struct是Linux内核的一种数据结构,他会被装载到内存里并保存着进程的信息
信息分类如下: 状态:描述进程的状态,因为进程有挂起,阻塞,运行等好几个状态,所以都有个标识符来记录进程的执行状态。 内存指针:程序代码和进程相关数据的指针。 程序计数器:程序中即将被执行的下一条指令的地址。 上下文数据:进程执行时处理器的寄存器中的数据。 记账信息:包括处理器的时间总和,记账号等等。 标识符:与进程相关的唯一标识符,用来区别正在执行的进程和其他进程。 优先级:如果有好几个进程正在执行,就涉及到进程被执行的先后顺序的问题,这和进程优先级这个标识符有关。 I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和进程使用的文件列表等。 volatile long states;
状态 常见的可能取值: TASK_RUNNING:表示进程正在执行或者处于准备执行的状态 TASK_INTERRUPTIBLE:进程因为等待某些条件处于阻塞(挂起的状态),一旦等待的条件成立,进程便会从该状态转化成就绪状态 TASK_UNINTERRUPTIBLE:意思与TASK_INTERRUPTIBLE类似,但是我们传递任意信号等不能唤醒他们,只有它所等待的资源可用的时候,他才会被唤醒。 TASK_STOPPED:进程被停止执行 TASK_TRACED:进程被debugger等进程所监视。 EXIT_ZOMBIE:进程的执行被终止,但是其父进程还没有使用wait()等系统调用来获知它的终止信息,此时进程成为僵尸进程 EXIT_DEAD:进程被杀死,即进程的最终状态。 TASK_KILLABLE:当进程处于这种可以终止的新睡眠状态中,它的运行原理类似于 TASK_UNINTERRUPTIBLE,只不过可以响应致命信号 unsigned long flags;
进程标志: *PF_ALIGNWARN 打印“对齐”警告信息。 *PF_PTRACED 被ptrace系统调用监控。 *PF_TRACESYS 正在跟踪。 *PF_FORKNOEXEC 进程刚创建,但还没执行。 *PF_SUPERPRIV 超级用户特权。 *PF_DUMPCORE dumped core。 *PF_SIGNALED 进程被信号(signal)杀出。 *PF_STARTING 进程正被创建。 *PF_EXITING 进程开始关闭。 *PF_USEDFPU 该进程使用FPU(SMP only)。 *PF_DTRACE delayed trace (used on m68k)。 进程优先级,CPU分配资源的先后顺序就是进程的优先权。优先权高的有优先执行的权利。使用ps -l命令可以查看系统进程的相关信息,我们重点会关注到以下几个信息: UID:代表执行者的身份 PID:代表这个进程的代号 PPID:代表该进程是由哪一个进程发展而来的,即其父进程代号 PRI:代表该进程的优先级,其值越小越先被执行 NI:代表该进程的nice值 NI:PRI值越小进程越先被执行,加入nice值后,PRI值就会被改变为
PRI(new) = PRI(old) + nice。nice取值的范围为[-20,19]。当nice值为负值时,进程的优先级值会减小,其对应的进程的优先级更高所以更先被执行。所以说,Linux下想要更改进程的优先级,只需要更改nice值即可。
修改优先级命令: 启动进程前调整:nice(例如:nice -n -2 ./test) 调整已存在进程nice:renice(例如:renice -3 -p 3267//PID为3267的进程的nice值设置为-3) 用top命令更改已存在进程的nice值:top进入以后输入r然后输入进程PID再输入nice值 上下文数据 (重要)   (1) struct desc_struct *ldt;
  进程关于CPU段式存储管理的局部描述符表的指针,用于仿真WINE Windows的程序。其他情况下取值NULL,进程的ldt就是arch/i386/traps.c定义的default_ldt。   (2) struct thread_struct tss;
  任务状态段,其内容与INTEL CPU的TSS对应,如各种通用寄存器.CPU调度时,当前运行进程的TSS保存到PCB的tss,新选中进程的tss内容复制到CPU的TSS。结构定义在include/linux/tasks.h中。   (3) unsigned long saved_kernel_stack;
  为MS-DOS的仿真程序(或叫系统调用vm86)保存的堆栈指针。
  
  (4) unsigned long kernel_stack_page;
  在内核态运行时,每个进程都有一个内核堆栈,其基地址就保存在kernel_stack_page中。
进程队列指针
  (1) struct task_struct *next_task,*prev_task;
  所有进程(以PCB的形式)组成一个双向链表。next_task和就是链表的前后指针。链表的头和尾都是init_task(即0号进程)。   (2) struct task_struct *next_run,*prev_run;
  由正在运行或是可以运行的,其进程状态均为TASK_RUNNING的进程所组成的一个双向循环链表,即run_queue就绪队列。该链表的前后向指针用next_run和prev_run,链表的头和尾都是init_task(即0号进程)。   (3) struct task_struct *p_opptr,*p_pptr;和struct task_struct *p_cptr,*p_ysptr,*p_osptr;
  以上分别是指向原始父进程(original parent)、父进程(parent)、子进程(youngest child)及新老兄弟进程(younger sibling,older sibling)的指针。
进程标识 pid_t pid;//进程的唯一标识 pid_t tgid;//线程组的领头线程的pid成员的值 在Linux系统中,一个线程组中的所有线程使用和该线程组的领头线程(该组中的第一个轻量级进程)相同的PID,并被存放在tgid成员中。只有线程组的领头线程的pid成员才会被设置为与tgid相同的值。注意,getpid()系统调用返回的是当前进程的tgid值而不是pid值。(线程是程序运行的最小单位,进程是程序运行的基本单位。)
进程调度 int prio, static_prio, normal_prio; unsigned int rt_priority; const struct sched_class *sched_class; struct sched_entity se; struct sched_rt_entity rt; unsigned int policy; static_prio用于保存静态优先级,可以通过nice系统调用来进行修改。
rt_priority用于保存实时优先级。
normal_prio的值取决于静态优先级和调度策略(进程的调度策略有:先来先服务,短作业优先、时间片轮转、高响应比优先等等的调度算法)
prio用于保存动态优先级。
policy表示进程的调度策略,目前主要有以下五种: #define SCHED_NORMAL 0//按照优先级进行调度(有些地方也说是CFS调度器) #define SCHED_FIFO 1//先进先出的调度算法 #define SCHED_RR 2//时间片轮转的调度算法 #define SCHED_BATCH 3//用于非交互的处理机消耗型的进程 #define SCHED_IDLE 5//系统负载很低时的调度算法 #define SCHED_RESET_ON_FORK 0x40000000 SCHED_NORMAL用于普通进程,通过CFS调度器实现;
SCHED_BATCH用于非交互的处理器消耗型进程;
SCHED_IDLE是在系统负载很低时使用;
SCHED_FIFO(先入先出调度算法)和SCHED_RR(轮流调度算法)都是实时调度策略. 附:
以下是对task_struct的定义及注释: 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; };