Linux中进程之task_struct结构体

2017-04-13 14:21 阅读 1,869 次 评论 0 条

一个程序被加载到内存当中运行,那么在内存内的那个数据就被称为进程(process)。进程是操作系统上非常重要的一个概念,所有系统上面跑的数据都会以进程的类型存在。

进程与程序的区别

程序(program):通常以二进制程序放置在存储媒介中(如硬盘、光盘、软盘、磁带等),以物理文件的形式存在。

进程(process):程序被触发后,执行者的权限与属性、程序的程序代码与所需数据都会被加载到内存中,操作系统并给予这个内存内的进程一个标识符(PID),可以说,进程就是一个正在运行中的程序。

当然,程序与进程之间是有剪不断理还乱的关系的:

①进程是程序的一次运行活动,属于一种动态的概念。而程序是一组有序的静态指令,是一种静态的概念。

②一个进程可以执行一个或多个程序。

③程序可以作为一种软件资源长期保存,而进程则是一次执行过程,它是暂时的,是动态的产生和终止的。

④进程需要一种机构才能执行程序,这种机构称之为处理机(Processor)。

Linux中进程控制块

每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块是task_struct结构体。

task_struct是Linux内核的一种数据结构,它被装在到RaM里并且包含着进程的信息。每个进程都把它的信息放在task_struct这个数据结构中,task_struct包含了以下内容:

标识符:描述本进程的唯一标识符,用来区别其他进程。

状态:任务状态,退出代码,退出信号等。

优先级:相对于其他进程的优先级。

程序计数器:程序中即将被执行的下一条指令的地址。

内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。

上下文数据:进程执行时处理器的寄存器中的数据。

I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。

记账信息:可以包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。

保存进程信息的数据结构叫做task_struct,并且可以在include/linux/sched.h里找到它。所以运行在系统里的进程都以task_struct链表的形式存在于内核中。

task_struct成员用法说明

1)进程状态

2)state成员的可能取值如下

系统中的进程必然处于上述进程状态的一种,下面就各状态做简要说明:

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

3)进程标识符(PID)

在CONFIG_BASE_SMALL配置为0的情况下,PID的取值是0~32767,即系统中的进程数最大为32767个。

进程标识符的引入是为了区别每个进程,tgid的引入是由于Unix程序员希望同一组线程具有相同的pid所以就引入了tgid。getpid()系统调用返回的是当前进程的tgid值而不是pid值。

4)进程内核栈

进程通过alloc_thread_info函数分配它的内核栈,通过free_thread_info函数释放所分配的内核栈。

其中,THREAD_SIZE_ORDER宏在linux-2.6.38.8/arch/arm/include/asm/thread_info.h文件中被定义为1,也就是说alloc_thread_info函数通过调用__get_free_pages函数分配2个页的内存(它的首地址是8192字节对齐的)。

Linux内核通过thread_union联合体来表示进程的内核栈,其中THREAD_SIZE宏的大小为8192。

当进程从用户态切换到内核态时,进程的内核栈总是空的,所以ARM的sp寄存器指向这个栈的顶端。因此,内核能够轻易地通过sp寄存器获得当前正在CPU上运行的进程。

5)进程标记符

flags反应进程的状态信息,用于内核识别当前进程的状态。

flags的取值范围如下:

鉴于状态量过多,只说明几个常用的状态。

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

6)表示进程亲属关系的成员

在Linux系统中,所有进程之间都有着直接或间接地联系,每个进程都有其父进程,也可能有零个或多个子进程。拥有同一父进程的所有进程具有兄弟关系。

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

7)ptrace系统调用

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

8)优先级

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

实时优先级和静态优先级的取值范围中,值越大,优先级越低。实时优先级范围是0到MAX_RT_PRIO-1(即99),而普通进程的静态优先级范围是从MAX_RT_PRIO到MAX_PRIO-1(即100到139)。

9)进程地址空间

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

成员 描述
mm 进程所拥有的内存空间描述符,对于内核线程的mm为NULL
active_mm 指进程运行时所使用的进程描述符
rss_stat 被用来记录缓冲信息

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

总之,Linux进程控制块task_struct结构体是非常重要的,操作系统对进程的调度依赖于进程控制块PCB, 进程状态的改变也基于进程控制块的作用。博客中部分参考了http://blog.csdn.net/bit_clearoff/article/details/54292300

版权声明:本文著作权归原作者所有,欢迎分享本文,谢谢支持!
转载请注明:Linux中进程之task_struct结构体 | 术与道的分享
分类:操作系统 标签:,
1024do.com导航_术与道导航平台

发表评论


表情