2017-06-12 117 views
0

我目前正在研究信号量和互斥实际如何工作,并遇到以下问题。信号量等待()在多核

我们假设我们在CPU上有两个内核。我们有两个进程,每个核心都运行一个进程。现在我们正在呼吁两个内核的wait()调用,因为我们希望进入一个关键部分:如果两个内核并行执行代码

wait(){ 
    while(s.value <= 0) 
    // busy waiting 
    s.value--; 
} 

,初始信号值是1,无论阅读while循环语句,其结果为false(自s = 1)。这意味着,几乎在同一时间减少信号量,其结果为s = -1。现在,两个流程同时进入他们的关键部分,而这在互斥方面是不可能的。

我得到什么错误?

感谢您的澄清。

+1

非零计数和减量的等待通常在操作系统的内核中进行处理,方式是使检查和递减为原子操作。 – rcgldr

回答

1

您可以使用java和其他语言同步来同步代码块或函数,并避免此类问题,因为当一个线程正在执行同步方法或块时,调用同步方法的所有其他线程或者阻塞(挂起执行)直到第一个线程完成。

1

正如你已经发现的那样,这些不是简单的用户空间函数 - 它会非常棘手(不可能?)让你自己实现一个信号量或互斥量,而不使用内核提供的函数。

例如,在Linux上您有:


你有概念,正确的,但两个操作(检查和INC/dec)需要在“原子”的方式进行 - 简单地说,这意味着它们作为无法拆分的一个操作发生(在Linearizability上阅读)。

此外,值得注意的是,你已经实现了一个“忙循环”,它与操作系统工作时是一个坏主意因为你是剥夺了其他任务/从CPU时间,提高电力使用的过程,而这样做没有实际的工作 - 上面提到的功能将“”与0%的CPU使用率,而你的将“”与100%的CPU使用率,如果有机会。


你将有更多的运气试图“玩”这样的概念在单一内核上运行时,(你可以限制你的应用程序执行,以单核 - 看sched_setaffinity()

然而,即使你会发现你很难控制你的程序是否在不好的时间安排好,导致你的示例应用程序以完全相同的方式中断。通过拨打sched_setscheduler()SCHED_FIFO可能会进一步提高正确操作的机会,尽管我还没有亲身体验过这方面的经验(ref,ref)。

无论哪种方式,这不可能是100%可靠的,而内核支持的功能应该是。


如果你为它,那么最好的方式与在自己的职能的实现细节发挥将是实现一个非常基本的循环调度(不中断的任务),并运行它在微观或单一线程中。

1

使用信号量的内置函数可能会更好。要等待使用pthreads库,您需要使用pend()函数,并且要使信号量可用,您可以使用post()函数。

pend将等到s的值大于0.然后它会自动递减s的值,然后继续向前。当然,如何正确实施取决于图书馆。它可能看起来像这样:

sem_wait(){ 
    // Make a kernel function call to guarantee that the following code is atomic 
    enter_critical_section(); 
    // Test the semaphore value. If it is positive, let the code continue. 
    int sem_val = s.value; 
    s.value--; 
    if (sem_val > 0) { 
     exit_critical_section(); 
     return; 
    } 
    else { 
    // At this point we know the semaphore value is negative, so it's not available. We'd want to block the caller and make a context switch to a different thread or task. 
    ... // Put the current thread on list of blocked threads 
    ... // Make a context switch to a ready thread. 
    } 
    // When the semaphore is made available with a sem_post() function call somewhere else, there will eventually be a context switch back to this (blocked) thread. Simply exit the critical section, return back to the calling function, and let the program execute normally. 
    exit_critical_section(); 
} 

这段代码实际上是基于一个为类实现的RTOS。每个实现看起来都会非常不同,而且我还没有在这里展示很多,但它应该给你一个关于它如何工作的基本概念。

最后,在您的假设情况下,您提到了共享单个信号量的2个独立进程。这是可能的,你只需要做出正确的函数调用来使信号量在进程之间共享。