This is what I'm trying to do: a parent process creates two child processes, then pipes stdout of one to stdin of another. Then parent waits for 5 secs and kills the first child.
This is how I approached it: First I create a pipe. Then fork to create the first child (gen in code) and dup2 to duplicate pipe's write onto stdout. Another fork for second child (called cons in code), dup2 to duplicate read end onto stdin. Cons just prints the data. Parent sends SIGTERM to first child, second child reads until EOF, so it closes on it's own.
Nothing but my error output (here used for debugging) is printed. Gen generates two random numbers, but loop in cons doesn't get executed. So I suppose there's nothing on stdin of cons.
I consulted Google and followed this How do I chain stdout in one child process to stdin in another child in C?, but didn't manage to figure out what I messed up. Would appreciate any help. Thanks
Compilation: gcc -std=c99 -Wall -Werror main.c -o main on Bash in Windows 10
Code:
#define _POSIX_SOURCE
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <signal.h>
#define READ_END 0
#define WRITE_END 1
#define BUF_SIZE 10
int main(int argc, char* argv[])
{
pid_t gen, cons;
int fd[2];
if (pipe(fd) < 0) {
// pipe error
exit(1);
}
gen = fork();
if (gen < 0) {
// fork error
close(fd[READ_END]);
close(fd[WRITE_END]);
exit(2);
} else if (gen == 0) {
// gen child
close(fd[READ_END]);
time_t t;
srand((unsigned)time(&t));
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[WRITE_END]);
while(1) {
int a = rand() % 1000;
int b = rand() % 1000;
printf("gen %d %d\n", a, b);
fprintf(stderr, "err, gen %d %d\n", a, b);
sleep(1);
}
}
else {
cons = fork();
if (cons < 0) {
// fork error
close(fd[READ_END]);
close(fd[WRITE_END]);
kill(gen, SIGKILL);
exit(2);
} else if (cons == 0) {
// cons child
close(fd[WRITE_END]);
dup2(fd[READ_END], STDIN_FILENO);
close(fd[READ_END]);
char line[BUF_SIZE];
while (fgets(line, sizeof(line), stdin)) {
printf("cons received: %s\n", line);
fprintf(stderr, "cons lives!\n");
}
} else {
// parent
close(fd[READ_END]);
close(fd[WRITE_END]);
sleep(5);
kill(gen, SIGTERM);
}
}
return 0;
}
Standard output is by default buffered, so your gen child only queues output for later sending. So you must fflush it to force your messages to be immedialely delivered:
...
printf("gen %d %d\n", a, b);
fprintf(stderr, "err, gen %d %d\n", a, b);
fflush(stdout); // <= force immediate send
sleep(1);
...
Related
I am trying to create some communication between two programs by forking in the child program within the parent program.
When I execute the child program separately, it works. The purpose of it is that if someone types 1, 2, or 3 followed by enter, that program prints that number as a word. But if one presses 0 and enter, the program exits.
Now I am trying to make the parent program execute the child program in a way where all it does is exit the program while showing the progress of action.
When I execute my program, I see:
Child to start
Parent running OK
Which suggest the child program is running, otherwise I would see:
Exec failed
So instead of me seeing any actual useful output, the system decides to gradually slow down to the point where at first the mouse cursor doesn't move smoothly when I move the mouse, then It got to the point where it wouldn't respond to the keyboard, so I literally had to hold the power button to reset my computer.
How do I fix this so that it can work with any program (that I use as a child) that can exit when I press 0 and enter from within it?
This is my code for the parent:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
//Setup child read and write file handle named parr and parw respectively
//and parent read and write file handle named to parr and parw respectively
#define kidr wrp[0]
#define kidw rdp[1]
#define parw wrp[1]
#define parr rdp[0]
int main(){
int cmd=0;
//setup and start pipes
int wrp[2],rdp[2];
if (pipe(wrp) == -1 || pipe(rdp) == -1){printf("ERROR: cant run pipes.\n");return -1;}
//Start fork
pid_t f=fork();
if (f > 0){
int wstat; //wait state data
char buff[100]; //our data buffer
close(kidr); //we are parent so close child handles
close(kidw);
struct timeval tv;
fd_set readfds;
tv.tv_sec = 1;
tv.tv_usec = 0;
printf("Parent running OK\n");
while(1){
//process other async events here
pid_t wpid=waitpid(-1,&wstat,WNOHANG);
if (wpid==-1){printf("Wait PID error\n");break;}
if (wpid > 0){printf("Children closed OK\n");break;}
//Process data only when child data is readable via pipe
FD_ZERO(&readfds);FD_SET(parr, &readfds);
select(parr+1, &readfds, NULL, NULL, &tv);
if(FD_ISSET(parr, &readfds)){
memset(buff,0,99);
int rd=read(parr, buff, 50);
//doesnt seem to reach this point...
if (rd > 0){
printf("Got: %s\n", buff);
}else{
if (cmd==0){
printf("sending data...\n");
char*dat="0\n"; //parent sends 0 and the enter button.
cmd++; //so this doesn't get called again
write(parw,dat,strlen(dat));
}
}
}
}
//close everything and exit
close(parr);
close(parw);
return 0;
}
if (f==0){
printf("Child to start\n");
//Child mode.
//Close parents
close(parr);close(parw);
//make stdio as child handles
dup2(kidr,STDIN_FILENO);dup2(kidw,STDOUT_FILENO);
//close old child handles
close(kidw);close(kidr);
execlp("/path/to/forkt","forkt",NULL);
//We shouldn't get here unless 'ls' command isnt found
printf("Exec failed\n");
_exit(-1);
}
if (f==-1){
//If fork() doesnt work...
printf("Fork error\n");
}
return 0;
}
This is my code for the child and I compiled it so its named forkt.
#include <stdio.h>
#include <stdlib.h>
int main(){
printf("The child has started\n\n");
char c[100];
while (1){
printf("Enter number or 0 to exit: \n>");
scanf("%s",c);
if (c[0]=='1'){printf("one\n");}
if (c[0]=='2'){printf("two\n");}
if (c[0]=='3'){printf("three\n");}
if (c[0]=='0'){return 0;}
}
}
Update
I took a suggestion of running my parent code through the gdb debugger.
I compiled my code using the gcc -g switch then executed it with gdb a.out
Then in gdb, I set a break point to first line of code then used the "run" command then i kept using the "step" command until I found the crashing point which is here:
pid_t f=fork();
if (f > 0){ // <- right here
This suggests that somehow the child is creating the lockup(?) even though the child runs fine if it is run by itself without a parent?
AFAICS, the parent won't write anything to the child until the child sends something back, but the child won't send anything until it gets something from the parent. That's a deadlock. There's also a problem with buffering. The pipes are not 'interactive devices' so the output streams are not flushed until the buffer is full, the stream is closed, or you call fflush().
Here's some alternative but very similar code to yours:
forkt.c
#include <stdio.h>
int main(void)
{
printf("The child has started\n\n");
fflush(stdout);
char c[100];
while (1)
{
printf("Enter number or 0 to exit:\n>");
fflush(stdout);
if (scanf("%s", c) != 1)
return 0;
fprintf(stderr, "Child received: [%s]\n", c);
if (c[0] == '1')
{
printf("one\n");
}
if (c[0] == '2')
{
printf("two\n");
}
if (c[0] == '3')
{
printf("three\n");
}
if (c[0] == '0')
{
return 0;
}
fflush(stdout);
}
}
This is mostly noticeable for a collection of calls fflush(stdout).
parent.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define kidr wrp[0]
#define kidw rdp[1]
#define parr rdp[0]
#define parw wrp[1]
int main(void)
{
int cmd = 0;
int wrp[2], rdp[2];
fprintf(stderr, "Parent process: PID %d\n", getpid());
if (pipe(wrp) == -1 || pipe(rdp) == -1)
{
fprintf(stderr, "ERROR: cant run pipes.\n");
exit(1);
}
pid_t f = fork();
if (f == -1)
{
fprintf(stderr, "Fork error\n");
exit(1);
}
if (f > 0)
{
int wstat;
char buff[100];
close(kidr);
close(kidw);
fprintf(stderr, "Parent running OK - child %d\n", f);
while (1)
{
pid_t wpid = waitpid(-1, &wstat, WNOHANG);
if (wpid == -1)
{
fprintf(stderr, "Wait PID error\n");
break;
}
if (wpid > 0)
{
fprintf(stderr, "Child %d exited\n", wpid);
break;
}
for (cmd = 3; cmd >= 0; cmd--)
{
char buffer[30];
int nb = snprintf(buffer, sizeof(buffer), "%d\n", cmd);
int wr = write(parw, buffer, strlen(buffer));
if (wr != nb)
{
fprintf(stderr, "Parent failed to write: %d\n", wr);
exit(1);
}
fprintf(stderr, "Parent sent: %s", buffer);
memset(buff, 0, 99);
int rd = read(parr, buff, 50);
if (rd > 0)
{
fprintf(stderr, "Got: [[%.*s]]\n", rd, buff);
}
else
{
fprintf(stderr, "Parent read failed\n");
exit(1);
}
}
}
close(parr);
close(parw);
return 0;
}
if (f == 0)
{
fprintf(stderr, "Child %d to start\n", getpid());
close(parr);
close(parw);
dup2(kidr, STDIN_FILENO);
dup2(kidw, STDOUT_FILENO);
close(kidw);
close(kidr);
execlp("forkt", "forkt", NULL);
fprintf(stderr, "Exec failed\n");
_exit(-1);
}
return 0;
}
The surgery here is more extensive.
When I ran the code, one time I got the output:
Parent process: PID 94693
Parent running OK - child 94694
Parent sent: 3
Child 94694 to start
Got: [[The child has started
]]
Parent sent: 2
Child received: [3]
Got: [[Enter number or 0 to exit:
>]]
Parent sent: 1
Child received: [2]
Got: [[three
Enter number or 0 to exit:
>]]
Parent sent: 0
Child received: [1]
Got: [[two
Enter number or 0 to exit:
>]]
Child received: [0]
Parent sent: 3
Got: [[one
Enter number or 0 to exit:
>]]
Parent sent: 2
Parent read failed
Note that the prompts from the child are mixed up with the output.
There are already multiple answers to this question but none of them have been able to help me solve my problem. I am trying to understand IPC using an anonymous pipe in C.
From my understanding of pipes, they are a one way communication channel with one read end and one write end.
Assuming we have two c files one named parent.c and the other child.c. What I am trying to achieve is to be able to create 5 or more child processes. After this the parent and the child should communicate with the child processes through standard input and standard output, but since I want to be able to print what the parent receives from the child I'll instead tie the pipes to standard error output using dup2.
In summary
1. Run a parent program which spins up 5 or more child processes and runs them.
2. The child process waits for an input from the parent using scanf.
3. The parent sends a message to the child process.
4. The child process receives the message and sends a reply to the parent and exits.
5. The parent process prints the received message and prints it then exits.
parent.c
// Parentc
#include <stdio.h>
#include <stdlib.h>
#include <uinstd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, const char *argv[]){
// File descriptors for the pipes
int read_pipe[2]; // From child to parent
int write_pipe[2]; // From parent to child
pid_t process_id;
int exit_status;
// Try to fork 5 child processes
for(int i = 0; i < 5; i++){
if(pipe(write_pipe) == -1 || pipe(read_pipe) == -1){
perror("Pipe");
exit(1);
}
// Spin a child process
process_id = fork();
if(process_id == -1){
perror("Fork");
exit(1);
} else if(processId == 0) {
// The child process
// I don't know what to do here, The idea is to close the
// unneeded end of the pipes and wait for input from the parent
// process
// Start the ./child
execl("./child", "");
} else {
// The parent process
char recieved_data[1024];
// Send data to child since stderr is duplicated in the pipe
// It sends the pid of the child
fprintf(stderr, "Test data to %d ", process_id);
// Wait to recieve data from child
// Don't know how to do that
// Print the recieved data
printf("Parent recieved: \"%s\"\n", recieved_data);
wait(&exit_status); // Will wait till all children exit before exiting
}
}
return 0;
}
The child.c is a simple program as shown below
child.c
#include <stdio.h>
int main(int argc, const char *argv[]){
char data_buffer[1024];
// Wait for input from parent
scanf("%s", data_buffer);
// Send data back to parent
printf("Child process: %s", data_buffer);
return 0;
}
Expected output
$ ./parent
parent recived: "Child process: Test data to 12345"
parent recived: "Child process: Test data to 12346"
parent recived: "Child process: Test data to 12347"
parent recived: "Child process: Test data to 12348"
parent recived: "Child process: Test data to 12349"
Where 12345, 12346....12349 is the process id of the child process
Here you have a code i did, and i will use to explain to you:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main() {
char buff[1024];
int aux, i, count;
int fds[2], fdss[2];
pipe(fds); //Here we initialize the file descriptors
pipe(fdss);
mode_t fd_mode = S_IRWXU;
for (i = 0; i < 3; i++) {
aux = fork();
if (aux == 0)
break;
}
switch (i) {
case 0:
printf("Write something:\n");
scanf("%s[^\n]", buff);
i = 0;
count = 0;
while(buff[i] != '\0') {
count++;
i++;
}
dup2(fds[1], 1);
close(fds[1]);
close(fds[0]);
close(fdss[0]);
close(fdss[1]);
write (1, buff, sizeof(buff));
break;
case 1:
dup2(fds[0], 0);
dup2(fdss[1], 1);
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
//
if (execl("/bin/grep", "grep", "example", NULL) == -1) {
printf("Error\n");
exit (1);
}
break;
case 2:
aux = open("result.txt", O_RDWR | O_CREAT , S_IRWXU);
dup2(fdss[0], 0);
dup2(aux, 1);
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
close(aux);
if (execl("/usr/bin/wc", "wc", "-l", NULL) == -1) {
printf("Error \n");
exit (1);
}
}
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
for (i = 0; i < 3; i++) wait(NULL);
return 0;
}
Ok, let's start:
We create and initialize pipes with pipe()
Then we write our code and before execl() we change the file descriptors, in order to pass the text we will write in the console, through processes and finally write in a file called result.txt the result of the "grep example" command applied to the text we have written.
The function dup2(new_descriptor, old_descriptor) is copying the new descriptor into the old descriptor and closes the old descriptor. For example:
Before dup2(fds[1], 1) we have:
0 STDIN
1 STDOUT
2 STDERR
After dup2(fds[1], 1) we have:
0 STDIN
1 fds[1]
2 STDERR
NOTE: If you don't want to use 1, yo can simply write STDOUT_FILENO
So now we are able to write through processes and in my example to a file too
Here is the code, where parent process writes a string input in pipe and children processes read this from pipe. If child process reads from pipe the word "end", then i want to terminate all the processes and then terminate itself, and if reads the word "finish" i want to raise a signal to father for killing all the processes and then exit. I run the code and i had segmentation fault. Why it is wrong?
#define _POSIX_SOURCE
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
void measure_time(int sig)
{
printf("child [%d] received signal %d\n", getpid(), sig);
}
int main(int argc, char *argv[])
{
int n_task = 4;
pid_t pid;
pid_t pid_array[n_task];
int fd[2];
for (int i = 0; i < n_task; i++)
{
pid = fork();
if (pipe(fd) == -1)
{
perror(" pipe ");
exit(1);
}
if (pid < 0)
{
perror("fork");
exit(1);
}
if (pid == 0) //child
{
char *buf;
close(fd[1]);
read(fd[0], buf, 10);
printf("I read: %s", buf);
if (strcmp(buf, "end") == 0)
{
for (int i = 0; i < n_task; i++)
kill(pid_array[i], SIGUSR1);
}else if(strcmp(buf,"finish") == 0){
/*Here i want father to kill all children and then exit.*/
}
exit(0);
}
close(fd[0]);
char *buf;
printf("Give the input string: \n");
scanf("%s", buf);
write(fd[1], buf, strlen(buf));
close(fd[1]);
pid_array[i] = pid;
}
sleep(1);
for (int i = 0; i < n_task; i++)
wait(NULL);
return (0);
}
Besides the issue of uninitialized buf identified by #G. Sliepen, the pipe() need be called before fork() as file descriptors are kept open when forking child process(s). This is also how pipe works.
You can try to change your code snippet to put pipe() before fork().
...
if (pipe(fd) == -1)
{
perror(" pipe ");
exit(1);
}
pid = fork();
if (pid < 0)
{
perror("fork");
exit(1);
}
...
Please read the manual page of pipe(2) in which an example presented.
SO has this post fork() and pipes() in c explained this as well.
Update for terminating process(s)
This child process has no knowledge about existence of its siblings, but its parent process has. If not explicitly required, you can let the parent to do so, i.e. to "end" all child processes.
BTW, instead of sending signal SIGUSR1 it is better to send SIGTERM signal. Although SIGUSSR1 can cause the target process be terminated by default (see signal(7)).
To "finish", i.e. to kill (or terminate) all the child processes as well as parent process, you can simplly kill the parent. All its descendants got killed as well. Or, you can send signal to the same process group. See kill(2).
You are declaring a pointer buf, but did not initialize it. Subsequent calls to read() and scanf() will fail because the pointer is invalid.
You need to make sure buf is initialized and pointing to valid memory. A simple way to fix your code is to do:
char buf[10];
read(fd[0], buf, 10);
If you enable compiler warnings with -Wall, then the compiler will warn you about initialized variables.
Be aware of potential buffer overflows: if you declare char buf[10], make sure you will never write more than ten bytes into it. Also, check the return value of functions like read(), write(), scanf() to ensure no errors were encountered, otherwise the contents of the buffers or output files might not be as expected.
I am having serious trouble working with pipes in C. I'm supposed to take in arguments from the command line (example: ./myprogram 123 45 67), read the arguments one character at a time into a buffer, send the character to the child process to be counted, and then return the total number of characters read to the parent process. My code is as follows(note: the comments are what I'm supposed to be doing):
// Characters from command line arguments are sent to child process
// from parent process one at a time through pipe.
// Child process counts number of characters sent through pipe.
// Child process returns number of characters counted to parent process.
// Parent process prints number of characters counted by child process.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
static int toChild[2];
static int fromChild[2];
static char buffer;
int main(int argc, char **argv)
{
int status;
int nChars = 0;
pid_t pid;
pipe(toChild);
pipe(fromChild);
if ((pid = fork()) == -1) {
printf("fork error %d\n", pid);
return -1;
}
else if (pid == 0) {
close(toChild[1]);
close(fromChild[0]);
// Receive characters from parent process via pipe
// one at a time, and count them.
int count = 0;
printf("child about to read\n");
while(read(toChild[0], &buffer, 1)){
count++;
}
// Return number of characters counted to parent process.
write(fromChild[1], &count, sizeof(count));
close(toChild[0]);
close(fromChild[1]);
printf("child exits\n");
}
else {
close(toChild[0]);
close(fromChild[1]);
// -- running in parent process --
printf("CS201 - Assignment 3 - Chris Gavette\n");
write(toChild[1], &argv[1], 1);
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
read(fromChild[0], &nChars, 1);
// Wait for child process to return. Reap child process.
// Receive number of characters counted via the value
// returned when the child process is reaped.
close(toChild[1]);
close(fromChild[0]);
waitpid(pid, &status, 0);
printf("child counted %d chars\n", nChars);
printf("parent exits\n");
return 0;
}
}
The child process seems to hang even though I've closed both ends of both pipes.
For starters, this is wrong.
write(toChild[1], &count, 1)
It will eventually contribute to your problem. count is a int, not char or unsigned char. You need to send sizeof(count). Also, the read-function upon hitting an error will return EOF, which is non-zero, so your child exit condition is not appropriate. it should look something like this:
while(read(toChild[0], &buffer, 1) == 1)
Finally, your parent process should cycle through each argument in argv[] sending each as a strlen sized buffer.
I'm nearly certain this is what you're trying to do. Note that in order to maintain sanity in knowing which descriptor is used for a specific purpose, I prefer using a #define to note what each process uses for reading and writing. This can be extended to any number of processes, btw, which I'm sure is not too far down the line for your next assignment:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
// P0_READ - parent read source
// P0_WRITE - parent write target
// P1_READ - child read source
// P1_WRITE - child write target
#define P0_READ 0
#define P1_WRITE 1
#define P1_READ 2
#define P0_WRITE 3
#define N_PIPES 4
int main(int argc, char **argv)
{
int fd[N_PIPES], count = 0, i;
pid_t pid;
char c;
if (pipe(fd) || pipe(fd+2))
{
perror("Failed to open pipe(s)");
return EXIT_FAILURE;
}
// fork child process
if ((pid = fork()) == -1)
{
perror("Failed to fork child process");
return EXIT_FAILURE;
}
// child process
if (pid == 0)
{
// close non P1 descriptors
close(fd[P0_READ]);
close(fd[P0_WRITE]);
// get chars from input pipe, counting each one.
while(read(fd[P1_READ], &c, 1) == 1)
count++;
printf("Child: count = %d\n", count);
write(fd[P1_WRITE], &count, sizeof(count));
// close remaining descriptors
close(fd[P1_READ]);
close(fd[P1_WRITE]);
return EXIT_SUCCESS;
}
// parent process. start by closing unused descriptors
close(fd[P1_READ]);
close(fd[P1_WRITE]);
// send each arg
for (i=1; i<argc; ++i)
write(fd[P0_WRITE], argv[i], strlen(argv[i]));
// finished sending args
close(fd[P0_WRITE]);
// Wait for child process to return.
wait(NULL);
// wait for total count
if (read(fd[P0_READ], &count, sizeof(count)) == sizeof(count))
printf("Parent: count = %d\n", count);
// close last descriptor
close(fd[P0_READ]);
return 0;
}
Input
./progname argOne argTwo
Output
Child: count = 12
Parent: count = 12
Edit: Single Pipe with Child Return Status
It seems from the comments of the original question your assignment may call for reaping the return status of the child process as the result count rather than returning it in a pipe. In doing so, you can do this with a single pipe-descriptor pair. I prefer the first method, but this works as well:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
// P0_WRITE - parent write target
// P1_READ - child read source
#define P1_READ 0
#define P0_WRITE 1
#define N_PIPES 2
int main(int argc, char **argv)
{
int fd[N_PIPES], count = 0;
pid_t pid;
char c;
if (pipe(fd))
{
perror("Failed to open pipe(s)");
return EXIT_FAILURE;
}
// fork child process
pid = fork();
if (pid == -1)
{
perror("Failed to fork child process");
return EXIT_FAILURE;
}
if (pid == 0)
{
// close non P1 descriptors
close(fd[P0_WRITE]);
// Return number of characters counted to parent process.
while(read(fd[P1_READ], &c, 1) == 1)
++count;
close(fd[P1_READ]);
printf("Child: count = %d\n", count);
return count;
}
// parent process. start by closing unused descriptors
close(fd[P1_READ]);
// eacn each arg entirely
for (int i=1; i<argc; ++i)
write(fd[P0_WRITE], argv[i], strlen(argv[i]));
// finished sending args
close(fd[P0_WRITE]);
// Wait for child process to return.
if (wait(&count) == -1)
{
perror("Failed to wait for child process");
return EXIT_FAILURE;
}
printf("Parent: count = %d\n", WEXITSTATUS(count));
return 0;
}
The results are the same, but note this is a biach to to debug as most debuggers will signal-trip on your child process and the real exit status is lost. On my Mac, for example, running this under Xcode trips:
Failed to wait for child process: Interrupted system call
while running from the command line gives:
Child: count = 12
Parent: count = 12
One of the many reasons I prefer the two-pipe methodology.
Sorry for the length of this post... I've encountered about a zillion problems in this. Up front I'll say I'm a student and my professor is a worthless resource. So, all I want to to do is have producer fork, then the parent producer will count some stuff in a file and send two ints to consumer, which was launched by the child process. I've tested everything, the fork and the file stuff works and I have printf statements all over the place so I know what is being done and where the code is at.
When I added the
if (pipe(pipefd) == -1) {
perror("pipe");
}
it caused my parent to just terminate. It reaches "parent pipe open" but then it dies. I checked with $ ps to see if it was just hung, but it's not there; it just dies. If I take that snippet out, it runs to the end but I presume if that code isn't there, then it's not actually aware that pipefd is a pipe... right?
I did search on this site and found another example of this and followed what he did as well as the answer and mine just refuses to work. I'm pretty sure it's a trivially easy thing to fix but I've run out of ideas of what to try :(
I don't really want to post all my code because it'll be a huge wall of text but I don't want to accidentally cut something out that turns out to be important either.
producer.c
#include <stdio.h> /* printf, stderr, fprintf */
#include <sys/types.h> /* pid_t */
#include <unistd.h> /* _exit, fork, execl */
#include <stdlib.h> /* exit */
#include <errno.h> /* errno */
#include <string.h> /* strlen */
#include <sys/wait.h> /* wait */
#define SLEEP_TIME 8
int main (int argc, char *argv[]){
//PID
pid_t local_pid;
local_pid = fork();
//Logic to determine if the process running is the parent or the child
if (local_pid == -1) {
/* Error:
* When fork() returns -1, an error happened
* (for example, number of processes reached the limit).
*/
fprintf(stderr, "can't fork, error %d\n", errno);
exit(EXIT_FAILURE);
} else if (local_pid == 0) {
//Child specific code
int child;
char *temp[] = {NULL};
printf("Child PID found\n");
child = execv("./consumer", temp);
_exit(0);
} else {
//Parent specific code
printf("Parent running\n");
//open file
FILE * randStrings;
randStrings = fopen("randStrings.txt", "r");
int file_length;
int num_of_e = 0;
int c; //using this as a char
//until eof
while (feof(randStrings) == 0) {
c = fgetc(randStrings);
//calculate length of file
file_length++;
//count e chars
if (c == 'e') {
num_of_e++;
}
}
//close file
fclose(randStrings);
//send bundle to child
int a[2];
a[0] = num_of_e;
a[1] = file_length;
printf("num of e = %i\n", a[0]);
printf("len = %i\n", a[1]);
//set up parent pipe
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
printf("x\n");
}
printf("parent pipe open\n");
close(pipefd[0]); //close the read end
write(pipefd[1], &a[0], sizeof(int));
write(pipefd[1], &a[1], sizeof(int));
close(pipefd[1]);
printf("parent pipe closed\n");
//wait for child to finish running
wait(NULL);
printf("parent out\n");
//terminate
}
}
and consumer.c
#include <stdio.h> /* printf, stderr, fprintf */
#include <sys/types.h> /* pid_t */
#include <unistd.h> /* _exit, fork, execl */
#include <stdlib.h> /* exit */
#include <errno.h> /* errno */
#define SLEEP_TIME 5
int main (int argc, char *argv[]){
sleep(SLEEP_TIME);
printf("Child program launched\n");
//receive bundle
int pipefd[2];
int buf[2];
if (pipe(pipefd) == -1) {
perror("pipe");
printf("child x\n");
}
close(pipefd[1]); //child closes write end
buf[0] = 0;
buf[1] = 0;
/*int i = 0; // i dont like this
while (read(pipefd[0], &buf[i], sizeof(int)) > 0) {
i++;
}*/
printf("child reading pipe\n");
read(pipefd[0], &buf[0], sizeof(int));
read(pipefd[0], &buf[1], sizeof(int));
close(pipefd[0]);
//buf should have the stuff in it
int num_of_e = buf[0];
int file_length = buf[1];
printf("child num of e = %i\n", num_of_e);
printf("child len = %i\n", file_length);
//open file
FILE * resultStrings;
resultStrings = fopen("resultStrings.txt", "w");
for (int i = 0; i < num_of_e; i++) {
//write num_of_e e chars
fputc('e', resultStrings);
}
//or if no e chars, write - chars
if (num_of_e == 0) {
for (int i = 0; i < file_length; i++) {
//write file_length '-' chars
fputc('-', resultStrings);
}
}
//close file
fclose(resultStrings);
printf("child out\n");
}
if you're still here after all that, you deserve a thank you just due to the length of this.
You're doing it wrong. The whole mechanism works because a child process inherits the parent's open file descriptors.
It should go like this:
Open the pipe with pipe(pipefd)
fork()
Parent (producer):
closes the read side (pipefd[0])
writes to the write side (pipefd[1])
Child (consumer):
closes the write side (pipefd[1])
reads from the read side (pipefd[0]) or calls exec
You are opening distinct pipes in both the parent and child process (after you've forked.) It needs to happen before you fork.
Now since you're execing, the new process needs to be aware of read-only pipe. There are a couple ways you could do this:
Pass it the file descriptor number (pipefd[0]) on the command line
dup2(1, fd) it to be the stdin of the newly exec'd process