操作系统知识(笔记)

进程、线程

进程(process)和线程(thread)的区别?

进程和线程本质上都是CPU的一个工作时间段,进程包括了CPU加载程序上下文、CPU执行、CPU保存程序上下文。线程包含在进程中,一个进程至少有一个线程,也可以有多个线程,进程的不同线程之间共享CPU和程序上下文。
进程是操作系统资源分配(包括CPU、内存、磁盘IO等)的最小单元;线程是CPU调度和分配的基本单元,是最小的执行单元。
PS:不同进程之间的连续虚拟地址空间被MMU映射到不同的离散物理地址,因此不同进程间的用户空间的数据是不共享的。此外,内核空间的代码和数据是不同进程之间共享的。

协程(coroutine),协同程序?

协程又称微线程,是一段子程序。在执行过程中,子程序内部可以中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。(在一个子程序中中断,去执行其他子程序,不是函数调用!)
协程是子程序的切换而不是线程的切换,和多线程相比,节省了线程切换的开销,执行效率高。
PS:协程切换完全在用户态进行,它的开销只有切换CPU上下文;线程切换只有最高权限的内核态才能完成,它的开销除了CPU上下文切换,还有用户态和内核态的切换的开销以及线程调度算法完成线程调度的开销。
PS:多线程+协程,即充分用多核,又充分发挥协程的高效率,可以获得极高的性能。

生产者消费者模式?

生产者和消费者问题是线程模型的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间中取走产品,当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞
单生产者-单消费者:缓存队列+互斥量+条件变量实现。(队列模拟存储空间;互斥量保证多个读写线程之间互斥;条件变量保证队列为空时消费者线程会被阻塞,等待队列非空,队列为满时生产者线程会被组设,等待队列非满) -> 应用场景:缓存IO(如TCP套接字的缓冲区)
简单的实现:
维护两个位置变量和条件变量:

size_t read_pos; // 消费者读取产品位置.
size_t write_pos; // 生产者写入产品位置.
std::mutex mtx; // 互斥量,保护产品缓冲区
std::condition_variable repo_not_full; // 条件变量, 指示产品缓冲区不为满.
std::condition_variable repo_not_empty; // 条件变量, 指示产品缓冲区不为空.

当(write_pos+1)%repository_size == read_pos时表明队列已满,要阻塞生产。(repository_size为存储空间(缓存)的大小)

...
while(((ir->write_pos + 1) % repository_size) == ir->read_pos) { 
// item buffer is full, just wait here.
    (ir->repo_not_full).wait(lock); // 生产者等待"产品库缓冲区不为满"这一条件发生.
}
... // 写入产品,写入位置后移
(ir->repo_not_empty).notify_all(); // 通知消费者产品库不为空.

当write_pos == read_pos时,表明队列为空,需要阻塞消费者。

...
while(ir->write_pos == ir->read_pos) {
// item buffer is empty, just wait here
    (ir->repo_not_empty).wait(lock); // 消费者等待"产品库缓冲区不为空"这一条件发生.
}
... // 读取产品,读取位置后移
(ir->repo_not_full).notify_all(); // 通知消费者产品库不为满.

多生产者-多消费者:需要额外维护消费者取走产品的计数器和生产者放入产品的计数器,用于判断程序是否要结束。
PS:生产者消费者模式还可以用协程实现。改用协程,生产者生产消息后,直接通过yield 跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高。(参考博客
协程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章