簡體   English   中英

c 中的進程間消息隊列?

[英]Interprocess message queue in c?

我正在使用進程間通信在 C 中編寫一個程序,具體來說,我正在嘗試使用進程間消息隊列編寫一個程序。 該程序應該像這樣工作:

  • 命令行接受 n 個文件(至少一個)。 將創建與文件一樣多的 N 個進程。

  • n 個進程必須將文件的內容發送給一個名為 Receiver 的進程,該進程的任務是打印接收到的消息。

問題是:並非文件的所有內容都被打印出來,即使進程發送消息也是如此。 為什么? 誰能告訴我哪里出錯了?

這是我的代碼

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>

#define DIM_MSG 1024

#define TYPE_W 2

typedef struct
{
    long mtype;
    char eof;
    char mtext[DIM_MSG];
} msg;

void child_r(int coda, const char *file)
{

    FILE *r_stream;
    if ((r_stream = fopen(file, "r")) == NULL)
    {
        perror("errore apertura file");
        exit(1);
    }

    printf("%s:\n",file);

    msg messaggio;

    while (fgets(messaggio.mtext, DIM_MSG, r_stream) != NULL)
    {
      

        messaggio.mtype = TYPE_W;
        messaggio.eof = 0;
        if (msgsnd(coda, &messaggio, sizeof(msg) - sizeof(long) , 0) == -1)
        {
            perror("msgsnd");
            exit(1);
        }
        printf("\tMessage send: %s", messaggio.mtext);
    }

    strcpy(messaggio.mtext, "quit");
    messaggio.eof = 1;
    messaggio.mtype = TYPE_W;
    if (msgsnd(coda, &messaggio, sizeof(msg) - sizeof(long) , 0) == -1)
    {
        perror("msgsnd");
        exit(1);
    }

    fclose(r_stream);
    exit(0);
}

void child_f(int coda)
{
    msg messaggio;

    printf("\nReceiver\n");

    do
    {
        if (msgrcv(coda, &messaggio, sizeof(msg) - sizeof(long), TYPE_W, 0) == -1)
        {
            perror("msgrcv");
            exit(1);
        }

        if (strcmp(messaggio.mtext, "quit") != 0)
        {
            printf("\tMessage rcv: %s ", messaggio.mtext);
        }

    } while (messaggio.eof != 1);

    exit(0);
}

int main(int argc, char const *argv[])
{
    char *file_name = NULL;
    struct stat sb;
    int child=0;


    int ds_coda;

    if(argc<1)
    {
        fprintf(stderr,"Utilizzo %s <file-1> <file-2> <file-n>....",argv[0]);
        exit(1);
    }

    if((ds_coda=msgget(IPC_PRIVATE,IPC_CREAT|IPC_EXCL|0600))==-1)
    {
        perror("coda");
        exit(1);
    }

    /* analizza la command-line */
    for (int i = 1; i < argc; i++) {
            if ((stat(argv[i], &sb) == 0) && (S_ISREG(sb.st_mode)))
            {
                file_name = (char*)argv[i];
                child++;
                if(fork()==0)
                {
                    
                    child_r(ds_coda, file_name);
                }
            }
            else {
                perror(argv[i]);
                exit(1);
            }
    }
        
    if(child==0)
    {
        fprintf(stderr,"Parametri non validi!\n");
        exit(1);
    }

    if(fork() == 0)
    {
        // child_w
         sleep(1);
        child_f(ds_coda);
        
    }
    else wait(NULL);

    msgctl(ds_coda, IPC_RMID, NULL);
   
    return 0;
}

幾個問題...

  1. 主進程需要在執行IPC_RMID之前循環wait 否則,發送進程將在msgsnd上失敗,因為ds_coda不再有效。 主進程與發送方/接收方進程“賽跑”,並在其他進程完成之前刪除了帶有IPC_RMID的 id。
  2. 所有發件人都將設置messsaggio.eof但接收者在收到一個后停止。 它必須知道有多少發送者並等待直到所有發送者都發送了 EOF。 (ie) 它必須保持計數。

在調試之前,我必須增強日志記錄。 因此,我創建了tscgetflogopenlogprt來為每個帶有時間戳的進程創建單獨的日志。

當我接近時,我將發件人字段添加from消息中,因為接收方正在獲取數據但不知道是哪個進程發送的。 這有助於診斷 EOF 問題。


這是重構的代碼。 它用錯誤和修復進行了注釋。

默認情況下,它會顯示 EOF 問題(即它會掛起)。 要應用 EOF 問題的修復程序,請使用-DFIXEOF進行編譯

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <time.h>

#define DIM_MSG 1024

#define TYPE_W 2

typedef struct {
    long mtype;
    int from;
    char eof;
    char mtext[DIM_MSG];
} msg;

int pididx;                         // linear process ID (0=main)
int child = 0;                      // number of spawned children
int ds_coda;

FILE *xflog;                        // debug log stream

#define logprt(_fmt...) \
    do { \
        fprintf(xflog,"[%.9f/%5.5d] ",tscgetf(),pididx); \
        fprintf(xflog,_fmt); \
        fflush(xflog); \
    } while (0)

double tsczero;

// tscget -- get timestamp
// RETURNS: elapsed seconds
double
tscgetf(void)
{
    struct timespec ts;
    double sec;

    clock_gettime(CLOCK_MONOTONIC,&ts);
    sec = ts.tv_nsec;
    sec /= 1e9;
    sec += ts.tv_sec;

    sec -= tsczero;

    return sec;
}

void
logopen(void)
{
    char logf[100];

    if (xflog != NULL)
        fclose(xflog);

    sprintf(logf,"log%3.3d",pididx);

    xflog = fopen(logf,"w");
    if (xflog == NULL) {
        perror(logf);
        exit(1);
    }
}

void
child_r(int coda, const char *file)
{
    FILE *r_stream;

    logopen();

    if ((r_stream = fopen(file, "r")) == NULL) {
        perror("errore apertura file");
        exit(1);
    }

    logprt("child_r: reading %s\n", file);

    msg messaggio;

    messaggio.from = pididx;

    while (fgets(messaggio.mtext, DIM_MSG, r_stream) != NULL) {
        messaggio.mtype = TYPE_W;
        messaggio.eof = 0;
        if (msgsnd(coda, &messaggio, sizeof(msg) - sizeof(long), 0) == -1) {
            perror("msgsnd");
            exit(1);
        }
        logprt("Message send: %s", messaggio.mtext);
    }

    strcpy(messaggio.mtext, "quit");
    messaggio.eof = 1;
    messaggio.mtype = TYPE_W;
#if 1
    strcpy(messaggio.mtext,"I_AM_EOF\n");
#endif
    if (msgsnd(coda, &messaggio, sizeof(msg) - sizeof(long), 0) == -1) {
        perror("msgsnd");
        exit(1);
    }
    logprt("Message EOF: %s", messaggio.mtext);

    fclose(r_stream);

    logprt("child_r: finished %s\n", file);

    fclose(xflog);
    exit(0);
}

void
child_f(int coda)
{
    msg messaggio;

    logopen();

    // NOTE: we are started last so the count we need to wait for is one less
    int waitcnt = pididx - 1;

    logprt("Receiver starting -- waitcnt=%d\n",waitcnt);

    while (1) {
        if (msgrcv(coda, &messaggio, sizeof(msg) - sizeof(long), TYPE_W, 0) == -1) {
            perror("msgrcv");
            exit(1);
        }

        if (strcmp(messaggio.mtext, "quit") != 0) {
            logprt("Message rcv (from %d): %s",
                messaggio.from, messaggio.mtext);
        }

        if (messaggio.eof == 1) {
// NOTE/BUG: we can't stop after the first EOF message -- we must wait for all
// of them
#if ! FIXEOF
            logprt("got EOF\n");
            break;
#else
            logprt("got EOF -- waitcnt=%d\n",waitcnt);
            if (--waitcnt <= 0)
                break;
#endif
        }
    }

    logprt("child_f: complete\n");

    fclose(xflog);
    exit(0);
}

// start_rcv -- start receiver process
void
start_rcv(void)
{

    logprt("start_rcv:\n");

    child++;
    pid_t pid = fork();

    if (pid == 0) {
        pididx = child;
        // child_w
// NOTE/BUG: not necessary with other fixes
#if 0
        sleep(1);
#endif
        child_f(ds_coda);
    }

    logprt("start_rcv: pid=%d child=%d\n",pid,child);
}

int
main(int argc, char const *argv[])
{
    char *file_name = NULL;
    struct stat sb;

    tsczero = tscgetf();

#if 1
    pid_t pid;
    setlinebuf(stdout);
    setlinebuf(stderr);
#endif

    logopen();

    if (argc < 1) {
        fprintf(stderr, "Utilizzo %s <file-1> <file-2> <file-n>....", argv[0]);
        exit(1);
    }

    if ((ds_coda = msgget(IPC_PRIVATE, IPC_CREAT | IPC_EXCL | 0600)) == -1) {
        perror("coda");
        exit(1);
    }

// NOTE: early attempt to fix (receiver should start first) but didn't fix it
// and won't work because receiver needs to know the number of EOF messages to
// wait for
#if RCVEARLY
    start_rcv();
#endif

    /* analizza la command-line */
    for (int i = 1; i < argc; i++) {
        if ((stat(argv[i], &sb) == 0) && (S_ISREG(sb.st_mode))) {
            file_name = (char *) argv[i];
            child++;
            pid = fork();
            if (pid == 0) {
                pididx = child;
                child_r(ds_coda, file_name);
            }
#if 1
            else {
                logprt("forked: pid=%d child=%d\n",pid,child);
            }
#endif
        }
        else {
            perror(argv[i]);
            exit(1);
        }
    }

    if (child == 0) {
        fprintf(stderr, "Parametri non validi!\n");
        exit(1);
    }

// NOTE/FIX: main process must wait for _all_ children to complete before
// doing IPC_RMID
#if 1
#if ! RCVEARLY
    start_rcv();
#endif

    while (1) {
        pid_t pid = wait(NULL);
        logprt("waitfor: %d child=%d\n",pid,child);
        if (pid <= 0)
            break;
        --child;
    }
#endif

    msgctl(ds_coda, IPC_RMID, NULL);

    fclose(xflog);

    return 0;
}

在上面的代碼中,我使用cpp條件來表示舊代碼與新代碼:

#if 0
// old code
#else
// new code
#endif

#if 1
// new code
#endif

注意:這可以通過unifdef -k運行文件來清理

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM