首页 🧬操作系统

并发与并行


并发Concurrency
并行Parallelism
假设系统中只有一个CPU
操作系统可以同时加载多个程序(进程)

  • 每个进程都有独立的地址空间:互相不会干扰
  • 每隔一定时间,就切换到另一个进程执行(看起来进程好像在同时运行)

事实上,并发性的来源是进程会调用操作系统的API

  • write(fd,buf,1 TiB)(TiB宏实现)
  • 运行在处理器中的高特权级:能访问硬件设备(否则就不能写数据了)
  • 但又不能一直霸占处理器运行(否则系统就卡死了)
  • 因此必须允许write到一半时,让另一个进程执行,中断处理程序

    • 如果另一个进程比如调用read(fd,buf,512MiB),访问了同一个设备文件,将512M读入到内存空间,这时操作系统的文件对象的访问就并发了。
    • 虽然每个进程在地址空间上是独立的,操作系统的对象是可以被进程共享的,所以操作系统API实现需要考虑并发。

典型的并发系统

并发:多个执行流可以不按照一个特定的顺序执行
并行:允许多个执行流同时执行(多个处理器)

处理器数量共享内存典型的并发系统并发/并行
单处理器共享内存OS内核/多线程程序并发不并行
多处理器共享内存OS内核/多线程程序/GPU Kernel并发、并行
多处理器不共享内存分布式系统(消息通信)并发、并行

线程

线程:多个执行流并发/并行执行,并且他们共享内存

  • 两个执行流共享代码和所有全局变量(数据、堆区)
  • 线程之间指令的执行顺序是不确定的(non-deterministic)
int x=0,y=0

void thread_1(){
  [1] x=1;
  [2] printf("y=%d\n",y);
}

void thread_2(){
  [3] y=1;
  [4] printf("x=%d\n",x);
}
  • 1→2→3→4 ()
  • 1→3→2→4 ()
  • 1→3→4→2 ()
  • ...

线程:什么该共享、什么不共享?

extern 0
int foo(){
    int volatile t=x;
    t+=1;
    x=t;
}

考虑如果有两个执行流同时调用foo,哪些资源是共享的?

  • foo的代码(1140--115f)
  • 寄存器:rip,rsp,rax
  • t:-0x4(%rsp),x:0x2eb5(%rip)

上述三类资源,foo代码是可以共享的,因为系统中可以有若干个不同的线程,每个线程都可以调用foo代码,线程间会共享数据,所以全局数据x也是可以共享的;剩余资源均被线程独享,试想如果两个线程T1、T2共享寄存器,两个线程同时从foo开始执行,那么T1开始执行指令后,rip的值就会发生改变,就会指向操作后的指令,而T2还未被执行,但是系统中仅有1个rip寄存器,如果这个时候发生一次上下文切换,这时rip还停留于T1,处理时可能会产生不合法指令,由于每个线程都有独享的寄存器,所以对应的堆栈也是独享的,volatile t=x;如果局部变量t是共享的,在被赋值之后,如果另一个线程再赋别的值,那么foo的含义就改变了。

POSIX Threads

linux下用C开发多线程程序,Linux系统下的多线程遵循POSIX线程接口,称为pthread
POSIX提供的线程库(pthreads)

  • 使用pthreads_create创建并运行线程
  • 使用pthreads_join等待某个线程结束

为了简化threads.h封装了线程API

  • create(fn)
  • join(fn)

无论系统是单处理器还是多处理器,都得到若干共享了当前进程地址空间的线程

  • 共享代码:所有线程的代码都来自当前进程的代码
  • 共享数据:全局数据/堆区可以自由引用
  • 独立堆栈:每个线程有独立的堆栈



扫描二维码,在手机阅读!
文章评论

目录