linux中的PCB---task_struct

2019-07-14 06:34发布

   说起PCB相信学过操作系统的童鞋都知道这是进程控制块是用来描述进程基本信息的数据结构,今天我就从linux下的task_struct这个类似PCB的概念来理解什仫是PCB?以及PCB存在什仫样的基本信息?     要了解PCB,首先需要了解什仫是进程? 一.进程的概念  在之前学过的操作系统课本上是这样描述进程的,进程是程序的一次动态执行的过程,进程=程序+数据+PCB(这样官方的描述真的可以让我们理解什仫是进程吗?反正我不太理解!!!但是学习的时候为了考试也就是记住就好了),现在重新学习linux下的操作系统却让我有了不一样的认识。     在OS操作系统的观点下:进程是程序的一个执行实例;是正在执行的程序;能够分配处理器并由处理器执行的实体;在内核的观点下:担当分配系统资源(CPU时间,内存)的实体.  总结:      进程是加载到物理内存中的程序,操作系统给该应用程序创建一系列数据结构PCB,是动态的文件,是被内存管理起来通过页表映射到物理内存中的数据结构. 二.进程控制块---PCB  进程控制块是描述进程的基本信息的数据结构,那仫为什仫需要进程控制块呢?一个进程被创建的标志是什仫?在单批道处理器中我们每次只能处理一个进程,我们是如何知道正在使用CPU的是哪个进程?当该进程执行完毕之后我们是如何选择哪个进程将被执行?假如一个正在被执行的进程由于时间片或者挂起等操作被迫让出CPU的使用权当该未执行完的进程下一次被CPU调度的时候如何知道执行到什仫位置?。。。此时就需要进程控制块来解决这些问题了.      为了处理以上这些问题,PCB至少应该存在以下几种信息:进程标识(是进程的唯一标识,PID);进程的优先级(决定哪个进程将被处理机调度);记录进程的上下文信息(当再次被处理机调度的时候可快速恢复);程序和数据的地址等等各种信息,下文将更加详细的介绍task_stcuct的数据结构.      总之,在进程的整个生命周期中进程控制块都起着不可或缺的作用,下面就让我们来理解task_struct的基本信息  三.linux中的PCB---task_struct      每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块是task_struct结构体,task_struct是Linux内核的一种数据结构,它会被装载到RAM里并且包含着进程的信息      每个进程都把它的信息放在 task_struct 这个数据结构里,task_struct 包含了以下内容:        1).标示符 : 描述本进程的唯一标示符,用来区别其他进程。 2).状态 :任务状态,退出代码,退出信号等。 3).优先级 :相对于其他进程的优先级。 4).程序计数器:程序中即将被执行的下一条指令的地址。 5).内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针 6).硬件上下文数据:进程执行时处理器的寄存器中的数据。 7).I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。 8).记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
   四.剖析task_struct      task_struct的功能类似PCB,可以在include/linux/sched.h中找到它,下面直接附上该头文件的链接:      http://lxr.free-electrons.com/source/include/linux/sched.h       task_struct的实现可以参考以上的源码连接,下面介绍一下task_struct的基本成员吧!!! 1.进程状态         volatile long state;/* -1 unrunnable, 0 runnable, >0 stopped */
     上面这个变量就是描述进程状态的成员,volatile关键字是降低编译器对代码的优化,使得state变量从内存而不是寄存器中读取数据;保证了对操作系统状态实时访问的稳定性.      state成员的可能取值如下:       /* * Task state bitmask. NOTE! These bits are also * encoded in fs/proc/array.c: get_task_state(). * * We have two separate sets of flags: task->state * is about runnability, while task->exit_state are * about the task exiting. Confusing, but this way * modifying one set can't modify the other one by * mistake. */ #define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define TASK_STOPPED 4 #define TASK_TRACED 8 /* in tsk->exit_state */ #define EXIT_DEAD 16 #define EXIT_ZOMBIE 32 #define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD) /* in tsk->state again */ #define TASK_DEAD 64 #define TASK_WAKEKILL 128 /** wake on signals that are deadly **/ #define TASK_WAKING 256 #define TASK_PARKED 512 #define TASK_NOLOAD 1024 #define TASK_STATE_MAX 2048 /* Convenience macros for the sake of set_task_state */ #define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE) #define TASK_STOPPED (TASK_WAKEKILL | __TASK_STOPPED) #define TASK_TRACED (TASK_WAKEKILL | __TASK_TRACED)



 2.进程标识符           pid_t pid; //进程的标识符 pid_t tgid; //线程组标识符 pid_t ppid //父进程的标识符,在linux下一般为bash


    当然task_struct中还存在其他的标识符,在这里只列举常见的.     进程标识符是为了区分各个进程,是唯一的标识;由于Unix程序员希望同一组进程具有相同的pid所以引入tgid 3.进程标记符        unsigned int flags; /* per process flags, defined below */
      flags反应进程的状态信息,用于内核识别当前进程的状态,它的取值范围如下:        /* * Per process 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 */
        4.表示进程亲属关系的成员         /* * pointers to (original) parent process, youngest child, younger sibling, * older sibling, respectively. (p->father can be replaced with * p->real_parent->pid) */ struct task_struct __rcu *real_parent; /* real parent process */ struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */ /* * children/sibling forms the list of my natural children */ 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 */
       
5.ptrace系统调用         ptrace提供了一种父进程,用来控制子进程的运行,常被用于断点调试,当被设置为0时则不需要追踪.          /* * Ptrace flags * * The owner ship rules for task->ptrace which holds the ptrace * flags is simple. When a task is running it owns it's task->ptrace * flags. When the a task is stopped the ptracer owns task->ptrace. */ #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<


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

7.进程的地址空间          struct mm_struct *mm, *active_mm; /* per-thread vma caching */ u32 vmacache_seqnum; struct vm_area_struct *vmacache[VMACACHE_SIZE]; #if defined(SPLIT_RSS_COUNTING) struct task_rss_stat rss_stat; #endif /* http://lxr.free-electrons.com/source/include/linux/sched.h?V=4.5#L1484 */ #ifdef CONFIG_COMPAT_BRK unsigned brk_randomized:1; #endif 成员 描述信息 mm 进程所拥有的内存空间描述符,对于内核线程的mm为NULL active_mm 指进程运行时所使用的进程描述符 rss_stat 被用来记录缓冲信息 如果当前内核线程被调度之前运行的也是另外一个内核线程时候,那么其mm和avtive_mm都是NULL
          总结:对于linux中的task_struct只介绍这些,有兴趣的童鞋可以自己参考上面的给出的源码连接进行剖析。。。总之PCB的创建代表着进程的创建,同时PCB的撤销也意味着进程生命周期的结束.