进程描述符中包含的数据能完整地描述一个正在执行的程序:他打开的文件,进程的地址空间,挂起的信号,进程的状态等。
1:分配进程描述符
linux通过使用slab分配器分配task_struct结构,这样能够达到对象复用和缓存着色的目的。现在只需在栈底或栈顶创建一个新的结构struct thread_info结构即可。
首先我们先看一下thread_info的结构:
struct thread_info {
struct task_struct *task;
struct exec_domain *exec_domain;
unsigned long flags;
unsigned long status;
__u32 cpu;
int preempt_count;
mm_segment_t addr_limit;
void *sysenter_return;
struct restart_block restart_block;
unsigned long previous_esp;
__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.
*/
/* in tsk->exit_state */
/* in tsk->state again */
这几行代码描述了一个进程的所有的状态。其中,在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)是等价的。
下面我们来看一下这里面一些函数的定义。
do { (tsk)->state = (state_value); } while (0)
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()
*/
do { current->state = (state_value); } while (0)
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);
}
3:获取init进程,init进程的进程描述符是作为init_task静态分配的。
struct task_struct *task;
for(task = current;task != &init_task;task = task->parent);
4:遍历进程的另一个方法
struct task_struct *task;
for_each_process(task){
}
<script type="text/javascript">
$(function () {
$('pre.prettyprint code').each(function () {
var lines = $(this).text().split('\n').length;
var $numbering = $('<ul/>').addClass('pre-numbering').hide();
$(this).addClass('has-numbering').parent().append($numbering);
for (i = 1; i <= lines; i++) {
$numbering.append($('<li/>').text(i));
};
$numbering.fadeIn(1700);
});
});
</script>
版权声明:本文为博主原创文章,未经博主允许不得转载。
分享到:
相关推荐
实验一:模拟进程管理一 目的:模拟进程管理实现进程的创建 撤销 封锁和唤醒功能 二 功能描述1 创建进程: 申请PCB表;填写PCB表;连接到就绪队列中2 撤销功能:按给定进程的标识符从等待队列中撤销一个进程3 封锁...
实验二 进程管理 Windows所创建的每个进程都从调用CreateProcess() API函数开始,该函数的任务是在对象管理器子系统内初始化进程对象。每一进程都以调用ExitProcess() 或TerminateProcess() API函数终止。通常...
通过Linux系统中进程控制有关的系统调用的编程和调试,加深对于进程概念的理解,体验进程创建、撤销的过程和进程调度,掌握进程控制的方法。 加深对进程调度的理解,体验进程调度机制的功能,了解Linux系统中进程...
这是关于linux操作系统实验的文档,对大家学习linux很有帮助,希望能给大家带来帮助,祝你学习愉快。
实验一操作系统Windows“任务管理器”的进程管理.doc
北邮 操作系统 进程管理 实验二 含源代码 linux下
运行进程管理及进程通信(一)中给出的例子,查看自己运行的结果,并进行分析。 2、编写程序 (1)、要求程序运行时,系统中存在如下图(一)的进程树结构,写出程序源代码及程序运行结果并在进程树的相应进程节点上标...
操作系统实验二Windows任务管理器的进程管理实用.pdf
目录六、进程管理1.程序和进程2、linux下的进程结构3、进程状态4、进程状态转换图5、init进程6、获取进程标识7、fork系统调用8、替换一个进程映像(exec)9、启动新的进程(system)10、wait和waipid函数11、exit和_...
\学习\课件\计算机操作系统\第二章 进程管理 \学习\课件\计算机操作系统\第二章 进程管理
操作系统实验二Windows任务管理器的进程管理.doc
一、目的:模拟进程管理实现进程的创建、撤销、封锁和唤醒功能。 二、功能描述 1 创建进程: 申请PCB表;填写PCB表;连接到就绪队列中 2 撤销功能:按给定进程的标识符从等待队列中撤销一个进程 3 封锁功能: 把当前...
使用系统调用pipe()建立一个管道,两个子进程分别向管道写信息,父进程则从管道读出来自子进程的信息,显示在屏幕上,记录屏幕的显示结果,分析原因。 任务 编制一段程序,实现进程的管道通信。使用系统调用pipe()...
哈工大 操作系统 实验二 进程管理
实验2 Linux进程管理.pdf
非图像界面 建立一个交互式命令接口或图形接口的小型系统。...进程控制块内容包括进程标识符、主要寄存器内容、进程状态、等待原因、进程页表位置、中间结果等等(为以后扩充系统)。模拟系统最多容纳10个进程块。
该系统还添加了单步运行的功能,可以更加直观的观看进程调度的过程。 支持3中调度算法:1.时间片轮转 2.静态优先级 3.动态优先级(比较简单) 注:在该项目中需要把读取txt代码的路径改为项目目录下。
运行进程管理及进程通信(一)中给出的例子,查看自己运行的结果,并进行分析。 程序1 #include "stdio.h" main() { int i,j,k; if (i=fork()) { j=wait(); printf("Parent Process!\n"); printf("i=%d,j=%d,k=%...
(二)、实验内容 1.给出进程调度的算法描述(如基于先来先服务,短进程优先,动态优先级和时间片轮转调度算法的描述)。 2.用C语言设计一个对n个并发进程进行调度的程序,每个进程由一个进程控制块(PCB)结构...
该文档中包含操作系统三个实验的具体步骤和代码: 实验一 掌握进程同步与同步机制,了解Linux内核并发性和各种同步...了解x86的分段机制,知道什么是物理存储管理和进程虚拟存储管理。 统计系统和单个进程的缺页次数。