进程、线程等基础知识

进程

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
生命周期:
image.png
就绪-→执行:对就绪状态的进程,当进程调度程序按一种选定的策略从中选中一个就绪进程,为之分配了处理机后,该进程便由就绪状态变为执行状态;
执行-→阻塞:正在执行的进程因发生某等待事件而无法执行,则进程由执行状态变为阻塞状态。
如:进程提出输入/输出请求而变成等待外部设备传输信息的状态,进程申请资源(主存空间或外部设备)得不到满足时变成等待资源状态,进程运行中出现了故障(程序出错或主存储器读写错等)变成等待干预状态等等;
阻塞-→就绪: 处于阻塞状态的进程,在其等待的事件已经完成,如输入/输出完成,资源得到满足或错误处理完毕时,处于等待状态的进程并不马上转入执行状态,而是先转入就绪状态,然后再由系统进程调度程序在适当的时候将该进程转为执行状态;
如:进程提出输入/输出请求而变成等待外部设备传输信息的状态,进程申请资源(主存空间或外部设备)得不到满足时变成等待资源状态,进程运行中出现了故障(程序出错或主存储器读写错等)变成等待干预状态等等;
执行-→就绪: 正在执行的进程,因时间片用完而被暂停执行,或在采用抢先式优先级调度算法的系统中,当有更高优先级的进程要运行而被迫让出处理机时,该进程便由执行状态转变为就绪状态。
小结: 当程序申请完资源时,该进程到达就绪状态,若分的cpu去处理则到达执行态,在运行中若因为I/O阻塞导致不需要cpu则处理阻塞/挂起状态,此时i/o完成后则从阻塞走到执行态。
[

](https://blog.csdn.net/ganghaodream/article/details/101154544)

线程

操作系统能够进行运算调度的最小单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
生命周期:
新建状态: 当用new操作符创建一个线程时。此时程序还没有开始运行线程中的代码。
就绪状态: 一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。
运行态: 当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法。
阻塞状态: 线程运行过程中,可能由于各种原因进入阻塞状态:
①线程通过调用sleep方法进入睡眠状态;
②线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
③线程试图得到一个锁,而该锁正被其他线程持有;
④线程在等待某个触发条件;
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
死亡状态: 有两个原因会导致线程死亡:
①run方法正常退出而自然死亡;
②一个未捕获的异常终止了run方法而使线程猝死;
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法,如果是可运行或被阻塞,这个方法返回true;如果线程仍旧是new状态且不是可运行的,或者线程死亡了,则返回false。

可以把上锁的过程具体表示为:

  1. 读内存表示锁的变量
  2. 判断锁的状态
  3. 如果已经加锁,返回失败
  4. 把锁设置为上锁状态,
  5. 返回成功

上面的每一个步骤都对应一条汇编语句,可以认为这每一步操作都是原子的。但又由于该语句指令都是原子因此可以在每一步操作中都发生中断,那么第四次操作就可能重复。
"test and set"指令将读取内存、判断和设置值作为一个原子操作, 因此可以解决单核情况下。
在多核情况下使用锁总线的情况下并使用** "test and set"可以解决锁问题。
总线按功能和规范可分为五大类型
😗*

  • 数据总线(Data Bus):在CPU与RAM之间来回传送需要处理或是需要储存的数据。
  • 地址总线(Address Bus):用来指定在RAM(Random Access Memory)之中储存的数据的地址。
  • 控制总线(Control Bus):将微处理器控制单元(Control Unit)的信号,传送到周边设备。
  • 扩展总线(Expansion Bus):外部设备和计算机主机进行数据通信的总线,例如ISA总线,PCI总线。
  • 局部总线(Local Bus):取代更高速数据传输的扩展总线。

目前的理解是只有三条线:一条传控制信息、一条在Cpu 和 RAM 之间传输数据,一条用来控制计算数据在RAM存放/读取地址。

用户/内核态

https://juejin.cn/post/6923863670132850701

Fork

Fork 的本义是 叉子(名词)** 。**
image.png

比较自然的引申成 分叉(动词) ,就像上面叉子,从左到从右,一条线变成多条了。
Linux 的 Fork 函数
运行着的程序分成2个(几乎)完全一样的进程,每个进程都启动一个从代码的同一位置开始执行的线程,这两个进程中的线程继续执行,就像是两个用户同时启动了该应用程序的两个副本。
:::success
https://baike.baidu.com/item/fork/7143171
:::
就是本来只有一个进行app,然后它调用了fork()函数,然后就产生了子进程,原来的进程叫父进程。这个子进程也是进程,但凡是进程,都有自己的虚拟地址空间。虚拟地址空间是从0到4G的大小,其中3-4G是属于内核的。创建完子进程后,父进程继续运行app(即原来的进程)的代码,刚创建出来的子进程拥有和父进程完全一样的代码段,数据段,也就是说完完全全拷贝了一份父进程,和父进程完全一样。即clone父进程0-3G的内容,而3-4G的kernel只需要重新映射一下到物理地址的kernel即可。但是操作系统要如何区分这两个进程呢?答案就是进程ID,即pid。pid是存储在PCB当中的类似身份证的东西。子进程会clone父进程的PCB到子进程,但是PCB里的pid会从操作系统中获取,得到新的pid。PCB存储在3-4G的内核中。 如下图:
image.png
fork()完以后,父进程和子进程由于有着同样的数据段和代码段,栈,PCB也大部分相同,所以两个进程就会干着同样的事情,这样对我们没有意义,所以需要识别哪个是父进程,哪个是子进程,然后让父进程接着干原来的事,子进程去干新的事情。

我们只能通过fork的返回值来判断当前进程是父进程还是子进程
其实,fork底层是调用了内核的函数来实现fork的功能的,即先create()先创建进程,此时进程内容为空,然后clone()复制父进程的内容到子进程中,此时子进程就诞生了,接着父进程就return返回了。而子进程诞生后,是直接运行return返回的,然后接着执行后面的程序,这里注意:子进程是不会执行前面父进程已经执行过的程序了得,因为PCB中记录了当前进程运行到哪里,而子进程又是完全拷贝过来的,所以PCB的程序计数器也是和父进程相同的,所以是从fork()后面的程序继续执行
写时复制: 因为可以映射到同一物理内存,所以在数据读取时可以减少空间浪费使用同一内存读取,如果当前物理内存发生改变时,会使用写时复制将数据复制到子进程中。
写时复制: 数据更新时同时复制数据到另一内存块,下次更新时不再复制, 可以看下百度百科释义。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值