【问题标题】:Synchronizing processes with semaphores and signals in C在 C 中使用信号量和信号同步进程
【发布时间】:2015-06-12 22:37:31
【问题描述】:

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

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

第二个问题是信号 - 发送一个后,我必须按 Enter(对于 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  "[ \033[1;32mOK\033[0m ]\n"
#define ERR "[\033[1;31mFAIL\033[0m] "
#define SIGNAL "\033[1;33m\033[5m>> SIGNAL <<\033[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 中指定。每个进程都应该显示他们的信息(退出/停止/等)。

实际行为:

[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 之后我可以发送消息,按两次回车然后我得到了预期的行为(退出程序)。当我发送另一个信号时,按 enter 也可以:

$ 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.

再说一遍 - 在发送信号后,我必须发送一条消息以便处理它。所以,它有点工作。但是非常非常糟糕。

【问题讨论】:

  • 获得帮助的第一步是提供可编译的代码。此代码尝试在 main() 内定义函数 main_quit()
  • 它是可编译的。它正在工作,您可以检查是否需要。
  • 啊,好的,这是一个 gcc 扩展。 Nvm(虽然由于便携性问题我不会真的这样做)
  • 好吧,我想你是对的,所以我添加了一个快速(临时)修复。
  • 我刚尝试用gcc在ubuntu linux 14.04上编译,编译的结果是10个警告,其中一些非常关键,比如#include'ing ipc.h时,必须#定义 _SVID_SOURCE 或 #define _XOPEN_SOURCE。并传递一个 size_t(一个 long int),其中应该使用一个 int 和不正确的类型来创建信号量(sem_t),从 fork()返回值(pid_t)。将 res[] 数组用于两个完全不同的目的。

标签: c linux multiprocessing signals semaphore


【解决方案1】:

使用 GCC 5.1.0 将您的代码逐字放到运行 Mac OS X 10.10.3 的 Mac 上,使用我用于从 Stack Overflow 编译代码的标准编译选项,我收到编译警告,如图所示(我将您的代码称为 @ 987654326@):

$ 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 规范,它似乎在&lt;sys/sem.h&gt; 中定义了union semun 明确地说:

semctl() 函数提供了各种由cmd 指定的信号量控制操作。第四个参数是可选的,取决于请求的操作。如果需要,它是 union semun 类型,应用程序应明确声明:

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;它在失败时返回 NULL(或 0——我也使用 0)。

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

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

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

无视how to avoid using printf() in a signal handler 的建议,致main_quit()。除了 'c' 拼写错误之外,我还修复了 'e' 之前的 'i'。同样,“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”部分从未成功。这很令人费解。我输入了“每个人的商品”,从表面上看,它被完全忽略了。我尝试了另一个中断,这显然打断了事情。我尝试了 control-D (EOF),它显然什么也没做。我使用了控制反斜杠 (control-\) 来生成退出信号,这确实停止了程序。我以前做过; reruns 显示了在我收到失败消息后发现 FIFO 已经创建后进行清理的智慧,然后重新运行代码并且它工作正常。输出显示绿色 OK 消息,并闪烁黄色 &gt;&gt;&gt; SIGNAL &lt;&lt;&lt; 消息。

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

您的“无编译器警告”断言有点乐观,但与事实相差不远。

现在您需要升级问题以指定/说明您提供的输入以及该输入的预期行为,并显示您实际从中获得的意外行为。

semshm.c 的一个半工作版本

这是我编译并运行的“已编译”、部分注释的代码。要在 Linux 或其他更准确的 POSIX 兼容平台(即比 Mac OS X 更准确)上编译它,您需要取消注释 union semun 块之一。我通常会删除 &lt;sys/types.h&gt;(现代 POSIX 几乎从不要求您包含它;上个千年的旧版本确实需要它 - 请参阅 POSIX 1997 确实需要它,但与 POSIX 2004 不需要它相比)。

#include <stdio.h>

...代码被选入问题...参见修订版 3,https://stackoverflow.com/posts/30813144/revisions...

...我使用此代码超过了 30,000 个字符的限制...

}

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  "[ \033[1;32mOK\033[0m ]\n"
#define ERR "[\033[1;31mFAIL\033[0m] "
#define SIGNAL "\033[1;33m\033[5m>> SIGNAL <<\033[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);
}

注意标记为“关键”的两个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...
$

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

【讨论】:

  • 谢谢,非常感谢您的帮助。关于未使用的变量等 - 这是多次尝试修复代码后留下的内容,对此感到抱歉。另外,很抱歉拼写错误。我已将您的代码添加为程序的实际版本。现在不需要取消注释union senum,因为它没有在程序中使用。在我将它用作 semctl 中的参数之前。同样在我的问题中,我试图更好地解释我得到了什么以及我想要得到什么作为输出。我希望现在很清楚。再次感谢您的所有努力。
  • @JayL:查看(主要)更新。请记住测试 I/O 操作是否成功。请记住释放在信号量上等待的进程。重写代码以避免信号量(它们不是必需的)。检查共享内存是否相关,如果不相关,也将其删除。这将大大减少您的程序大小。
  • 非常感谢!你已经帮了我很多,但如果可以的话,我想问一些问题。您能否向我解释一下,为什么这两个额外的 sem_up() 很重要?他们在循环之外,我不明白。另外,我看到在发送信号后我仍然必须发送一条消息,然后它就被送达了。我做错了什么还是正确的方式?
  • 删除它们(将它们注释掉),重建并重新运行。将EOF指示到进程1;发生什么了?如果没有sem_up(),其他两个进程最终会在它们的信号量上等待一个永远不会到来的“up”。
  • 我不确定你想要什么作为信号处理行为。如果它没有做你想做的事,(a) 清楚地定义应该发生的事情,(b) 实现代码以使其工作。评论:在构建这样的程序时,请分阶段进行,而不是一次性完成。让基本的同步活动正常工作,然后添加异步信号处理代码来执行您需要的任何其他操作。
猜你喜欢
  • 1970-01-01
  • 2015-05-06
  • 2015-04-13
  • 1970-01-01
  • 2019-06-05
  • 2016-08-04
  • 1970-01-01
  • 2017-08-31
相关资源
最近更新 更多