Linux2.6.32内核笔记(3)进程管理子系统

    一、进程与程序的区别

    程序是一段存放在存储介质上的一系列代码和数据的映像,是一个静态的实体。

    进程是执行起来的程序,是一个动态地实体,还包括它管理的资源(如打开的文件,挂起的信号,地址空间等等)。

 

    二、进程四要素

    1.有一段程序供其执行,该程序不一定是一个进程独享,也可以和其他进程共享。

    2.有进程专用的内核空间堆栈

    3.在内核中有一个名为“进程控制块”的task_struct,内核通过结构对进程进行调度控制。

    4.有独立的用户空间。有独立的用户空间的是进程,有共享的用户空间的是用户线程,没有用户空间的是内核线程。

   

    三、进程状态

    进程有三态:就绪太,执行态和阻塞态。当进程被创建以后,就处于一个就绪太,等待cpu的调度,当cpu执行它的时候就处于执行态,在执行过程中进程比如访问别的资源,获取锁,信号量不成功,或者产生了中断,进程就会处于阻塞状态,当中断结束,得到锁和信号量之后进程就又从阻塞态变为就绪态,等待调度执行。

    比较重要的进程状态有以下三种:

    TASK_RUNNING(可执行状态)

    只有在该状态的进程才可能在CPU上运行。而同一时刻可能有多个进程处于可执行状态,这些进程的task_struct结构(进程控制块)被放入对应CPU的可执行队列中(一个进程最多只能出现在一个CPU的可执行队列中)。进程调度器的任务就是从各个CPU的可执行队列中分别选择一个进程在该CPU上运行。

    TASK_INTERRUPTIBLE(可中断睡眠状态)

    处于这个状态的进程因为等待某某事件的发生(比如等待socket连接、等待信号量),而被挂起。这些进程的task_struct结构被放入对应事件的等待队列中。当这些事件发生时(由外部中断触发、或由其他进程触发),对应的等待队列中的一个或多个进程将被唤醒。事实上,我们的很多进程大多时间都在这个状态。。。

    TASK_UNINTERRUPTIBLE/TASK_KILLABLE(不可中断睡眠状态)

    与TASK_INTERRUPTIBLE状态类似,进程处于睡眠状态,但是此刻进程是不可中断的,即使给他sigkill也不会中断,不会响应异步信号。这个状态有个缺点,就是假如这个进程一直处于这种状态,那么它就会占用cpu资源,使得其它进程一直处于等待状态,但是引入这个状态时为了对一些内核操作进行保护,比如与硬件设备进行交互的时候,为了避免此短暂的交互过程被打断,需要引入这个状态。为了对这个状态进行改进,linux2.6.25之后引入了         TASK_KILLABLE该状态和TASK_UNINTERRUPTIBLE是类似的,但是它可以被致命信号SIGKILL唤醒。

    其他的进程状态包括:

    TASK_TRACED:处于被调试状态的进程。

    TASK_DEAD:进程退出时所处的状态。

    EXIT_ZOMBIE:表示进程的执行被终止,但是其父进程还没有使用wait()等系统调用来获知它的终止信。

   

    四、Linux进程描述

    Linux进程描述主要是是task_struct这个结构体,在/include/linux下的sched.h文件中,在第1215行:

    1.状态描述:

    volatile long state;

    int exit_state;

    状态有哪些我们上面已经分析过了。

   

    2.进程标识符:

    pid_tpid;

    pid_ttgid;

    这个pid就是每个进程都有一个标号,系统中有最大32768个进程,给每一个进程都分配一各进程号便于管理,Linux将一个线程组中的所有线程使用和该线程组的领头线程(该组中的第一个轻量级进程)相同的PID存放在tgid成员中。

   

   3.内核堆栈:

    void*stack;

    进程通过alloc_thread_info函数分配它的内核栈,通过free_thread_info函数释放所分配的内核栈,这里不做深究。

 

   4.进程亲属关系:

    structtask_struct *real_parent; /* real parent process*/

    structtask_struct *parent; /* recipient of SIGCHLD,wait4() reports */

    /*

     * children/sibling forms the list of mynatural children

     */

    structlist_head children;  /* list of my children */

    structlist_head sibling;   /* linkage in my parent's children list */

    structtask_struct *group_leader;  /* threadgroup leader */

    看到这个命名有点想笑,还有真爸爸和爸爸的区分(进程也会喜当爹吗?)

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

   

    5.进程调度优先级:

    intprio, static_prio, normal_prio;

    unsignedint rt_priority;

    conststruct sched_class *sched_class;

    structsched_entity se;

    structsched_rt_entity rt;

   

    优先级定义在这里:

    #defineMAX_USER_RT_PRIO     100

    #defineMAX_RT_PRIO      MAX_USER_RT_PRIO

    #defineMAX_PRIO     (MAX_RT_PRIO+ 40)

    #defineDEFAULT_PRIO     (MAX_RT_PRIO + 20)

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

   

    五、Linux进程调度

    1.调度策略

    在sched.h中调度策略有以下几种:

    #defineSCHED_NORMAL     0

    #defineSCHED_FIFO       1

    #defineSCHED_RR     2

    #defineSCHED_BATCH      3

    /* SCHED_ISO:reserved but not implemented yet */

    #defineSCHED_IDLE       5

    /* Canbe ORed in to make sure the process is reverted back to SCHED_NORMAL on fork */

    #defineSCHED_RESET_ON_FORK     0x40000000

    SCHED_NORMAL用于普通进程,通过CFS调度器实现。

    SCHED_BATCH用于非交互的处理器消耗型进程。

    SCHED_IDLE是在系统负载很低时使用。

    SCHED_FIFO:先入先出调度算法。

    SCHED_RR:时间片轮流调度算法。

    其中SCHED_FIFO和SCHED_RR是实时调度算法,具体算法思想比较简单,顾名思义,具体实现这里不做深究。

 

    2.调度时机

    主动式

    当进程等待资源停止运行的时候,会处于睡眠状态,这时候直接调用schedule()请求调度,让出cpu。

    例:

    current->state= TASK_INTERRUPTIBLE

    schedule();

    使用指向当前进程状态的指针,将state改为可中断睡眠状态,然后调用schedule(),这样cpu就会调度其他资源执行,当然这个过程比较复杂,因为还涉及到当前状态的保存,进程资源是否回收等等。

    抢占式调度:

    首先,抢占的含义,当我们一个进程A在执行的时候,B进程在执行一项更加重要的任务,这时候就需要把cpu的资源让给B,如果A不能像上面一样主动地让出,那么B就去抢占cpu的资源。Linux2.4只支持用户态抢占,2.6既支持用户态抢占,也支持内核态抢占,这是2.6实时性提高的一个重要原因。当然也有非抢占机制,它的优势是中断响应很快,几乎不需要信号量来保护共享数据,但是明显的缺点就是实时性较差。2.6为什么要支持内核态抢占呢?因为有些进程或者线程一旦运行到内核态,就会一直运行,不出来,那它就会一直占有cpu的资源,其他紧急的进程或者线程就会处于等待状态,这样实时性就大大降低了。所以2.6开始允许优先级更高的进程在内核态中进行抢占。打个比方,贪官携款潜逃去从中国去了美国(进程A占有cpu资源从用户态进去了内核态),他就一直待在美国,没有引渡条例啊,这时候纪检的人很着急的要找他问话,怎么办呢?等呗。。。万一人家一辈子不回来了呢?有些事就不能处理了。所以现在国家正在积极的和欧美商讨引渡条例,也就是我们的2.6支持内核态抢占啦。

    用户态抢占发生的时机

    1.从系统调用返回用户空间。

    2.从中断处理程序返回用户空间。

    3.当某个进程耗尽它的时间片的时候。

    4.当一个优先级更高的进程处于可执行状态的时候。

    以上抢占情况发生时,还要满足need_resched被设置这个条件,这是一个触发条件。

    内核态抢占发生的时机:

    1.中断处理程序完成,返回内核空间之前。(贪官从美国(内核态)跑回中国来(用户态)来看爹妈老婆,返回去之前,被抓了(抢占)。。。)

    2.当内核代码解锁和使用软中断的时候,这时候再次具有课抢占性。

    不允许内核抢占时机

    1.内核正在运行中断处理程序

    在Linux内核中进程不能抢占中断(中断只能被其他中断中止、抢占,进程不能中止、抢占中断),在中断例程中不允许进行进程调度。进程调度函数schedule()会对此作出判断,如果是在中断中调用,会打印出错信息。

    2.内核正在进行中断上下文处理

    硬件中断返回前会执行软中断,此时仍然处于中断上下文中。

    3.持有锁的时候不应被抢占,如自旋锁,读写锁

    内核中的这些锁是为了在SMP系统中短时间内保证不同CPU上运行的进程并发执行的正确性。当持有这些锁时,内核不应该被抢占,否则由于抢占将导致其他CPU长期不能获得锁而死等。

    4.内核正在执行调度程序schedule的时候。

    抢占的原因就是为了进行新的调度,没有理由将调度程序抢占掉再运行调度程序。

    5.内核正在对每个CPU“私有”的数据结构操作。

    在SMP中,对于per-CPU数据结构未用spinlocks保护,因为这些数据结构隐含地被保护了(不同的CPU有不一样的per-CPU数据,其他CPU上运行的进程不会用到另一个CPU的per-CPU数据)。但是如果允许抢占,但一个进程被抢占后重新调度,有可能调度到其他的CPU上去,这时定义的Per-CPU变量就会有问题,这时应禁抢占。当然这里对于我们的s3c2440是不存在的。

    那么linux为了不让上述情况被抢占,设置了preempt_count抢占计数,被设置在进程的thread_info结构中。preempt_count()函数用于获取preempt_count的值,preemptible()用于判断内核是否可抢占。当进程进入上述状态时,就+1,退出时就-1,根据它的值来判断是否可抢占。

   

   3.调度步骤

    第一步:清理当前运行中的进程的一些资源。

    第二步:根据调度策略选择一个运行的进程。

    第三步:设置新的进程运行环境,例如堆栈,sp等。

    第四步:进程上下文切换,退出A,切到B。

 

    笔记做到这里,如有不正确的地方还请指出,大家共同进步。

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 1 进程的组织 5 1.1 进程相关数据结构 5 1.1.1 进程的基本信息 6 1.1.2 进程状态 10 1.1.3 TASK_RUNNING状态的进程链表 11 1.1.4 进程间关系 12 1.2 Linux的线程——轻量级进程 15 1.3 进程的创建——do_fork()函数详解 19 1.4 执行进程间切换 33 1.4.1 进程切换之前的工作 33 1.4.2 进程切换实务 —— switch_to宏 37 1.4.3 __switch_to函数 39 1.5 fork与vfock系统调用的区别 42 1.6 内核线程 46 1.7 挂起状态进程的组织 49 1.7.1 等待队列头 49 1.7.2 等待队列的操作 50 1.7.3 进程资源限制 55 1.8 系统调用execve() 56 1.8.1 拷贝用户态参数 57 1.8.2 重要的数据结构 61 1.8.3 search_binary_handler函数 66 1.8.4 目标文件的装载和投入运行 69 1.8.5 库函数 92 2 中断控制 94 2.1 中断的分类 94 2.2 中断的硬件环境 95 2.2.1 外部中断请求IRQ 95 2.2.2 中断描述符表 96 2.2.3 中断和异常的硬件处理 97 2.3 中断描述符表 99 2.3.1 中断门、陷阱门及系统门 99 2.3.2 IDT的初步初始化 100 2.4 异常处理 101 2.5 中断处理 106 2.5.1 中断向量 107 2.5.2 IRQ数据结构 108 2.5.3 do_IRQ()函数 113 2.5.4 中断服务例程 115 2.5.5 IRQ线的动态分配 116 2.6 下半部分 117 2.6.1 软中断 118 2.6.2 tasklet 121 2.6.3 工作队列 122 2.7定时器中断 124 2.7.1 时钟与定时器 124 2.7.2 定时器中断相关的数据结构 127 2.7.3 定时器中断的上半部分 129 3 进程调度 138 3.1 进程调度的概念 138 3.2 进程调度的数据结构和优先级 141 3.2.1 进程的优先级 141 3.2.2 数据结构 145 3.3 调度程序所使用的函数 151 3.3.1 scheduler_tick函数 151 3.3.2 try_to_wake_up函数 156 3.3.3 recalc_task_prio函数 160 3.4 schedule()函数 163 3.4.1 直接调用 163 3.4.2 延迟调用 164 3.4.3 进程切换之前所做的工作 168 3.4.4 完成进程切换时所执行的操作 171 3.4.5 进程切换后所执行的操作 173 3.5 多处理器运行队列的平衡 175 3.5.1 调度域 176 3.5.2 rebalance_tick()函数 178 3.5.3 load_balance()函数 180 3.5.4 move_tasks()函数 183 3.6 进程退出 187 3.6.1 进程终止 187 3.6.2 进程删除 189 4 进程的并发性体现 191 4.1 内核抢占 193 4.1.1 内核抢占概念 193 4.1.2 同步技术总揽 196 4.2 每CPU变量 197 4.3 原子操作 199 4.4 优化屏障和内存壁垒 203 4.4.1 优化屏障 204 4.4.2 内存壁垒 204 4.5 自旋锁 206 4.6 读写自旋锁 211 4.6.1 为读获取和释放一个锁 213 4.6.2 为写获取或释放一个锁 214 4.7 顺序锁 215 4.8 RCU机制 217 4.9 信号量 219 4.9.1 获取和释放信号量 221 4.9.2 读/写信号量 224 4.9.3 补充信号量 225 4.10 禁止本地中断 226 4.10.1 禁止本地中断 227 4.10.2 禁止下半部(可延迟函数) 229 4.11 一些避免竞争条件的实例 231 4.11.1 引用计数器 231 4.11.2 大内核锁 231 4.11.3 内存描述符读/写信号量 232 4.11.4 slab高速缓存链表的信号量 233 4.11.5 索引节点的信号量 233 4.12 内核同步与互斥的总结 233
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值