第一次作业:深入源码分析进程模型
Posted QianQiYing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第一次作业:深入源码分析进程模型相关的知识,希望对你有一定的参考价值。
写在最前:
- 本次使用的内核源码:Linux 0.12版本。
- 这篇文章在讲什么:旨在利用源码来帮助理解操作系统中进程这一概念。
进程:这是对正在运行程序的一个抽象。操作系统的其它所有内容都是围绕着进程的概念展开的。一个进程就是一个正在执行程序的实例。
一.操作系统是如何组织进程的
1.进程数
在sched.h中,有如下语句:
1 #define NR_TASKS 64
该语句定义了系统中的最多任务(进程)数,即:在同一瞬间,系统中最多可有64个进程。
2. 进程标识符
- 在sched.h中有一个进程的结构体:task_struct。在结构体中,定义了进程的标识符(唯一):
1 long pid;
- 注:任务0是“空闲”任务,当没有其他任务可以运行时会被调用。 它不能被杀死,它无法入睡。 任务[0]中的\'状态\'信息从不使用。
- 该结构体的完整定义如下:
1 struct task_struct {
2 /* these are hardcoded - don\'t touch */
3 long state; //任务的运行状态,-1 不可运行, 0 可运行(就绪), >0 已停止
4 long counter; //任务运行时间计数
5 long priority;
6 long signal;
7 struct sigaction sigaction[32];
8 long blocked; /* bitmap of masked signals */
9 /* various fields */
10 int exit_code;
11 unsigned long start_code,end_code,end_data,brk,start_stack;
12 long pid,pgrp,session,leader; //从左到右依次为:进程标识号(进程号)、进程组号、会话号、会话首领
13 int groups[NGROUPS]; //进程所属组号,一个进程可以属于多个组
14 /*
15 * pointers to parent process, youngest child, younger sibling,
16 * older sibling, respectively. (p->father can be replaced with
17 * p->p_pptr->pid)
18 */
19 //从左到右:指向父进程的指针、指向最新子进程的指针、指向比自己后创建的相邻进程的指针、指向比自己早创建的相邻进程的指针。
20 struct task_struct *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
21 unsigned short uid,euid,suid;
22 unsigned short gid,egid,sgid;
23 unsigned long timeout,alarm;
24 long utime,stime,cutime,cstime,start_time;
25 struct rlimit rlim[RLIM_NLIMITS];
26 unsigned int flags; /* per process flags, defined below */
27 unsigned short used_math;
28 /* file system info */
29 int tty; /* -1 if no tty, so it must be signed */
30 unsigned short umask;
31 struct m_inode * pwd;
32 struct m_inode * root;
33 struct m_inode * executable;
34 struct m_inode * library;
35 unsigned long close_on_exec;
36 struct file * filp[NR_OPEN];
37 /* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
38 struct desc_struct ldt[3];
39 /* tss for this task */
40 struct tss_struct tss;
41 };
3. 进程可能的状态的定义
1 //这里定义了进程运行时可能处于的状态
2 #define TASK_RUNNING 0 //进程正在运行或已准备就绪
3 #define TASK_INTERRUPTIBLE 1 //进程处于可中断等待状态
4 #define TASK_UNINTERRUPTIBLE 2 //进程处于不可中断等待状态,主要用于I/O操作等待
5 #define TASK_ZOMBIE 3 //进程处于僵死状态,已经停止运行,但父进程还没发信号。
6 #define TASK_STOPPED 4 //进程已停止
二.进程状态如何转换(给出进程状态转换图)
-
进程的三个状态:
运行态:该时刻进程实际占有CPU。
就绪态:可运行,但因为其它进程正在运行而暂时停止。
阻塞态:除非某种外部时间发生,否则进程不能运行。
-
进程状态转换图:
-
转换关系图解释如下:
1. 进程因为等待输入而被阻止。
2. 调度程序选择另一个进程。
3. 调度程序选择这个进程。
4. 出现有效输入。
三.进程是如何调度的
调度:当系统中就绪的进程数大于系统中可用的CPU数时,可用的CPU必须选择下一个要运行的进程。在操作系统中,完成选择工作的这一部分称为调度程序(scheduler),该程序使用的算法称为调度算法(scheduling algorithm)。
在Linux 0.12中采用基于优先级排队的调度策略。
schedule()函数首先扫描任务数组。通过比较每个就绪态(TASK RUNNING) 任务的运行时间递减滴答计数counter 的值,来确定当前哪个进程运行的时间最少。哪一个的值大,就表示运行时间还不长,于是就选中该进程,并使用任务切换宏函数切换到该进程运行。
源码:
1 void schedule(void)
2 {
3 int i,next,c;
4 struct task_struct ** p; //任务结构指针的指针
5
6 /* check alarm, wake up any interruptible tasks that have got a signal */
7
8 for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
9 if (*p) {
10 if ((*p)->timeout && (*p)->timeout < jiffies) {
11 (*p)->timeout = 0;
12 if ((*p)->state == TASK_INTERRUPTIBLE)
13 (*p)->state = TASK_RUNNING;
14 }
15 if ((*p)->alarm && (*p)->alarm < jiffies) {
16 (*p)->signal |= (1<<(SIGALRM-1));
17 (*p)->alarm = 0;
18 }
19 if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
20 (*p)->state==TASK_INTERRUPTIBLE)
21 (*p)->state=TASK_RUNNING;
22 }
23
24 /* this is the scheduler proper: */
25 //-------------------这是调度程序的主要部分-------------------
26 while (1) {
27 c = -1;
28 next = 0;
29 i = NR_TASKS;
30 p = &task[NR_TASKS];
31 while (--i) {
32 if (!*--p)
33 continue;
34 if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
35 c = (*p)->counter, next = i;
36 }
37 if (c) break;
38 for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
39 if (*p)
40 (*p)->counter = ((*p)->counter >> 1) +
41 (*p)->priority;
42 }
43 switch_to(next);
44 }
四.谈谈自己对该操作系统进程模型的看法
调度的实现有很多不同的算法,Linux 0.12版本较为早期了,后期的版本代码量似乎大了很多,有关进程调度的也一定更为完善。
最后:
参考资料:
- Linux内核完全剖析-基于0.12内核 (赵炯) 中文pdf扫描版 下载地址:http://www.jb51.net/books/415345.html ;
- 《现代操作系统》第四版。
以上是关于第一次作业:深入源码分析进程模型的主要内容,如果未能解决你的问题,请参考以下文章