用 C 中的信号量和信号同步进程

Synchronizing processes with semaphores and signals in C

我必须在 Linux 上用 C 语言编写程序。它必须有 3 个进程 - 首先从 STDIN 读取,通过 FIFO 将消息发送到第二个进程,第二个进程计算接收到的消息的长度并将结果发送到第三个进程(也通过 FIFO),后者将其显示在 STDOUT 上。我必须使用信号量来同步它。我还必须添加信号处理(我正在使用共享内存)——一个信号结束程序,第二个信号停止它,第三个信号恢复。信号可以发送到任何进程。我已经有了一些代码,但它没有正常工作。

第一个问题是同步 - 正如您在 运行 中看到的那样,第二个进程收到了第一条消息,但随后卡住了。第一和第二个进程正在显示它们的消息,但不是第三个。有很相似,所以很混乱。我要发另一条消息,然后P3显示上一条的长度。

第二个问题是信号 - 发送信号后,我必须按回车键(对于 SIGUSR)或发送消息(对于 SIGINT)才能得到服务。

有什么问题吗?我之前发布的内容有一些改进,但它仍然不能正常工作,我没有太多时间来完成它(直到星期一)。我知道这是很多代码,但如果有人能分析第二个和第三个进程的通信,我将不胜感激。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <sys/sem.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>

#define WRITE 1
#define READ 0
#define MEM_ID 1
#define MEM_SIZE 1

#define OK  "[ 3[1;32mOK3[0m ]\n"
#define ERR "[3[1;31mFAIL3[0m] "
#define SIGNAL "3[1;33m3[5m>> SIGNAL <<3[0m\n"

#define S1 SIGINT
#define S2 SIGUSR1
#define S3 SIGUSR2
#define S4 SIGCONT

/* union semun - from POSIX specification for semctl() */
/* NB: on Mac OS X, and apparently in defiance of POSIX, <sys/sem.h> declares union semun */
/*
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short  *array;
} arg;
*/

/*
union semun
{
    int val;
    ushort *array;
};
*/

int process1(void);
int process2(void);
int process3(void);
void signal_callback(int signo);
//void signal_handling(int snd, int rcv);   // JL
void signal_handling(void);     // JL
void sem_down(int semid, int semnum);
void sem_up(int semid, int semnum);

char work = 1, quit = 0;
char inter_snd = 0, inter_rcv = 0;
struct sembuf semstruct;
int sem23, sem12, mem;
char *message;
int res[3];

static const char *fifoname[] = { "1fifo2", "2fifo3" };

static
void main_quit(int n)
{
    printf("%s(): signal %d\n", __func__, n);  // JL
    kill(res[0], S1);
}

int main(void)
{
    //union semun arg;  // JL
    printf("[G] Launching\n");

    signal(SIGINT, main_quit);
    signal(SIGTERM, main_quit);
    // creating FIFO
    printf("[G] Creating FIFO... ");
    res[0] = mkfifo(fifoname[0], 0644);
    res[1] = mkfifo(fifoname[1], 0644);
    if ((res[0] == -1) || (res[1] == -1))
    {
        perror(ERR);
        unlink(fifoname[0]);
        unlink(fifoname[1]);
        return 1;
    }
    else
        printf(OK);

    // create two semaphores and set values
    printf("[G] Creating semaphores... ");
    sem12 = semget(READ, 1, IPC_CREAT | 0644);
    sem23 = semget(WRITE, 1, IPC_CREAT | 0644);

    if ((sem23 == -1) || (sem12 == -1))
    {
        perror(ERR);
        return 1;
    }
    else
        printf(OK);

    printf("[G] Initializing semaphores values... ");
    semctl(sem12, 0, SETVAL, 0);
    semctl(sem12, 1, SETVAL, 1);
    semctl(sem23, 0, SETVAL, 0);
    semctl(sem23, 1, SETVAL, 1);
    printf(OK);

    // creating shared memory
    printf("[G] Reserving shared memory... ");
    mem = shmget(MEM_ID, MEM_SIZE, IPC_CREAT | 0644);
    message = (char *)shmat(mem, 0, 0);

    if (mem == -1)
    {
        perror(ERR);
        return 1;
    }
    else
        printf(OK);

    if ((res[0] = fork()) == 0)
    {
        process1();
        exit(0);
    }

    if ((res[1] = fork()) == 0)
    {
        process2();
        exit(0);
    }

    if ((res[2] = fork()) == 0)
    {
        process3();
        exit(0);
    }

    printf("[G] Building process tree... ");
    if ((res[0] == -1) || (res[1] == -1) || (res[2] == -1))
    {
        perror(ERR);
        return 1;
    }
    else
    {
        printf(OK);
        printf("[G] P1[pid]: %d, P2[pid]: %d, P3[pid]: %d\n", res[0], res[1], res[2]);
    }

    wait(NULL);
    wait(NULL);
    wait(NULL);

    printf("[G] Deleting FIFO... ");
    res[0] = unlink(fifoname[0]);
    res[1] = unlink(fifoname[1]);
    if ((res[0] == -1) || (res[1] == -1))
        perror(ERR);
    else
        printf(OK);

    printf("[G] Freeing shared memory... ");
    res[0] = shmdt((char *)message);
    res[1] = shmctl(mem, IPC_RMID, 0);
    if ((res[0] == -1) || (res[1] == -1))
        perror(ERR);
    else
        printf(OK);

    printf("[G] Deleting semaphores... ");
    res[0] = semctl(sem23, 0, IPC_RMID, 0);
    res[1] = semctl(sem12, 0, IPC_RMID, 0);
    if ((res[0] == -1) || (res[1] == -1))
        perror(ERR);
    else
        printf(OK);

    printf("[G] Ending...\n");
    return 0;
}

int process1(void)
{
    char tab[100];
    FILE *fifoh;

    signal(S1, signal_callback);
    signal(S2, signal_callback);
    signal(S3, signal_callback);
    signal(S4, signal_callback);

    fifoh = fopen(fifoname[0], "w");
    setbuf(fifoh, NULL);

    printf("[P1] Ready.\n");
    //while (fgets(tab, sizeof(tab), stdin) > 0)    // JL
    while (fgets(tab, sizeof(tab), stdin) != 0)     // JL
    {
        if (work)
        {
            sem_down(sem12, WRITE);
            printf("[P1] Sending: %s", tab);
            fprintf(fifoh, "%s\n", tab);
            sem_up(sem12, READ);
        }
        //signal_handling(inter_snd, inter_rcv);    // JL
        signal_handling();  // JL
    }
    fclose(fifoh);
    printf("[P1] Ending...\n");
    return 0;
}

int process2(void)
{
    char tab[100];
    FILE *fifo_in, *fifo_out;

    printf("[P2] Ready.\n");

    fifo_in = fopen(fifoname[0], "r");
    fifo_out = fopen(fifoname[1], "w");

    setbuf(fifo_out, NULL);
    setbuf(fifo_in, NULL);

    signal(S1, signal_callback);
    signal(S2, signal_callback);
    signal(S3, signal_callback);
    signal(S4, signal_callback);

    do
    {
        if (work)
        {
            sem_down(sem12, READ);
            fscanf(fifo_in, "%s", (char *)tab);
            sem_up(sem12, WRITE);
            printf("[P2] Received \"%s\" with length %zu.\n", tab, strlen(tab));
            sem_down(sem23, WRITE);
            fprintf(fifo_out, "%d\n", (int)strlen(tab));
            sem_up(sem23, READ);
        }
        //signal_handling(inter_snd, inter_rcv);    // JL
        signal_handling();  // JL
    } while (!quit);

    fclose(fifo_in);
    fclose(fifo_out);
    printf("[P2] Ending...\n");
    return 0;
}

int process3(void)
{
    FILE *fifo_in;
    int count;

    printf("[P3] Ready.\n");

    signal(S1, signal_callback);
    signal(S2, signal_callback);
    signal(S3, signal_callback);
    signal(S4, signal_callback);

    fifo_in = fopen(fifoname[1], "r");
    setbuf(fifo_in, NULL);

    do
    {
        if (work)
        {
            sem_down(sem23, READ);
            fscanf(fifo_in, "%d\n", (int *)&count);
            sem_up(sem23, WRITE);
            printf("[P3] Received: %d characters.\n", count);
        }
        //signal_handling(inter_snd, inter_rcv);    // JL
        signal_handling();  // JL
    } while (!quit);
    fclose(fifo_in);
    printf("[P3] Ending...\n");
    return 0;
}

//void signal_handling(int snd, int rvc)
void signal_handling(void)
{
    if (inter_snd > 0)
    {
        printf("Signal received...\n");

        semstruct.sem_op = -3;
        semop(sem23, &semstruct, 1);

        *message = inter_snd;
        inter_snd = 0;

        semstruct.sem_op = 3;
        semop(sem12, &semstruct, 1);

        printf("Sending to other processes\n");
        kill(0, S4);
    }
    if (inter_rcv)
    {
        inter_rcv = 0;

        semstruct.sem_op = -1;
        semop(sem12, &semstruct, 1);

        switch (*message)
        {
        case 1:
            printf("Quitting...\n");
            quit = 1;
            break;
        case 2:
            printf("Stopping...\n");
            work = 0;
            break;
        case 3:
            printf("Starting...\n");
            work = 1;
            break;
        default:
            printf("There's garbage in memory :/..\n");
        }
        semstruct.sem_op = 1;
        semop(sem23, &semstruct, 1);
    }
}

void signal_callback(int signo)
{
    printf(SIGNAL);
    switch (signo)
    {
    case S1:
        inter_snd = 1;
        break;
    case S2:
        inter_snd = 2;
        break;
    case S3:
        inter_snd = 3;
        break;
    case S4:
        inter_rcv = 1;
        break;
    }
}

void sem_down(int semid, int semnum)
{
    semstruct.sem_flg = 0;
    semstruct.sem_num = semnum;
    semstruct.sem_op = -1;
    do
    {
        errno = 0;
        semop(semid, &semstruct, 1);
    } while (errno == EINTR);
}

void sem_up(int semid, int semnum)
{
    semstruct.sem_flg = 0;

    semstruct.sem_num = semnum;
    semstruct.sem_op = 1;
    semop(semid, &semstruct, 1);
}

预期行为:

[P3] Ready.
[P2] Ready.
[P1] Ready.
asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
[P3] Received: 3 characters.
as
[P1] Sending: as
[P2] Received "as" with length 2.
[P3] Received: 2 characters.

信号:

应该接收信号并将其发送到所有其他进程。接下来发生的事情在 singnal_handling 中指定。每个进程都应该显示它们的消息 (Quitting/Stopping/etc)。

实际行为:

[P3] Ready.
[P2] Ready.
[P1] Ready.
asd 
[P1] Sending: asd
[P2] Received "asd" with length 3.
dd
[P1] Sending: dd
[P2] Received "dd" with length 2.
[P3] Received: 3 characters. //first message
as
[P1] Sending: as
[P2] Received "as" with length 2.
[P3] Received: 2 characters. //second
asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
[P3] Received: 2 characters. // third

有了信号,嗯...我发现在 SIGINT 之后我可以发送消息,按回车键两次,然后我得到了预期的行为(退出程序)。当我发送另一个信号时,按回车键也有效:

$ kill -s SIGUSR1 2900
$ kill -s SIGUSR2 2900

给出:

asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
[P3] Received: 3 characters.
Signal received...
Sending to other processes
>> SIGNAL <<
Stopping...
>> SIGNAL <<
>> SIGNAL <<
>> SIGNAL <<
Signal received...
Sending to other processes
>> SIGNAL <<
>> SIGNAL <<
>> SIGNAL <<
Starting...

[P1] Sending: 
Starting...
asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
Starting...
[P3] Received: 3 characters.

所以再次 - 发送信号后,我必须发送一条消息才能对其进行处理。所以,它有点工作。但是非常非常糟糕。

将您的代码逐字记录到 Mac 运行ning Mac OS X 10.10.3,使用 GCC 5.1.0,使用我使用的标准编译选项为了从 Stack Overflow 编译代码,我收到如图所示的编译警告(我调用了你的代码 semshm.c):

$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
>     -Wold-style-definition -Werror semshm.c -o semshm
semshm.c:29:7: error: redefinition of ‘union semun’
 union semun {
       ^
In file included from semshm.c:8:0:
/usr/include/sys/sem.h:176:7: note: originally defined here
 union semun {
       ^
semshm.c:48:6: error: no previous prototype for ‘main_quit’ [-Werror=missing-prototypes]
 void main_quit(int n) {
      ^
semshm.c: In function ‘main_quit’:
semshm.c:48:20: error: unused parameter ‘n’ [-Werror=unused-parameter]
 void main_quit(int n) {
                    ^
semshm.c: In function ‘main’:
semshm.c:54:17: error: unused variable ‘arg’ [-Werror=unused-variable]
     union semun arg;
                 ^
semshm.c: In function ‘process1’:
semshm.c:161:43: error: ordered comparison of pointer with integer zero [-Werror=extra]
     while (fgets(tab, sizeof(tab), stdin) > 0) {
                                           ^
semshm.c: In function ‘process2’:
semshm.c:197:20: error: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘size_t {aka long unsigned int}’ [-Werror=format=]
             printf("[P2] Recieved \"%s\" with length %d.\n", tab, strlen(tab));
                    ^
semshm.c:197:20: error: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘size_t {aka long unsigned int}’ [-Werror=format=]
semshm.c: In function ‘signal_handling’:
semshm.c:239:26: error: unused parameter ‘snd’ [-Werror=unused-parameter]
 void signal_handling(int snd, int rvc) {
                          ^
semshm.c:239:35: error: unused parameter ‘rvc’ [-Werror=unused-parameter]
 void signal_handling(int snd, int rvc) {
                                   ^
cc1: all warnings being treated as errors

第一个问题是Mac OS X特有的;尽管 semctl() 的 POSIX 规范似乎在 <sys/sem.h> 中定义了 union semun 明确地说:

The semctl() function provides a variety of semaphore control operations as specified by cmd. The fourth argument is optional and depends upon the operation requested. If required, it is of type union semun, which the application shall explicitly declare:

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short  *array;
} arg;

POSIX 要求的唯一借口是糟糕的历史先例,Mac OS X 已经推翻了。因此,struct semun 的双重声明不会对您不利。奇怪的是,与您指定的内容相比,POSIX 需要一个额外的成员。不管怎样,我把那个声明注释掉了。

还有其他警告,尽管其中很多都不是那么严重。未使用的变量不好,但危害不大。 %dsize_t 之间的格式不匹配很重要,如果在我的机器上,%d 查找 32 位值而 size_t 是 64 位数量。 fgets() 的测试应该是 != 0;失败时 returns NULL(或 0 — 我也使用 0)。

最奇怪的问题是 signal_handling() 的未使用参数。这些调用将全局变量 inter_sndinter_rcv 传递给函数,但函数忽略这些值并简单地操作全局变量。调用代码和被调用代码之间至少存在严重的脱节。

为了继续生活,我将 signal_handling() 转换为无参数函数 void signal_handling(void); 并保持其主体不变(但修复了对它的所有调用)。我补充说:

printf("%s(): signal %d\n", __func__, n);

main_quit() 无视 how to avoid using printf() in a signal handler 上的建议。我还修复了 'e' 之前的 'i' 除了 'c' 之后的拼写错误。另外 'quitting' 有两个 t,'stopping' 有两个 p——英语是一种很奇怪的语言。

最后,我将两个FIFO名称(1fifo22fifo3)提取到一个数组中:

static const char *fifoname[] = { "1fifo2", "2fifo3" };

并使用 fifoname[0]fifoname[1] 代替文字。重复的文件名文字是个坏主意。我还使用新名称清理 (unlink(fifoname[0]); unlink(fifoname[1]);) FIFO,如果它们在创建失败后存在。

  • 尽管收集了警告,但代码还不错;很少有程序会在不进行一些编辑以使其干净地通过的情况下毫发无损地通过这组选项。

随着代码的编译,我运行试了几次。最后 运行 之一是:

$ ./semshm
[G] Launching
[G] Creating FIFO... [ OK ]
[G] Creating semaphores... [ OK ]
[G] Initializing semaphores values... [ OK ]
[G] Reserving shared memory... [ OK ]
[G] Building process tree... [ OK ]
[G] P1[pid]: 36030, P2[pid]: 36031, P3[pid]: 36032
[P2] Ready.
[P1] Ready.
[P3] Ready.
^C>> SIGNAL <<
main_quit(): signal 2
>> SIGNAL <<
>> SIGNAL <<
>> SIGNAL <<
absolute baloney
[P1] Sending: absolute baloney
Signal received...
[P2] Received "absolute" with length 8.
Signal received...
commestible goods for everyone
^C>> SIGNAL <<
>> SIGNAL <<
main_quit(): signal 2
>> SIGNAL <<
Sending to other processes
>> SIGNAL <<
Sending to other processes
>> SIGNAL <<
>> SIGNAL <<
>> SIGNAL <<
Quitting...
>> SIGNAL <<
Quitting...
[P2] Ending...
[P3] Received: 8 characters.
Signal received...
Sending to other processes
>> SIGNAL <<
>> SIGNAL <<
Quitting...
>> SIGNAL <<
[P3] Ending...
^\Quit: 3
$

我打了一个中断,已经处理了。我输入了'absolute baloney',其中'absolute'部分显然是在一个地方收到的,但是'baloney'部分一直没有收到。真令人费解。我输入了 'commestible goods for everyone' 并且很高兴 运行ces,它被完全忽略了。我尝试了另一个中断,这显然打断了事情。我尝试了 control-D (EOF),它显然什么也没做。我使用 control-backslash (control-\) 来生成退出信号,这确实停止了程序。我以前做过; re运行s 在我收到失败消息时发现 FIFO 已经创建后展示了清理的智慧,然后 re运行 代码并且它工作正常。输出显示绿色 OK 消息和闪烁的黄色 >>> SIGNAL <<< 消息。

所以,您没有告诉我们如何使用该程序,也没有告诉我们该程序的预期输出。

您的 'no compiler warnings' 断言有点乐观,但与事实相差无几。

现在您需要将问题升级为 specify/illustrate 您提供的输入和该输入的预期行为,并显示您实际从中获得的意外行为。

semshm.c

的一个半工作版本

这是 'as compiled',我编译的部分注释代码和 运行。要在 Linux 或其他更准确的 POSIX 兼容平台上编译它(比 Mac OS X 更准确,即),您需要取消注释一个union semun 个块。我通常会删除 <sys/types.h>(现代 POSIX 几乎从不要求您包含它;上个千年的旧版本确实需要它——请参阅 POSIX 1997 which did, but compare with POSIX 2004,但没有)。

#include <stdio.h>

…code co-opted into question…see revision 3 at https://whosebug.com/posts/30813144/revisions

…I ran over the 30,000 character limit with this code on board…

}

semshm.c

的另一个主要工作版本

获取有关代码应该如何工作的额外信息,并进行大量额外的诊断打印,并观察到进程 2 和 3 卡在等待未发出信号的信号量上,我想出了使用此代码变体:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <sys/sem.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>

#define WRITE 1
#define READ 0
#define MEM_ID 1
#define MEM_SIZE 1

#define OK  "[ 3[1;32mOK3[0m ]\n"
#define ERR "[3[1;31mFAIL3[0m] "
#define SIGNAL "3[1;33m3[5m>> SIGNAL <<3[0m\n"

#define S1 SIGINT
#define S2 SIGUSR1
#define S3 SIGUSR2
#define S4 SIGCONT

int process1(void);
int process2(void);
int process3(void);
void signal_callback(int signo);
void signal_handling(void);
void sem_down(int semid, int semnum);
void sem_up(int semid, int semnum);

char work = 1, quit = 0;
char inter_snd = 0, inter_rcv = 0;
struct sembuf semstruct;
int sem23, sem12, mem;
char *message;
int res[3];

static const char *fifoname[] = { "1fifo2", "2fifo3" };

static
void main_quit(int n)
{
    printf("%s(): signal %d\n", __func__, n);
    kill(res[0], S1);
}

int main(void)
{
    printf("[G] Launching\n");

    signal(SIGINT, main_quit);
    signal(SIGTERM, main_quit);
    // creating FIFO
    printf("[G] Creating FIFO... ");
    res[0] = mkfifo(fifoname[0], 0644);
    res[1] = mkfifo(fifoname[1], 0644);
    if ((res[0] == -1) || (res[1] == -1))
    {
        perror(ERR);
        unlink(fifoname[0]);
        unlink(fifoname[1]);
        return 1;
    }
    else
        printf(OK);

    // create two semaphores and set values
    printf("[G] Creating semaphores... ");
    sem12 = semget(READ, 1, IPC_CREAT | 0644);
    sem23 = semget(WRITE, 1, IPC_CREAT | 0644);

    if ((sem23 == -1) || (sem12 == -1))
    {
        perror(ERR);
        return 1;
    }
    else
        printf(OK);

    printf("[G] Initializing semaphores values... ");
    semctl(sem12, 0, SETVAL, 0);
    semctl(sem12, 1, SETVAL, 1);
    semctl(sem23, 0, SETVAL, 0);
    semctl(sem23, 1, SETVAL, 1);
    printf(OK);

    // creating shared memory
    printf("[G] Reserving shared memory... ");
    mem = shmget(MEM_ID, MEM_SIZE, IPC_CREAT | 0644);
    message = (char *)shmat(mem, 0, 0);

    if (mem == -1 || message == 0)
    {
        perror(ERR);
        return 1;
    }
    else
        printf(OK);

    if ((res[0] = fork()) == 0)
    {
        process1();
        printf("Returned from process1() - exiting\n");
        exit(0);
    }

    if ((res[1] = fork()) == 0)
    {
        process2();
        printf("Returned from process2() - exiting\n");
        exit(0);
    }

    if ((res[2] = fork()) == 0)
    {
        process3();
        printf("Returned from process3() - exiting\n");
        exit(0);
    }

    printf("[G] Building process tree... ");
    if ((res[0] == -1) || (res[1] == -1) || (res[2] == -1))
    {
        perror(ERR);
        return 1;
    }
    else
    {
        printf(OK);
        printf("[G] P1[pid]: %d, P2[pid]: %d, P3[pid]: %d\n", res[0], res[1], res[2]);
    }

    int corpse;
    int status;
    while ((corpse = wait(&status)) > 0)
        printf("[G] PID %d exited with status 0x%.4X\n", corpse, status);

    printf("[G] Deleting FIFO... ");
    res[0] = unlink(fifoname[0]);
    res[1] = unlink(fifoname[1]);
    if ((res[0] == -1) || (res[1] == -1))
        perror(ERR);
    else
        printf(OK);

    printf("[G] Freeing shared memory... ");
    res[0] = shmdt((char *)message);
    res[1] = shmctl(mem, IPC_RMID, 0);
    if ((res[0] == -1) || (res[1] == -1))
        perror(ERR);
    else
        printf(OK);

    printf("[G] Deleting semaphores... ");
    res[0] = semctl(sem23, 0, IPC_RMID, 0);
    res[1] = semctl(sem12, 0, IPC_RMID, 0);
    if ((res[0] == -1) || (res[1] == -1))
        perror(ERR);
    else
        printf(OK);

    printf("[G] Ending...\n");
    return 0;
}

int process1(void)
{
    char tab[100];
    FILE *fifoh;

    signal(S1, signal_callback);
    signal(S2, signal_callback);
    signal(S3, signal_callback);
    signal(S4, signal_callback);

    fifoh = fopen(fifoname[0], "w");
    setbuf(fifoh, NULL);

    printf("[P1] Ready.\n");
    while (fgets(tab, sizeof(tab), stdin) != 0)
    {
        if (work)
        {
            sem_down(sem12, WRITE);
            printf("[P1] Sending: %s", tab);
            fprintf(fifoh, "%s", tab);
            sem_up(sem12, READ);
        }
        else
            printf("[P1] Ignoring line because work is zero: %s", tab);
        signal_handling();
    }
    fclose(fifoh);
    printf("[P1] Ending...\n");
    sem_up(sem12, READ);    // Crucial
    return 0;
}

int process2(void)
{
    char tab[100];
    FILE *fifo_in, *fifo_out;

    printf("[P2] Ready.\n");

    fifo_in = fopen(fifoname[0], "r");
    fifo_out = fopen(fifoname[1], "w");

    setbuf(fifo_out, NULL);
    setbuf(fifo_in, NULL);

    signal(S1, signal_callback);
    signal(S2, signal_callback);
    signal(S3, signal_callback);
    signal(S4, signal_callback);

    do
    {
        if (work)
        {
            printf("[P2]: Waiting on semaphore sem12/R\n");
            sem_down(sem12, READ);
            printf("[P2]: Proceed on semaphore sem12/R\n");
            if (fscanf(fifo_in, "%s", (char *)tab) != 1)
            {
                printf("[P2]: EOF\n");
                break;
            }
            printf("[P2]: Signalling semaphore sem12/W\n");
            sem_up(sem12, WRITE);
            printf("[P2]: Proceeding semaphore sem12/W\n");
            printf("[P2] Received \"%s\" with length %zu.\n", tab, strlen(tab));
            printf("[P2]: Waiting on semaphore sem23/R\n");
            sem_down(sem23, WRITE);
            printf("[P2]: Proceed on semaphore sem23/R\n");
            fprintf(fifo_out, "%zu\n", strlen(tab));
            printf("[P2]: Signalling semaphore sem23/W\n");
            sem_up(sem23, READ);
            printf("[P2]: Proceeding semaphore sem23/W\n");
        }
        else
            printf("[P2] Looping: work is zero\n");
        printf("[P2]: signal handling\n");
        signal_handling();
        printf("[P2]: signal handling done\n");
    } while (!quit);

    fclose(fifo_in);
    fclose(fifo_out);
    printf("[P2] Ending...\n");
    sem_up(sem23, READ);    // Crucial
    return 0;
}

int process3(void)
{
    FILE *fifo_in;
    int count;

    printf("[P3] Ready.\n");

    signal(S1, signal_callback);
    signal(S2, signal_callback);
    signal(S3, signal_callback);
    signal(S4, signal_callback);

    fifo_in = fopen(fifoname[1], "r");
    setbuf(fifo_in, NULL);

    do
    {
        if (work)
        {
            printf("[P3]: Waiting on semaphore sem23\n");
            sem_down(sem23, READ);
            if (fscanf(fifo_in, "%d", &count) == 1)
                printf("[P3] Received: %d as the length.\n", count);
            else
            {
                printf("[P3] Failed to read an integer\n");
                break;
            }
            sem_up(sem23, WRITE);
        }
        else
            printf("[P3] Looping: work is zero\n");
        printf("[P3]: signal handling\n");
        signal_handling();
        printf("[P3]: signal handling done\n");
    } while (!quit);
    fclose(fifo_in);
    printf("[P3] Ending...\n");
    return 0;
}

void signal_handling(void)
{
    if (inter_snd > 0)
    {
        printf("PID %d: Signal received...\n", (int)getpid());

        semstruct.sem_op = -3;
        semop(sem23, &semstruct, 1);

        *message = inter_snd;
        inter_snd = 0;

        semstruct.sem_op = 3;
        semop(sem12, &semstruct, 1);

        printf("Sending to other processes\n");
        kill(0, S4);
    }
    else
        printf("PID %d: inter_snd = %d\n", (int)getpid(), inter_snd);

    if (inter_rcv)
    {
        inter_rcv = 0;

        semstruct.sem_op = -1;
        semop(sem12, &semstruct, 1);

        switch (*message)
        {
        case 1:
            printf("Quitting...\n");
            quit = 1;
            break;
        case 2:
            printf("Stopping...\n");
            work = 0;
            break;
        case 3:
            printf("Starting...\n");
            work = 1;
            break;
        default:
            printf("There's garbage in memory :/..\n");
        }
        semstruct.sem_op = 1;
        semop(sem23, &semstruct, 1);
    }
    else
        printf("PID %d: inter_rcv = %d\n", (int)getpid(), inter_rcv);
}

void signal_callback(int signo)
{
    printf("%sSignal %d in PID %d\n", SIGNAL, signo, (int)getpid());
    switch (signo)
    {
    case S1:
        inter_snd = 1;
        break;
    case S2:
        inter_snd = 2;
        break;
    case S3:
        inter_snd = 3;
        break;
    case S4:
        inter_rcv = 1;
        break;
    }
}

void sem_down(int semid, int semnum)
{
    semstruct.sem_flg = 0;
    semstruct.sem_num = semnum;
    semstruct.sem_op = -1;
    do
    {
        errno = 0;
        semop(semid, &semstruct, 1);
    } while (errno == EINTR);
}

void sem_up(int semid, int semnum)
{
    semstruct.sem_flg = 0;

    semstruct.sem_num = semnum;
    semstruct.sem_op = 1;
    semop(semid, &semstruct, 1);
}

注意标记为 'crucial' 的两个 sum_up() 调用;它们确实至关重要。另请注意,输入操作经过错误检查;这也是至关重要的。如果您不检查它们是否有效,您就会错过 EOF 指示。始终,但 始终 检查 I/O 操作是否正常。

使用该代码,示例 运行 为:

$ ./semshm
[G] Launching
[G] Creating FIFO... [ OK ]
[G] Creating semaphores... [ OK ]
[G] Initializing semaphores values... [ OK ]
[G] Reserving shared memory... [ OK ]
[G] Building process tree... [ OK ]
[G] P1[pid]: 36545, P2[pid]: 36546, P3[pid]: 36547
[P2] Ready.
[P3] Ready.
[P1] Ready.
[P3]: Waiting on semaphore sem23
[P2]: Waiting on semaphore sem12/R
absolutely-ineffable-twaddle-for-onward-transmission
[P1] Sending: absolutely-ineffable-twaddle-for-onward-transmission
PID 36545: inter_snd = 0
PID 36545: inter_rcv = 0
[P2]: Proceed on semaphore sem12/R
[P2]: Signalling semaphore sem12/W
[P2]: Proceeding semaphore sem12/W
[P2] Received "absolutely-ineffable-twaddle-for-onward-transmission" with length 52.
[P2]: Waiting on semaphore sem23/R
[P2]: Proceed on semaphore sem23/R
[P2]: Signalling semaphore sem23/W
[P2]: Proceeding semaphore sem23/W
[P2]: signal handling
PID 36546: inter_snd = 0
PID 36546: inter_rcv = 0
[P2]: signal handling done
[P2]: Waiting on semaphore sem12/R
[P3] Received: 52 as the length.
[P3]: signal handling
PID 36547: inter_snd = 0
PID 36547: inter_rcv = 0
[P3]: signal handling done
[P3]: Waiting on semaphore sem23
[P1] Ending...
Returned from process1() - exiting
[P2]: Proceed on semaphore sem12/R
[P2]: EOF
[P2] Ending...
Returned from process2() - exiting
[P3] Failed to read an integer
[P3] Ending...
Returned from process3() - exiting
[G] PID 36545 exited with status 0x0000
[G] PID 36546 exited with status 0x0000
[G] PID 36547 exited with status 0x0000
[G] Deleting FIFO... [ OK ]
[G] Freeing shared memory... [ OK ]
[G] Deleting semaphores... [ OK ]
[G] Ending...
$

我还没有计算出可以删除多少代码 — 但我怀疑 'quite a lot' 是一个准确的陈述。信号量操作真的没有必要;无论如何,这些进程都会正确地阻止文件流。我也不相信共享内存有任何帮助。而且我不相信 work 有很大帮助,但它可能会在信号处理下做一些有用的事情。请注意,它不在进程之间共享(它对每个进程都是私有的),因此一个进程对 work 所做的更改只会影响该进程,不会影响其他进程。