The first assignment: In-depth source code analysis process model based on Linux

 Foreword:

Based on Linux, in-depth source code analysis of its process model, including the following:

  • How the operating system organizes processes
  • How the process state transitions (given the process state transition diagram)
  • How processes are scheduled
  • Talk about your own views on the operating system process model

1. How the operating system organizes processes


 

1. The concept of process

  A process is a running activity of a program in a computer on a data set, the basic unit of resource allocation and scheduling in the system, and the basis of the operating system structure. In the early computer structure of process-oriented design, the process is the basic execution entity of the program; in the contemporary computer structure of thread-oriented design, the process is the container of the thread. A program is a description of instructions, data, and their organization, and a process is the entity of a program.

The process should contain the following:

  • The code of the program, since the process is a running program, the code of the program is naturally required

  • program data

  • The value of CPU registers, including general registers, program counter

  • The heap is used to save the memory space dynamically allocated when the process is running

  • The stack (stack) has two purposes, 1 to save the context information of the operation. 2 Save the formal parameters or local variables of the called function when the function is called

  • A set of system resources occupied by a process, such as open files

 

2. Characteristics of the process


 

Dynamics, independence, and concurrency are the three characteristics of a process.
(1) Dynamic: in the process of running the program, its state is constantly changing. For example, when a program is running, it is executed one instruction after another, and each time an instruction is executed, the values ​​of those general-purpose registers in the CPU will also change, and the value of the program counter (Program Counter) will also change. Points to the next instruction to be executed. In addition, the contents of the heap and stack are constantly changing, data is constantly being pushed into and out of the stack, and the heap space is constantly being allocated and released. After all, change is happening all the time.
(2) Independence: A process is an independent entity and a unit of use of computer system resources. Each process has "its own" registers and internal state , and runs independently of other processes. Of course, this "self" is quoted, that is to say: physically, there is only one set of registers in the CPU, such as only one PC register, but no process has its own logical PC. Physical registers are true hardware registers.
(3) Concurrency: For the case of a single CPU, from a macro point of view, each process runs in the system at the same time, but in fact, from a micro point of view, at a certain moment, only one program is running, in other words, each process In between are actually run sequentially one after the other. Because there is one CPU, only one process can use it at a time.
 

 3. The identifier of the process


 

Each process has a process identifier, a user identifier, and a group identifier.

domain name

meaning

Pid

process identifier

Uid gid

User ID, Group ID

Euid 、 egid

Effective User Identifier, Effective Group Identifier

Suid 、 sgid

Backup user ID, backup group ID

Fsuid 、 fsgid

file system user identifier, file system group identifier


 

 

 

 

 

 

 

 

 

4. Process control block


 

The system maintains a process control block (Process Control Block, PCB) for each process, which is used to save various state information related to the process. The PCB is the unique identification of the process. Every time a new process is generated, a PCB is created for it and then initialized. If you want to cancel a process, you only need to recycle the PCB. Process switching can be performed by operating the PCB. PCB is just a statement in the basic principle, and is called a task struct for Linux. Defined as follows:

1 struct task_struct 
2 {
3 volatile long state;
4 pid_t pid;
5 unsigned long timestamp;
6 unsigned long rt_priority;
7 struct mm_struct *mm, *active_mm
8 }

 

5. Process View


 

 

2. How to change the state of the process


 1. The state of the process


   Process is a dynamic entity, so it is alive. From creation to death, is the entire life cycle of a process. During this cycle, a process may go through various states. In general, all processes go through the following three states:

(1) Ready state. Indicates that the process has obtained all other required resources, is requesting processing processor resources, and is ready to start execution. In this case, the process is said to be in the ready state.

(2) Blocking state. It means that the process gives up the processor because it needs to wait for the required resources, or the process does not own the processor, and other resources are not satisfied, so even if the processor is obtained, it cannot start running. In this case, the process is blocked. The blocking state is also called the sleep state or the waiting state.

(3) Running state. The process gets the processor and does not need to wait for any other resources. The executing state is called the running state. Only in the running state, the process can use the requested resources.

In the Linux system, various states are reorganized, thus obtaining several states of the Linux process:

  • RUNNING: A process that is running or waiting to run in the ready queue. That is, the synthesis of the running state and the ready state process mentioned above. A process is in the RUNNING state, it does not mean that it must be executed. In a multitasking system, each ready process needs to be executed concurrently, so at a certain moment, only one of these processes in the RUNNING state can get the processor, and the other processes must wait in a ready queue. Even in a multiprocessor system, Linux can only have one processor perform tasks at a time.
  • UNINTERRUPTABLE: Uninterruptible blocking state. The process in this state is waiting in the queue. When the resource is available, it can be woken up by the operating system, otherwise, it will be in the waiting state.
  • INTERRUPTABLE: Interruptible blocking state. As in the uninterruptible blocking state, processes in this state are in a waiting queue and can be woken up by the operating system when resources are available. The difference from the uninterruptible blocking state is that a process in this state can also be woken up by signals from other processes.
  • STOPPED: Suspended state. The process is suspended and needs to be woken up by signals from other processes. There are two reasons for this state. One is the reaction to the relevant signal (SIGSTOP, SIGSTP, SIGTTIN or SIGTTOU). The second is to be controlled by the parent process ptrace call, and temporarily hand over the processor to the control process.
  • ZOMBIE: Zombie state. Represents a state in which a process has ended but not yet died. At this point, the process has finished running and released most of the resources, but the process control block has not been released.

 

2. Process state transition


 As shown below:

 

3. How processes are scheduled


 1 Introduction


 Process Scheduling: Scheduling in operating systems refers to a type of resource allocation.

Linux process scheduling uses preemptive multitasking, so processes do not need to cooperate with each other to suspend and continue running.

Linux divides processes into two types when scheduling processes: 1. Ordinary processes; 2. Real-time processes ; the priority of real-time processes is always higher than that of ordinary processes.

The indicators to measure the performance of process scheduling are: turnaround time, response time, waiting time, throughput, and CPU utilization.
 

2. Process scheduling strategy


 

  • SCHED_OTHER  time- sharing scheduling strategy.

  • SCHED_FIFO real-time scheduling strategy, first come first serve.

  • SCHED_RR real-time scheduling strategy, time slice rotation.

 

3. Process priority


 

  • Use nice values: larger nice values ​​mean lower priority. (between -19 ~ 20)

  • Real-time priority: Configurable, higher means higher process priority.

  • Time slice: Linux does not allocate time slices with a fixed time value (such as 10ms), but divides the usage ratio of the processor as a "time slice" to the process.

 4. Scheduler


 

The Linux scheduler consists of two schedulers: the main scheduler and the periodic scheduler (the two are collectively referred to as the generic scheduler or the core scheduler).

(1) Main scheduler

The evolution of the Linux master scheduler

field Version
O(n) Initial Scheduling Algorithm linux-0.11~2.4
O(1) scheduler linux-2.5
CFS scheduler linux-2.6 ~ now

 

sched_setscheduler function

  1 asmlinkage void __sched schedule(void)
  2 {
  3     long *switch_count;//进程切换的次数
  4     task_t *prev, *next;
  5     runqueue_t *rq;
  6     prio_array_t *array;
  7     struct list_head *queue;
  8     unsigned long long now;
  9     unsigned long run_time;
 10     int cpu, idx;
 11 
 12     /*
 13      * Test if we are atomic.  Since do_exit() needs to call into
 14      * schedule() atomically, we ignore that path for now.
 15      * Otherwise, whine if we are scheduling when we should not be.
 16      */
 17     if (likely(!(current->state & (TASK_DEAD | TASK_ZOMBIE)))) {
 18         if (unlikely(in_atomic())) {
 19             printk(KERN_ERR "bad: scheduling while atomic!\n");
 20             dump_stack();
 21         }
 22     }
 23 
 24 need_resched:
 25     preempt_disable();//禁用内核抢占
 26     prev = current;
 27     rq = this_rq();//获得当前CPU的运行队列
 28 
 29     /*
 30      * The idle thread is not allowed to schedule!
 31      * Remove this check after it has been exercised a bit.
 32      */
 33     if (unlikely(current == rq->idle) && current->state != TASK_RUNNING) {
 34         printk(KERN_ERR "bad: scheduling from the idle thread!\n");
 35         dump_stack();
 36     }
 37 
 38     release_kernel_lock(prev);//保证PREV不占用大内核锁
 39     schedstat_inc(rq, sched_cnt);
 40     now = sched_clock();
 41     if (likely(now - prev->timestamp < NS_MAX_SLEEP_AVG))
 42         run_time = now - prev->timestamp;
 43     else
 44         run_time = NS_MAX_SLEEP_AVG;
 45 
 46     /*
 47      * Tasks with interactive credits get charged less run_time
 48      * at high sleep_avg to delay them losing their interactive
 49      * status
 50      */
 51     if (HIGH_CREDIT(prev))
 52         run_time /= (CURRENT_BONUS(prev) ? : 1);//PREV 所使用的CPU的时间长度
 53 
 54     spin_lock_irq(&rq->lock);//在寻找新进程前先禁止本地中断
 55 
 56     /*
 57      * if entering off of a kernel preemption go straight
 58      * to picking the next task.
 59      */
 60     switch_count = &prev->nivcsw;
 61     if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {//PREV非可运行状态且没有被抢占
 62         switch_count = &prev->nvcsw;
 63         if (unlikely((prev->state & TASK_INTERRUPTIBLE) &&
 64                 unlikely(signal_pending(prev))))//如果是在等待队列中,则置为运行状态
 65             prev->state = TASK_RUNNING;
 66         else
 67             deactivate_task(prev, rq);//从运行队列中移除PREV
 68     }
 69 
 70     cpu = smp_processor_id();
 71     if (unlikely(!rq->nr_running)) {//如果运行队列中没有可运行的进程
 72 go_idle:                                //
 73         idle_balance(cpu, rq);            //从另外的CPU上迁移一些进程来
 74         if (!rq->nr_running) {//如果还是没有可运行的进程
 75             next = rq->idle;    //则调用IDLE进程
 76             rq->expired_timestamp = 0;
 77             wake_sleeping_dependent(cpu, rq);
 78             /*
 79              * wake_sleeping_dependent() might have released
 80              * the runqueue, so break out if we got new
 81              * tasks meanwhile:
 82              */
 83             if (!rq->nr_running)
 84                 goto switch_tasks;
 85         }
 86     } else {
 87         if (dependent_sleeper(cpu, rq)) {
 88             schedstat_inc(rq, sched_goidle);
 89             next = rq->idle;
 90             goto switch_tasks;
 91         }
 92         /*
 93          * dependent_sleeper() releases and reacquires the runqueue
 94          * lock, hence go into the idle loop if the rq went
 95          * empty meanwhile:
 96          */
 97         if (unlikely(!rq->nr_running))
 98             goto go_idle;
 99     }
100 
101     array = rq->active;//有可运行的进程
102     if (unlikely(!array->nr_active)) {//活动进程链表中没有可运行的
103                                         //则交换active和expired
104         /*
105          * Switch the active and expired arrays.
106          */
107         schedstat_inc(rq, sched_switch);
108         rq->active = rq->expired;
109         rq->expired = array;
110         array = rq->active;
111         rq->expired_timestamp = 0;
112         rq->best_expired_prio = MAX_PRIO;
113     } else
114         schedstat_inc(rq, sched_noswitch);
115 //搜索活动进程集合位掩码的第一个非0位
116     idx = sched_find_first_bit(array->bitmap);
117     queue = array->queue + idx;
118     next = list_entry(queue->next, task_t, run_list);//找到这个进程描述符
119 
120     if (!rt_task(next) && next->activated > 0) {
121         //更新睡眠时间和动态优先级
122         unsigned long long delta = now - next->timestamp;
123 
124         if (next->activated == 1)
125             delta = delta * (ON_RUNQUEUE_WEIGHT * 128 / 100) / 128;
126 
127         array = next->array;
128         dequeue_task(next, array);
129         recalc_task_prio(next, next->timestamp + delta);
130         enqueue_task(next, array);
131     }
132     next->activated = 0;
133 switch_tasks:
134     prefetch(next);
135     clear_tsk_need_resched(prev);//清除TIF_NEED_RESCHED标志
136     rcu_qsctr_inc(task_cpu(prev));
137 
138     prev->sleep_avg -= run_time;
139     if ((long)prev->sleep_avg <= 0) {
140         prev->sleep_avg = 0;
141         if (!(HIGH_CREDIT(prev) || LOW_CREDIT(prev)))
142             prev->interactive_credit--;
143     }
144     prev->timestamp = prev->last_ran = now;
145 
146     sched_info_switch(prev, next);
147     if (likely(prev != next)) {//如果PREV不等于NEXT则进行切换
148         next->timestamp = now;
149         rq->nr_switches++;
150         rq->curr = next;
151         ++*switch_count;
152 
153         prepare_arch_switch(rq, next);
154         prev = context_switch(rq, prev, next);
155         barrier();
156 
157         finish_task_switch(prev);
158     } else
159         spin_unlock_irq(&rq->lock);
160 
161     reacquire_kernel_lock(current);
162     preempt_enable_no_resched();
163     if (unlikely(test_thread_flag(TIF_NEED_RESCHED)))//如果TIF_NEED_RESCHED置位则再次进行调度
164         goto need_resched;
165 }

 

 (2)周期调度器

scheduler_tick函数

/*
 * This function gets called by the timer code, with HZ frequency.
 * We call it with interrupts disabled.
 */

void scheduler_tick(void)
{
    /*  1.  获取当前cpu上的全局就绪队列rq和当前运行的进程curr  */

    /*  1.1 在于SMP的情况下,获得当前CPU的ID。如果不是SMP,那么就返回0  */
    int cpu = smp_processor_id();

    /*  1.2 获取cpu的全局就绪队列rq, 每个CPU都有一个就绪队列rq  */
    struct rq *rq = cpu_rq(cpu);

    /*  1.3 获取就绪队列上正在运行的进程curr  */
    struct task_struct *curr = rq->curr;

    sched_clock_tick();

    /*  2 更新rq上的统计信息, 并执行进程对应调度类的周期性的调度  */

    /*  加锁 */
    raw_spin_lock(&rq->lock);

    /*  2.1 更新rq的当前时间戳.即使rq->clock变为当前时间戳  */
    update_rq_clock(rq);

    /*  2.2 执行当前运行进程所在调度类的task_tick函数进行周期性调度  */
    curr->sched_class->task_tick(rq, curr, 0);

    /*  2.3 更新rq的负载信息,  即就绪队列的cpu_load[]数据
     *  本质是讲数组中先前存储的负荷值向后移动一个位置,
     *  将当前负荷记入数组的第一个位置 */
    update_cpu_load_active(rq);



    /*  2.4 更新cpu的active count活动计数
     *  主要是更新全局cpu就绪队列的calc_load_update*/
    calc_global_load_tick(rq);

    /* 解锁 */
    raw_spin_unlock(&rq->lock);

    /* 与perf计数事件相关 */
    perf_event_task_tick();

#ifdef CONFIG_SMP

     /* 当前CPU是否空闲 */
    rq->idle_balance = idle_cpu(cpu);

    /* 如果到是时候进行周期性负载平衡则触发SCHED_SOFTIRQ */
    trigger_load_balance(rq);

#endif

    rq_last_tick_reset(rq);
}

 

4.调度算法


 

调度算法是指: 根据系统的资源分配策略所规定的资源分配算法。

(1)先来先服务和短作业(进程)优先调度算法
   a.先来先服务调度算法(FCFS)

  先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。当在作业调度中采用该算法时,每次调度都是从后备作业队列中选择一个或多个最先进入该队列的作业,将它们调入内存,为它们分配资源、创建进程,然后放入就绪队列。在进程调度中采用FCFS算法时,则每次调度是从就绪队列中选择一个最先进入该队列的进程,为之分配处理机,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后才放弃处理机。

  FIFO算法服务质量不佳,容易引起作业用户不满,常作为一种辅助调度算法。

特点:(1)有利于长作业,不利于短作业;

          (2)有利于处理机繁忙的作业,不利于I/O繁忙的作业;

  b.短作业(进程)优先调度算法SJ(P)F

  短作业(进程)优先调度算法SJ(P)F,是指对短作业或短进程优先调度的算法。它们可以分别用于作业调度和进程调度。短作业优先(SJF)的调度算法是从后备队列中选择一个或若干个估计运行时间最短的作业,将它们调入内存运行。而短进程优先(SPF)调度算法则是从就绪队列中选出一个估计运行时间最短的进程,将处理机分配给它,使它立即执行并一直执行到完成,或发生某事件而被阻塞放弃处理机时再重新调度。

  该算法虽可获得较好的调度性能,但难以准确地知道下一个CPU执行期,而只能根据每一个进程的执行历史来预测。

(2)高优先权优先调度算法

  a.优先权调度算法的类型(FPF)

  为了照顾紧迫型作业,使之在进入系统后便获得优先处理,引入了最高优先权优先(FPF)调度算法。此算法常被用于批处理系统中,作为作业调度算法,也作为多种操作系统中的进程调度算法,还可用于实时系统中。当把该算法用于作业调度时,系统将从后备队列中选择若干个优先权最高的作业装入内存。当用于进程调度时,该算法是把处理机分配给就绪队列中优先权最高的进程,这时,又可进一步把该算法分成如下两种。

  1) 非抢占式优先权算法

  在这种方式下,系统一旦把处理机分配给就绪队列中优先权最高的进程后,该进程便一直执行下去,直至完成;或因发生某事件使该进程放弃处理机时,系统方可再将处理机重新分配给另一优先权最高的进程。这种调度算法主要用于批处理系统中;也可用于某些对实时性要求不严的实时系统中。

  2) 抢占式优先权调度算法

  在这种方式下,系统同样是把处理机分配给优先权最高的进程,使之执行。但在其执行期间,只要又出现了另一个其优先权更高的进程,进程调度程序就立即停止当前进程(原优先权最高的进程)的执行,重新将处理机分配给新到的优先权最高的进程。因此,在采用这种调度算法时,是每当系统中出现一个新的就绪进程i 时,就将其优先权Pi与正在执行的进程j 的优先权Pj进行比较。如果Pi≤Pj,原进程Pj便继续执行;但如果是Pi>Pj,则立即停止Pj的执行,做进程切换,使i 进程投入执行。显然,这种抢占式的优先权调度算法能更好地满足紧迫作业的要求,故而常用于要求比较严格的实时系统中,以及对性能要求较高的批处理和分时系统中。

(3)基于时间片的轮转调度算法

  a.时间片轮转法

   前几种算法主要用于批处理系统中,不能作为分时系统中的主调度算法,在分时系统中,都采用时间片轮转法。

(4)几种算法的比较:

 

四、谈谈自己对该操作系统进程模型的看法


 

对该操作系统进程模型的看法

  我对接触Linux不多,对Linux平台的开发更是一无所知。 而现在的趋势越来越表明,作为一个优秀的软件开发人员,或计算机IT行业从业人员, 掌握Linux是一种很重要的谋生资源与手段。回顾过去十年,Linux早期被使用者部署到各自的行业来增加竞争优势,特别在金融服务行业尤为普遍。如今,Linux作为云计算、大数据和移动时代的首选操作系统,重新树立行业发展趋势。因此,如同2004年的市场发展,未来Linux发展前途无量,可见学习Linux系统是如此之重要。

 

五、参考文献


 

https://blog.csdn.net/woshixuye/article/details/53931303

 http://www.cnblogs.com/chenglei/archive/2009/11/20/1606881.html

http://www.bubuko.com/infodetail-2573299.html

 https://blog.csdn.net/gatieme/article/details/51872561

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325143770&siteId=291194637