进程切换和线程调度

进程切换和线程调度

文章目录

  • 进程切换和线程调度
    • 一、前言
    • 二、进程切换
      • [2.1 进程的终止](#2.1 进程的终止)
      • [2.2 进程的状态](#2.2 进程的状态)
      • [2.3 进程的层次结构(管理结构)](#2.3 进程的层次结构(管理结构))
      • [2.4 进程状态模型](#2.4 进程状态模型)
        • [2.4.1 三态模型](#2.4.1 三态模型)
        • [2.4.2 五态模型](#2.4.2 五态模型)
        • [2.4.3 七态模型](#2.4.3 七态模型)
    • 三、线程调度
      • [3.1 线程概念](#3.1 线程概念)
      • [3.2 线程的使用](#3.2 线程的使用)
      • [3.3 多线程解决方案](#3.3 多线程解决方案)
      • [3.4 单线程](#3.4 单线程)
    • 四、小结

一、前言

先前了解了什么是进程,那么进程是怎样切换的呢?什么又是线程呢?它和进程有什么区别?

二、进程切换

2.1 进程的终止

  • 正常退出(自愿的)

    main函数运行到returnreturn后面的值作为退出的状态

    退出的状态有用吗

    进程执行完,需要告诉调度的程序一个状态。在Linux中有一个变量?,这个变量名是获取上一个进程退出的状态

    0-代表成功

    2-退出为2号状态,这也属于自愿退出,因为进程发现了这个错误,并把状态返回给调用者

    每个非0数字代表一个错误号

    最后返回的0是对于上一个进程echo $?的返回

  • 错误退出(自愿的)

    正常是0,错误是非0,但是是主动发出错误的,并将错误信息告诉进程,再返回给父进程,父进程将错误信息放到一个变量里暂存一下

  • 严重错误(非自愿的)

    不可控的因素导致必须退出。比如:程序闪退

    cpp 复制代码
    char *s = "123";
    s[0] = 'a';

    程序没有错,但是是异常访问。

    这种错误不再关注返回的状态数字号,因为这种状态值一般和系统的上下文有关,可能是随机的

  • 被其他进程杀死(非自愿的)

    比如:遇到程序死循环了,就可以利用另外一个终端kill -9 进程号来杀死进程,类似"强制退出"

    退出

    • 普通退出

      普通退出有可能会被系统忽略

    • 强制退出

      强制退出是不会被系统忽略的(系统会屏蔽某些信号,但是有些信号是不会被屏蔽-强制信号)

2.2 进程的状态

shell 复制代码
ps aux
shell 复制代码
man ps

重点记忆:D、R、S

sleep:阻塞 = 睡眠,放弃CPU了,可唤醒的睡眠状态

Linux睡眠分两类

  • 可中断的

  • 不可中断的

    IO,和磁盘打交道,把命令发给磁盘IO了,这是不可中断的

快速设备:CPU等,慢速设备:磁盘IO等

进程的业务分为两种:

  • IO密集型

    c 复制代码
    while(1)
    {
        printf("====\n");
    }

    CPU很闲

  • CPU密集型

    c 复制代码
    int i = 0;
    while(1)
    {
        i++;
    }

    CPU很累

2.3 进程的层次结构(管理结构)

UNIX:树状结构,父和子的概念->父进程和子进程

父进程比子进程先退出,子进程还在不在

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>

void childProcess()
{
    sleep(1);
    printf("========%d : %d=======\n", getpid(), getppid());
}

int main()
{
    printf("fork test...\n");
    pid_t pid;

    pid = fork();   // 返回值会返回1次,由于复制,会父进程返回一次,子进程返回一次
    if(pid < 0)
    {
        perror("fork");
        exit(2);        // 释放PCB
    }
    else if(pid == 0)
    {
        childProcess();
    }
    else
    {
        exit(0);
    }
    
    return 0;
}

运行结果:

解释:父进程死掉了,但是子进程还在,但是其父进程是1(子进程是3095,按理来说,其父进程应该是3094),这种情况下的子进程被称为"孤儿进程 ",在OS中,其资源最后会被1号进程回收。(这就是Linux的层次结构)

但是Windows没有,是平级的状态(只要产生进程,彼此之间就是平级的),彼此是独立的。

由此,OS之间是不同的,我们学习OS,学习的更多是普适的观念

2.4 进程状态模型

关于进程状态,这里解释的都是理论模型,在工程中,完全可以自己去设计,合理即可。

注意:Linux没有使用下面任何一种模型

2.4.1 三态模型
  • 就绪态(Ready State)

    所谓的就绪态就是一个就绪队列(由OS内核维护),和CPU交互就靠就绪队列,即:CPU和就绪队列之间实现了通信(OS中的代码可以实现:CPU从就绪队列中取东西,也可以把CPU中正在运行的东西放到就绪队列中)

  • 运行态(Running State)

    在CPU中的东西就是运行态,运行态和就绪态是双向的。

    a:从就绪态中取出一个东西加载到CPU,开始运行,这个加载过程就是调度行为

    b:运行态也有可能把进程送到就绪态,会有几种可能触发的情况:

    • 时间片到了,OS争夺到执行权,OS根据就绪队列的情况,把正在运行的东西放到就绪队列中,再通过调度算法获取一个进程,重新加载到CPU(运行态)【基于分时】
    • 基于抢占式(有高优先级的进程)的,当任务创建时,根据进程的优先级决定:把正在运行的程序打断(OS将其送到就绪态),把高优先级的进程马上送到CPU运行
  • 阻塞态(Blocked State)

    c:访问IO设备(耗时)、P操作(后续会讲)、等待事件发生的情况

    如何让阻塞态的进程有机会被CPU光顾

    不能由阻塞态直接指向运行态。这会造成运行态紊乱(两个指向运行态)。

    可以将其放到就绪态,这就是唤醒------d。谁唤醒呢?

    比如键盘,scanf(这个进程就不能跟CPU打交道了),调用OS的键盘驱动,等待中断发生(键盘按下),键盘的驱动缓存中没有数据,睡眠(当前调用这个驱动的进程在队列里睡觉)

    键盘一旦按下,就触发了中断,被CPU捕获,进入到键盘的处理函数,就会读取具体按键的数据放入到缓存区,开始判断(调用wake_up唤醒队列),唤醒队列后底层驱动就发现有数据了,将驱动缓存的数据解析到用户给的空间里,之后scanf继续执行

    阻塞态的队列有多个,每个事件都有一个队列

2.4.2 五态模型

增加了创建和终止

2.4.3 七态模型
  • 挂起:将数据结构从内存搬移到磁盘
  • 阻塞、睡眠:等待事件发生,任务还在内存上
  • 运行:CPU,单核CPU的运行态只有一个
  • 就绪:可以有多个就绪态

阻塞队列多个,就绪队列一个,有的OS就绪队列是多个(有优先级)

三、线程调度

3.1 线程概念

进程 & 线程

进程:任务调度的单位(task_struct,有一个结构体指针mm_struct*指向一个内存映射表,完成CPU的任务调度),资源的维护单位(mm_struct资源表空间0-3G)

多进程:多任务,任务一切换,(指针、空间也要跟着换)开销比较大。

任务一切换,缓存区的内容就变了

进程 :多个任务被CPU调度,这些任务独享某些内存资源(既是任务的调度体,也是资源的维护单位)

线程 :多个任务被CPU调度,这些任务共享某些内存资源(仍然是调度体,但是共享某个进程的任务资源)

进程释放时,依附于进程的所有线程也都释放

调度的角度

  • 纯用户空间的角度(很麻烦)

  • 纯内核空间的角度(轻量级)

    内核空间给OS和CPU使用

  • 混合角度(更灵活)

3.2 线程的使用

  • 资源共享

  • 轻量级与高效性

    创建线程时,仅是分配一个任务而已,仅是存一些状态,没有必要把整个资源重映射

  • 性能提升

    web服务器需要大量的响应,进程调度少点性能更高

3.3 多线程解决方案

Web服务进程采用多线程:

  • 调度线程:分发任务,分发出各个工作线程,保证各个任务都能响应。
  • 工作线程:共享Web页面的高速缓存,不用直接去访问内核

优点:共享了资源

3.4 单线程

有个矛盾:多个程序怎么保证先处理哪个程序

2套解决方案

  • 改为I/O复用模型

    epoll,后续详解

  • 改为非阻塞状态机模型

    阻塞就是一直等到响应才走,非阻塞就是不响应就立即走(轮询)。

    将每一个要发生的事件设置成为一个状态,根据状态的切换关系设计主程序。

    非常麻烦,一般不用

四、小结

进程的切换不是那么容易的:进程的终止分4种,进程的状态重点把握D、R、S,进程的层次结构:Linux的树形结构->父子进程和Windows的平级结构,进程的状态模型:重点把握三态模型。

线程和进程看似相同,其实不同,进程是独占资源,线程是共享资源,各具不同的应用。

相关推荐
DonnyCoy3 小时前
Android性能之数据结构
数据结构
oMcLin3 小时前
如何在Manjaro Linux上配置并优化Caddy Web服务器,确保高并发流量下的稳定性与安全性?
linux·服务器·前端
济6173 小时前
linux(第七期)--gcc编译软件-- Ubuntu20.04
linux·运维·服务器
天赐学c语言3 小时前
1.7 - 删除排序链表中的重要元素II && 哈希冲突常用解决冲突方法
数据结构·c++·链表·哈希算法·leecode
ChineHe3 小时前
Redis基础篇004_Redis Pipeline流水线详解
数据库·redis·缓存
corpse20103 小时前
Linux监控软件Monitorix 安装部署
linux·安全
coding消烦员3 小时前
在 Windows 内网搭建 Git 仓库:共享普通仓库 vs 中心 bare 仓库
windows·git
wdfk_prog3 小时前
[Linux]学习笔记系列 -- [fs]super
linux·笔记·学习
菜鸟233号4 小时前
力扣96 不同的二叉搜索树 java实现
java·数据结构·算法·leetcode
姚青&4 小时前
四.文件处理命令-文本编辑
linux