进程的秘密


前言

进程是操作系统(Operating System)结构的基础。


一、进程(Process)

操作系统执行main函数的程序,机器指令需要加载到内存中执行,因此需要记录下内存的起始地址长度;同时要找到main函数的入口地址写到PC寄存器中。

  • 与协程的区别
    当普通函数返回后,进程的地址空间中不会再保存该函数运行时的任何信息,而协程返回后,函数的运行时信息是需要保存下来的

二、线程

PC寄存器指向的非main函数,存在互斥和同步问题

  • 线存共享进程的内存地址空间
  • 操作系统要为每个线程在进程的地址空间中分配一个栈(stack),消耗进程内存空间

1.线程池

一批创建后不再释放的线程,复用,可控,包括:

  1. 处理数据的函数;
  2. 需要处理的数据;

代码如图

Struct task{
	Data; //数据
	Handler;//方法
}
  • 线程池中的线程会阻塞在队列上。
  • 当生产者向队列中写入数据后,线程池中的某个线程会被唤醒,该线程从队列中取出上述结构体执行。

2.线程安全

2.1.共享资源有哪些?

在这里插入图片描述

  • 用于动态分配内存的堆区,我们用C/C++中的malloc或者new就是在堆区上申请的内存。
  • 全局区(数据区),这里存放的就是全局变量。
  • 文件,我们知道线程是共享进程打开的文件。
  • 代码区和动态链接库是只读的,不能被修改。

2.2.识别线程核心资源

在这里插入图片描述

  • 线程私有资源,没有线程安全问题
  • 线程间以某种秩序使用共享资源也能实现线程安全

2.3.什么是线程安全的代码?

线程安全的代码:

  • 无状态函数
  • 当我们在多个线程中同时且多次调用的这段代码,都能给出正确的结果
  • 按值传参,参数是线程的私有资源

线程不安全的代码:

  • 按引用传参,参数在数据区或者是全局资源

2.4.线程(进程)的同步、异步

  1. 什么是回调函数?
    在计算机科学中,回调函数是指一段以参数的形式传递给其它代码的可执行代码。

  2. 案例:
    假如有一个主线程,一个副线程。

    • 同步:主线程等待副线程执行完毕后,才继续执行。
    • 异步:将主线程在副线程执行完后才执行的部分,封装为一个函数,将函数传入副线程。如果主线程不关心副线程的执行情况,副线程就会一直执行下去。如果主线程关心副线程的执行情况,主线程在接收到消息后,继续处理上一个请求的后半部分。

三.协程

函数只是协程的一种特例,函数其实只是没有挂起点的协程。
与普通函数只有一个返回点(yield)不同,协程可以有多个返回点。
协程返回后还能继续调用该协程,并且从该协程的上一个返回点后继续执行。

代码:

void func(){
	print("a")
	yield
	print("b")
	yield
	print("c")
}
def A():
	co = func() # 得到该协程
	next(co)    # 调用该协程
	print("in function A") # do something
	next(co)    # 再次调用该协程

协程会在函数被暂停运行时,保存函数的运行状态,并可以从保存的状态中恢复并继续运行,类似操作系统对线程的调度。
在这里插入图片描述

3.1.怎么实现协程?

假设进程中只有一个线程:
在这里插入图片描述
栈区中有四个栈帧,main函数调用A函数,A函数调用B函数,B函数调用C函数,函数的运行时状态就保存在栈区的栈帧中(上下文),两个协程的栈区都是在上分配的,这样我们就可以随时中断或者恢复协程的执行了。
一个线程中,即使你创建了N多协程,但在操作系统看来依然只有一个线程,协程对操作系统来说是不可见的。

四.数据结构

4.1.树的递归遍历

函数的调用过程具有数据结构中栈的性质,也就是先进后出。

void C() {
}
void A() {
B();
}
void B() {
C();
}
void main() {
A();
}

A()会调用B(),B()会调用C(),那么函数调用过程如图所示:在这里插入图片描述

4.2.链表

链表是计算机科学中极其经典的一种数据结构,货车就好比数组,火车就好比链表。

struct node {
struct node* next; // 下一节车厢是谁 (内存地址)
int value; // 装载的货物
};

五.CPU执行函数指令

在这里插入图片描述
假设函数A调用了函数B,函数A将一些参数写入相应的寄存器,当CPU执行函数B时就可以从这些寄存器中获取参数了,当参数个数多于寄存器数量时剩下的参数直接放到栈帧中,这样被调函数就可以从前一个函数的栈帧中获取到参数了

5.1.栈溢出

栈区是有大小限制的,当超过限制后就会出现栈溢出问题。

  1. 不要创建过大的局部变量(函数内部变量)
  2. 函数栈帧,也就是调用层次不能太多

总结

了解到进程相关的概念后,下一章讲讲操作系统及内核。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值