基于linux操做系统深刻源码进程模型分析

1.进程css

1.1进程的概念node

  计算机上全部可运行的软件,一般也包括操做系统,被组织成若干顺序进程(sequential process),简称进程(process)。linux

  一个进程就是一个正在执行程序的实例,包括程序设计器、寄存器和变量的当前值。一个进程是某种类型的一种活动,它有程序、输入、输出以及状态。单个处理器能够被若干进程共享,它使用某种调度算法决定什么时候中止一个进程的工做,并转而为了另外一个进程服务功能。算法

1.2进程的特征数组

动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
并发性:任何进程均可以同其余进程一块儿并发执行
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;
异步性:因为进程间的相互制约,使进程具备执行的间断性,即进程按各自独立的、不可预知的速度向前推动
结构特征:进程由程序、数据和进程控制块三部分组成。
多个不一样的进程能够包含相同的程序:一个程序在不一样的数据集里就构成不一样的进程,能获得不一样的结果;可是执行过程当中,程序不能发生改变。
 
2.进程的组织
2.1进程的控制块(PCB)

  Linux系统中主要的活动实体就是进程。服务器

  每一个进程执行一段独立的程序而且在进程初始化的时候拥有一个独立的控制线程。换句话说,每个进程都拥有一个独立的程序计数器,用这个这个程序计数器能够追踪下一条将要被执行的指令。session

  全部的进程都被放在一个叫作进程控制块(PCB),的数据结构中,能够理解为进程属性的集合,该控制块由操做系统建立和管理。每一个进程在内核中都有一个进程控制块来维护进程相关的信息,Linux内核的进程控制块是(task_struct)结构体。数据结构

2.2进程的标识符(PID)多线程

pid_t pid;      //内核中用以标识进程的id
pid_t tgid;    //用来实现线程机制
struct pid
{
    atomic_t count;
    unsigned int level;

    /* lists of tasks that use this pid */
    struct hlist_head tasks[PIDTYPE_MAX];
    struct rcu_head rcu;
    struct upid numbers[1];
};

每一个进程都有一个惟一的标识符(PID),内核经过这个标识符来识别不一样的进程,同时,进程标识符(PID)也是内核提供给用户程序的接口,用户程序经过PID对进程发号施令。并发

  PID是32位的无符号整数,它被顺序编号:新建立进程的PID一般是前一个进程的PID加1。然而,为了与16位硬件平台的传统Linux系统保持兼容,在Linux上容许的最大PID号是32767,当内核在系统中建立第32768个进程时,就必须从新开始使用已闲置的PID号。在64位系统中,PID可扩展到4194303。

2.3Linux系统组织进程

  • Linux经过task_struct结构体来描述一个进程的全部信息,结构体被定义在 include/linux/sched.h中。
  • task_struct结构体便是PCB。PCB是进程的惟一标识,PCB由链表实现(为了动态插入和删除)。进程建立时,为该进程生成一个PCB;进程终止时,回收PCB。
  • 部分代码:
    struct task_struct {     
            volatile long state;    //进程状态  
            void *stack;            //内存指针  
            atomic_t usage;                  
            unsigned int flags;    //进程标号(进程名字)  
            unsigned int ptrace;                                        
      
            int lock_depth;        //BLK 锁深度  
      
    #ifdef CONFIG_SMP  
    #ifdef __ARCH_WANT_UNLOCKED_CTXSW            //配置多核多线程   
            int oncpu;  
    #endif  
    #endif  
      
            int prio, static_prio, normal_prio;  //进程的优先级  
            unsigned int rt_priority;            //实时进程的优先级  
            const struct sched_class *sched_class;   //调度器的指针  
            struct sched_entity se;              //调度器 实例化的对象  
            struct sched_rt_entity rt;          //实时 调度器的一个对象  
      
    #ifdef CONFIG_PREEMPT_NOTIFIERS            //配置抢占通知器   
            /* struct preempt_notifier列表 */  
            struct hlist_head preempt_notifiers;     
    #endif  
      
            /*fpu_count 里面内容是若是一个浮点运算器被使用,它记录着连续的上下文切换的次数,若是fpu_Count超过一个 
             临界值,不怎么工做的FPU会火力全开以致于当fpu_count超过 256次后才变得闲置下来,为了解决这个问题,FPU 
             仅仅使用一段时间 */                                 
            unsigned char fpu_counter;  //定义 fpu_count   
    #ifdef CONFIG_BLK_DEV_IO_TRACE      //配置 BLK 锁开发版的输入输出跟踪器  
           unsigned int btrace_seq;           
    #endif  
      
            unsigned int policy;        
            cpumask_t cpus_allowed;  
      
    #ifdef CONFIG_TREE_PREEMPT_RCU     //配置抢占树,抢占的结构体的读写机制,即RCU机制。  
            int rcu_read_lock_nesting;  
            char rcu_read_unlock_special;  
            struct rcu_node *rcu_blocked_node;  
            struct list_head rcu_node_entry;  
    #endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */  
      
    #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)  
            struct sched_info sched_info;          //调度器的状态   
    #endif  
      
            struct list_head tasks;  
            struct plist_node pushable_tasks;  
      
            struct mm_struct *mm, *active_mm;     //虚拟地址空间的结构体   
      
                                     //进程退出时getpid 就获取status就是它。  
            int exit_state;                //task 状态 ,正常退出状态   
            int exit_code, exit_signal;   //退出信号  
            int pdeath_signal;    //当成为孤儿进程时发送信号   
    
            unsigned int personality;   //代表进程的状态     
            unsigned did_exec:1;  
            unsigned in_execve:1; //第一个表已经调过了exec族函数,已经发生了进程的程序替换 第二个表明该进程正在调用execve函数 第三个 正在等待i/o设备 第四个 表示当fork生成子进程时,是否恢复了进程的默认优先级  
            unsigned in_iowait:1;  
    
            /* 在分叉时恢复默认优先级/策略*/  
            unsigned sched_reset_on_fork:1;  
            pid_t pid;  
            pid_t tgid;  
      
    #ifdef CONFIG_CC_STACKPROTECTOR        //配置堆栈保护措施  
            unsigned long stack_canary;     //canary值 保护编译器 防止堆栈溢出 致使的返回地址被填充   
    #endif   
            struct task_struct *real_parent; 
            struct task_struct *parent;  
            struct list_head children;         //子节点和兄弟节点的定义  
            struct list_head sibling;       
            struct task_struct *group_leader;  //线程组的头结点  
           
            struct list_head ptraced;                  //跟踪器的头结点,跟踪器 跟踪 进程的逻辑流,即PC指令流  
            struct list_head ptrace_entry;              
       
            struct pid_link pids[PIDTYPE_MAX];   //定义 PID_LINK 结构体用它经过PID在哈希散列表中查找相应的task_struct  
            struct list_head thread_group;         //用来保存线程组的PID  
      
            struct completion *vfork_done;            
            int __user *set_child_tid;               //指向用户创造创立的线程的TID号   
            int __user *clear_child_tid;             //指向被清除的线程的TID号   
      
            cputime_t utime, stime, utimescaled, stimescaled;  
            cputime_t gtime;  
            cputime_t prev_utime, prev_stime;  
            unsigned long nvcsw, nivcsw;   //上下文切换的次数   
            struct timespec start_time;             
            struct timespec real_start_time;        
            unsigned long min_flt, maj_flt;  
      
            struct task_cputime cputime_expires;  
            struct list_head cpu_timers[3];  
      
            const struct cred *real_cred;   
            const struct cred *cred;         
            struct mutex cred_guard_mutex;  
            struct cred *replacement_session_keyring; 
      
            char comm[TASK_COMM_LEN]; 
                             //文件系统信息  
            int link_count, total_link_count;  
    #ifdef CONFIG_SYSVIPC        //配置进程的通讯机制  
     
            struct sysv_sem sysvsem;  
    #endif  
    #ifdef CONFIG_DETECT_HUNG_TASK   
            unsigned long last_switch_count;  
    #endif       
            struct thread_struct thread;       //CPU特殊状态的测试,线程结构体   
            struct fs_struct *fs;    //fs 指向一个文件系统信息结构体,该结构体有文件系统的信息  
         //指向记录打开文件信息的 结构体   
            struct files_struct *files;  
            //命名空间的定义   
            struct nsproxy *nsproxy;  
            //配置进程的信号处理    
            struct signal_struct *signal;   //如下是普通讯号部分  
            struct sighand_struct *sighand; //这个指向 handler表   
      
            sigset_t blocked, real_blocked;  //这个表示进程的屏蔽字  
            sigset_t saved_sigmask;  
            struct sigpending pending; //pending表  
      
            unsigned long sas_ss_sp;  // 如下是实时信号部分  
            size_t sas_ss_size;  
            int (*notifier)(void *priv);  
            void *notifier_data;  
            sigset_t *notifier_mask;  
            struct audit_context *audit_context;  
    #ifdef CONFIG_AUDITSYSCALL   // 配置系统调用   
            uid_t loginuid;  
            unsigned int sessionid;  
    #endif  
            seccomp_t seccomp;  
      
    #ifdef CONFIG_UTRACE  
            struct utrace *utrace;  
            unsigned long utrace_flags;  
    #endif  
            u32 parent_exec_id;  
            u32 self_exec_id;  
    /* 配置器保护措施配置  */
            spinlock_t alloc_lock;  
      
    #ifdef CONFIG_GENERIC_HARDIRQS  
            struct irqaction *irqaction;  
    #endif  
            spinlock_t pi_lock;  
      
    #ifdef CONFIG_RT_MUTEXES  // 互斥的配置    
            struct plist_head pi_waiters;  
            struct rt_mutex_waiter *pi_blocked_on;  
    #endif  
    #ifdef CONFIG_LOCKDEP                 // 死锁模块的配置   
    # define MAX_LOCK_DEPTH 48UL  
            u64 curr_chain_key;  
            int lockdep_depth;  
            unsigned int lockdep_recursion;  
            struct held_lock held_locks[MAX_LOCK_DEPTH];  
            gfp_t lockdep_reclaim_gfp;  
    #endif  
      
       // 文件系统的日志信息  
            void *journal_info;          
            struct bio *bio_list, **bio_tail;  
       
    //VM 虚拟机的状态   
            struct reclaim_state *reclaim_state;  
      
            struct backing_dev_info *backing_dev_info;  
      
            struct io_context *io_context;  
      
            unsigned long ptrace_message;  
            siginfo_t *last_siginfo; 
            struct task_io_accounting ioac;  
    #ifdef CONFIG_CPUSETS  
           nodemask_t mems_allowed;      //定义一个结构体 标志 内存是否容许访问 保护配置器的锁的  
    #ifndef __GENKSYMS__   
            unsigned short cpuset_mem_spread_rotor;  
            unsigned short cpuset_slab_spread_rotor;  
            int mems_allowed_change_disable;  
    #else  
            int cpuset_mem_spread_rotor;  
            int cpuset_slab_spread_rotor;  
    #endif  
    #endif  
    #ifdef CONFIG_CGROUPS // 配置控制组信息   
            struct css_set *cgroups;  
            struct list_head cg_list;  
    #endif 
    #endif  
    };

     

3.进程的状态

3.1进程的六种状态

#define TASK_RUNNING

1.表示进程要么正在执行,要么正在准备执行。

#define TASK_INTERRUPTIBLE 

2.表示进程被阻塞(睡眠),只有当某个条件是TRUE时,其状态相应的设置为 TASK_RUNNING。它能够被信号和wake_up唤醒。

#define TASK_UNINTERRUPTIBLE

3.表示进程被阻塞(睡眠),只有当某个条件是TRUE时,其状态相应的设置为 TASK_RUNNING。它只能被wake_up唤醒。

#define TASK_STOPPED

4.表示进程被中止执行。

#define TASK_TRACED 

5.表示进程被debugger等进程监视着。

#define EXIT_ZOMBIE

6.表示进程的执行被终止,可是其父进程尚未使用wait()等系统调用来获知它的终止信息。

#define EXIT_DEAD

7.表示进程的最终状态。

3.2状态转换图

 4.进程的调度

4.1Linux下的O(1)调度算法

4.1.1O(1)调度器

        在O(1)调度中,要问最重要的数据结构是运行队列。运行队列描绘了进程队列的结构,在内核源码中用runqueue结构体表示。

struct runqueue 
{   unsigned long nr_running; 
    task_t *curr;
    prio_array_t *active,*expired,arrays[2]; 
};

4.1.2优先级数组

  O(1)算法的另外一个核心数据结构即为prio_array结构体。该结构体中有一个用来表示进程动态优先级的数组queue,它包含了每一种优先级进程所造成的链表。

#define
 MAX_USER_RT_PRIO        100
#define
 MAX_RT_PRIO             MAX_USER_RT_PRIO
#define
 MAX_PRIO                (MAX_RT_PRIO + 40)
typedef struct prio_array
 prio_array_t;
struct prio_array
 {
      unsigned int nr_active;
      unsigned long bitmap[BITMAP_SIZE];
      struct list_head
 queue[MAX_PRIO];
};

4.1.3静态优先级和动态优先级

       进程有两个优先级,一个是静态优先级,一个是动态优先级.静态优先级是用来计算进程运行的时间片长度的,动态优先级是在调度器进行调度时用到的,调度器每次都选取动态优先级最高的进程运行.

静态优先级的计算:
nice值和静态优先级之间的关系是:静态优先级=100+nice+20
而nice值的范围是-2019,因此普通进程的静态优先级的范围是100~139
动态优先级的计算:
动态优先级=max(100 , min(静态优先级 – bonus + 5 , 139))

4.1.4时间片

       O(1)算法采用过时进程数组和活跃进程数组解决以往调度算法所带来的O(n)复杂度问题。过时数组中的进程都已经用完了时间片,而活跃数组的进程还拥有时间片。当一个进程用完本身的时间片后,它就被移动到过时进程数组中,同时这个过时进程在被移动以前就已经计算好了新的时间片。能够看到O(1)调度算法是采用分散计算时间片的方法,并不像以往算法中集中为全部可运行进程从新计算时间片。当活跃进程数组中没有任何进程时,说明此时全部可运行的进程都用完了本身的时间片。那么此时只须要交换一下两个数组便可将过时进程切换为活跃进程,进而继续被调度程序所调度。两个数组之间的切换其实就是指针之间的交换,所以花费的时间是恒定的。

struct prop_array *array = rq->active;
if (array->nr_active != 0) {
    rq->active = rq->expired;
    rq->expired = array;
}

      上面的代码说明了两个数组之间的交换,经过分散计算时间片、交换过时和活跃两个进程集合的方法可使得O(1)算法在恒定的时间内为每一个进程从新计算好时间片。

进程运行的时间片长度的计算
静态优先级<120,基本时间片=max((140-静态优先级)*20, MIN_TIMESLICE)
静态优先级>=120,基本时间片=max((140-静态优先级)*5, MIN_TIMESLICE)

4.1.5调度算法

       在每次进程切换时,内核依次扫描就绪队列上的每个进程,计算每一个进程的优先级,再选择出优先级最高的进程来运行;尽管这个算法理解简单,可是它花费在选择优先级最高进程上的时间却不容忽视。系统中可运行的进程越多,花费的时间就越大,时间复杂度为O(n)。

//伪代码
for (系统中的每一个进程) {
    从新计算时间片;
    从新计算优先级;
}

4.2CFS调度器

  CFS 背后的主要想法是维护为任务提供处理器时间方面的平衡(公平性)。这意味着应给进程分配至关数量的处理器。分给某个任务的时间失去平衡时(意味着一个或多个任务相对于其余任务而言未被给予至关数量的时间),应给失去平衡的任务分配时间,让其执行。

  要实现平衡,CFS 在叫作虚拟运行时的地方维持提供给某个任务的时间量。任务的虚拟运行时越小, 意味着任务被容许访问服务器的时间越短 — 其对处理器的需求越高。CFS 还包含睡眠公平概念以便确保那些目前没有运行的 任务(例如,等待 I/O)在其最终须要时得到至关份额的处理器。

  可是与以前的 Linux 调度器不一样,它没有将任务维护在运行队列中,CFS 维护了一个以时间为顺序的红黑树(以下图)。 红黑树是一个树,具备不少有趣、有用的属性。首先,它是自平衡的,这意味着树上没有路径比任何其余路径长两倍以上。 第二,树上的运行按 O(log n) 时间发生(其中 n 是树中节点的数量)。这意味着能够快速高效地插入或删除任务。

  任务存储在以时间为顺序的红黑树中(由 sched_entity 对象表示),对处理器需求最多的任务 (最低虚拟运行时)存储在树的左侧,处理器需求最少的任务(最高虚拟运行时)存储在树的右侧。 为了公平,调度器而后选取红黑树最左端的节点调度为下一个以便保持公平性。任务经过将其运行时间添加到虚拟运行时, 说明其占用 CPU 的时间,而后若是可运行,再插回到树中。这样,树左侧的任务就被给予时间运行了,树的内容从右侧迁移到左侧以保持公平。 所以,每一个可运行的任务都会追赶其余任务以维持整个可运行任务集合的执行平衡。

  • task_struct任务结构和红黑树的结构层次

  树的根经过 rb_root 元素经过 cfs_rq 结构(在 ./kernel/sched.c 中)引用。红黑树的叶子不包含信息,可是内部节点表明一个或多个可运行的任务。红黑树的每一个节点都由 rb_node 表示,它只包含子引用和父对象的颜色。 rb_node 包含在 sched_entity 结构中,该结构包含 rb_node 引用、负载权重以及各类统计数据。最重要的是,sched_entity 包含 vruntime(64 位字段),它表示任务运行的时间量,并做为红黑树的索引。 最后,task_struct 位于顶端,它完整地描述任务并包含 sched_entity 结构。

  就 CFS 部分而言,调度函数很是简单。 在 ./kernel/sched.c 中,有通用 schedule() 函数,它会先抢占当前运行任务(除非它经过 yield() 代码先抢占本身)。注意 CFS 没有真正的时间切片概念用于抢占,由于抢占时间是可变的。 当前运行任务(如今被抢占的任务)经过对 put_prev_task 调用(经过调度类)返回到红黑树。 当schedule 函数开始肯定下一个要调度的任务时,它会调用 pick_next_task 函数。此函数也是通用的(在 ./kernel/sched.c 中),但它会经过调度器类调用 CFS 调度器。 CFS 中的 pick_next_task 函数能够在 ./kernel/sched_fair.c(称为 pick_next_task_fair())中找到。 此函数只是从红黑树中获取最左端的任务并返回相关 sched_entity。经过此引用,一个简单的 task_of() 调用肯定返回的 task_struct 引用。通用调度器最后为此任务提供处理器。

  • 优先级和CFS:CFS 不直接使用优先级而是将其用做容许任务执行的时间的衰减系数。 低优先级任务具备更高的衰减系数,而高优先级任务具备较低的衰减系数。 这意味着与高优先级任务相比,低优先级任务容许任务执行的时间消耗得更快。 这是一个绝妙的解决方案,能够避免维护按优先级调度的运行队列。
  • 跟CFS有关进程:
    • 建立新进程: 建立新进程时, 须要设置新进程的vruntime值以及将新进程加入红黑树中. 并判断是否须要抢占当前进程。
    • 进程唤醒: 唤醒进程时, 须要调整睡眠进程的vruntime值, 而且将睡眠进程加入红黑树中. 并判断是否须要抢占当前进程。
    • 进程的调度: 进程调度时, 须要把当前进程加入红黑树中, 还要从红黑树中挑选出下一个要运行的进程。
    • 时钟周期中断: 在时钟中断周期函数中, 须要更新当前运行进程的vruntime值, 并判断是否须要抢占当前进程。
  • 部分代码:

     彻底公平运行队列:描述运行在同一个cpu上的处于TASK_RUNNING状态的普通进程的各类运行信息

struct cfs_rq {
    struct load_weight load;  //运行队列总的进程权重
    unsigned int nr_running, h_nr_running; //进程的个数

    u64 exec_clock;  //运行的时钟
    u64 min_vruntime; //该cpu运行队列的vruntime推动值, 通常是红黑树中最小的vruntime值

    struct rb_root tasks_timeline; //红黑树的根结点
    struct rb_node *rb_leftmost;  //指向vruntime值最小的结点
    //当前运行进程, 下一个将要调度的进程, 立刻要抢占的进程, 
    struct sched_entity *curr, *next, *last, *skip;

    struct rq *rq; //系统中有普通进程的运行队列, 实时进程的运行队列, 这些队列都包含在rq运行队列中  
    ...
};

          调度实体:记录一个进程的运行状态信息

struct sched_entity {
    struct load_weight  load; //进程的权重
    struct rb_node      run_node; //运行队列中的红黑树结点
    struct list_head    group_node; //与组调度有关
    unsigned int        on_rq; //进程如今是否处于TASK_RUNNING状态

    u64         exec_start; //一个调度tick的开始时间
    u64         sum_exec_runtime; //进程从出生开始, 已经运行的实际时间
    u64         vruntime; //虚拟运行时间
    u64         prev_sum_exec_runtime; //本次调度以前, 进程已经运行的实际时间
    struct sched_entity *parent; //组调度中的父进程
    struct cfs_rq       *cfs_rq; //进程此时在哪一个运行队列中
};

         建立进程,设置新进程的vruntime值,task_fork_fair()函数部分代码:

static void task_fork_fair(struct task_struct *p)
{
    struct cfs_rq *cfs_rq;
    struct sched_entity *se = &p->se, *curr;
    int this_cpu = smp_processor_id();
    struct rq *rq = this_rq();
    unsigned long flags;

    raw_spin_lock_irqsave(&rq->lock, flags);

    update_rq_clock(rq);

    cfs_rq = task_cfs_rq(current);
    curr = cfs_rq->curr;

    rcu_read_lock();
    __set_task_cpu(p, this_cpu); //设置新进程在哪一个cpu上运行
    rcu_read_unlock();

    update_curr(cfs_rq); //更新当前进程的vruntime值

    if (curr)
        se->vruntime = curr->vruntime; //先以父进程的vruntime为基础
    place_entity(cfs_rq, se, 1); //设置新进程的vruntime值, 1表示是新进程

    if (sysctl_sched_child_runs_first && curr && entity_before(curr, se)) { //sysctl_sched_child_runs_first值表示是否设置了让子进程先运行

        swap(curr->vruntime, se->vruntime); //当子进程的vruntime值大于父进程的vruntime时, 交换两个进程的vruntime值 
        resched_task(rq->curr); //设置从新调度标志TIF_NEED_RESCHED
    }

    se->vruntime -= cfs_rq->min_vruntime; //防止新进程运行时是在其余cpu上运行的, 这样在加入另外一个cfs_rq时再加上另外一个cfs_rq队列的min_vruntime值便可(具体能够看enqueue_entity函数)

    raw_spin_unlock_irqrestore(&rq->lock, flags);
}

          进程的主动调度函数是schedule():

asmlinkage void __sched schedule(void)  
{  
    struct task_struct *prev, *next;  
    unsigned long *switch_count;  
    struct rq *rq;  
    int cpu;  
need_resched:  
    preempt_disable(); //在这里面被抢占可能出现问题,先禁止它!  
    cpu = smp_processor_id();  
    rq = cpu_rq(cpu);  
    rcu_qsctr_inc(cpu);  
    prev = rq->curr;  
    switch_count = &prev->nivcsw;  
    release_kernel_lock(prev);  
need_resched_nonpreemptible:  
    spin_lock_irq(&rq->lock);  
    update_rq_clock(rq);  
    clear_tsk_need_resched(prev); //清除须要调度的位  

    if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {  
        if (unlikely(signal_pending_state(prev->state, prev)))  
            prev->state = TASK_RUNNING;  
        else  
            deactivate_task(rq, prev, 1);  //出队, 此处主要是把prev->on_rq赋值为0, 由于当前进程原本就没在红黑树中. on_rq为0后, 后面的put_prev_task函数就不会把当前进程加入红黑树了
        switch_count = &prev->nvcsw;  
    }  
    if (unlikely(!rq->nr_running))  
        idle_balance(cpu, rq);  

    prev->sched_class->put_prev_task(rq, prev);  //把当前进程加入红黑树中
    next = pick_next_task(rq, prev);  //从红黑树中挑选出下一个要运行的进程, 并将其设置为当前进程
    if (likely(prev != next)) {  
        sched_info_switch(prev, next);  
        rq->nr_switches++;  
        rq->curr = next;  
        ++*switch_count;  
        //完成进程切换 
        context_switch(rq, prev, next); 

        cpu = smp_processor_id();  
        rq = cpu_rq(cpu);  
    } else  
        spin_unlock_irq(&rq->lock);  
    if (unlikely(reacquire_kernel_lock(current) < 0))  
        goto need_resched_nonpreemptible;  
    preempt_enable_no_resched();  
    //这里新进程也可能有TIF_NEED_RESCHED标志,若是新进程也须要调度则再调度一次  
    if (unlikely(test_thread_flag(TIF_NEED_RESCHED)))  
        goto need_resched;  
}  

5.对进程的见解

     进程是操做系统最核心的概念,这是对正在运行的程序的一个抽象。全部操做系统其余的全部内容都是围绕着进程的概念展开的。进程是操做系统的核心之一,对于 Linux 技术而言,唯一不变的就是永恒的变化,不断追求更优更有效率的算法,让计算机给人们提供更好的服务。咱们从进程模型中学习到的理论知识与算法的更新模式等,都应与现实结合,好好实践。

相关文章
相关标签/搜索