使用信号量

时间:2017-04-07 12:48:02

标签: c linux process semaphore

我很难理解如何使用信号量在两个进程之间交替控制。这是一个过程处理代码的人为例子。

int pid = fork();

if (pid) {
  int counter = 0;
  while (true) {
    counter += 1;
    printf("P%d = %d", pid, counter);
  }
} else {
  int counter = 0;
  while (true) {
    counter += 1;
    printf("P%d = %d", pid, counter);
  }
}

我期待上面的代码并行运行,但似乎控制流会立即继续进行分叉进程,并且稍后才会继续执行父进程。

这从根本上打破了我现有的代码,它使用信号量来控制哪个进程可以起作用。

int id = get_semaphore(1);
int pid = fork();

if (pid) {
  int counter = 0;
  while (true) {
    sem_wait(id);
    counter += 1;
    printf("P%d = %d\n", pid, counter);
    sem_signal(id);
  }
} else {
  int counter = 0;
  while (true) {
    sem_wait(id);
    counter += 1;
    printf("P%d = %d\n", pid, counter);
    sem_signal(id);
  }
}
  • sem_wait帮助器只是从信号量值中减去1并阻塞,直到结果为> 0(在引擎盖下使用semop。)

  • sem_signal助手只是将信号量值加1(在引擎盖下使用semop)。

我希望代码在两个进程之间交替,使用sem_wait来阻止,直到另一个进程使用sem_signal释放资源。所需的输出是:

P1 = 0
P0 = 0
P1 = 1
P0 = 1
...

但是,由于进程之间的初始执行延迟,子进程获取可用的信号量资源,使用它来打印数字,然后恢复它并循环 - 此时资源再次可用,因此它继续等待其他过程。

如果进程自身发布,阻止进程使用资源的最佳方法是什么?

2 个答案:

答案 0 :(得分:1)

信号量有两个一般用例。一个是互斥,第二个是同步。你的代码中做了什么是相互排斥。你真正想要的是父进程和子进程之间的同步(交替)。

让我解释一下:
相互排斥意味着在任何时候只有一次进程可以访问“关键部分”,这是一段代码,您希望一次只能访问一个进程/线程。关键部分通常有一个操作共享资源的代码。

来到您的代码,由于您只使用了一个信号量,因此无法保证允许每个进程进入临界区的“顺序”。 例如:代码中的sem_wait(id)可以由任何进程执行,并且两个进程不必交替进行。

对于进程同步(更具体地说是交替),您需要为父级使用两个信号量,为子级使用另一个信号量。

    Sample code:
    int pid = fork();

    int parent_sem = get_semaphore(0);
    int child_sem = get_semaphore(1);

    if (pid) {
       int counter = 0;
       while (true) {
           sem_wait(child_sem);
           counter += 1;
           printf("P%d = %d", pid, counter);
           sem_signal(parent_sem);
       }
     } else {
       int counter = 0;
       while (true) {
            sem_wait(parent_sem);
            counter += 1;
            printf("P%d = %d", pid, counter);
            sem_signal(child_sem);
       }
     }

您需要将一个信号量(在我的情况下为子)初始化为1,将第二个信号量初始化为零。这样,只有两个进程才开始,而另一个进入等待。一旦孩子完成打印,它就会向父母发出信号。现在,孩子的信号量值为零,因此当孩子发出信号的父母执行时,等待等待(child_sem)。下一次,父母发出信号,然后执行。这在交替序列中继续,并且是典型的同步问题。

答案 1 :(得分:1)

  

对于分叉进程,控制流似乎立即继续,并且稍后仅为父进程恢复

这是因为流IO缓冲fflush()上的输出直到

  • 缓冲区已满
  • stdout 上调用
  • \n
  • 遇到换行符(stdout

在你的程序中,每个进程都会填充缓冲区,然后将其内容发送到printf,使一个进程的外观运行很长一段时间,然后运行另一个进程。使用\n终止get_semaphore()语句的格式字符串,您会在第一个程序中看到更符合您预期的行为。

我不确定为什么你的信号量不起作用 - 我对系统V信号量不是很了解,但是在你分叉后你得到信号量对我来说似乎是一个红旗。对于更常见的POSIX信号量,信号量必须在内存中,两个进程都可以看到它的两个信号量。

无论如何,假设你的{{1}}函数做正确的事情来共享信号量,仍然存在一个问题,因为无法保证当一个进程发出信号信号时,另一个进程很快就会启动它在第一个进程循环并抓住它之前再次抓住它。

您需要两个信号量,一个用于父信号,一个用于孩子信号量。在打印之前,每个进程都应该等待自己的信号量。打印完成后,每个进程都应发出其他信号量的信号。此外,一个信号量应初始化为1,另一个应初始化为0。