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
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.
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.调度算法
调度算法是指: 根据系统的资源分配策略所规定的资源分配算法。
先来先服务(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