如何控制一个进程运行的核心?

我可以理解如何编写一个使用多个进程或线程的程序:fork()一个新进程并使用IPC,或者创build多个线程并使用这些types的通信机制。

我也理解上下文切换。 也就是说,只有一个CPU,操作系统为每个进程安排时间(并且有大量的调度algorithm在那里),从而实现同时运行多个进程。

而现在我们有了多核处理器(或者多处理器的计算机),我们可以在两个独立的内核上同时运行两个进程。

我的问题是关于最后的情况:内核如何控制一个进程运行的核心? 哪些系统调用(在Linux甚至Windows中)在特定内核上调度进程?

我问这个问题的原因是:我正在为一个项目寻找计算方面的最新课题 – 我select了多核架构。 在如何在这种环境下编程(如何监视死锁或竞赛状况)似乎有很多材料,但在控制单个核心本身方面却没有太多的材料。 我希望能够编写一些演示程序,并给出一些汇编指令或C代码,以便大意:“看,我在第二个内核上运行一个无限循环,查看特定内核的 CPU利用率高峰” 。

任何代码示例? 还是教程?

编辑:澄清 – 很多人都说这是操作系统的目的,而且应该让操作系统照顾这个。 我完全同意! 但是,我所要求的(或试图去感受)是操作系统实际做到的。 不是调度algorithm,而是更多“一旦核心被选中,必须执行什么指令才能让核心开始获取指令?

正如其他人所说,处理器亲和力是特定于操作系统的 。 如果你想在操作系统的范围之外做到这一点,你会感到很开心,因此我的意思是痛苦。

也就是说,其他人也提到了Win32的SetProcessAffinityMask 。 没有人提到过设置处理器亲和力的Linux内核方法,所以我会。 您需要使用sched_set_affinity函数。 这里有一个很好的教程如何。

通常由系统决定应用程序将运行在哪个核心上。 但是,您可以将应用程序的“关联”设置为特定内核,以告诉操作系统只在该内核上运行应用程序。 通常这不是一个好主意,但有一些罕见的情况下可能是有道理的。

要在Windows中执行此操作,请使用任务pipe理器,右键单击该过程,然后select“设置亲和性”。 您可以使用SetThreadAffinityMask,SetProcessAffinityMask或SetThreadIdealProcessor等函数在Windows中以编程方式执行此操作。

ETA:

如果您对操作系统的调度方式感兴趣,您可能需要查看以下链接:

关于上下文切换的Wikipedia文章

维基百科关于调度的文章

在linux内核中进行调度

对于大多数现代操作系统,操作系统安排一个线程在核心上执行一小段时间。 当时间片到期,或者线程执行IO操作,导致它自动产生内核时,OS将调度另一个线程在内核上运行(如果有任何线程准备运行)。 具体哪个线程的调度取决于操作系统的调度algorithm。

上下文切换的具体实现细节取决于CPU和操作系统。 它通常会涉及到切换到内核模式,操作系统保存前一个线程的状态,加载新线程的状态,然后切换回用户模式,并恢复新加载的线程。 上面链接到的上下文切换文章对此有更多的细节。

什么都没有告诉核心“现在开始运行这个过程”。

核心不会看到进程,只知道可执行代码和各种运行级别以及可执行指令的相关限制。

计算机启动时,为了简单起见,只有一个核心/处理器处于活动状态,并且实际运行任何代码。 那么如果操作系统是多处理器的能力的话,它会使用一些系统特定的指令来激活其他内核,而其他内核很可能从与其他内核完全相同的地方接收并从那里运行。

因此,调度程序所做的是通过操作系统内部结构(任务/进程/线程队列)来查找并select一个,并将其标记为运行在其核心。 然后在其他核心上运行的其他调度程序实例将不会触及它,直到任务再次处于等待状态(并且未标记为固定到特定核心)。 在任务被标记为正在运行之后,调度程序执行切换到用户区,任务在之前暂停的位置恢复。

从技术上讲,没有什么能够阻止内核在同一时间运行完全相同的代码(以及许多解锁函数),但是除非代码被写入期望的内容,否则它可能会让自己满意。

情景变得更奇特的内存模型(上面假设“通常”线性单工作内存空间),其中内核不一定都看到相同的内存,并可能需要从其他内核的离合器获取代码,但它更容易处理保持任务固定到核心(AFAIK索尼PS3架构与SPU的就是这样)。

OpenMPI项目有一个库,用于以便携的方式在Linux上设置处理器关联 。

有些时候,我已经在一个项目中使用它,它工作得很好。

警告:我不知道在找出操作系统如何对内核进行编号方面存在一些问题。 我在一个2个Xeon CPU系统中使用了4个内核。

看一下cat /proc/cpuinfo可能会有所帮助。 在我使用的盒子上,这很奇怪。 下降的输出是在结束。

显然,编号均匀的核心位于第一个cpu上,编号奇数的核心位于第二个cpu上。 但是,如果我没有记错的话,caching就有问题了。 在这些英特尔至强处理器上,每个CPU上的两个内核共享其二级高速caching(我不记得该处理器是否具有三级高速caching)。 我认为虚拟处理器0和2共享一个L2高速caching,1和3共享一个,4和6共享一个和5和7共享一个。

由于这种怪异(1.5年前,我找不到有关Linux中进程编号的任何文档),我会小心做这种低级别的调整。 但是,显然有一些用途。 如果你的代码运行在几种机器上,那么这样做可能是值得的。 另一个应用程序将使用像StreamIt这样的特定领域的语言,编译器可以做这个肮脏的工作,并计算一个聪明的时间表。

 processor : 0 physical id : 0 siblings : 4 core id : 0 cpu cores : 4 processor : 1 physical id : 1 siblings : 4 core id : 0 cpu cores : 4 processor : 2 physical id : 0 siblings : 4 core id : 1 cpu cores : 4 processor : 3 physical id : 1 siblings : 4 core id : 1 cpu cores : 4 processor : 4 physical id : 0 siblings : 4 core id : 2 cpu cores : 4 processor : 5 physical id : 1 siblings : 4 core id : 2 cpu cores : 4 processor : 6 physical id : 0 siblings : 4 core id : 3 cpu cores : 4 processor : 7 physical id : 1 siblings : 4 core id : 3 cpu cores : 4 

正如其他人所说,它是由操作系统控制的。 根据操作系统的不同,它可能会也可能不会提供系统调用,使您可以影响给定进程执行的内核。 但是,您通常应该让操作系统执行默认行为。 如果您有一个运行37个进程的4核心系统,其中34个进程正在hibernate,那么将会将其余3个活动进程安排到不同的核心上。

在专用的multithreading应用程序中,您可能只会看到使用核心关系提高速度。 例如,假设您有一个带有2个双核处理器的系统。 假设你有一个拥有3个线程的应用程序,并且两个线程在同一组数据上运行很多,而第三个线程使用不同的一组数据。 在这种情况下,通过在同一个处理器上交互的两个线程和另一个处理器上的第三个线程,您将从中受益最多,因为它们可以共享一个caching。 操作系统不知道每个线程需要访问什么内存,所以它可能不会适当地分配线程到内核。

如果您对操作系统如何感兴趣,请阅读日程安排 。 Intel 64和IA-32架构软件开发人员手册中详细介绍了x86上多处理器的细节。 第3A卷第7章和第8章包含相关信息,但要记住这些手册是非常技术性的。

操作系统知道如何做到这一点,你不必这样做。 如果指定运行哪个内核,则可能遇到各种问题,其中一些实际上可能会减慢进程速度。 让操作系统找出来,你只需要启动新的线程。

例如,如果你告诉一个在核心x上启动的进程,但是核心x已经处于沉重的负载下,那么比如果你让操作系统处理它,情况会更糟糕。

我不知道汇编指令。 但是Windows API函数是SetProcessAffinityMask 。 您可以看到前段时间我拼凑在一起的示例 ,仅在一个内核上运行Picasa

要找出处理器的数量,而不是使用/ proc / cpuinfo,只需运行:

 nproc 

要在一组特定处理器上运行进程:

 taskset --cpu-list 1,2 my_command 

会说我的命令只能运行在cpu 1或2上。

在4个不同的处理器上运行一个程序使用参数化。 该scheme的论据告诉它做一些不同的事情:

 for i in `seq 0 1 3`; do taskset --cpu-list $i my_command $i; done 

一个很好的例子就是在一个数组中处理8百万个操作,所以0到(2mil-1)到处理器1,2mil到(4mil-1)到处理器2,等等。

您可以通过使用apt-get / yum安装htop并在命令行上运行来查看每个进程的负载:

  htop