首页 文章

使用信号量同步进程

提问于
浏览
1

我有一个棘手的时间了解如何使用信号量在两个进程之间交替控制 . 这是一个过程处理代码的人为例子 .

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 回答

  • 1

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

    这是因为流IO缓冲 stdout 上的输出,直到两者之一

    • 缓冲区已满
      stdout 上调用

    • fflush()

    • 遇到换行符( \n

    在你的程序中,每个进程都会在将其内容发送到 stdout 之前填充缓冲区,使得一个进程长时间运行,然后运行另一个进程 . 使用 \n 终止 printf 语句的格式字符串,您将在第一个程序中看到更像您期望的行为 .

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

    无论如何,假设您的 get_semaphore() 函数做正确的事情来共享信号量,仍然存在一个问题,因为无法保证当一个进程发信号通知信号量时,另一个进程很快就会启动它以便在第一个进程之前再次获取它 . 进程循环并 grab 它自己 .

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

  • 1

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

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

    来到您的代码,因为您只使用了一个信号量,所以无法保证允许每个进程进入临界区的“顺序” . 例如:代码中的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) . 下一次,父母发出信号,然后执行 . 这在交替序列中继续并且是典型的同步问题 .

相关问题