[英]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;
}
幾個問題...
IPC_RMID
之前循環wait
。 否則,發送進程將在msgsnd
上失敗,因為ds_coda
不再有效。 主進程與發送方/接收方進程“賽跑”,並在其他進程完成之前刪除了帶有IPC_RMID
的 id。messsaggio.eof
但接收者在收到第一個后停止。 它必須知道有多少發送者並等待直到所有發送者都發送了 EOF。 (ie) 它必須保持計數。 在調試之前,我必須增強日志記錄。 因此,我創建了tscgetf
、 logopen
和logprt
來為每個帶有時間戳的進程創建單獨的日志。
當我接近時,我將發件人字段添加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.