Lecture 4 Process Scheduling(进程调度)

1 进程切换

  • 是什么触发了进程切换?
  • 进程切换时要做什么?

中断技术

  • 中断是指程序执行过程中
    • 当发生某个事件时,中止 CPU上现行程序的运行
    • 引出该事件的处理程序执行
    • 执行完毕返回原程序中断 点继续执行

Lecture 4 Process Scheduling(进程调度)_第1张图片

中断源

  • 外中断:来自处理器之外的硬件中断信号
    • 如时钟中断、键盘中断、外围设备中断
    • 外部中断均是异步中断
  • 内中断(异常 Exception):来自于处理器内部,指 令执行过程中发生的中断,属同步中断
    • 硬件异常:掉电、奇偶校验错误等
    • 程序异常:非法操作、地址越界、断点、除数为0
    • 系统调用

中断处理过程
msedge_LuF68qeEcv.png
特权指令和非特权指令

  • 特权指令
    • 只能在内核模式下运行的指令称作特权指令
    • I/O指令
    • 关闭所有中断
    • 设置计时器
    • 进程交换
  • 非特权指令
    • 在用户模式下运行的指令称作非特权指令

模式切换

Lecture 4 Process Scheduling(进程调度)_第2张图片

进程切换

  • 切换时机
    • 进程需要进入等待状态
    • 进程被抢占CPU而进入就绪状态
  • 切换过程
    • 保存被中断进程的上下文信息(Context)
    • 修改被中断进程的控制信息(如状态等)
    • 将被中断的进程加入相应的状态队列
    • 调度一个新的进程并恢复它的上下文信息

2 进程调度

进程控制块

Lecture 4 Process Scheduling(进程调度)_第3张图片


进程在物理内存

Lecture 4 Process Scheduling(进程调度)_第4张图片


进程队列

Lecture 4 Process Scheduling(进程调度)_第5张图片


进程调度
进程在整个生命周期中会在各个调度队列中迁移, 由操作系统的一个调度器(scheduler)来执行。

Lecture 4 Process Scheduling(进程调度)_第6张图片

3 创建进程的实验

  1. 使用编辑器gedit新建一个helloProcess.c源文件,并输入后面的范例代码。
#include 
#include 
#include 

int main()
{
    //pid_t是数据类型,实际上是一个整型,通过typedef重新定义了一个名字,用于存储进程id
    pid_t pid,cid; 
    //getpid()函数返回当前进程的id号
    printf("Before fork Process id :%d\n", getpid());
    
    /*
    fork()函数用于创建一个新的进程,该进程为当前进程的子进程,创建的方法是:将当前进程的内存内容完整拷贝一份到内存的另一个区域,两个进程为父子关系,他们会同时(并发)执行fork()语句后面的所有语句。
    fork()的返回值:
      如果成功创建子进程,对于父子进程fork会返回不同的值,对于父进程它的返回值是子进程的进程id值,对于子进程它的返回值是0.
      如果创建失败,返回值为-1.
    */
    cid = fork(); 
    
    printf("After fork, Process id :%d\n", getpid());
  
    return 0;
}

保存退出gedit,使用gcc对源文件进行编译,然后运行,观察结果并解释原因。

Lecture 4 Process Scheduling(进程调度)_第7张图片


出现这种现象的原因是创建子进程后,子进程会将当前进程内存内容完整拷贝到内存的另一个区域。

Lecture 4 Process Scheduling(进程调度)_第8张图片

PPID就是指的它的父进程。

  1. 通过判断fork的返回值让父子进程执行不同的语句。
#include 
#include 
#include 

int main()
{
    pid_t cid;
    printf("Before fork process id :%d\n", getpid());
    
    cid = fork();
    
   if(cid == 0){ //该分支是子进程执行的代码
   
       printf("Child process id (my parent pid is %d):%d\n", getppid(),getpid());
       for(int i=0; i<3 ; i++)
            printf("hello\n");
            
    }else{ //该分支是父进程执行的代码
    
        printf("Parent process id :%d\n", getpid());
        for(int i=0; i<3 ; i++)
            printf("world\n");
    }
    
    return 0;
}

执行结果:

Lecture 4 Process Scheduling(进程调度)_第9张图片


将循环次数改成 3000,此时的执行结果为:

Lecture 4 Process Scheduling(进程调度)_第10张图片


说明了子进程和父进程是并发的。
fork的工作流程。

Lecture 4 Process Scheduling(进程调度)_第11张图片

  1. 验证父子进程间的内存空间是相互独立的。在终端中进入自己的主目录,使用gedit命令新建一文件helloProcess2.c,输入下面的代码,然后编译运行。
#include 
#include 
#include 

int main()
{
    pid_t cid;
    int x = 100;
    
    cid = fork();
    
   if(cid == 0){ //该分支是子进程执行的代码
       x++;
       printf("In child: x=%d\n",x);
            
    }else{ //该分支是父进程执行的代码
       x++;  
       
       printf("In parent: x=%d\n",x);
       
    }
    
    return 0;
}

Lecture 4 Process Scheduling(进程调度)_第12张图片


说明了子进程和父进程是相互独立的。
在上一步的代码的20行添加如下语句,同时代码最顶端要包含一个新的头文件.

#include
wait(NULL);

wait函数会让调用者陷入等待,直到子进程的状态变为可用(即子进程结束前父进程一直处于等待状态)。
为了让效果更清楚,请将wait语句从20行移到18行,并在15行加上如下语句:

sleep(3);

sleep该函数可以让调用进程睡上指定的时间长度(单位是second)。
重新编译代码运行,我们特意让子进程输出完毕后睡了3秒,在这期间父进程什么事也没有做一直在wait,直到子进程结束后父进程才执行return 0语句。

你可能感兴趣的:(粗略学习操作系统,单片机,嵌入式硬件)