SMP多线程如何共享内存和中断?

9

我正在为我的内核做一些输入缓冲区的工作,有一些问题需要解答。在双核机器上,我知道可以同时运行多个“进程”。但我不知道操作系统和各个程序如何保护数据冲突。

关于这个话题,我想了解两件事情:

(1) 中断发生在哪里?它们是否保证只在一个核心上发生,而不是在另一个核心上发生?这个特性能否用来确保一个核心上的实时操作不会被其他核心上的文件IO等操作所中断?(我逻辑上认为中断会发生在第一个核心上,但这总是正确的吗?如何判断?或者每个核心是否都有自己的中断设置?这是否会导致每个核心对同一个中断同时做出反应,可能以不同的方式?)

(2) 双核处理器如何处理操作码内存冲突?如果一个核心在读取内存中的某个地址,恰好在同一时间,另一个核心正在向该内存中的相同地址写入数据,会发生什么?会抛出异常吗,还是会读取一个值?(我认为无论如何写入都会成功)。如果读取一个值,它是否保证是冲突发生时的旧值或新值中的一个?

我知道程序最好是避免这些复杂情况,但操作系统肯定不能指望所有程序都能做到这一点,必须能够处理这些事件而不会崩溃。


多处理器操作系统的设计/实现并非易如反掌。 :-) - Brian Knoblauch
3
一般来说,操作系统的设计并不是一项容易完成的任务,但这绝对不会阻止我想要去做。^_^ - Blank
如果您需要一个最小的示例来测试这些内容:https://dev59.com/jXNA5IYBdhLWcg3wZ81R#33651438 - Ciro Santilli OurBigBook.com
3个回答

7
在x86处理器中,这由APIC处理。您可以在Intel® 64和IA-32架构软件开发人员手册中查看详细信息,特别是在第9章的第3卷中,以及在x2APIC规范中。
如果您不想了解所有细节,我只会简要概述。
中断可能来自三个不同的来源:
1. 外部引脚(在Intel处理器中,直到Core i7,您有LINT0、LINT1、SMI、INIT。我不知道它们在Core i7或AMD或Via处理器中被称为什么)。 2. 总线事务。这是一个线程向另一个线程发送中断的主要方式。它们被称为IPI - Inter-Processor Interrupts。 3. 内部事件,例如热中断、事件监视器或内部错误。
每个逻辑处理器(SMT系统中的线程、非SMT多核系统中的核心、非SMT非多核系统中的处理器)都有一个APIC。APIC控制逻辑处理器如何响应任何此类中断。
简而言之:
SMI和INIT引脚总是路由到SMI或INIT。
如果禁用了APIC,则将LINT0路由到INTR,将LINT1路由到NMI,并忽略IPI。
如果启用了APIC:
1. LINT0、LINT1、热事件、事件监视器和错误在LVT(逻辑向量表)中各自具有一个条目,指定它是否被屏蔽,如果不是,则它将成为什么类型的中断。 2. 处理IPI。IPI包括中断类型(即INTR、SMI、NMI、INIT、SIPI)和目标。每个逻辑处理器都有一个APIC-ID。如果IPI的目标与其ID匹配,则处理该中断。否则,它会忽略它。
如果您想了解有关启用APIC、编程LVT、设置APIC-ID和发送IPI的详细信息,则必须查看我链接的手册。

2
操作系统可以设置中断的处理位置。Linux会负载平衡中断,以便它们可以由两个CPU处理。每个中断处理程序需要获取一个锁来避免在不同的CPU上并发执行相同的处理程序,还需要保护其他运行在非中断上下文中的内核代码,并访问相同的数据结构。但是,我认为可以将给定中断的执行绑定到给定的CPU上。
关于问题(2):所提供的保证基本上与SMP机器提供的保证相同,即不会抛出异常,并且结果取决于谁先执行/提交值到内存/提交值到共享缓存。无论如何,您不能依赖读取的值 - 实际上,所提供的保证要比您期望的弱得多。
在互联网上(在Google或Wikipedia上)查找数据竞争是什么,并从研究如何正确编写Java多线程代码开始。学习这些知识使我更容易理解Linux内核的并发机制。
或者你可以查看C/C++几乎“官方”的内存模型FAQ,Linux内核源代码的Documentation/memory-barriers.txt文件,或者Jeremy Manson关于这个问题的帖子。无论如何,我忘了指出你读取的值不一定是由某个处理器实际写入的。对于32位值,这是由于32位写入是原子的而得到保证的。对于64位值,通常情况下并非如此(我不确定64位平台是否也是如此,但出于可移植性的原因,我通常不依赖这一点)。
无论如何,如果你发现自己在问这个问题,你可能应该改进你的代码所使用的锁。在内核中工作时,你需要先编写自己的自旋锁/信号量库来解决这个问题。
当你说“你的内核”时,不清楚你的意思是什么,但我认为你很可能并不是指“我正在编写的一个内核”。无论如何,我不会让任何问第二个问题的人在我的机器上运行多线程程序 :-).
我理解程序最好应该避免这些复杂情况,但操作系统肯定不能期望程序能做到这一点,需要能够处理这些事件而不会因此崩溃。

回答这个问题是你编写用户空间多线程程序时需要知道的东西。虽然你不需要知道“你读取的值是哪个”,但正因为如此,即使你为特定处理器编写汇编代码,也是实现定义的。简单来说,因为你不能依赖两个并行线程的相对速度。永远不行。

不,恐怕我确实是指我正在编写的内核。我在重新发明轮子,以便更多地了解轮子,我认为学习内核工作原理的最简单方法就是编写一个内核,而且我经历了一次艰难的旅程。^_^ 但这是很好的经验,谁知道,它可能真的会变得有用。 - Blank

1

IA-32参考手册会明确地解答您的问题。

我的直觉是两个内核都会接收中断,而操作系统将对其进行排序。每个内核上可能有一个设置寄存器,指定哪个内核接收哪个中断。

碰撞。不能保证。要更精确,请查看缓存机制以及它们如何解决一致性问题。

关于此事的其他线程:

多核/多CPU机器中的中断如何工作?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接