计算机系统学习之(1):基础知识概要——进程、中断、线程、竞态条件、关键区域、死锁、进程调度
文章目录
- 进程的创建
- 哪些事件导致进程的创建
- fork 和 exec 命令创建和控制进程
- fork() 命令
- execve() 命令
- 进程的状态
- 中断
- 中断的种类
- 线程
- 线程共享内容
- 线程独有内容
- 进程和线程的比较
- 线程的好处
- 线程的类别及区别
- user-level thread(用户级别的线程)
- user-level thread 的优点
- user-level thread 的缺点
- kernel-level thread(内核级别的线程)
- kernel-level thread 的优点
- kernel-level thread 的缺点
- 竞态条件
- Critical region
- 解决 Critical region 的各种问题可以使用的策略
- 范例研究
- 通过 lock variable 来解决 critical region 的 race condition 问题
- 通过 busy waiting 来解决 critical region 的 race condition 问题
- busy waiting 的特点和缺点
- 死锁 deadlock
- 何时发生
- 解决死锁
- CPU 密集型 / IO 密集型进程
- 进程调度 Process scheduler
- 几种重要的时间
- 调度的目标
进程的创建
哪些事件导致进程的创建
- system initialization:当系统启动的时候会按照设定创建和启动一些固定的进程
- Execution of a process creation system call by a running process:一个进程在执行的时候可能通过 “进程创建系统” 来创建一个新的进程
- A user request to create a new process:用户创建新的进程(双击打开某个软件)
- Initiation of a batch job
fork 和 exec 命令创建和控制进程
fork() 命令
- fork() 命令会创建一个新的进程,自己作为 parent 进程; fork 命令会在 parent 执行 fork 命令的时候返回 child 的进程号,
- 然后把子进程执行过程中路过 fork 命令的时候将进程号置为 0
- 当子进程创建的时候,他会和父进程共享程序计数器(program counter),cpu寄存器,和打开的文件(openfile)
#include<stdio.h>
#include<unistd.h>int main(){int x;int count = 0;x = fork();printf("\nrun ...");printf("%d",x);}
run ...94779
run ...0
首先当这个进程从 main 中的第一行开始执行,通过 fork 创建了子进程并返回了子进程的 id=94779
然后这个时候子进程被创建了,依然是从头开始执行这个进程,但是在经过 fork() 的那一行,x 被设置成了 0
你可以这么理解,就是系统能够识别到底是父进程发出的 fork 命令,还是子进程中的 fork 命令,当子进程经过 fork 命令的时候,返回 0
如果程序如下,会产生什么样的结果呢:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(){int pid;int count = 0;printf("fork1:%d:%d\n",getpid(),fork());printf("fork2:%d:%d\n",getpid(),fork());}
fork1:95301:95302
fork2:95301:95303
fork2:95303:0
fork1:95302:0
fork2:95302:95304
fork2:95304:0
- 这个过程可能不好理解,但是你可以试着去想一下:
- 父进程 95301 经过第一个 fork 创建了 child1 95302
- 父进程 95301 经过第二个 fork 创建了 child2 95303
- child2 经过第一个 fork 的时候,啥反应也没有,所以什么也没打印
- child2 经过第二个 fork 的时候,返回了 0 所以打印了 fork2:95303:0
- child1 经过第一个 fork 的时候,返回了 0 所以打印了 fork1:95302:0
- child1 经过第二个 fork 的时候,又创建了 childchild 并返回了它的进程编号, fork2:95302:95304
- childchild 经过第一个 fork 的时候,啥反应也没有,所以什么也没打印
- childchild 经过第二个 fork 的时候,返回了 0,所以打印了 fork2:95304:0
- 所以经过 上述的代码命令,最终 parent 是产生了 3 个 孩子进程( childchild 进程也是它的孩子进程)
execve() 命令
- 如果只通过 fork 产生一个进程,那么这个进程的子进程的所有操作都和父进程一模一样;那么这个分身就没有什么意义了,而通过 execve 命令,可以让这个复制出来的进程采用不同的操作。
进程的状态
就绪态
运行态
阻塞态
运行态就是这个程序正在占用 CPU 资源进行操作,
就绪态就是随时可以执行,但没有得到 CPU 的运行资源,等 CPU 资源轮询到他,立马就可以处理任务
阻塞态就是,等待某个事件的发生,比如等待用户的输入,即使这个时候有空闲的 CPU 也不能马上处理,因为他要等待用户输入完成才能解除阻塞状态,进入就绪状态,然后才能进行任务处理。
中断
中断的种类
- 真正的中断产生于硬件;伪中断产生于 CPU
- 用户程序可能不经意间产生伪中断(pseudo-interrupts),例如除以0操作,这种事件经常被称作 “exceptions”这些异常会导致一些程序终止
- 用户可以自发地通过命令产生伪中断(ctrl + c)
线程
线程共享内容
- 地址空间和内存
- 代码和数据部分
- 存储空间内容(全局变量和堆空间(heap))
- 打开的文件
- 子进程
- 信号和信号处理器
线程独有内容
- 程序计数器
- 寄存器
- 栈空间(维护局部变量和函数调用的数据空间)
- 状态(就绪/等待…)
堆空间 heap:可以暂时认为堆区是一块没有经过分配的内存地址,是和栈区等内存空间分开的,当我们调用方法的和使用局部变量的时候,其实都是在 stack 区进行操作,只有当我们使用 malloc 在 C 语言中申请一块空间的时候才会在堆区操作
进程和线程的比较
- 可以说线程是轻量化的进程,而且线程可以被调度器直接管理,提高了程序的并发性
- 进程被创建之后,其实是有一个主线程 main thread 和系统分配的各种资源(代码、数据、文件、寄存器、栈);这个时候如果不创建其他线程,那么进程就只有一个主线程。
- 而当进程创建多个线程的时候,随着线程的创建,他们会同时创建自己的栈区、寄存器
线程的好处
- 创建方便,快速
- 终止也方便、快速
- 在不同的线程之间切换很简单
- 因为一个进程的不同线程之间共享内存,所以线程之间的通信很方便
线程的类别及区别
参考文章:https://www.tutorialspoint.com/user-level-threads-and-kernel-level-threads
user-level thread(用户级别的线程)
- 用户级线程是由用户实现的,内核不知道这些线程的存在。它像处理单线程进程(single-threaded process)一样处理它们。用户级线程比内核级线程小且快得多。它们由程序计数器(PC)、堆栈、寄存器和一个小的过程控制块(small process control block)组成。另外,在用户级线程的同步中与内核(kernel)无关。
user-level thread 的优点
- 创建迅速,管理容易(可以通过调度器直接管理)
- 可以在任何操作系统上运行
- 在用户级线程中切换线程不需要内核模式特权。
user-level thread 的缺点
- 用户级线程中的多线程应用程序不能充分利用 multiprocessing
- 如果一个用户级线程执行阻塞操作,整个进程将被阻塞
kernel-level thread(内核级别的线程)
kernel-level thread 的优点
- 在内核级线程中,同一个进程的多个线程可以调度在不同的处理器上(multiprocessing)
- 内核例程也可以是多线程的
- 当一个线程阻塞了,不影响其他线程的运行
kernel-level thread 的缺点
- 两个 kernel-level thread 在切换的时候,要转移控制权 transfer control
- kernel-level thread 的创建和管理都比 user-level 的要慢(几乎接近于创建和管理一个进程的时间)
- 最大的好处是,创建和管理方便; 最大的缺点是一个阻塞全都阻塞
- 因为涉及到阻塞行为和 system call,因此要选用 kernel-level thread
竞态条件
https://www.techtarget.com/searchstorage/definition/race-condition#:~:text=A%20race%20condition%20is%20an,sequence%20to%20be%20done%20correctly.
从上面的文字中我们可以得到:
- race condition 可以发生在多个 program(程序) process(进程)或者多个(thread)线程之间,只要他们要在同一时间访问公共的资源就会发生。
在 race condition 的情况下:
- 所有资源的争夺方都可以对资源进行读和写
- 当输出依赖于操作的顺序时,就会出现 race condition
- 非常难 debug,因为很难重复这些资源争夺方的执行顺序
假设现在有两个进程 A,B 来同时进行这个函数的调用,用 A1 表示 A 进程执行到第一行代码,B2 代表 B 进程执行到第二行代码
假设 stack 里面现在还有一个值
case1:A1 -> A2 -> B1 这段程序就会执行完毕,并且 B 的返回值是 0
case2:A1 -> B1 -> A2 -> B2 这段程序就会报错,因为当 B 去 pop 的时候已经没有值 pop 了
Critical region
https://www.educative.io/edpresso/what-is-the-critical-section-problem-in-operating-systems
- 指进程访问共享资源(如公共变量、文件等)并对其进行"写"操作的代码段
- 因为读操作哪怕同时进行也不会导致程序出现问题,但是写操作是绝对不能同时进行的。因此要对写操作的代码段进行并发的控制来避免 race condition 的情况
解决 Critical region 的各种问题可以使用的策略
- mutual exclusion:当一个进程在处理它的 critical region 的时候,其他的进程不允许访问 critical region
- progress:当 critical region 里没有进程在执行,并且有一个进程希望进入临界区时,它不应该无限期地等待进入critical region。
- bounded waiting:在另一个进程 A 请求进入 critical region 时,在 A 请求被接受之前,一个进程允许在临界区时执行的次数必须有一个上限。这是为了防止某个进程不断地、密集地获得进入 critical region 的权力,导致其他进程不能得到资源
范例研究
- 通过下面几个例子分析通过这几种方式是否能够达到解决 critical region 问题的目的。
通过 lock variable 来解决 critical region 的 race condition 问题
假设现在有两个进程 A,B, 用 A1, B1 分别表示两个进程执行到代码段的第 1 行
当 A1->A2->B1->… 的情况,即当 A1,A2 发生在 B1 之前,那么这种情况可以保证 A B 不会发生 critical region 的 race condition 问题,但是如果发生顺序是:
- A1->B1->… 这种情况,则无法避免 A,B 之间的 critical region 中的 race condition 问题
通过 busy waiting 来解决 critical region 的 race condition 问题
这种情况与上一种相比,不再存在同时越过 while 条件的情况,也就是说,一定会有一个线程卡在第二行的 while 处无法访问 critical region;但是这种写法依然存在问题,设想下面的情况:
- A1->B1->A2->B2->A3->B2->A4->B3->A5->B4 -> B5 ->B5 -> B5
- 当 thread A 执行完毕,但是 thread B 依然卡在 B5 很长时间,这个时候,因为 turn = 0,所以 A 会再开始一轮,直到 A 再次执行完 A4, B 还是没有执行完 B5,那么由于 A 在这个循环中的 A4 中将 turn设置 = 1,所以这个时候如果 B 不执行完 B5,A 也将无法再开始下一次循环。
- 所以这种情况下,A,B 都依赖于对方执行的结束,否则自己也无法继续往下执行,因此也没有真正解决 race condition 的问题
busy waiting 的特点和缺点
当一个进程想要进入 critical region 的时候
- 需要 check 是否可以进入
- 如果不能进入,那就不断地执行循环操作(就是上例中的 while 循环),直到能够访问 critical region 为止
缺点:
- 大量消耗 CPU, 其实完全可以用其他方法来代替,比如让 CPU 每隔一段时间来检查一遍
- 优先级倒置问题
https://www.sciencedirect.com/topics/computer-science/priority-inversion#:~:text=Priority%20inversion%20is%20a%20situation,thus%20the%20name%20priority%20inversion).
死锁 deadlock
https://www.geeksforgeeks.org/introduction-of-deadlock-in-operating-system/
何时发生
- 发生死锁的四个必要条件:
- 两个或多个非共享的资源(非共享的意思就是每种资源一次只能被一个进程访问),
- 一个进程占有了最少一种(非共享的)资源并且在等待另外一个资源(非共享的)
- 某种资源一旦被某个进程占用,就不可被抢占(必须等到这个进程执行完毕才能释放当前资源)
- 多个进程等待其他进程的资源,这个过程形成了环结构
解决死锁
- 忽略这个问题
- 通过图算法来检测死锁的发生并进行修复
- 通过精细的资源分配避免死锁
CPU 密集型 / IO 密集型进程
- CPU 密集型的进程就是运行过程中特别消耗CPU资源的进程
- IO 密集型的进程就是进程中很长的时间都在阻塞状态,使用 CPU 的机会并不多
进程调度 Process scheduler
https://www.geeksforgeeks.org/cpu-scheduling-in-operating-systems/?ref=lbp
几种重要的时间
- 到达时间:一个进程到达 ready queue (就绪队列)的时间点;代表这个进程只要获得 CPU 时间就马上可以运行
- 完成时间:进程完成任务的时间点
- 触发时间:进程真正使用 cpu 的时间段
- 周转时间:完成时间点 - 到达时间点 (即完成整个进程使用的时间周期,这个过程可能包括了进程等待 CPU 的时间 + CPU 处理这个进程的时间,这两部分共同构成了周转时间)
- 等待时间:周转时间 - 触发时间
参考:
https://www.geeksforgeeks.org/difference-between-arrival-time-and-burst-time-in-cpu-scheduling/#:~:text=Burst%20Time%20(BT)%20%3A,running%20time%20of%20the%20process.
调度的目标
- 最大化利用 CPU 资源(让 CPU 尽可能忙)
- 最大化 CPU 的吞吐量(让 CPU 尽可能在一个时间单位完成更多进程的任务)
- 最小化周转时间(让一个进程完成从进入就绪队列到自己的任务用的时间尽可能短)
- 最小化等待时间(让进程等待的时间尽可能短)
- 最小化响应时间(一个进程产生响应的最小时间)
等待时间和响应时间的区别:
- 响应时间:就绪状态到获得 CPU 的时间段
- 等待时间:在整个等待队列中待的时间
计算机系统学习之(1):基础知识概要——进程、中断、线程、竞态条件、关键区域、死锁、进程调度相关推荐
- JAVA基础知识系列---进程、线程安全
1.1 临界区 保证在某一时刻只有一个线程能访问数据的简便方法,在任意时刻只允许一个线程对资源进行访问.如果有多个线程试图同时访问临界区,那么在有一个线程进入后,其他所有试图访问临界区的线程将被挂起, ...
- Nginx学习(一)——Nginx基础知识
目录 1.Nginx学习(一)--Nginx基础知识 2.Nginx学习(二)--配置文件.反向代理与负载均衡 3.Nginx搭建HTTPS服务器 一.初试Nginx 一.下载安装 1.安装必要的一些 ...
- JNI学习开始篇 基础知识 数据映射及学习资料收集
JNI学习开始篇 基础知识 数据映射及学习资料收集 JNI介绍 JNI(Java Native Interface) ,Java本地接口. 用Java去调用其他语言编写的程序,比如C或C++. JNI ...
- OpenCV与图像处理学习一——图像基础知识、读入、显示、保存图像、灰度转化、通道分离与合并
OpenCV与图像处理学习一--图像基础知识.读入.显示.保存图像.灰度转化.通道分离与合并 一.图像基础知识 1.1 数字图像的概念 1.2 数字图像的应用 1.3 OpenCV介绍 二.图像属性 ...
- php基础教学笔记,php学习笔记:基础知识
php学习笔记:基础知识 2.每行结尾不允许有多余的空格 3.确保文件的命名和调用大小写一致,是由于类Unix系统上面,对大小写是敏感的 4.方法名只允许由字母组成,下划线是不允许的,首字母要小写,其 ...
- 计算机网络基础心得体会结尾,学习《计算机网络基础知识》心得体会
学习<计算机网络基础知识>心得体会 ... 如今已经是信息时代,作为主流信息工具的网络越来越重 要,网络是信息的载体,是人们传递感情的工具.随着信息社会 的不断发展,网络的应用将会更加广泛 ...
- 计算机学生要学的基础知识,中小学生应注重学习计算机的基础知识
"知识爆炸"和"知识老化"这两大问题,不断困扰着现代教育,人们解决这一问题的良方之一,就是加强学生对基础知识的学习.近年来在中国兴起的中小学生学习计算机热,也同 ...
- 【学习笔记--FMCW基础知识】
学习笔记--FMCW基础知识 前言 mmWave测距原理 mmWave区分多个物体 mmWave的距离分辨率(Range Solution) mmWave的最大测量距离 前言 由于工作原因需要了解TI ...
- 使用Vue3学习Vue的基础知识
创建 Vue 应用 vue的安装有多种方式,本文只讨论基础知识,其他安装方式请自行查阅官网 https://v3.cn.vuejs.org/guide/installation.html 本文使用CD ...
最新文章
- linux查看特定文件的位置
- IOS后台运行机制详解(二)
- HDOJ(HDU) 2502 月之数(进制)
- cookies与session
- toj 4596 一行盒子
- vim的一些基本应用
- Tomcat是如何将请求一步步传递到我们编写的HttpServlet类中的
- 特斯拉电池检测_特斯拉风格的割草机,也是采用电池供电
- Mahout的taste里的几种相似度计算方法
- 用CSS Houdini画一片星空
- TCP/IP四层模型
- 新浪微博批量删除微博的方法
- oracle box怎么全屏,Oracle VM VirtualBox 虚拟机设置全屏与共享
- ElasticSearch-索引生命周期(ILM)-日期分割索引
- 不限速,无需登录就能下载的网盘工具,非常适合您!
- Mac快捷键大全及cheatsheet插件
- 【数值分析】Doolittle分解和Cholesky分解的Python实现
- 拾忆Elasticsearch02:Elasticsearch的基本命令回顾
- 圆的周长面积(YZOJ-1020)
- IDEA终于支持云端了,可同步所有配置和插件,一招搞定,重装不愁~