liam0205.github.io icon indicating copy to clipboard operation
liam0205.github.io copied to clipboard

程序员的自我修养(二):操作系统、进程与线程 | 始终

Open Liam0205 opened this issue 6 years ago • 23 comments

https://liam.page/2017/01/17/layers-and-operation-system/

前作简单介绍了计算机硬件的发展历史和当前计算机硬件的基本结构。我们知道,作为软件开发者,我们几乎只需要知道计算机硬件由 CPU、内存和 I/O 设备组成就可以了,其他细节不一定要详细地了解。 本文我们将简单介绍计算机的层次化结构和操作系统;而后讨论 Linux 中的进程与线程。

Liam0205 avatar Jan 12 '19 08:01 Liam0205

Dear dalao: 我也是计算机专业的,关于这篇文章,请教个问题: 问题背景: 我写了一个程序,这个程序里面有两个子函数(F1,F2),我创建了两个进程(P1,P2),主函数执行时,P1去执行F1,P2去执行F2。假设两个进程之间没有通信和临界资源的访问冲突。 问题描述: 那么操作系统会为这个程序创建几个进程控制块PCB?假设此时计算机只执行这个程序,此时计算机中有几个进程?

是一个进程?因为进程是这个程序的执行过程,只有一个程序。 还是两个进程?因为程序中创建了两个进程?

可能是个幼稚的问题,但是此时有些许的糊涂,望解答。

muyuuuu avatar Feb 26 '19 14:02 muyuuuu

@muyuuuu 额,你可以去掉「也」字。因为我不是计算机专业的,我属于半路出家。

首先你要搞清楚程序和进程的区别。程序是磁盘上保存的那个可执行文件,对 Linux 来说,是 ELF 可执行文件。进程是操作系统把可执行文件装载(load)到内存中,开辟了虚存空间,准备好了一系列变量,然后把控制权交给可执行程序之后,在系统中实际「运行着的」实例。

所以,如果主函数 fork 出 P1 来执行 F1,接着 fork 出 P2 来执行 F2。那系统里有这个程序的三个进程: main 函数执行着的进程、 F1 执行着的进程和 F2 执行着的进程。

此外要注意,P1 和 P2 是两个进程,虚存空间相互独立,不存在线程安全意义上的通信和临界资源访问冲突的问题。进程间通信需要操作系统内核支持的。

Liam0205 avatar Feb 26 '19 15:02 Liam0205

23333 半路出家都比我这种学过操作系统的人专业.(说来惭愧,我看过你的很多博客,在C++算法,数据库,latex,vim等好多方面都比不上,我不好意思说我是计算机专业的了)

解释的很详细,涉及到线程的话,对临界资源加锁还是会的。

另外,最近在研究python的多线程编程(其实语言无所谓),发现了新大陆——协程。大佬对这个有概念嘛,我也是刚接触到协程,据说,只是据说。比线程强大,没有资源冲突,处理IO密集的任务(比如爬虫)。

这里是我的博客:https://muyuuuu.github.io/2019/02/26/process/ 介不介意看一下有没有描述不当的地方?

muyuuuu avatar Feb 26 '19 16:02 muyuuuu

@muyuuuu 协程比线程的好处在于切换协程不需要切换 CPU 上下文,这样速度会更快。

之前在写过两篇基于 Python2 的协程的文章:

  • https://liam.page/2017/06/30/understanding-yield-in-python/
  • https://liam.page/2017/06/30/generator-coroutine-in-python-basic-topics/

Python3 对协程的支持更好,这两篇文章其实写出来就已经 outdated 了。

Liam0205 avatar Feb 27 '19 03:02 Liam0205

@muyuuuu 文章我看过了。首先为持续总结输出点赞。那边 issue 没初始化,无法评论,就放在这里了。

在这里,并不是睡眠1秒后输出一个’yes’,而是在睡眠4次之后,总共最后一起输出4个yes。

其实是每次 sleep 前输出 yes ,但是这个输出并不是输出到控制台,而是输出到缓冲区。缓冲区的内容如果没有被输出到控制台之前就挂掉,缓冲区里的内容就不会有机会输出了。你可以用 sys.stdout.flush() 刷新缓冲区,将缓冲区的内容全部输出到控制台再 sleep 就好了。

另外就是 Python 里的 mutex 最好是结合 with 语法来用,这样省下写 m.release() 的功夫,也不会忘记。这种情况下 with 的作用类似 C++ 里的 RAII。

Liam0205 avatar Feb 27 '19 03:02 Liam0205

你好我想请问下(nullptr == pInst)里面, nullptr写在前面的好处是什么呀?

hjml69351 avatar Oct 09 '19 13:10 hjml69351

@hjml69351 万一漏了一个等号,你猜会怎样?

Liam0205 avatar Oct 10 '19 01:10 Liam0205

翻到你的网站突然感觉自己发现了宝藏男孩

NINGNINGSHI avatar Nov 05 '19 21:11 NINGNINGSHI

@NINGNINGSHI 谢谢……

Liam0205 avatar Nov 05 '19 23:11 Liam0205

能不能谈一下用 pthread create的线程能不能被Linux的CFS调度器所识别及参与调度。如果CFS调度不了这里的thread,那这里的thread是不是就相当于main里面调用的一个函数,实际CPU还是把整个main看成一个process顺序执行的,就算有多个空闲的CPU核也不会同时执行一个process里的多个thread呢?网上一直找不到这方面很清楚的解释,难道真是因为linux现在版本的调度器调度不了pthread里create的thread吗?多谢。

debing-wei avatar Jan 01 '20 02:01 debing-wei

@Debbin 能不能谈一下用 pthread create的线程能不能被Linux的CFS调度器所识别及参与调度。如果CFS调度不了这里的thread,那这里的thread是不是就相当于main里面调用的一个函数,实际CPU还是把整个main看成一个process顺序执行的,就算有多个空闲的CPU核也不会同时执行一个process里的多个thread呢?网上一直找不到这方面很清楚的解释,难道真是因为linux现在版本的调度器调度不了pthread里create的thread吗?多谢。

Linux 内核不区分线程与进程,这个特性并没有改变过。

psi-cmd avatar Apr 15 '20 16:04 psi-cmd

编译器优化的那个例子,pthread库,好像不用volatile。 https://stackoverflow.com/questions/3208060/does-guarding-a-variable-with-a-pthread-mutex-guarantee-its-also-not-cached

fourier307 avatar Nov 02 '20 08:11 fourier307

另外,不少人说 多线程不用加 volatile https://stackoverflow.com/questions/35345899/why-is-volatile-keyword-not-needed-for-thread-synchronisation

fourier307 avatar Nov 02 '20 09:11 fourier307

@fourier307 C++ 里的 volatile 和多线程没有关系,在 Java 当中它可以保证操作的原子性和顺序一致性。

文中提到 volatile 是用来阻止一些编译器优化:防止写入寄存器而不写回内存;防止编译器乱序。提到 volatile 跟多线程同步本身没关系。

C++ 的 volatile 可以阻止编译器乱序,但是不能阻止 CPU 的乱序执行(OOO)。这是 volatile 不能用于 C++ 里的线程同步的原因。当然,MSVC 的 volatile 做了扩展,和 Java 里的是一个语义。这时候它是顶用的。

你说的多线程不用 volatile 的问题,我也写过。参看:https://liam.page/2018/01/18/volatile-in-C-and-Cpp/

Liam0205 avatar Nov 02 '20 09:11 Liam0205

@fourier307 编译器优化的那个例子,pthread库,好像不用volatile。 https://stackoverflow.com/questions/3208060/does-guarding-a-variable-with-a-pthread-mutex-guarantee-its-also-not-cached

这里说的 cache 是 CPU 缓存。我说的用 volatile 解决的是寄存器的问题,不是缓存的问题。

缓存的问题,我这几天也在整理。最近会写一篇,你可以关注一下。

Liam0205 avatar Nov 02 '20 09:11 Liam0205

@Liam0205

@fourier307 编译器优化的那个例子,pthread库,好像不用volatile。 https://stackoverflow.com/questions/3208060/does-guarding-a-variable-with-a-pthread-mutex-guarantee-its-also-not-cached

这里说的 cache 是 CPU 缓存。我说的用 volatile 解决的是寄存器的问题,不是缓存的问题。

缓存的问题,我这几天也在整理。最近会写一篇,你可以关注一下。 越说越懵逼了,我理解像这篇文章说的,lock语义本身会保证内存的可见性,也就是只要在lock保护下,thread 1更新了变量x,thread 2一定是可以看到这个更新的,不需要加volatile,如果thread 2读的是寄存器里面的旧值,应该是不符合lock语义的。 https://stackoverflow.com/questions/35345899/why-is-volatile-keyword-not-needed-for-thread-synchronisation

我再捋捋。。。

fourier307 avatar Nov 02 '20 10:11 fourier307

@Liam0205 @fourier307 C++ 里的 volatile 和多线程没有关系,在 Java 当中它可以保证操作的原子性和顺序一致性。

文中提到 volatile 是用来阻止一些编译器优化:防止写入寄存器而不写回内存;防止编译器乱序。提到 volatile 跟多线程同步本身没关系。

C++ 的 volatile 可以阻止编译器乱序,但是不能阻止 CPU 的乱序执行(OOO)。这是 volatile 不能用于 C++ 里的线程同步的原因。当然,MSVC 的 volatile 做了扩展,和 Java 里的是一个语义。这时候它是顶用的。

你说的多线程不用 volatile 的问题,我也写过。参看:https://liam.page/2018/01/18/volatile-in-C-and-Cpp/

越说越懵逼了,我理解像这篇文章说的,lock语义本身会保证内存的可见性,也就是只要在lock保护下,thread 1更新了变量x,thread 2一定是可以看到这个更新的,不需要加volatile,如果thread 2读的是寄存器里面的旧值,应该是不符合lock语义的。 https://stackoverflow.com/questions/35345899/why-is-volatile-keyword-not-needed-for-thread-synchronisation

我再捋捋。。。

fourier307 avatar Nov 02 '20 10:11 fourier307

关键是平常没见几个人用volatile

fourier307 avatar Nov 02 '20 10:11 fourier307

@fourier307 还有一个问题。这篇文章其实是不基于任何特定架构的。在 x86 限定下的话,其实这里很多都不对。

volatile 的话,如果不接触驱动编程,可能一般就是用不着。

Liam0205 avatar Nov 02 '20 10:11 Liam0205

@Liam0205 @fourier307 还有一个问题。这篇文章其实是不基于任何特定架构的。在 x86 限定下的话,其实这里很多都不对。

volatile 的话,如果不接触驱动编程,可能一般就是用不着。

多谢,大概明白了。volatile那一段有比较大的前提限制,大部分其他情况并不会有问题。

“Basically whenever dealing with memory that is not under the control of your program (hardware registers, memory mapped IO).”

这样理解对么?

fourier307 avatar Nov 03 '20 02:11 fourier307

@fourier307 对的,是这个意思。

Liam0205 avatar Nov 03 '20 02:11 Liam0205

@fourier307 hmmm……

过几天吧,最近好忙……

Liam0205 avatar Nov 03 '20 03:11 Liam0205

借口->接口

ssk-wh avatar Oct 10 '23 10:10 ssk-wh