我有一个棘手的时间了解如何使用信号量在两个进程之间交替控制 . 这是一个过程处理代码的人为例子 .
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 回答
这是因为流IO缓冲
stdout
上的输出,直到两者之一缓冲区已满
在
stdout
上调用fflush()
遇到换行符(
\n
)在你的程序中,每个进程都会在将其内容发送到
stdout
之前填充缓冲区,使得一个进程长时间运行,然后运行另一个进程 . 使用\n
终止printf
语句的格式字符串,您将在第一个程序中看到更像您期望的行为 .我不确定为什么你的信号量不起作用 - 我对系统V信号量知之甚少,但是在你分叉后你得到信号量对我来说似乎是一个红旗 . 对于更常见的POSIX信号量,信号量必须在内存中,两个进程都可以看到它是两个信号量 .
无论如何,假设您的
get_semaphore()
函数做正确的事情来共享信号量,仍然存在一个问题,因为无法保证当一个进程发信号通知信号量时,另一个进程很快就会启动它以便在第一个进程之前再次获取它 . 进程循环并 grab 它自己 .你需要两个信号量,一个用于父信号,一个用于孩子信号量 . 在打印之前,每个进程都应该等待自己的信号量 . 打印完成后,每个进程都应发出其他信号量的信号 . 此外,一个信号量应该初始化,计数为1,另一个信号量应该初始化为0 .
信号量有两个一般用例 . 一个是互斥,第二个是同步 . 你的代码中做了什么是相互排斥 . 您真正想要的是父进程和子进程之间的同步(交替) .
让我解释一下:
相互排斥意味着在任何时候只有一次进程可以访问"critical section",这是一段代码,您希望一次只能访问一个进程/线程 . 关键部分通常有一个操作共享资源的代码 .
来到您的代码,因为您只使用了一个信号量,所以无法保证允许每个进程进入临界区的“顺序” . 例如:代码中的sem_wait(id)可以由任何进程执行,并且两个进程不必交替进行 .
对于进程同步(更具体地说是交替),您需要为父级使用两个信号量,为子级使用另一个信号量 .
您需要将一个信号量(在我的情况下为子)初始化为1,将第二个信号量初始化为零 . 这样,只有两个进程才开始,而另一个进入等待 . 一旦孩子完成打印,它就会向父母发出信号 . 现在,孩子的信号量值为零,因此当孩子发出信号的父母执行时,等待等待(child_sem) . 下一次,父母发出信号,然后执行 . 这在交替序列中继续并且是典型的同步问题 .