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;
}
Related
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
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;
}
I have user read/write permissions on a pipe. Group has read. Other has read. But program gets "stuck" when I run it. Program 1 is the "parent". Program 2 is the "child".
Program 1:
int main(int argc, char * argv[])
{
FILE *fptr; //for opening and closing input file
int fdw;// write to pipe;
int fdr; //read to pipe;
pid_t pid;
int inputarray[500];
int arraylength = 0; int j =0;
char *mypipe = "mypipe";
if (argc < 2)
{
printf("Need to provide the file's name. \n");
return EXIT_FAILURE;
}
//open input file
fptr = fopen(argv[1], "r");
if (fptr==NULL)
{
printf("fopen fail.\n");
return EXIT_FAILURE;
}
//read input file and fill array with integers
while (!feof(fptr))
{
fscanf(fptr,"%d",&inputarray[arraylength]);
arraylength = arraylength + 1;
}
fclose(fptr); //close input file
pid = fork();
mkfifo(mypipe, 0666);
fdw = open("mypipe",O_WRONLY);
if (fdw < 0)
{
perror("File can't open to write.");
return;
}
int b;
b=3;
write(fdw,&b,sizeof(b));
close(fdw);
if ( pid ==-1)
{
perror("fork");
exit(1);
}
int status; //exit status of child
if(pid==0)//if child process
{
execl("program2", (char*) NULL);
}
else //if parent process
{
wait(&status);}
if((WIFEXITED(status)))
{
printf("Child's exit code %d", WEXITSTATUS(status));
}
else{
printf("Child did not terminate with exit");}
}
Program 2:
int fdl;
int data;
fdl = open("mypipe",O_RDONLY);
if ( fdl < 0)
{
perror("File can't open to read.");
return;
}
read(fdl,&data,sizeof(data));
close(fdl);
The program will block on writing to the fifo until what it's writing is being read. The reading in the child process won't happen since the execl() doesn't happen until after the writing.
Also, it looks like both processes will actually attempt to write to the fifo since you fork() and then immediately start writing.
You should fork(), then test on the returned PID. The parent should then write to the fifo while the child should call execl(). The fifo should be created by the parent before the fork() call.
You should also consider using indent or clang-format to properly format your code, which eases reading it and may expose bugs (forgotten curly braces etc.).
A simple complete example program. The parent writes a string to the child and the child reads it character by character and outputs it to standard output:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
void parent(void);
void child(void);
int main(void) {
pid_t pid;
mkfifo("myfifo", 0666); /* fails if exists, but we don't care here */
if ((pid = fork()) < 0)
abort();
if (pid == 0)
child(); /* will not return */
else
parent();
return EXIT_SUCCESS;
}
void parent(void) {
int fd;
int len;
int ret;
int stat;
char *ptr;
char *msg = "Hello World!";
if ((fd = open("myfifo", O_WRONLY)) < 0)
abort();
len = strlen(msg) + 1;
ptr = msg;
puts("Parent: About to write to child");
while ((ret = write(fd, ptr, len)) != 0) {
if (ret > 0) {
len -= ret;
ptr += ret;
} else
abort();
}
close(fd);
puts("Parent: Waiting for child to exit");
wait(&stat);
printf("Parent: Child exited with status %d\n", stat);
}
void child(void) {
int fd;
int ret;
char ch;
if ((fd = open("myfifo", O_RDONLY)) < 0)
abort();
puts("Child: About to read from parent");
while ((ret = read(fd, &ch, 1)) != 0) {
if (ret > 0)
putchar(ch);
else
abort();
}
putchar('\n');
close(fd);
puts("Child: I'm done here");
exit(EXIT_SUCCESS);
}
In this case, since both child and parent processes are in the same context, I could have used an anonymous pipe pair created with pipe(), but this illustrates the flow, including the creation of the named pipe.
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;
}
I have prepared a program which emulates shell (cmd) interface using pipes. There are two versions of the program:
1. Using one pipe (using a pipe from parent to child communication)
2. Using double pipe (using two pipes from parent to child and from child to parent to communicate).
So, the first program provides desired interface and works how I want, but I cannot reach the same result (interface) in the second program (using dup2() and similar).
So, I relay on your help and put the both codes below.
B.S.: You may compile and try both programs with the same way using these commands:
$ gcc prog1.c -o prog1
Next let's run:
$ ./prog1
Next let's run new terminal and try to write some data to input.txt:
$ echo pwd > input.txt
And then watch the result in the first terminal.
(This working fine for the first program but I need to get this working wit the same interface in the second program)
CODE OF THE FIRST PROGRAM (WORKING FINE):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
void do_child(int data_pipe[]) {
int c;
int rc;
close(data_pipe[1]);
dup2(data_pipe[0], 0); /* This string provides the desired interface of the program */
char* cmd[] = { "bash", (char *)0 };
execvp("bash", cmd);
while ((rc = read(data_pipe[0], &c, 1)) > 0)
{
putchar(c);
}
exit(0);
}
void do_parent(int data_pipe[])
{
int c;
int rc;
FILE *in;
close(data_pipe[0]);
while (1)
{
in = fopen("input.txt", "r");
while ((c = fgetc(in)) > 0)
{
rc = write(data_pipe[1], &c, 1);
if (rc == -1)
{
perror("Parent: write");
close(data_pipe[1]);
exit(1);
}
}
fclose(in);
}
close(data_pipe[1]);
exit(0);
}
int main(int argc, char* argv[])
{
int data_pipe[2];
int pid;
int rc;
umask(0);
mknod("input.txt", S_IFIFO|0666, 0);
rc = pipe(data_pipe);
if (rc == -1)
{
perror("pipe");
exit(1);
}
pid = fork();
switch (pid)
{
case -1:
perror("fork");
exit(1);
case 0:
do_child(data_pipe);
default:
do_parent(data_pipe);
}
return 0;
}
CODE OF THE SECOND PROGRAM (NEED TO BE CORRECTED A LITTLE BIT):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
/* Original version got from http://www.iakovlev.org */
int parent_to_child[2];
int child_to_parent[2];
void do_parent()
{
int c;
char ch;
int rc;
FILE *in;
close(child_to_parent[1]); /* we don't need to write to this pipe. */
close(parent_to_child[0]); /* we don't need to read from this pipe. */
while (1)
{
in = fopen("input.txt", "r");
while ((c = fgetc(in)) > 0) {
ch = (char)c;
/* write to child */
rc = write(parent_to_child[1], &ch, 1);
if (rc == -1) {
perror("child: write");
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(1);
}
/* read back from child */
rc = read(child_to_parent[0], &ch, 1);
c = (int)ch;
if (rc <= 0) {
perror("parent: read");
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(1);
}
putchar(c);
}
fclose(in);
}
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(0);
}
void do_child()
{
int c;
char ch;
int rc;
close(parent_to_child[1]); /* we don't need to write to this pipe. */
close(child_to_parent[0]); /* we don't need to read from this pipe. */
//dup2(parent_to_child[0], STDIN_FILENO);
//dup2(child_to_parent[1], STDOUT_FILENO);
/* Some dup2() routines must be added here
to get this working as the first program above */
char* cmd[] = { "bash", (char *)0 };
execvp("bash", cmd);
while (read(parent_to_child[0], &ch, 1) > 0) {
c = (int)ch;
ch = (char)c;
putchar(ch);
rc = write(child_to_parent[1], &ch, 1);
if (rc == -1) {
perror("child: write");
close(parent_to_child[0]);
close(child_to_parent[1]);
exit(1);
}
}
close(parent_to_child[0]);
close(child_to_parent[1]);
exit(0);
}
int main(int argc, char* argv[])
{
int pid;
int rc;
umask(0);
mknod("input.txt", S_IFIFO|0666, 0);
rc = pipe(parent_to_child);
if (rc == -1) {
perror("main: pipe parent_to_child");
exit(1);
}
rc = pipe(child_to_parent);
if (rc == -1) {
perror("main: pipe child_to_parent");
exit(1);
}
pid = fork();
switch (pid) {
case -1:
perror("main: fork");
exit(1);
case 0:
do_child();
default:
do_parent();
}
return 0;
}
The major difference is here:
while ((c = fgetc(in)) > 0) {
ch = (char)c;
/* write to child */
rc = write(parent_to_child[1], &ch, 1);
/* .... */
/* read back from child */
rc = read(child_to_parent[0], &ch, 1);
/* .... */
putchar(c);
}
As I'm lazy to compile/test for you, I would simply speculate that the parent is blocked in the read(). Because other side (bash in child process) isn't guaranteed to echo every written character back. Or it might even decide to print more than one character what your code is incapable of handling.
In the case you have to poll() to see whether there is something to read or not. Or set the O_NONBLOCK flag on the child_to_parent[0] with fcntl(F_SETFL) and when errno==EAGAIN, simply skip the read() branch. And loop while there are still characters to read.
Edit1. BTW I totally missed the part: you in do_parent() loop have to use poll() on the both child_to_parent[0] and in, since other side might write something (read() wouldn't block) even when you do not write() any character to it.
Thanks to you it seems I got it's working.
So, here is updated code of do_parent:
void do_parent()
{
int c;
char ch;
int rc;
FILE *in;
struct pollfd fds[2];
int pol_ret;
fds[0].fd = child_to_parent[0];
close(child_to_parent[1]); /* we don't need to write to this pipe. */
close(parent_to_child[0]); /* we don't need to read from this pipe. */
while (1)
{
in = fopen("input.txt", "r");
fds[1].fd = fileno(in);
pol_ret = poll(fds, 2, 500);
while ((c = fgetc(in)) > 0) {
ch = (char)c;
/* write to child */
rc = write(parent_to_child[1], &ch, 1);
if (rc == -1) {
perror("child: write");
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(1);
}
/* read back from child */
if (fds[0].revents & POLLIN)
{
rc = read(child_to_parent[0], &ch, 1);
c = (int)ch;
if (rc <= 0) {
perror("parent: read");
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(1);
}
putchar(c);
}
}
fclose(in);
}
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(0);
}
Also I have added this into do_child():
dup2(parent_to_child[0], STDIN_FILENO);