进程管理(二)
进程描述符中包含的数据能完整地描述一个正在执行的程序:他打开的文件,进程的地址空间,挂起的信号,进程的状态等。
?1:分配进程描述符
linux通过使用slab分配器分配task_struct结构,这样能够达到对象复用和缓存着色的目的。现在只需在栈底或栈顶创建一个新的结构struct thread_info结构即可。
首先我们先看一下thread_info的结构:
struct thread_info {
//所在的进程结构体
struct task_struct *task; /* main task structure */
//执行域
struct exec_domain *exec_domain; /* execution domain */
//底层标志
unsigned long flags; /* low level flags */
//线程同步标志
unsigned long status; /* thread-synchronous flags */
//当前CPU
__u32 cpu; /* current CPU */
//0 : 表示可抢占,小于0 : bug
int preempt_count; /* 0 => preemptable, <0 => BUG */
/*
* 进程地址空间
* 0-0xBFFFFFFF 用于用户进程
* 0-0xFFFFFFFF 用于内核进程
*/
mm_segment_t addr_limit; /* thread address space:
0-0xBFFFFFFF for user-thead
0-0xFFFFFFFF for kernel-thread
*/
void *sysenter_return;
struct restart_block restart_block;
unsigned long previous_esp; /* ESP of the previous stack in case
of nested (IRQ) stacks
*/
__u8 supervisor_stack[0]; //放入栈顶
};
通过该结构就可以看出,主要知道thread_info结构体,就能获取需要的进程。
每个任务的thread_info结构在他的内核栈的尾端分配。
?2:进程描述符的存放
内核通过一个唯一的进程标识值或PID来标识每个进程。该值有一个上限,存放在/proc/sys/kernel/pid_max中。我们可以来看一下:
我们可以修改这个值,来改变系统中进程的最大数量。
下面我们来看一下current_thread_info()函数的使用。
获取当前进程的pid我们可以这样使用:
#include <asm/thread_info.h>
//....
int pid = current_thread_info()->task->pid;
//....
3:进程状态
下面请看一下,我们的linux内核代码:
/*
* 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_ZOMBIE 16
#define EXIT_DEAD 32
/* in tsk->state again */
#define TASK_NONINTERACTIVE 64
这几行代码描述了一个进程的所有的状态。其中,在task_struct结构体中,保存进程状态的属性为state域。总的来说,进程一共有5中状态,下面我们整理一下。
TASK_RUNNING - 进程是可执行的。表示进程正在执行,或者在任务队列中等待执行。在用户空间中,这是进程的唯一可能的状态,在内核空间也是可以有这个状态的。
TASK_INTERRUPTIBLE - 进程可中断。进程正在睡眠,等待某个事件的达成。该事件一旦达成,进程状态就会变成可运行状态。当然,该状态还可以由于某个信号而提前唤醒运行。
TASK_UNINTERRUPTIBLE - 进程不可中断。该状态对信号不做反应。他等待特定的事件发生,别的事件信号不能将它唤醒.
TASK_TRACED - 被其他进程跟踪的进程。例如通过ptrace对调试程序进行跟踪。
TASK_STOPPED - 进程停止执行。
下面这个图是进程状态的转换。
4:设置当前进程状态
内核在运行过程中,需要经常调整某个进程的状态,下面我们有一下几个方法来设置进程的状态
?1:set_task_state(task,state); /* 将任务task的状态设置为state
?2:如果没有内存屏障的话,也可以使用下面的方法来设置进程状态
? ?task->state = state;
其中,函数set_current_state(state)和set_task_state(current,state)是等价的。
下面我们来看一下这里面一些函数的定义。
#define __set_task_state(tsk, state_value)
do { (tsk)->state = (state_value); } while (0)
#define set_task_state(tsk, state_value)
set_mb((tsk)->state, (state_value))
/*
* set_current_state() includes a barrier so that the write of current->state
* is correctly serialised wrt the caller‘s subsequent test of whether to
* actually sleep:
*
* set_current_state(TASK_UNINTERRUPTIBLE);
* if (do_i_need_to_sleep())
* schedule();
*
* If the caller does not need such serialisation then use __set_current_state()
*/
#define __set_current_state(state_value)
do { current->state = (state_value); } while (0)
#define set_current_state(state_value)
set_mb(current->state, (state_value))
在其中就会牵扯到SMP的内存屏障的问题了,在这里先不讨论。
在上面的代码当中,我们发现,在宏定义中,使用了一个do{}while()的结构,在linux内核源码中,有很多这样的结构,下面我们就来分析一下使用这个结构的好处。
假设我们有这样一个例子:
#define fun(x) hello1(x);hello2(x)
如果我们有这样一段代码
if(x)
? ?fun(x);
这样的话,替换回来之后,我们就变成了这个样子:
if(x)
hello1(x);
hello2(x);
这样就失去了我们本来的目的。
如果我们使用{}括起宏定义。这样来定义:
#define fun(x) {hello1(x);hello2(x);}
如果我们的代码是这样子的:
if(x)
fun(x);
else
test(x);
则我们替换之后,就变成这个样子的:
if(x){
hello1(x);
hello2(x);
};
else
test(x);
这样也是错误的,所以,我们使用do{}while()结构函数很有用的。
5:进程家族树
在linux中,所有的进程都是pid为1的init进程的后代。内核在系统启动的最后阶段启动init进程。系统中的每个进程都有一个父进程,相应的,每一个进程都有零个或多个子进程。拥有同一个父进程的所有进程被成为兄弟进程。同样,进程之间的关系也是存放在进程描述符中。每一个进程都有一个父进程的指针和一个子进程的链表。
在进程描述符中代码如下:
struct task_struct *parent; /* parent process */
/*
* children/sibling forms the list of my children plus the
* tasks I‘m ptracing.
*/
struct list_head children; /* list of my children */
1:获取当前进程的父进程
? ?struct task_struct *myparent = current->parent;
2:遍历子进程
?struct task_struct *task;
?struct list_head *list;
?
?list_for_each(list,¤t->children){
? ?task = list_entry(list,struct task_struct,slibinig);
? ?//task指向当前的某个子进程
?}
?3:获取init进程,init进程的进程描述符是作为init_task静态分配的。
?struct task_struct *task;
?for(task = current;task != &init_task;task = task->parent);
?/*指向init进程 */
4:遍历进程的另一个方法
?struct task_struct *task;
?for_each_process(task){
? ?//进行操作
?} ?
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。