`
hongbochen1223
  • 浏览: 43987 次
文章分类
社区版块
存档分类
最新评论

进程管理(二)

 
阅读更多

进程描述符中包含的数据能完整地描述一个正在执行的程序:他打开的文件,进程的地址空间,挂起的信号,进程的状态等。

​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,&current->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){
    ​    ​//进行操作
    ​}    ​
<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函数终止。通常...

    实验二 进程管理.doc

    通过Linux系统中进程控制有关的系统调用的编程和调试,加深对于进程概念的理解,体验进程创建、撤销的过程和进程调度,掌握进程控制的方法。 加深对进程调度的理解,体验进程调度机制的功能,了解Linux系统中进程...

    进程管理实验二

    这是关于linux操作系统实验的文档,对大家学习linux很有帮助,希望能给大家带来帮助,祝你学习愉快。

    实验一操作系统Windows“任务管理器”的进程管理.doc

    实验一操作系统Windows“任务管理器”的进程管理.doc

    操作系统进程管理实验含源代码

    北邮 操作系统 进程管理 实验二 含源代码 linux下

    实验二、进程管理及进程通信(一)实验报告

    运行进程管理及进程通信(一)中给出的例子,查看自己运行的结果,并进行分析。 2、编写程序 (1)、要求程序运行时,系统中存在如下图(一)的进程树结构,写出程序源代码及程序运行结果并在进程树的相应进程节点上标...

    操作系统实验二Windows任务管理器的进程管理实用.pdf

    操作系统实验二Windows任务管理器的进程管理实用.pdf

    6、进程管理 – 看这一篇就够了

    目录六、进程管理1.程序和进程2、linux下的进程结构3、进程状态4、进程状态转换图5、init进程6、获取进程标识7、fork系统调用8、替换一个进程映像(exec)9、启动新的进程(system)10、wait和waipid函数11、exit和_...

    \学习\课件\计算机操作系统\第二章 进程管理

    \学习\课件\计算机操作系统\第二章 进程管理 \学习\课件\计算机操作系统\第二章 进程管理

    操作系统实验二Windows任务管理器的进程管理.doc

    操作系统实验二Windows任务管理器的进程管理.doc

    进程模拟管理(c++)

    一、目的:模拟进程管理实现进程的创建、撤销、封锁和唤醒功能。 二、功能描述 1 创建进程: 申请PCB表;填写PCB表;连接到就绪队列中 2 撤销功能:按给定进程的标识符从等待队列中撤销一个进程 3 封锁功能: 把当前...

    进程的管道通信 进程管理

    使用系统调用pipe()建立一个管道,两个子进程分别向管道写信息,父进程则从管道读出来自子进程的信息,显示在屏幕上,记录屏幕的显示结果,分析原因。 任务 编制一段程序,实现进程的管道通信。使用系统调用pipe()...

    哈工大 操作系统 实验二 进程管理

    哈工大 操作系统 实验二 进程管理

    实验2 Linux进程管理.pdf

    实验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=%...

    c/c++进程管理模拟程序

    (二)、实验内容 1.给出进程调度的算法描述(如基于先来先服务,短进程优先,动态优先级和时间片轮转调度算法的描述)。 2.用C语言设计一个对n个并发进程进行调度的程序,每个进程由一个进程控制块(PCB)结构...

    操作系统同步,进程管理,存储管理章节实验的实现

    该文档中包含操作系统三个实验的具体步骤和代码: 实验一 掌握进程同步与同步机制,了解Linux内核并发性和各种同步...了解x86的分段机制,知道什么是物理存储管理和进程虚拟存储管理。 统计系统和单个进程的缺页次数。

Global site tag (gtag.js) - Google Analytics