Interprocess message queue in c? - c

I'm writing a program in C using inter-process communication, specifically I'm trying to write a program using an inter-process message queue. The program should work like this:
The command line accepts n-files (at least one). N-processes will be created as much as files.
The n processes must send the contents of the file to a process called Receiver which will have the task of printing the messages received.
The problem is: not all the contents of the file are printed, even if the processes send the messages. Why ? Could anyone tell me where I'm going wrong?
This is my code
#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;
}

A few issues ...
The main process needs to loop on wait before doing IPC_RMID. Otherwise, the sender processes will fail on msgsnd because the ds_coda is no longer valid. The main process has "raced" with the sender/receiver processes and removed the id with IPC_RMID before the other processes have completed.
All senders will set messsaggio.eof but the receiver stops after receiving the first one. It must know how many senders there are and wait until all have sent EOF. (i.e.) It must maintain a count.
Before I could debug this, I had to enhance the logging. So, I created tscgetf, logopen, and logprt to create separate logs for each process with timestamps.
When I was getting close, I added the from field to the message because the receiver was getting data but didn't know which process sent it. This helped diagnose the EOF issue.
Here is the refactored code. It is annotated with the bugs and fixes.
By default, it will show the EOF issue (i.e. it will hang). To apply the fix for the EOF issue, compile with -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;
}
In the code above, I've used cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Note: this can be cleaned up by running the file through unifdef -k

Related

pipe with file fread & fwrite send file from one process to another

I Implement a pipe that "transfers" from one process data file size of 100MB to another process.
The Whole Send from one process to the another works well, but it takes soo long... around 2.5+ minutes.
I want to change my functions to fread&fwrite to make the function faster instead reading one char each time how I can do it with the pd[0] & pd[1] but I hardly understand how I can change it
Any help will be appreciated!
HERE IS MY CODE:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int fd[2];
pid_t childpid;
char readbuffer[65535];
pipe(fd);
if ((childpid = fork()) == -1) {
perror("fork");
exit(1);
}
if (childpid == 0) {
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
FILE *fp2 = fopen("data.txt", "rb");
if (fp2 == NULL) {
//handle error here
return -1;
}
int c;
while ((c = getc(fp2)) != EOF) {
if ((write(fd[1], &c, 1)) < 1) {
fprintf(stderr, "Write to pipe failed.\n");
perror("write");
exit(EXIT_FAILURE);
}
}
} else {
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
char *new_data = "new_data.txt";
FILE *fp = fopen(new_data, "wb");
ssize_t num_bytes;
while (num_bytes = (read(fd[0], readbuffer, sizeof(readbuffer))) > 0) {
fwrite(readbuffer, 1, num_bytes, fp);
memset(readbuffer, 0, 65535);
}
fclose(fp);
}
return 0;
}
EDITED ROUND 2:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int fd[2];
pid_t childpid;
char readbuffer[1024];
pipe(fd);
if ((childpid = fork()) == -1) {
perror("fork");
exit(1);
}
if (childpid == 0) {
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
FILE *fp2 = fopen("data.txt", "rb");
if (fp2 == NULL) {
//handle error here
return -1;
}
int c;
// while ((c = getc(fp2)) != EOF) {
// if ((write(fd[1], &c, 1)) < 1) {
// fprintf(stderr, "Write to pipe failed.\n");
// perror("write");
// exit(EXIT_FAILURE);
// }
// }
ssize_t num_bytes;
while ((num_bytes = fread(readbuffer, sizeof(readbuffer), 1024,fp2)) > 0) {
fwrite(readbuffer, 1, num_bytes, fd[1]);
//memset(readbuffer, 0, 65535);
}
} else {
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
char *new_data = "new_data.txt";
FILE *fp = fopen(new_data, "wb");
ssize_t num_bytes;
while ((num_bytes = read(fd[0], readbuffer, sizeof(readbuffer))) > 0) {
fwrite(readbuffer, 1, num_bytes, fp);
//memset(readbuffer, 0, 65535);
}
fclose(fp);
}
return 0;
}
A few issues ...
In your original code, the main issue [speed wise] was using read or write with a length of 1.
Also, although a stream may compensate a bit for fgetc, it is still one byte at a time.
The solution I've come up with is to implement what William Pursell suggested: Use fdopen to attach a stdio stream (i.e. FILE *) to the pipe.
We can do this for both parent and child.
Then, just looping on fread/fwrite in both processes works.
Note that the parent and should do fclose.
And, the parent should wait for the child to complete (e.g. waitpid).
Here is the modified code:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#ifdef DEBUG
#define dbgprt(_fmt...) \
do { \
int sverr = errno; \
fprintf(stderr,_fmt); \
errno = sverr; \
} while (0)
#else
#define dbgprt(_fmt...) \
do { } while (0)
#endif
int
main(void)
{
int fd[2];
FILE *fpinp;
FILE *fpout;
pid_t childpid;
int status;
ssize_t rlen;
ssize_t wlen;
#if 0
char buffer[65535];
#else
char buffer[1024];
#endif
setlinebuf(stdout);
setlinebuf(stderr);
pipe(fd);
if ((childpid = fork()) == -1) {
perror("fork");
exit(1);
}
if (childpid == 0) {
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
fpinp = fopen("data.txt", "rb");
if (fpinp == NULL) {
perror("child/fopen");
exit(7);
}
fpout = fdopen(fd[1],"wb");
if (fpout == NULL) {
perror("child/fdopen");
exit(8);
}
while (1) {
rlen = fread(buffer,1,sizeof(buffer),fpinp);
dbgprt("child: LOOP rlen=%zd\n",rlen);
if (rlen < 0) {
perror("child/fread");
exit(9);
}
if (rlen <= 0)
break;
wlen = fwrite(buffer,1,rlen,fpout);
dbgprt("child: LOOP wlen=%zd\n",wlen);
if (wlen < 0) {
perror("child/fwrite");
exit(9);
}
}
fclose(fpinp);
fclose(fpout);
exit(0);
}
else {
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
char *new_data = "new_data.txt";
fpout = fopen(new_data, "wb");
if (fpout == NULL) {
perror("parent/fopen");
exit(3);
}
fpinp = fdopen(fd[0],"rb");
if (fpinp == NULL) {
perror("parent/fdopen");
exit(4);
}
while (1) {
rlen = fread(buffer, 1, sizeof(buffer), fpinp);
dbgprt("parent: LOOP rlen=%zd\n",rlen);
if (rlen < 0) {
perror("parent/fread");
exit(5);
}
if (rlen <= 0)
break;
wlen = fwrite(buffer, 1, rlen, fpout);
dbgprt("parent: LOOP wlen=%zd\n",wlen);
if (wlen < 0) {
perror("parent/fwrite");
exit(6);
}
}
fclose(fpinp);
fclose(fpout);
waitpid(childpid,&status,0);
dbgprt("status=%8.8X\n",status);
}
return 0;
}

how to use pipe and execvp in c via linux

I'm trying to use pipe command and I can't understand how to.
I've a lot of versions but I can't make it work.
first of all the hierarchy:
main prog - nicecmp - that will execute the child prog and print the result
child prog - loopcmp - that will execute his child prog and get the returned value and send it back to the parent in nicecmp.
loopcmp's childs - lencmp/lexcmp - both prog will be executed in loopcmp and return value between -1 to 2. (100% works)
shortly, I need to create a pipe and a new process that will run new program (loopcmp - added in the end of the code) using execvp, and I need to print the res of the loopcmp in the parent.
I can send it directly from the prog that I executed and I can use WEXITSTATUS in the child after the end of the loopcmp.
what's the right way to do so (from the progrem execution or after that I've returned from the loopcmp)
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define LINELEN (80)
#define READFROM ("./loopcmp")
typedef enum { eLexcmp, eLencmp, eNumOfCmp } eCmpstr;
const char* cmpstr[eNumOfCmp] = { "./lexcmp", "./lencmp" };
int lencmp(const char *str1, const char *str2);
int lexcmp(const char *str1, const char *str2);
char *mygets(char *buf, int len);
int mygeti();
int main(int argc, char *argv[])
{
char str1[LINELEN + 1];
char str2[LINELEN + 1];
int index, rc, status, res;
int pfd[2];/* Pipe file descriptors */
if (pipe(pfd) == -1) /* Create pipe */
exit(-2); // pipe failed !
char* myargs[4];
myargs[0]=strdup(READFROM);
while (1)
{
printf("Please enter first string:\n");
if (mygets(str1, LINELEN) == NULL)
break;
printf("Please enter second string:\n");
if (mygets(str2, LINELEN) == NULL)
break;
myargs[2] = strdup(str1);
myargs[3] = strdup(str2);
do {
printf("Please choose:\n");
for (int i = 0; i < eNumOfCmp; i++)
printf("%d - %s\n", i, cmpstr[i]);
index = mygeti();
} while ((index < 0) || (index >= eNumOfCmp));
myargs[1] = strdup(cmpstr[index]);
rc = fork();
if (rc < 0) // fork failed !
{
printf("fork failed\n");
return -2;
}
else if (rc == 0) { // child !
if (close(pfd[1]) == -1) /* Write end is unused */
exit(-2);
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd[0] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd[0], STDIN_FILENO) == -1)
exit(-2);
if (close(pfd[0]) == -1)
exit(-2);
}
execvp(myargs[0],myargs);
}
else { // parent
if (close(pfd[1]) == -1) /* Write end is unused */
exit(-2);
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd[0] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd[0], STDIN_FILENO) == -1)
exit(-2);
if (close(pfd[0]) == -1)
exit(-2);
}
read(pfd[0], &res, sizeof(int));
printf("%d\n", res);
if (close(pfd[0]) == -1)
exit(-2);
}
}
return 0;
}
loopcmp ->
int main(int argc, char *argv[])
{
int status,rc,res = 0;
if (argc != 4)
{
return -1;
}
char* myargs[3];
for(int i=0;i<3;i++){
myargs[i]=argv[i+1];
}
rc = fork();
if (rc < 0) //fork failed
{
return -2;
}
else if (rc == 0) //I'm the child
{
if(execvp(myargs[1], myargs)==-1)
return -2;
}
else // parent
{
wait(&status);
res = WEXITSTATUS(status);
if(res ==254) // invalid file path ! (254== -2)
return -2 ;
}
write(fileno(stdout),&res,sizeof(int));
return res;
}

linux - exec'd program not terminating

I am writing program in C on Linux which has to fork 2 children.
First child will send two random numbers over pipe to the second child. It will listen for SIGUSR1 signal and will then terminate.
The second child will duplicate(dup2) pipe input as STDIN and file fp as STDOUT. It will then execl program which will print out some data according to its input and end.
My problem is, that the execl'd program will never terminate and I don't know why. Any help or tips will be appreciated.
main.c (parent):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
const int BUFFER_SIZE = 30;
int pipefd[2] = {0,0};
int parent_pid = 0;
int first_pid = 0;
int second_pid = 0;
int sleep_time = 5;
int debug = 0;
FILE *fp;
void parent_func() {
int wstatus = 0;
sleep(sleep_time);
kill(first_pid, SIGUSR1);
wait(&wstatus);
waitpid(second_pid, &wstatus, 0);
}
static void sigusr1_handler(int sig) {
if (sig == SIGUSR1) {
fputs("TERMINATED", stderr);
close(pipefd[1]);
exit(0);
}
}
void first_func() {
struct sigaction act;
char buffer[BUFFER_SIZE];
close(pipefd[0]);
memset(&act, '\0', sizeof(act)); // clear the sigaction struct
act.sa_handler = &sigusr1_handler; // sets function to run on signal
if (sigaction(SIGUSR1, &act, NULL) < 0) { // assign sigaction
fputs("cannot assign sigaction - exiting...", stderr);
exit(1);
}
while (1) {
sprintf(buffer, "%d %d\n", rand(), rand());
write(pipefd[1], buffer, strlen(buffer));
puts(buffer);
sleep(1);
}
}
void second_func() {
close(pipefd[1]);
fp = fopen("out.txt", "w");
char buf[30];
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
//dup2(fileno(fp), STDOUT_FILENO);
execl("./test", "", NULL);
perror("Error");
}
int main(int argc, char *argv[]) {
int fork_val = 0;
parent_pid = getpid();
if (pipe(pipefd)) {
fputs("cannot create pipe - exiting...", stderr);
return 1;
}
if (debug) {
sleep_time *= 10;
}
if ((fork_val = fork()) == -1) {
fputs("cannot fork process - exiting...", stderr);
return 1;
} else if (fork_val == 0) {
first_func();
} else {
first_pid = fork_val;
if ((fork_val = fork()) == -1) {
fputs("cannot fork process - exiting...", stderr);
return 1;
} else if (fork_val == 0) {
second_func();
} else {
second_pid = fork_val;
parent_func();
}
}
fclose(fp);
exit(0);
}
test.c (the execl'd file):
#include "nd.h"
#include "nsd.h"
#include <stdio.h>
#include <stdlib.h>
int main() {
int num1 = 0;
int num2 = 0;
char buffer[100];
while (fgets(buffer, 100, stdin) != NULL) {
if (sscanf(buffer, "%d %d", &num1, &num2) == 2) {
(num1 < 0) ? num1 = (num1 * -1) : num1;
(num2 < 0) ? num2 = (num2 * -1) : num2;
if (num1 == 1 || num2 == 1) {
puts("1");
} else if (num1 == num2) {
if (nd(num1) == 1) {
puts("prime");
} else {
printf("%d\n", num1);
}
} else if (nd(num1) == 1 && nd(num2) == 1) {
puts("prime");
} else {
printf("%d\n", nsd(num1, num2));
}
} else {
fputs("error\n", stderr);
}
}
fputs("DONE", stderr);
exit(0);
}
To be able to detect an end of file from a pipe you need to read from a empty pipe with no writer (no process with an open for writing descriptor).
As your writer (first_func()) never closes its descriptor and always writes something in a never ending loop the reader will either wait for some data or read some data.
Be also careful about closing non useful descriptors, if not you may encounter some problems with pipes, such has a single process that is a reader and a writer, so being unable to detect the end of file...

Linux inter-processes communication

I have to implement a testing program(quiz), which besides displaying the question and reading the answer, it has to display the time left at each one minute past. After finishing the examination time, by finishing the questions or by running out of time,the program has to get back from the beginning, when before the start, we enter the name of the candidate. This implementation has to be done using processes. Below is the code that i have written so far. The problem is that i am not sure that i am making a good communication between the process and the subprocesses, especially because i am not using a pipe. Some opinions?
#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>
#define T 180
void firstChildAction(){
static const char filename[] = "/home/osystems01/laura/text";
char question[100];
char answer[100];
FILE *file = fopen(filename,"r");
if(file != NULL){
while(fgets(question,sizeof question,file) != NULL){
fputs(question, stdout);
scanf("%s",&answer);
}
fclose(file);
}
else{
perror(filename);
}
}
void secondChildAction(){
int i;
for(i = T; i >= 0; i-=60){
if( i/60 != 0){
printf("You have %d %s left.\n", i/60,(i/60 > 1)?"minutes":"minute");
sleep(60);
}
else{
printf("The time is over\n");
break;
}
}
}
int main() {
pid_t pidA;
pid_t pidB;
pid_t wPid;
char name[20];
while(1){
printf("Enter the candidate name or Quit to exit: \n");
scanf("%s",&name);
if(strcmp(name,"Quit") == 0 || strcmp(name,"quit") == 0){
printf("The program is terminating.....\n");
break;
}
else{
pidA = fork();
if(pidA == 0){
firstChildAction();
exit(0);
}
else{
pidB = fork();
if(pidB == 0){
secondChildAction();
exit(0);
}
}
int status;
while(wPid = wait(&status)) > 0 ){
if(WIFEXITED(status)){
int result = WEXITSTATUS(status);
printf("Exit status of %d is %d\n", wPid, result);
if(wPid == pidA){
kill(pidB,SIGTERM);
kill(pidA,SIGTERM);
}
else if(wPid == pidB){
kill(pidA,SIGTERM);
kill(pidB,SIGTERM);
}
}
}
}
}
return 0;
}
Pipes as such don't require you to provide a regular file, but they can have a unique, globally visible name, which is provided by a (unused) filename you have to specify. The contents of the file, if any, is handled by the library.
There are (simple) pipes for communication among related processes (such as a child and a parent process in the same process hierarchy) where the pipe handle can easily be passed to other processes.
The other flavor is called 'named pipes' for processes with any relation, where one can lookup the pipe handle using the global name (as explained in the answer of the question I linked). You can think of a pipe as of a directly connected speaking tube, allowing two processes to chitchat about whatever they like, using read and write functions. On Linux, a pipe is a simplex (at a time, one talks, the other one listens). One would nee two pipes for bidirectional async IO in this case (https://unix.stackexchange.com/questions/53641/how-to-make-bidirectional-pipe-between-two-programs). The immediate buffer for input and output is abstracted. Its just like with network sockets.
I'd suggest to compile this nice example in the accepted answer to play around with: https://stackoverflow.com/a/2789967/1175253
Edit
Example code with error handling. Treat pipe.h & pipe.c as a library (link NamedPipeReader and NamedPipeWriter against it).
This code would need further testing, however, the code is able to (re)open named pipes in any order.
pipe.h
#ifndef PIPE_H_
#define PIPE_H_
//C headers
#include <errno.h>
#include <assert.h>
//Linux headers
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef __cplusplus
extern "C"
{
#endif
int open_named_pipe(const char* const name, const int permissions, const int mode, int* pipe_created);
#ifdef __cplusplus
}
#endif
#endif /* PIPE_H_ */
pipe.c
#include "pipe.h"
#include <stdio.h>
int open_named_pipe(const char* const name, const int permissions, const int mode, int* pipe_created)
{
int fd;
assert(name);
assert(permissions);
assert(pipe_created);
//Create or use an existing pipe special file
if (0 == mkfifo(name, permissions))
{
*pipe_created = 1;
printf("Successfully created named pipe '%s'\n", name);
}
else
{
switch (errno)
{
case EEXIST:
//this is OK, as the other process might already has created the special file
printf("Opened existing named pipe '%s'\n", name);
break;
default:
fprintf(stderr, "Failed to create or access named pipe '%s'\n", name);
perror(" ");
return -1;
};
}
fd = open(name, mode);
if (fd < 0)
{
perror("Could not open pipe for writing");
if (*pipe_created)
{
if (0 == unlink(name))
{
*pipe_created = 0;
}
else
{
perror("Failed to unlink named pipe");
}
}
}
return fd;
}
NamedPipeReader.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include "pipe.h"
//Globals
const char* const pipe_name = "/tmp/myfifo";
const int pipe_permissions = 0600;
const size_t read_buffer_size = 1024; //[bytes]
const size_t read_retry_delay = 25000; //[us]
int fd = -1;
int pipe_created = 0;
char* read_buffer = NULL;
//Handles EPIPE signal
void signal_handler(int signal)
{
fprintf(stderr, "cought signal %d\n", signal);
}
//Handles cleanup on exit
void exit_handler(void)
{
if (read_buffer)
free(read_buffer);
if (fd >= 0)
close(fd);
//if this process created the FIFO, we unlink it
if (pipe_created == 0)
unlink(pipe_name);
}
int main()
{
//Locals
int run = 1;
int received = 0;
//Install the exit handler
atexit(&exit_handler);
signal(EPIPE, signal_handler);
signal(EACCES, signal_handler);
//Allocate the buffer
read_buffer = (char*) malloc(read_buffer_size);
if (!read_buffer)
{
perror("Failed to allocate buffer");
return EXIT_FAILURE;
}
restart: ;
//Close if already open
if(fd >= 0)
close(fd);
//Create or use an existing pipe special file
fd = open_named_pipe(pipe_name, pipe_permissions, O_RDONLY, &pipe_created);
if (fd < 0)
{
return EXIT_FAILURE;
}
while (run)
{
assert(fd >= 0);
assert(read_buffer_size > 1);
received = read(fd, read_buffer, read_buffer_size - 1);
if (received > 0)
{
//add a NUL char for string termination
read_buffer[received] = '0';
printf("local process %llu received: %s\n", (unsigned long long) getpid(), read_buffer);
}
else if (received == 0)
{
//EOF reached, this happens in case the writer has closed its handle.
//Perform a delayed restart and recreate the named pipe
usleep(read_retry_delay);
printf("Restarting...\n");
goto restart;
}
else
{
switch (errno)
{
case EAGAIN:
//Wait, if the pipe is empty,
//happens when opened with the O_NONBLOCK flag
usleep(read_retry_delay);
break;
case EPIPE:
case EBADF:
case EBADFD:
perror("Pipe error");
printf("Restarting...\n");
goto restart;
default:
perror("Pipe error");
return EXIT_FAILURE;
};
}
}
return EXIT_SUCCESS;
}
NamedPipeWriter.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include "pipe.h"
//Globals
const char* const pipe_name = "/tmp/myfifo";
const int pipe_permissions = 0600;
const size_t write_buffer_size = 1024; //[bytes]
const size_t write_retry_delay = 25000; //[us]
const size_t write_interval = 1000000;
int fd = -1;
int pipe_created = 0;
char* write_buffer = NULL;
//Handles EPIPE signal
void signal_handler(int signal)
{
fprintf(stderr, "cought signal %d\n", signal);
}
//Handles cleanup on exit
void exit_handler(void)
{
if (write_buffer)
free(write_buffer);
if (fd >= 0)
close(fd);
//if this process created the FIFO, we unlink it
if (pipe_created == 0)
unlink(pipe_name);
}
//Main Function
int main()
{
//Locals
int run = 1;
int sent = 0;
int msg_len = 0;
//Install the exit handler
atexit(&exit_handler);
signal(EPIPE, signal_handler);
signal(EACCES, signal_handler);
//Allocate the buffer
write_buffer = (char*) malloc(write_buffer_size);
if (!write_buffer)
{
perror("Failed to allocate buffer");
return EXIT_FAILURE;
}
restart: ;
//Close if already open
if(fd >= 0)
close(fd);
//Create or use an existing pipe special file
fd = open_named_pipe(pipe_name, pipe_permissions, O_WRONLY, &pipe_created);
if (fd < 0)
{
return EXIT_FAILURE;
}
while (run)
{
//Print message into the buffer
msg_len = snprintf(write_buffer, write_buffer_size, "Greetings from process %llu\n", (unsigned long long) getpid());
{
char* msg_ptr = write_buffer;
char* msg_end = write_buffer + msg_len;
while (msg_ptr != msg_end)
{
assert(fd >= 0);
assert(msg_ptr < msg_end);
sent = write(fd, msg_ptr, msg_end - msg_ptr);
if (sent > 0)
{
msg_ptr += sent;
}
else if (sent == 0)
{
//retry delay for nonblocking writes
usleep(write_retry_delay);
}
else
{
switch (errno)
{
case EAGAIN:
//Wait, if the pipe is full,
//happens when opened with the O_NONBLOCK flag
usleep(write_retry_delay);
break;
case EPIPE:
case EBADF:
case EBADFD:
perror("Pipe error");
printf("Restarting...\n");
goto restart;
default:
perror("Pipe error");
return EXIT_FAILURE;
};
}
}
printf("Written: %s\n", write_buffer);
usleep(write_interval);
}
}
return EXIT_SUCCESS;
}

child process pending after read in coprocess

I'm writing a coprocess program using pipe. It works fine when the child read some data, handle it and output it. But when I read all the data and handle it, it just pending. Any body have some idea? Thank you.
Here is the source code:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
#define MAXSIZE 1024
char workload[MAXSIZE];
char result[MAXSIZE];
workload[strlen(workload)] = EOF;
int workload_size = strlen(workload);
int fd1[2], fd2[2];
int n;
pid_t pid;
if (pipe(fd1) < 0 || pipe(fd2) < 0) {
fprintf(stderr, "pipe error: %s\n", strerror(errno));
exit(1);
}
if ((pid = fork()) < 0) {
fprintf(stderr, "fork error: %s\n", strerror(errno));
exit(1);
} else if (pid > 0) {
close(fd1[0]);
close(fd2[1]);
while(fgets(workload, MAXSIZE, stdin) != NULL)
{
workload_size = strlen(workload);
if (write(fd1[1], workload, workload_size) != workload_size) {
fprintf(stderr, "write to pipe error: %s\n", strerror(errno));
exit(1);
}
if ((n = read(fd2[0], result, MAXSIZE)) < 0) {
fprintf(stderr, "read from pipe error: %s\n", strerror(errno));
exit(1);
}
if (n == 0) {
fprintf(stderr, "child closed the pipe\n");
exit(1);
}
result[n] = 0;
if (puts(result) == EOF) {
fprintf(stderr, "fputs error\n");
exit(1);
}
}
} else {
close(fd1[1]);
close(fd2[0]);
if (fd1[0] != STDIN_FILENO) {
if (dup2(fd1[0] ,STDIN_FILENO) != STDIN_FILENO) {
fprintf(stderr, "dup2 error to stdin.\n");
exit(1);
}
close(fd1[0]);
}
if (fd2[1] != STDOUT_FILENO) {
if (dup2(fd2[1] ,STDOUT_FILENO) != STDOUT_FILENO) {
fprintf(stderr, "dup2 error to stdout.\n");
exit(1);
}
close(fd2[1]);
}
if (execl("./a.out", "a.out", NULL) < 0) {
fprintf(stderr, "execl error: %s\n", strerror(errno));
exit(1);
}
exit(0);
}
return 0;
}
Here is the source code of a.out, it works well with this:
#include <stdio.h>
#include <string.h>
int main()
{
#define MAXSIZE 1024
char x[MAXSIZE];
int n;
while(scanf("%s", x) != EOF)
{
printf("len:%d %s", strlen(x), x);
fflush(stdout);
}
return 0;
}
But it seems just pending when I write the code like this:
#include <stdio.h>
#include <string.h>
int main()
{
#define MAXSIZE 1024
char x[MAXSIZE];
int n;
while(scanf("%s", x) != EOF);
printf("Ok\n");
fflush(stdout);
return 0;
}
The way you are calling scanf with %s may overflow the x buffer. You should at least modify the scanf with a width modifier.
#include <stdio.h>
#include <string.h>
int main()
{
#define MAXSIZE 1024
char x[MAXSIZE];
int n;
while(scanf("%1024s", x) != EOF)
{
printf("len:%d %s", strlen(x), x);
fflush(stdout);
}
return 0;
}
And similarly for your other program.
The reason your program is getting blocked is because your second a.out program is looped doing another scanf, when at the same time the parent program is trying to read a response back into result.
You should test and loop while not feof and you might use popen & pclose
You probably want to use some multiplexing system call like poll

Resources