Related
Ok so short story is:
I have a program that need to do X and it does 0.25X. I use 2 forks and 4 pipes, and I dont know how to debug this. (using eclipse c/c++ in linux env).
Long story:
I have a program that need to calculate gcd (Greatest common divisor) from a text file containing pairs of ints. This program has a father (main) and 2 childerns (forks) that need to talk to the father through pipes. (2 pipes for each childern.)
When I complie and run the program in ubuntu, I get no error but the proram doesnt complete its tasks. I have no idea where/why it breaks. How can I debug this? Im coding in eclipse c/c++ and debugger can't handle the forks.. When I debug it is reading all numbers from file (doesnt calculate gcd) but when I run in ubuntu terminal it only reads first line and breaks. here is the full code:
int main(int argc, char **argv) {
if (argc != 2 || strcmp(argv[1], "--help") == 0) {
fprintf(stderr, "usage: %s <FILE NAME>\n", argv[0]);
return EXIT_FAILURE;
}
int pfd_child1_r[2], pfd_child1_w[2], pfd_child2_r[2], pfd_child2_w[2];
if (pipe(pfd_child1_r) == -1 || pipe(pfd_child1_w) == -1
|| pipe(pfd_child2_r) == -1 || pipe(pfd_child2_w) == -1) {
perror("cannot pipe()");
return EXIT_FAILURE;
}
createChilds(pfd_child1_r, pfd_child1_w, pfd_child2_r, pfd_child2_w);
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
perror("fopen(): ");
return EXIT_FAILURE;
}
char line[100];
char *token;
int numbers[2], num, line_count = 1, counter = 0, result = 0;
while (fgets(line, sizeof(line), fp) != NULL) {
token = strtok(line, " ");
while (token != NULL) {
num = atoi(token);
if (num < 1 || counter == 2) {
fprintf(stderr, "illegal input at line %d\n",
line_count);
return EXIT_FAILURE;
}
numbers[counter] = num;
counter++;
token = strtok(NULL, " ");
}
counter = 0;
if (line_count % 2 == 0) { // use first child
write(pfd_child1_w[1], &numbers[0], sizeof(int));
write(pfd_child1_w[1], &numbers[1], sizeof(int));
} else { // use second child
write(pfd_child2_w[1], &numbers[0], sizeof(int));
write(pfd_child2_w[1], &numbers[1], sizeof(int));
}
if (line_count > 1) { // after first run alternate to get result
if (line_count % 2 == 0) { // read from second child
read(pfd_child2_r[0], &result, sizeof(int));
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
} else { // read from first child
read(pfd_child1_r[0], &result, sizeof(int));
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
}
}
line_count++;
}
fclose(fp);
return EXIT_SUCCESS;
}
void createChilds(int pfd_child1_r[2], int pfd_child1_w[2], int pfd_child2_r[2],
int pfd_child2_w[2]) {
switch (fork()) {
case -1:
perror("cannot fork()");
exit(EXIT_FAILURE);
case 0: /* First child: */
if (close(pfd_child1_r[0]) == -1) { /* Read end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}
if (close(pfd_child1_w[1]) == -1) { /* Write end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}
/* Duplicate stdout on write end of pipe; close duplicated descriptor */
if (pfd_child1_w[1] != STDOUT_FILENO) { /* Defensive check */
if (dup2(pfd_child1_r[1], STDOUT_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child1_r[1]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd_child1_w[1] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd_child1_w[0], STDIN_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child1_w[0]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
execlp("./v1_child", "./v1_child", NULL); /* Writes to pipe */
exit(EXIT_SUCCESS);
default: /* Parent go to next child */
break;
}
switch (fork()) {
case -1:
perror("cannot fork()");
exit(EXIT_FAILURE);
case 0: /* Second child: exec 'wc' to read from pipe */
if (close(pfd_child2_r[0]) == -1) { /* Read end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}
if (close(pfd_child2_w[1]) == -1) { /* Write end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}
/* Duplicate stdout on write end of pipe; close duplicated descriptor */
if (pfd_child2_w[1] != STDOUT_FILENO) { /* Defensive check */
if (dup2(pfd_child2_r[1], STDOUT_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child2_r[1]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd_child2_w[1] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd_child2_w[0], STDIN_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child2_w[0]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
execlp("./v1_child", "./v1_child", NULL); /* Writes to pipe */
exit(EXIT_SUCCESS);
default: /* Parent falls through */
break;
}
/* Parent closes unused file descriptors for pipe */
if (close(pfd_child1_r[1]) == -1 || close(pfd_child1_w[0]) == -1
|| close(pfd_child2_r[1]) == -1 || close(pfd_child2_w[0]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
the second file is the gcd file, i still have not finish it and the loop the should keep geting the numbers is not present. but i just want to get the first line working properly then i will fix the rest.
int gcd(int n1, int n2) {
if (n2 == 0)
return n1;
return gcd(n2, n1 % n2);
}
int main(int argc, char **argv) {
int n1, n2, result;
if (scanf("%d %d", &n1,&n2) != 2) {
fprintf(stderr, "error reading numbers in child\n");
return -1;
}
if (n1 > n2)
result = gcd(n1, n2);
else
result = gcd(n2,n1);
printf("%d", result);
}
How to Debug
An easy way to debug is always to add fprintf(stderr, "...") statements to the child program. Then you can run the program and also see what the child processes are doing.
Transfer Values
Since you redirect stdin and stdout and use sscanf/printf in the v1_child program which calculates gcd, I assume you want to transfer the values as strings.
An easy way could be to use fprintf to write ints as formatted strings. You could use fdopen to associate a stream to an existing pipe file descriptor.
Accordingly, you must convert numbers from and to strings.
Variable Length Data and Buffered I/O
If you use strings to transfer values, each pair of values has a variable length. Typically, a newline character is used in a C program to signal a complete input record.
Another reason for reading/writing a whole line is that read/write calls can also transfer only a partial number of bytes. You must therefore know when an input record is completed. An alternative would be to have a binary format, which would automatically represent a format with fixed lengths.
By using streams you work with buffered I/O, with fflush you can ensure that all buffered data is written through the underlying write function of the stream.
Functions
One could divide the features into several functions to make the flow a little easier to understand.
Possible Improvements
This should perhaps already be a start.
Another possible improvement could be the use of strtol instead of atoi, since atoi does not perform error checking. Similar sscanf does not report conversion errors (e.g. non-numeric characters at the end of the line), at least we look at the number of assigned input items.
There are presumably still possibilities to improve the readability of the code.
With waitpid the exit status code of the child could be checked in the parent.
Program
Your code, slightly modified in the points mentioned above, could then look like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void create_pipe(int *);
void close_fd(int);
int child(const int *, const int *);
int read_input_line(FILE *fp, char *line, int max, int *numbers, int line_count);
void write_to_child(FILE *fp, const int *numbers);
int read_from_child(FILE *fp);
int main(int argc, char *argv[]) {
if (argc != 2 || strcmp(argv[1], "--help") == 0) {
fprintf(stderr, "usage: %s <FILE NAME>\n", argv[0]);
return EXIT_FAILURE;
}
int pfd_child1_r[2];
int pfd_child1_w[2];
int pfd_child2_r[2];
int pfd_child2_w[2];
create_pipe(pfd_child1_r);
create_pipe(pfd_child1_w);
create_pipe(pfd_child2_r);
create_pipe(pfd_child2_w);
pid_t pid1 = fork();
if (pid1 == 0) { //child 1
close_fd(pfd_child2_r[0]);
close_fd(pfd_child2_r[1]);
close_fd(pfd_child2_w[0]);
close_fd(pfd_child2_w[1]);
return child(pfd_child1_r, pfd_child1_w);
} else if (pid1 > 0) {
close_fd(pfd_child1_r[1]);
close_fd(pfd_child1_w[0]);
pid_t pid2 = fork();
if (pid2 == 0) { //child 2
close_fd(pfd_child1_r[0]);
close_fd(pfd_child1_w[1]);
return child(pfd_child2_r, pfd_child2_w);
} else if (pid2 > 0) {
close_fd(pfd_child2_r[1]);
close_fd(pfd_child2_w[0]);
FILE *fp_child1_w = fdopen(pfd_child1_w[1], "w");
FILE *fp_child2_w = fdopen(pfd_child2_w[1], "w");
FILE *fp_child1_r = fdopen(pfd_child1_r[0], "r");
FILE *fp_child2_r = fdopen(pfd_child2_r[0], "r");
if (!fp_child1_w || !fp_child2_w || !fp_child1_r || !fp_child2_r) {
perror("fdopen() failed");
return EXIT_FAILURE;
}
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
perror("fopen(): ");
return EXIT_FAILURE;
}
char line[100];
int numbers[2], line_count = 0;
while (read_input_line(fp, line, sizeof(line), numbers, line_count) == 2) {
if (line_count % 2 == 0) {
write_to_child(fp_child1_w, numbers);
} else {
write_to_child(fp_child2_w, numbers);
}
if (line_count % 2 == 0) {
int result = read_from_child(fp_child1_r);
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
} else {
int result = read_from_child(fp_child2_r);
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
}
line_count++;
}
//fclose closes also associated file descriptor
fclose(fp_child1_w);
fclose(fp_child2_w);
fclose(fp_child1_r);
fclose(fp_child2_r);
fclose(fp);
return EXIT_SUCCESS;
} else {
perror("second fork failed");
return EXIT_FAILURE;
}
} else {
perror("first fork failed");
return EXIT_FAILURE;
}
}
int read_input_line(FILE *fp, char *line, int max, int *numbers, int line_count) {
char *token;
int num, counter = 0;
line[0] = '\0';
if (fgets(line, max, fp) != NULL) {
token = strtok(line, " ");
while (token != NULL) {
num = atoi(token);
if (num < 1 || counter == 2) {
fprintf(stderr, "illegal input at line %d\n", line_count + 1);
exit(EXIT_FAILURE);
}
numbers[counter] = num;
counter++;
token = strtok(NULL, " ");
}
}
return counter;
}
int read_from_child(FILE *fp) {
char buf[128];
int result = -1;
if (fgets(buf, sizeof(buf), fp)) {
if (sscanf(buf, "%d", &result) == 1)
return result;
}
return -1;
}
void write_to_child(FILE *fp, const int *numbers) {
fprintf(fp, "%d %d\n", numbers[0], numbers[1]);
fflush(fp);
}
int child(const int *pfd_child_r, const int *pfd_child_w) {
dup2(pfd_child_r[1], STDOUT_FILENO);
dup2(pfd_child_w[0], STDIN_FILENO);
close_fd(pfd_child_r[0]);
close_fd(pfd_child_r[1]);
close_fd(pfd_child_w[0]);
close_fd(pfd_child_w[1]);
execlp("./v1_child", "./v1_child", NULL);
fprintf(stderr, "execution of v1_child failed\n");
exit(EXIT_FAILURE);
}
void create_pipe(int *fd) {
if (pipe(fd) == -1) {
perror("cannot pipe()");
exit(EXIT_FAILURE);
}
}
void close_fd(int fd) {
if (close(fd) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
The corresponding v1_child.c could look like:
#include <stdio.h>
#include <stdlib.h>
int gcd(int n1, int n2) {
if (n2 == 0)
return n1;
return gcd(n2, n1 % n2);
}
int main(void) {
int n1, n2, result;
char buf[128];
while(fgets(buf, sizeof(buf), stdin)) {
if (sscanf(buf, "%d %d", &n1, &n2) != 2) {
fprintf(stderr, "error reading numbers in child\n");
return -1;
}
if (n1 > n2)
result = gcd(n1, n2);
else
result = gcd(n2, n1);
printf("%d\n", result);
fflush(stdout);
}
return EXIT_SUCCESS;
}
Test
With the input of
5 25
49 14
64 462
1155 84
the output would be
5 25 gcd: 5
49 14 gcd: 7
64 462 gcd: 2
1155 84 gcd: 21
You write binary values to the pipes, but try to read text representation. So either use a variation of printf (like dprintf) in the parent, or (better) use read (and write) in the child.
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...
I'm trying to create n = 10 child processes and make its execute a peace of code ..
However it creates 14 child processes indifferent of n.
Why is that?
This is the sample code :
#include <stdlib.h>
#include <stdio.h>
int main()
{
printf("It worked! ");
return 0;
}
And this is the main program:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
int main (int argc, char *argv[])
{
int n = 10;
pid_t pid;
int status = 0;
int fd2[2];
int i = 0;
while (i < n)
{
/*create the pipe */
if (pipe(fd2) == -1)
{
fprintf(stderr, "Problem at pipe: %s\n", strerror(errno));
exit(1);
}
/*create fork*/
pid = fork();
if (pid == -1)
{
fprintf(stderr, "Problem at fork: %s\n", strerror(errno));
exit(1);
}
else if (pid == 0) /*in child*/
{
close(fd2[0]);
close(1);
dup2(fd2[1], 1);
close(fd2[1]);
execl("sample.bin", "sample.bin", NULL);
fprintf(stderr, "Problem at exec: %s", strerror(errno));
exit(1);
}
/* in parent */
close(fd2[1]);
char line[255];
if (n = read(fd2[0], line, 254))
{
printf("%d The message is: %s\n", i, line);
}
close(fd2[0]);
wait(&status);
i++;
}
return 0;
}
I corrected the code, now the output is what I've expected. And of course another problem was that I used at read the same variable n.
I modified from this:
if (n = read(fd2[0], line, 254))
{
printf("%d The message is: %s\n", i, line);
}
To this:
int m;
while((m = read(fd2[0], line, 254) > 0)
{
printf("%d The message is: %s\n", i, line);
}
I'm writing a program that has an interface like:
myprog file1 file2 c
This program creates two children and P2 with an execlp opens file2, makes a grep -c on this file for founding c and gives the results to his brother P1 (I have to do it closing the STDOUT's FD and dupping the pipe p2p1 between them). P1 receives this from p2p1 and sends this value to P0. Moreover this also makes the same thing with file1 and gives the result to P0, which will print them.
The problem is: the father P0 reads something, but it is wrong.
What I'm supposed to do? Here's the code and thanks for your attention.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define MAX_STRING_LENGTH 128
/**************************/
/* DICHIARAZIONE FUNZIONI */
/**************************/
void wait_child();
void processo_p2(char *inputfile, char *c);
void processo_p1(char *inputfile, char *c);
/*********************/
/* VARIABILI GLOBALI */
/*********************/
int p1p0[2], p2p1[2];
int main(int argc, char* argv[])
{
int pid[2], i, value, count=0;
char *c, buf[10];
if (argc !=4)
{
fprintf(stderr, "Numero di argomenti errato\n");
fprintf(stderr, "Usage: %s file1 file2 C\n", argv[0]);
exit(EXIT_FAILURE);
}
c=argv[3];
/* Init */
pipe(p1p0);
pipe(p2p1);
for (i=0; i<2; i++)
{
pid[i] = fork();
if (pid[i] < 0)
{
fprintf(stderr, "P0: Errore nella fork");
exit(EXIT_FAILURE);
}
else if (pid[i] == 0)
{
if (i==0) /*P1*/
{
close(p1p0[0]);
close(p2p1[1]);
sleep(1);
processo_p1(argv[1], c);
close(p2p1[0]);
close(p1p0[1]);
exit(EXIT_SUCCESS);
}
else if (i==1)
{
close(p2p1[0]);
close(p1p0[0]);
close(p1p0[1]);
processo_p2(argv[2],c);
close(p2p1[1]);
exit(EXIT_SUCCESS);
}
}
else
{
printf("P0: created child P%d with PID %d\n", i+1, pid[i]);
close(p2p1[0]);
close(p2p1[1]);
close(p1p0[1]);
}
}
i=0;
int nread;
while ( (nread = read(p2p1[0], &buf[i], sizeof(char)) ) > 0 ) {
i++;
buf[i] = '\0';
printf("%s\n",buf);
for(i=0;i<2;i++)
{
wait_child();
}
return 0;
}
void processo_p2(char *inputfile, char *c)
{
int fd, nread, i=0, found=0;
char temp, row[100];
close(1);
dup(p2p1[1]);
execlp("grep", "grep", "-c", c, inputfile, (char *)0);
perror("P2: errorr in exec");
close(1);
}
void processo_p1(char *inputfile, char *c)
{
int fd, nrw, sk, nread, p2=0, i=0;
int value=1;
char temp, row[100], buf[10];
//RECEIVING DATA FROM P2 AND SENDING TO P0
while ( (nread = read(p2p1[0], &buf[i], sizeof(char)) ) > 0 ) {
i++;
}
buf[i] = '\0';
printf("from p2: %s\n",buf); //NOTHING STAMPED
write(p1p0[1],&buf,strlen(buf)+1);
close(1);
dup(p1p0[1]);
execlp("grep", "grep", "-c", c, inputfile, (char *)0);
perror("P1: errore in exec");
close(p1p0[1]);
}
void wait_child() {
int pid_terminated,status;
pid_terminated=wait(&status);
if (pid_terminated < 0)
{
fprintf(stderr, "%d\n", getpid());
perror("P0: errore in wait");
exit(EXIT_FAILURE);
}
if(WIFEXITED(status))
{
printf("P0: terminazione volontaria del figlio %d con stato %d\n",
pid_terminated, WEXITSTATUS(status));
if (WEXITSTATUS(status) == EXIT_FAILURE)
{
fprintf(stderr, "P0: errore nella terminazione del figlio pid_terminated\n");
exit(EXIT_FAILURE);
}
}
else if(WIFSIGNALED(status))
{
fprintf(stderr, "P0: terminazione involontaria del figlio %d a causa del segnale %d\n",
pid_terminated,WTERMSIG(status));
exit(EXIT_FAILURE);
}
}
Trivia (but it stops the code compiling): The code shown originally has a close brace in the wrong place — the functions are all apparently embedded inside main(). The last brace should be moved up the file considerably.
In the loop that launches the child processs, you have the parent process execute:
printf("P0: created child P%d with PID %d\n", i+1, pid[i]);
close(p2p1[0]);
close(p2p1[1]);
close(p1p0[1]);
In the parent process, immediately after the loop, you have:
int nread;
while ( (nread = read(p2p1[0], &buf[i], sizeof(char)) ) > 0 ) {
i++;
buf[i] = '\0';
printf("%s\n",buf);
for(i=0;i<2;i++)
{
wait_child();
}
return 0;
}
'Tis a pity that you closed the file descriptor you're trying to read from. The parent should be reading from p1p0[0], which you did leave open.
Fixes:
Move the set of three closes out of the loop.
Read from the correct file descriptor.
Insert a missing close brace for the while loop shown, or remove its open brace.
The question in the comments has, as usual for comments, inscrutable code. The problem though is that you have too big a loop.
This is what the tail of my fixed version of your main() function looks like:
…rest of loop to launch children…
else
{
printf("P0: created child P%d with PID %d\n", i + 1, pid[i]);
}
}
close(p2p1[0]);
close(p2p1[1]);
close(p1p0[1]);
i = 0;
int nread;
while ((nread = read(p1p0[0], &buf[i], sizeof(char)) ) > 0)
i++;
buf[i] = '\0';
printf("%s\n", buf);
for (i = 0; i < 2; i++)
{
wait_child();
}
return 0;
}
Given that I saved the source code in a file pip.c and created the program pip from it, a sample run produced:
$ ./pip pip.c pip.c c
P0: created child P1 with PID 75458
P0: created child P2 with PID 75459
from p2: 49
49
P0: terminazione volontaria del figlio 75459 con stato 0
P0: terminazione volontaria del figlio 75458 con stato 0
$
I'm not entirely happy with all the newlines, but at least the same answer is given twice, as it obviously should be.
To solve my problem, I set
prctl(PR_SET_PDEATHSIG, SIGHUP); as in stackoverflow answer before i called exec*, and took out the part where we pipe the PID. It works!!!!! Wow....
HOWEVER, stackoverflow won't let me say I've answered my own question yet...
So I tried to write a program, which I want to run a program, and kill that program after a cpl seconds if it doesn't finish. DADDY forks off a CHILD, which forks off another BABY, CHILD pipes the PID of the BABY to DADDY, which then waits a second and kills them both if they haven't wrapped up their business (it's a macabre scene). But it doesn't work, DADDY stays in S+ State, and the infinite loop that is Baby goes on forever until I ctr+c. On the bright side, this code is an amalgamation of everything I've learnt on stack-overflow. Here we go.
#include <math.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static int read_from_pipe(int file)
{
int c;
FILE *stream = fdopen(file, "r");
if (fscanf(stream, "%d", &c) != 1)
{
fprintf(stderr, "Failed to read integer from pipe\n");
exit(1);
}
fclose(stream);
return c;
}
static void write_to_pipe(int file, int pidRacket)
{
FILE *stream = fdopen(file, "w");
fprintf(stream, "%d", pidRacket);
fclose(stream);
}
static int spawnpipe(char *fileName, int *fd)
{
int pid;
int pipe_fds[2];
char *command[] = {"racket", fileName, NULL};
if (pipe(pipe_fds) < 0)
{
fprintf(stderr, "FE: pipe\n");
exit(1);
}
switch ((pid = fork()))
{
case -1:
printf("syserr");
exit(1);
case 0:
close(1);
close(2);
dup(pipe_fds[1]);
close(pipe_fds[0]);
close(pipe_fds[1]);
execvp(*command, command);
perror("execv");
exit(EXIT_FAILURE);
default:
*fd = pipe_fds[0];
close(pipe_fds[1]);
return pid;
}
}
static int spawnfp(char *fileName, FILE **fpp)
{
int fd, pid;
pid = spawnpipe(fileName, &fd);
*fpp = fdopen(fd, "r");
return pid;
}
int main(int argc, char *argv[])
{
pid_t pid;
int mypipe[2];
if (pipe(mypipe))
{
fprintf(stderr, "Pipe failed.\n");
return EXIT_FAILURE;
}
pid = fork();
if (pid < (pid_t) 0)
{
fprintf(stderr, "Fork failed.\n");
return EXIT_FAILURE;
}
else if (pid != (pid_t) 0)
{
double diff = 0;
clock_t launch = clock();
close(mypipe[1]);
int pidRacket = read_from_pipe(mypipe[0]);
while (diff < 1.3)
{
clock_t done = clock();
diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
}
kill(pidRacket, SIGKILL);
kill(pid, SIGKILL);
return EXIT_SUCCESS;
}
else if (pid == (pid_t) 0)
{
close(mypipe[0]);
char buf[100];
FILE *fp;
char *fileName = argv[1];
int pidRacket = spawnfp(fileName, &fp);
write_to_pipe(mypipe[1], pidRacket);
if (argc == 1)
{
printf("Not enough arguments!");
_exit(EXIT_FAILURE);
}
else if (argc == 2)
{
}
sleep(1);
while (fgets(buf, sizeof buf, fp))
{
printf("%s\n", buf);
}
fclose(fp);
kill(pid, SIGKILL);
return 0;
}
}
Credit to quinsley and vijay!
Various comments as I look at the code:
End messages with newlines; you're on Linux now, not Windows. Windows systems seem to encourage people to leave messages without newlines, but it won't work well on Unix in general and Linux in particular.
Don't use _exit() if you want your error messages to appear, especially ones that don't end in a newline.
Don't report error messages on standard output; report them on standard error (that's what it is for!).
Writing else if (argc == 2) { } (with nothing in the braces) is a little odd if there is an else clause after it, but it is pointless when there is no else clause. You should arguably test for argc != 2 since that is the correct number of arguments (or, perhaps more accurately, any arguments beyond argc == 2 are ignored).
If you want to sleep for a time involving sub-second timing (e.g. 1.3 seconds), use one of the appropriate sub-second sleep commands. In this case, nanosleep() is probably the function to use.
Don't use SIGKILL except in dire emergency. The process signalled with SIGKILL has no chance to clean up or anything; it is killed immediately (assuming your process is allowed to send a signal to the other at all, of course).
case -1: printf("syserr"); with no break; after it means that on error, the flow of control goes into the following case 0: code, which is not what's required. Either break; or exit(1); is probably appropriate. (Bullet 3 applies too.)
Don't close standard error. The code:
close(1);
close(2);
dup(pipe_fds[1]);
close(pipe_fds[0]);
close(pipe_fds[1]);
execvp(*command, command);
perror("execv");
_exit(EXIT_FAILURE);
is never going to report an error; you closed standard error. Remember that programs are entitled to have a standard error channel. The C standard guarantees it, but you have to cooperate and make sure you've not closed standard error.
Some of the casts in:
diff = ((double)((uintmax_t)(clock_t)done) - (double)((uintmax_t)(clock_t)launch)) / (double)CLOCKS_PER_SEC;
are unnecessary. Since both done and launch are of the type clock_t, the casts to clock_t are unnecessary. The intermediate cast to uintmax_t also isn't really necessary. You could simply write:
diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
and even then, two of the three casts are theoretically redundant (any two of the three could be removed).
The code in read_from_pipe() is curious and error prone. Since you've got a file stream, simply read an integer from it using fscanf(), rather than the curious construct using double arithmetic and fractional values that are then multiplied at the end. This is especially appropriate since the write_to_pipe() code uses printf("%d", ...); to write the data. Since c is already an int, the cast in return (int)c; is superfluous.
Theoretically, it would be a good idea to check the streams returned by fdopen() to ensure that the operation did not fail.
If the pipe() function fails, you report the error on standard output and then continue as nothing had gone wrong.
It is not clear what the racket command actually does. It doesn't exist on my machine.
argv in spawnfp() is unused.
pid = fork(); if (pidDos < (pid_t) 0) generates a warning (accurately) that pidDos might be used uninitialized. The condition should presumably be using pid, not pidDos. You then send a SIGKILL signal to the PID identified at random by pidDos, which is unlikely to lead to happiness.
When I copy cat to racket and invoke the following code (as a program mk built from mk.c) as mk /etc/passwd, I get to see the password file double-spaced (and the message from the shell about Killed: 9.
#include <math.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static int read_from_pipe(int file)
{
int c;
FILE *stream = fdopen(file, "r");
if (fscanf(stream, "%d", &c) != 1)
{
fprintf(stderr, "Failed to read integer from pipe\n");
exit(1);
}
fclose(stream);
return c;
}
static void write_to_pipe(int file, int pidRacket)
{
FILE *stream = fdopen(file, "w");
fprintf(stream, "%d", pidRacket);
fclose(stream);
}
static int spawnpipe(char *fileName, int *fd)
{
int pid;
int pipe_fds[2];
char *command[] = {"racket", fileName, NULL};
if (pipe(pipe_fds) < 0)
{
fprintf(stderr, "FE: pipe\n");
exit(1);
}
switch ((pid = fork()))
{
case -1:
printf("syserr");
exit(1);
case 0:
close(1);
close(2);
dup(pipe_fds[1]);
close(pipe_fds[0]);
close(pipe_fds[1]);
execvp(*command, command);
perror("execv");
exit(EXIT_FAILURE);
default:
*fd = pipe_fds[0];
close(pipe_fds[1]);
return pid;
}
}
static int spawnfp(char *fileName, FILE **fpp)
{
int fd, pid;
pid = spawnpipe(fileName, &fd);
*fpp = fdopen(fd, "r");
return pid;
}
int main(int argc, char *argv[])
{
pid_t pid;
int mypipe[2];
if (pipe(mypipe))
{
fprintf(stderr, "Pipe failed.\n");
return EXIT_FAILURE;
}
pid = fork();
if (pid < (pid_t) 0)
{
fprintf(stderr, "Fork failed.\n");
return EXIT_FAILURE;
}
else if (pid != (pid_t) 0)
{
double diff = 0;
clock_t launch = clock();
close(mypipe[1]);
int pidRacket = read_from_pipe(mypipe[0]);
while (diff < 1.3)
{
clock_t done = clock();
diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
}
kill(pidRacket, SIGKILL);
kill(pid, SIGKILL);
return EXIT_SUCCESS;
}
else if (pid == (pid_t) 0)
{
close(mypipe[0]);
char buf[100];
FILE *fp;
char *fileName = argv[1];
int pidRacket = spawnfp(fileName, &fp);
write_to_pipe(mypipe[1], pidRacket);
if (argc == 1)
{
printf("Not enough arguments!");
_exit(EXIT_FAILURE);
}
else if (argc == 2)
{
}
sleep(1);
while (fgets(buf, sizeof buf, fp))
{
printf("%s\n", buf);
}
fclose(fp);
kill(pid, SIGKILL);
return 0;
}
}
I fixed some, but by no means all, of the issues identified in this revision of the code.
Oh, and item 16: the read end of the pipe isn't closed until the third process terminates. You need to pass mypipe[1] to spawnfp(), which needs to relay it to spawnpipe(), and the child created there needs to close the pipe descriptor before executing 'racket'. This is compounded by fscanf() looking for either EOF or a non-digit at the end of the PID it reads from the pipe. You could provide a newline or something at the end and that would also free up the parent process to spin in its timing loop. Since you say racket doesn't terminate, that's why you don't see anything much.
It's easier to paste the whole program again than present the diffs:
#include <assert.h>
#include <math.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static int read_from_pipe(int file)
{
int c;
FILE *stream = fdopen(file, "r");
assert(stream != 0);
if (fscanf(stream, "%d", &c) != 1)
{
fprintf(stderr, "Failed to read integer from pipe\n");
exit(1);
}
fclose(stream);
return c;
}
static void write_to_pipe(int file, int pidRacket)
{
FILE *stream = fdopen(file, "w");
assert(stream != 0);
fprintf(stderr, "%d: pidRacket = %d\n", (int)getpid(), pidRacket);
fprintf(stream, "%d", pidRacket);
fclose(stream);
}
static int spawnpipe(char *fileName, int *fd, int pfd)
{
int pid;
int pipe_fds[2];
char *command[] = {"racket", fileName, NULL};
if (pipe(pipe_fds) < 0)
{
fprintf(stderr, "FE: pipe\n");
exit(1);
}
switch ((pid = fork()))
{
case -1:
printf("syserr");
exit(1);
case 0:
close(pfd);
close(1);
//close(2);
dup(pipe_fds[1]);
close(pipe_fds[0]);
close(pipe_fds[1]);
execvp(*command, command);
perror("execv");
exit(EXIT_FAILURE);
default:
fprintf(stderr, "%d: pid = %d\n", (int)getpid(), pid);
*fd = pipe_fds[0];
close(pipe_fds[1]);
return pid;
}
}
static int spawnfp(char *fileName, FILE **fpp, int pfd)
{
int fd, pid;
pid = spawnpipe(fileName, &fd, pfd);
*fpp = fdopen(fd, "r");
assert(*fpp != 0);
return pid;
}
int main(int argc, char *argv[])
{
pid_t pid;
int mypipe[2];
if (pipe(mypipe))
{
fprintf(stderr, "Pipe failed.\n");
return EXIT_FAILURE;
}
pid = fork();
if (pid < (pid_t) 0)
{
fprintf(stderr, "Fork failed.\n");
return EXIT_FAILURE;
}
else if (pid != (pid_t) 0)
{
double diff = 0.0;
clock_t launch = clock();
close(mypipe[1]);
fprintf(stderr, "%d: Reading from pipe:\n", (int)getpid());
int pidRacket = read_from_pipe(mypipe[0]);
fprintf(stderr, "%d: Read PID %d from pipe\n", (int)getpid(), pidRacket);
while (diff < 1.3)
{
clock_t done = clock();
diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
printf("%f\n", diff);
}
kill(pidRacket, SIGKILL);
kill(pid, SIGKILL);
return EXIT_SUCCESS;
}
else if (pid == (pid_t) 0)
{
close(mypipe[0]);
char buf[100];
FILE *fp;
char *fileName = argv[1];
int pidRacket = spawnfp(fileName, &fp, mypipe[1]);
fprintf(stderr, "%d: Writing PID %d to pipe\n", (int)getpid(), pidRacket);
write_to_pipe(mypipe[1], pidRacket);
fprintf(stderr, "%d: Written PID to pipe\n", (int)getpid());
if (argc == 1)
{
printf("Not enough arguments!");
_exit(EXIT_FAILURE);
}
else if (argc == 2)
{
}
sleep(1);
while (fgets(buf, sizeof buf, fp))
{
printf("%s\n", buf);
}
fclose(fp);
fprintf(stderr, "%d: Finished reading from pipe\n", (int)getpid());
kill(pid, SIGKILL);
return 0;
}
}
I made this a while back for stupid fun, it uses up a big chunk of your cpu to run but I'm sure you can modify it to break at a certain point or to fit your needs maybe.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main(int argc, char*argv[])
{
int childpid;
int pids[100];
int count1 = 0, count2 = 0;
int count3 = 0;
L1:
childpid = fork();
if(childpid == 0)
{
}
else
{
if(childpid != 0 && childpid != -1)
{
if(count3 < 100)
{
pids[count3] = childpid;
printf("Pid:%d\n",pids[count3]);
count3++;
goto L1;
}
else
{
count3--;
goto L2;
}
}
L2:
while(count3 > 0)
{
if(pids[count3] != -1 || pids[count3] != 1)
{
printf("Killing pid:%d\n",pids[count3]);
kill(pids[count3],SIGKILL);
}
count3--;
}
if(count3 == 0)
{
goto L1;
}
}
return 0;
}