Questions about Systm calls like fork() and pipe() in C - c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<errno.h>
int main(int argc, char **argv){
int n = atoi(argv[1]);
int superdaddy = getpid();
int p[n+1][2];
int i=0;
int cpid,output;
int result = 0;
if(pipe(p[0])<0){
perror("1");
return 1;
}
if(pipe(p[n])<0){
perror("2");
return 1;
}
output = p[0][1];
if(getpid()==superdaddy){
if(write(p[0][1],&result,sizeof(result))<0){
perror("3");
return 1;
}
if(close(p[0][1])<0){
perror("4");
return 1;
}
}
while(1){
if(i==n){
if(read(p[n-1][0],&result,sizeof(result)<0)){
perror("5");
return 1;
}
result++;
output = p[n][1];
if(write(output,&result,sizeof(result))<0){
perror("6");
return 1;
}
if(close(p[n-1][0])<0){
perror("7");
return 1;
}
if(close(p[n][1])<0){
perror("8");
return 1;
}
break;
}
i++;
cpid = fork();
if(cpid==0){
if(i==n)
continue;
if(pipe(p[i])<0){
perror("9");
return 1;
}
if(read(p[i-1][0],&result,sizeof(result))<0){
perror("10");
return 1;
}
result++;
output = p[i][1];
if(write(output,&result,sizeof(result))<0){
perror("11");
return 1;
}
if(close(p[i-1][0])<0){
perror("12");
return 1;
}
if(close(p[i][1]<0)){
perror("13");
return 1;
}
continue;
}
else if(cpid<0){
perror("14");
return 1;
}
break;
}
if(getpid()==superdaddy){
wait(NULL);
if(read(p[n][0],&result,sizeof(result))<0){
perror("15");
return 1;
}
printf("Result: %d\n",result);
if(close(p[n][0])<0){
perror("16");
return 1;
}
}
return 0;
}
The Program aims to read a number n from command line and then forks n child process and create n pipes. process p0 will be parent of process p1, p1 will be parent of p2, so and so on. One variable (named result here) will be passed through pipes, every time it is passed it will be added by 1. So the output should be n as well. Pipe Fi connects Pi and P(i+1). Attached is my code.
When n=1 or n=2, the program can output correctly, which is 1 and 2 correspondingly. However, when n=3, it gives me a bad file error at error 5. I have hand-tracked the code for the whole afternoon but got no idea what is wrong with it. Anyone could help? Appreciate it first!

when n=3, it gives me a bad file error at error 5.
This could be fixed by removing that if(close(p[i][1]<0)){ in your code, because you need to read from p[i][0] in your last iteration, i.e.
if (i == n) {
if(read(p[n-1][0],&result,sizeof(result)<0)){
...
}
}
This is an implementation of your idea, I hope it may be helpful:
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s N\n", argv[0]);
exit(EXIT_FAILURE);
}
int n = atoi(argv[1]);
int pipes[n][2];
int i, val;
pid_t pid;
val = 0;
for (i = 0; i < n; i++) {
if (pipe(pipes[i]) < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}
if ((pid = fork()) < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
else if (pid == 0) {
close(pipes[i][1]);
if (read(pipes[i][0], &val, sizeof(val)) != sizeof(val)) {
perror("read");
exit(EXIT_FAILURE);
}
printf("C %d read %d\n", getpid(), val);
val++;
}
else {
close(pipes[i][0]);
printf("P %d writes %d\n", getpid(), val);
if (write(pipes[i][1], &val, sizeof(val)) != sizeof(val)) {
perror("write");
exit(EXIT_FAILURE);
}
if (waitpid(pid, NULL, 0) != pid) {
perror("waitpid");
exit(EXIT_FAILURE);
}
printf("%d is going to leave.\n", getpid());
exit(EXIT_SUCCESS);
}
}
printf("%d is going to leave.\n", getpid());
exit(EXIT_SUCCESS);
}
Testing run:
$ ./a.out 3
P 2005 writes 0
C 2006 read 0
P 2006 writes 1
C 2007 read 1
P 2007 writes 2
C 2008 read 2
2008 is going to leave.
2007 is going to leave.
2006 is going to leave.
2005 is going to leave.
Explanation:
The frame of that code is for (i = 0; i < n; i++) { pipe(); fork(); }, which means it will create n pipes, and n new processes. In each iteration, the parent will write to pipes[i][1] and child will read from pipes[i][0]. Eventually, it will create a process chain connected by a series of pipes, and a value is passed down from the first process to the last through that series of pipes.

Related

How the parent creates a pipe between each child and itself, and each child sends number of the words to the parent via the pipe

The code I've written finds out the number of words in multiple text files by creating multiple processes with each process being responsible for one file to count its words.
What I want to do is using pipes to find out total number of words in all files.
So the parent should:
creates a pipe between the each child and itself so it can get the number of words from each child
reports the total number of words in all the files by adding the numbers received through pipes
checks the exit status of each child and prints out how that child
exited
also let each child:
sends the number of the words to the parent via the pipe
send 0 as word count through the pipe to the parent if the file does
not exist or any other error happens
returns/exits with 0 if it is successfull in opening the file and
counting the words in that file, returns/exits with 1 if there is an
error (e.g., file does not exist etc.)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#define MAX_CHAR 100
pid_t getpid(void);
pid_t getppid(void);
char* itoa(int i, char b[]){
char const digit[] = "0123456789";
char* p = b;
if(i<0){
*p++ = '-';
i *= -1;
}
int shifter = i;
do{ //Move to where representation ends
++p;
shifter = shifter/10;
}while(shifter);
*p = '\0';
do{ //Move back, inserting digits as u go
*--p = digit[i%10];
i = i/10;
}while(i);
return b;
}
int countWords(char * fp, int pid) {
FILE * file;
int words = 0;
char word[MAX_CHAR];
//execute this function only if child process of parent, no gradchild is allowed to execute this function!
if (pid == getppid()) {
file = fopen(fp, "r");
if (file == NULL) {
return -1;
}
//find string in the file and count the words.
while (fscanf(file, "%s", word) != EOF) {
words++;
}
return words;
} else {
return -1;
}
return 0;
}
int main(int argc, char * arvg[]) {
//if invalid arguments
if (argc < 2) {
fprintf(stderr, "ERROR: INVALID ARGUMENTS");
exit(-1);
}
int count = 0, pid, ppid, status, totalwords;
int result = -1;
int fd[2];
char string[100];
char readbuffer[80];
int *write_fd = &fd[1];
int *read_fd = &fd[0];
result = pipe(fd);
if(-1 == result){
perror("pipe");
return -1;
}
//creates (argc - 1) child processes using fork()
pid = (int) malloc((argc - 1) * sizeof(int));
//parent pid
ppid = getpid();
//each child process to count the number of words in each file
for (int i = 1; i < argc; i++) {
//child process
pid = fork();
if( pid == -1){
perror("failed to fork");
return -1;
}else if (pid == 0) {
// call a function to count the number of words in file arvg[i]
int words = countWords(arvg[i], ppid);
close(*read_fd);
if (words >= 0) {
printf("Child process pid_%d for %s :number of words is %d\n", i, arvg[i], words);
//I don't know how to write int into the pipe,so below might be wrong
write(*write_fd, words, 1);
return 0;
} else if (words == -1) {
printf("Child process pid_%d for %s :does not exists\n", i, arvg[I]);
//I don't know how to write int into the pipe,so below might be wrong
write(STDOUT_FILENO, words, 1);
exit(1);
}
} else {
close(*write_fd);
//and I have no idea how to read int from pipes
read(*read_fd, &readbuffer, 1);
totalwords += ???
close(*read_fd);
//Wait until all child processes exit/return
if (ppid == getpid()) {
wait( & status);
}
//inspect their exit codes, WEXITSTATUS = return code when child exits
if (WEXITSTATUS(status) == 1) {
count++;
}
}
}
printf("Main process created %d child processes to count words in %d files\n", argc - 1, argc - 1);
printf("Total words is %d", totalwords);
printf("%d files have been counted sucessfully!\n", argc - 1 - count);
printf("%d files did not exist.\n", count);
return 0;
}```
Can someone help me to figure out this? I don't really know how to achieve my goal with pipe.
found some issues with the code. I fixed them for you (however, I would have done the same thing slight differently)
reading and writing int from a pipe is pretty straight forward, just typecast correctly while reading or writing to an int.
malloc to a pid was not necessary. Also malloc returns a pointer and should have been typecasted with (int*)
always add the right includes while using calls. Manual page or reading about the calls while trying to understand the parameters passed and return values is extremely useful.
Enough said, here is your working code
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_CHAR 100
pid_t getpid(void);
pid_t getppid(void);
char* itoa(int i, char b[]){
char const digit[] = "0123456789";
char* p = b;
if(i<0){
*p++ = '-';
i *= -1;
}
int shifter = i;
do{ //Move to where representation ends
++p;
shifter = shifter/10;
}while(shifter);
*p = '\0';
do{ //Move back, inserting digits as u go
*--p = digit[i%10];
i = i/10;
}while(i);
return b;
}
int countWords(char * fp, int pid) {
FILE * file;
int words = 0;
char word[MAX_CHAR];
//execute this function only if child process of parent, no gradchild is allowed to execute this function!
if (pid == getppid()) {
file = fopen(fp, "r");
if (file == NULL) {
return -1;
}
//find string in the file and count the words.
while (fscanf(file, "%s", word) != EOF) {
words++;
}
return words;
} else {
return -1;
}
return 0;
}
int main(int argc, char * arvg[]) {
//if invalid arguments
if (argc < 2) {
fprintf(stderr, "ERROR: INVALID ARGUMENTS");
exit(-1);
}
int count = 0, pid, ppid, status, totalwords = 0;
int result = -1;
int fd[2];
char string[100];
char readbuffer[80];
int *write_fd = &fd[1];
int *read_fd = &fd[0];
int recvd = 0;
result = pipe(fd);
if(-1 == result){
perror("pipe");
return -1;
}
//creates (argc - 1) child processes using fork()
//pid = (int) malloc((argc - 1) * sizeof(int));
//parent pid
ppid = getpid();
//each child process to count the number of words in each file
for (int i = 1; i < argc; i++) {
//child process
pid = fork();
if( pid == -1){
perror("failed to fork");
return -1;
}else if (pid == 0) {
printf ("%d child running \n", i);
// call a function to count the number of words in file arvg[i]
int words = countWords(arvg[i], ppid);
close(*read_fd);
if (words >= 0) {
printf("Child process pid_%d for %s :number of words is %d\n", i, arvg[i], words);
//I don't know how to write int into the pipe,so below might be wrong
write(*write_fd, (void *)&words, 1);
return 0;
} else if (words == -1) {
printf("Child process pid_%d for %s :does not exists\n", i, arvg[i]);
//I don't know how to write int into the pipe,so below might be wrong
write(STDOUT_FILENO, (void *)&words, 1);
exit(1);
}
} else {
close(*write_fd);
//and I have no idea how to read int from pipes
read(*read_fd, (void*)&recvd, 1);
totalwords += recvd;
printf("recvd %d \n", totalwords);
close(*read_fd);
//Wait until all child processes exit/return
if (ppid == getpid()) {
wait( & status);
}
//inspect their exit codes, WEXITSTATUS = return code when child exits
if (WEXITSTATUS(status) == 1) {
count++;
}
}
}
printf("Main process created %d child processes to count words in %d files\n", argc - 1, argc - 1);
printf("Total words is %d\n", totalwords);
printf("%d files have been counted sucessfully!\n", argc - 1 - count);
printf("%d files did not exist.\n", count);
return 0;
}
Well, the first time around, I did not focus on the algo. I fixed all of it. The problem is forking in a loop and reading and writing it would lead to wrong results. Moreover, Parent needs to look for EOF to ensure all read has happened. Anyways, Here is the code that should work
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_CHAR 100
pid_t getpid(void);
pid_t getppid(void);
char* itoa(int i, char b[]){
char const digit[] = "0123456789";
char* p = b;
if(i<0){
*p++ = '-';
i *= -1;
}
int shifter = i;
do{ //Move to where representation ends
++p;
shifter = shifter/10;
}while(shifter);
*p = '\0';
do{ //Move back, inserting digits as u go
*--p = digit[i%10];
i = i/10;
}while(i);
return b;
}
// count word from file provided
int countWords(char * fp, int pid) {
FILE * file;
int words = 0;
char word[MAX_CHAR];
//execute this function only if child process of parent, no gradchild is allowed to execute this function!
if (pid == getppid()) {
file = fopen(fp, "r");
if (file == NULL) {
return -1;
}
//find string in the file and count the words.
while (fscanf(file, "%s", word) != EOF) {
words++;
}
return words;
} else {
return -1;
}
return 0;
}
//do everything related to child here in this function
void child_process(int write_fd, char *filename, int ppid)
{
// call a function to count the number of words in file argv[i]
printf("counting words of %s\n", filename);
int words = countWords(filename, ppid);
if (words >= 0) {
printf("Child process pid for %s :number of words is %d\n", filename, words);
write(write_fd, (void *)&words, 1);
close(write_fd);
exit(0);
} else if (words == -1) {
printf("Child process pid for %s :does not exist\n", filename);
write(STDOUT_FILENO, (void *)&words, 1);
close(write_fd);
exit(1);
}
return;
}
int main(int argc, char * argv[]) {
//if invalid arguments
if (argc < 2) {
fprintf(stderr, "ERROR: INVALID ARGUMENTS");
exit(-1);
}
int pid = 0;
int ppid = 0;
int totalwords = 0;
int fd[2] = {0};
int write_fd = 0;
int read_fd = 0;
int recvd = 0;
// open a pipe
if(-1 == pipe(fd)){
perror("pipe");
return -1;
}
// assign write_fd and read_fd
write_fd = fd[1];
read_fd = fd[0];
//parent pid
ppid = getpid();
//each child process to count the number of words in each file
pid = fork();
for (int i = 0; i < argc-1; i++)
{
//child process
if (pid == 0) {
close(read_fd);
child_process(write_fd, argv[i+1], ppid);
break;
} else {
pid = fork();
}
}
// don't let child run beyond this point
if (pid == 0) {
exit(0);
}
// parent only code
if (pid > 0)
{
close(write_fd);
while (read(read_fd, (void*)&recvd, 1) > 0)
{
wait(NULL);
totalwords += recvd;
}
close(read_fd);
}
printf("Main process created %d child processes to count words in %d files\n", argc - 1, argc - 1);
printf("Total words is %d\n", totalwords);
printf("%d files have been counted sucessfully!\n", argc - 1);
}

bad file descriptor in c program with forks

this program is supposed to simulate a posix shell in regards to commands with pipes. The example I've tried to simulate and wanna make work is "ls | nl", but it doesn't and I can't figure out why. I've debugged this code for many hours with no success.
I get the error: "nl: input error: Bad file descriptor", and when I've tried not closing any of the file descriptors or closing only some (or in only one of the forks, or only the parent, etc...), and the errors change, or it works but then nl keeps waiting for input. Anyways, I'm pretty sure the errors are in fork_cmd or fork_cmds and has to do with close.
I've included all the code. I know there's nothing wrong with parser.h. I know this is pretty shitty code but it should still work I think.
I'm probably blind, but I would really appreciate it if someone could help me figure it out. Hopefully it's something that I and maybe others can learn something from.
#include "parser.h"
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#define READ 0
#define WRITE 1
void fork_error() {
perror("fork() failed)");
exit(EXIT_FAILURE);
}
void close_error() {
perror("Couldn't close file descriptor");
exit(EXIT_FAILURE);
}
void fork_cmd(char* argv[], int n, int read_pipe[2], int write_pipe[2], int (*all_fds)[2]) {
pid_t pid;
switch (pid = fork()) {
case -1:
fork_error();
case 0:
if (read_pipe != NULL) {
if (dup2(read_pipe[READ], STDIN_FILENO) < 0) {
perror("Failed to redirect STDIN to pipe");
exit(EXIT_FAILURE);
}
}
if (write_pipe != NULL) {
if (dup2(write_pipe[WRITE], STDOUT_FILENO) < 0) {
perror("Failed to redirect STDOUT to pipe");
exit(EXIT_FAILURE);
}
}
for (int i = 0; i < n - 1; i++) {
if (close(all_fds[i][READ]) == -1 || close(all_fds[i][WRITE] == -1)) {
close_error();
}
}
execvp(argv[0], argv);
perror("execvp");
exit(EXIT_FAILURE);
default:
printf("Pid of %s: %d\n", argv[0], pid);
break;
}
}
void fork_cmds(char* argvs[MAX_COMMANDS][MAX_ARGV], int n, int (*fds)[2]) {
for (int i = 0; i < n; i++) {
if (n == 1) {
fork_cmd(argvs[i], n, NULL, NULL, fds);
}
// n > 1
else if (i == 0) {
fork_cmd(argvs[i], n, NULL, fds[i], fds);
}
else if (i == n - 1) {
fork_cmd(argvs[i], n, fds[i - 1], NULL, fds);
}
else {
fork_cmd(argvs[i], n, fds[i - 1], fds[i], fds);
}
}
for (int i = 0; i < n - 1; i++) {
if (close(fds[i][READ]) == -1 || close(fds[i][WRITE] == -1)) {
close_error();
}
}
}
void get_line(char* buffer, size_t size) {
getline(&buffer, &size, stdin);
buffer[strlen(buffer)-1] = '\0';
}
void wait_for_all_cmds(int n) {
// Not implemented yet!
for (int i = 0; i < n; i++) {
int status;
int pid;
if ((pid = wait(&status)) == -1) {
printf("Wait error");
} else {
printf("PARENT <%ld>: Child with PID = %ld and exit status = %d terminated.\n",
(long) getpid(), (long) pid, WEXITSTATUS(status));
}
}
}
int main() {
int n;
char* argvs[MAX_COMMANDS][MAX_ARGV];
size_t size = 128;
char line[size];
printf(" >> ");
get_line(line, size);
n = parse(line, argvs);
// Debug printouts.
printf("%d commands parsed.\n", n);
print_argvs(argvs);
int (*fds)[2] = malloc(sizeof(int) * 2 * (n - 1)); // should be pointer to arrays of size 2
for (int i = 0; i < n - 1; i++) {
if (pipe(fds[i]) == -1) {
perror("Creating pipe error"); // Creating pipe error: ...
exit(EXIT_FAILURE);
}
printf("pipe %d: read: %d, write: %d\n", i, fds[i][READ], fds[i][WRITE]);
}
fork_cmds(argvs, n, fds);
wait_for_all_cmds(n);
exit(EXIT_SUCCESS);
}
The problem was that one of the parenthesis was at the wrong place in both fork_cmd and fork_cmds, it should be like this of course: close(fds[i][WRITE]). This was the original code:
for (int i = 0; i < n - 1; i++) {
if (close(fds[i][READ]) == -1 || close(fds[i][WRITE] == -1))<--
{
close_error();
}
}

Why the child process is not executed?

I have the following piece of code:
int main() {
int n = 1;
if(fork() == 0) {
printf("child");
n = n + 1;
exit(0);
}
n = n + 2;
printf("%d: %d\n", getpid(), n);
wait(0);
return 0;
}
The problem is that I don't understand why the child process is not executing.
The child process is executing only if i set sleep(1) in the parent process
Thanks in advance.
It is getting executed and it should be outputting the text. No newlines should be necessary:
https://ideone.com/a1tznH
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
int n = 1;
if(fork() == 0) {
printf("child");
n = n + 1;
exit(0);
}
n = n + 2;
printf("%ld: %d\n", (long)getpid(), n); //this is how you should print pids
wait(0);
return 0;
}
Example output:
child23891: 3
Perhaps you didn't notice the child text was at the beginning of your next prompt:
18188: 3
child[21:17] pskocik#laptop: $
The child is executed but two processes are trying to write on the same FD - STDOUT (File Descriptor).
If you want to see the result, put "\n" in printf of the child.
int main() {
int n = 1;
if(fork() == 0)
{
printf("child\n");
n = n + 1;
exit(0);
}
n = n + 2;
printf("%d: %d\n", getpid(), n);
wait(0);
return 0;
}
Try
pid_t pid;
pid = fork();
if(pid < 0)
{
printf("fail to fork");
}
else if (pid == 0)
{
printf("running child");
exit(0);
}
else
{
print("running parent");
wait(0);
print("child done");
}
return 0;
This is the basic structure of a program I wrote recently which works. Not totally sure why yours didn't work though.

C - dup2() not executing

This is my first question so I apologize if I'm omitting anything important. So I've been working on an assignment that handles piping via forking. My code is pretty messy, littered with printf statements so I see what's going on.
I've looked around online and I think I get the idea of how to handle piping, but the problem I'm having is that my code skips dup2() on any file descriptor except inFD and outFD.
Here's the code for my function. Also, from what I understand, my teacher made a macro called CHK which checks for errors. If there is an error (such as dup2 returning -1), it'll terminate with a print to stderr.
My includes, global variables and myhandler() for signal
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <strings.h>
#include <math.h>
#include <signal.h>
// Function calls
void parse(char *w, char **ptrArray, char *inArray, char *outArray, int *pipeArray);
int flagHandler(char **ptrArray, char *inArray, char *outArray);
int pipeHandler(char **ptrArray, char *inArray, char *outArray, int *pipeArray);
// Global Variables
const int STORAGE = 254;
const int MAXITEM = 100;
int inFD; // file descriptor for <
int outFD; // file descriptor for >
int complete = 0; // for sighandler
int readDes = 0;
int writeDes = 1;
int numPipes = 0;
int status;
int forCounter = 0;
int fildes[4];
int pipeIndex = 0;
// MetaChar flags
int lessthanSign = 0; // < flag
int greaterthanSign = 0; // > flag
int firstChildFlag = 0;
int lastChildFlag = 0;
void myhandler(int signum)
{
complete = 1;
}
My main function
int main()
{
char s[STORAGE]; // array of words
char *newargv[MAXITEM];
char inArray[STORAGE]; // for <
char outArray[STORAGE]; // for >
int firstCheck;
int pidBackground; // holds value from fork(), used for background calls
struct stat st; // for stat(), checks if file exists
// dynamic array based on numPipes
// first child doesn't use this array, as it uses newargv[0] and newargv
// only the middle children and last child use this array, hence 10
int *pipeArray = malloc(10 * sizeof(int));
int numLoops = 0;
int i = 0;
signal(SIGTERM, myhandler);
for(;;)
{
// Reset flags here
lessthanSign = 0;
greaterthanSign = 0;
pipeSign = 0;
firstChildFlag = 0;
lastChildFlag = 0;
pipeIndex = 0;
parse(s, newargv, inArray, outArray, pipeArray);
pipeHandler(newargv, inArray, outArray, pipeArray);
wait(NULL);
fflush(NULL);
} // end for
printf("Entering killpg; numLoops = %d\n", numLoops);
killpg(getpid(), SIGTERM);
printf("p2 terminated.\n");
exit(0);
} // end main
Main calls parse which fills in newargv[]. It also fills in inArray[] and outArray[] with the string immediately after a < and > respectively. When detecting a pipe sign, it puts a null on newargv[], as well as putting a value in pipeArray[] for indexing the executable's name in newargv. I omitted the parse() and flagHandler() calls to keep it minimal.
My parseHandler() function
int pipeHandler(char **ptrArray, char *inArray, char *outArray, int *pipeArray)
{
pid_t firstChild;
pid_t firstChildBackground;
pid_t middleChild;
pid_t lastChild;
pid_t lastChildBackground;
int i = 0; // plain integer for for loops
printf("Initializing pipes\n");
//pipe(fildes);
//pipe(fildes + 2);
for (i = 0; i < (2*numPipes); i+=2)
{
printf("pipe initializing; i is %d\n", i);
if (pipe(fildes + i) < 0)
{
perror("pipe initialization failed");
exit(EXIT_FAILURE);
}
}
fflush(stdout);
if ((firstChild = fork()) < 0)
{
perror("First child's fork failed!");
exit(EXIT_FAILURE);
}
printf("firstChild pid = %d\n", getpid());
if (firstChild == 0)
{
if (firstChildFlag == 1)
{
printf("inFD = open...\n");
inFD = open(inArray, O_RDONLY);
printf("Doing dup2 inFD\n");
if (dup2(inFD, STDIN_FILENO) < 0)
{
perror("First child's < dup2 failed");
exit(EXIT_FAILURE);
}
}
printf("doing dup2 fildes[writeDes]\n");
if (dup2(fildes[writeDes], STDOUT_FILENO) < 0)
{
perror("First child's dup2 failed");
exit(EXIT_FAILURE);
}
printf("*****doing dup2 fildes[writeDes] was a success!\n");
for (i = 0; i < 4; i++)
{
if (close(fildes[i]) < 0)
{
perror("close failed");
exit(EXIT_FAILURE);
}
}
if (firstChildFlag == 1)
{
lessthanSign = 0;
firstChildFlag = 0;
if (close(inFD) < 0)
{
perror("close inFD failed");
exit(EXIT_FAILURE);
}
}
writeDes += 2;
printf("About to execvp first child\n");
if (execvp(ptrArray[0], ptrArray) < 0)
{
perror("execvp failed");
exit(EXIT_FAILURE);
}
}
else
{
fflush(stdout);
if ((middleChild = fork() < 0))
{
perror("Middle child's fork failed");
exit(EXIT_FAILURE);
}
printf("middleChild pid = %d\n", getpid());
if (middleChild == 0)
{
if (dup2(fildes[readDes], STDIN_FILENO) < 0)
{
perror("Middle child's dup2 on reading failed");
exit(EXIT_FAILURE);
}
if (dup2(fildes[writeDes], STDOUT_FILENO) < 0)
{
perror("Middle child's dup2 on writing failed");
exit(EXIT_FAILURE);
}
for (i = 0; i < 4; i++)
{
if (close(fildes[i]) < 0)
{
perror("close failed");
exit(EXIT_FAILURE);
}
}
readDes += 2;
writeDes += 2;
if (execvp(ptrArray[pipeArray[0]], ptrArray + pipeArray[0]) < 0)
{
perror("Middle child's execvp failed");
exit(EXIT_FAILURE);
}
}
else
{
fflush(stdout);
if ((lastChild = fork() < 0))
{
perror("Last child's fork failed");
exit(EXIT_FAILURE);
}
printf("lastChild pid = %d\n", getpid());
if (lastChild == 0)
{
if (dup2(fildes[readDes], STDOUT_FILENO) < 0)
{
perror("Last child's dup2 on reading failed");
exit(EXIT_FAILURE);
}
if (lastChildFlag == 1)
{
outFD = open(outArray, O_CREAT | O_RDWR, 0400 | 0200);
if (dup2(outFD, STDOUT_FILENO) < 0)
{
perror("Last child's > dup2 failed");
exit(EXIT_FAILURE);
}
}
for (i = 0; i < 4; i++)
{
if (close(fildes[i]) < 0)
{
perror("close failed");
exit(EXIT_FAILURE);
}
}
if (lastChildFlag == 1)
{
greaterthanSign = 0;
lastChildFlag = 0;
if (close(outFD) < 0)
{
perror("close on outFD failed");
exit(EXIT_FAILURE);
}
}
printf("Execvp last child\n");
if (execvp(ptrArray[pipeArray[1]], ptrArray + pipeArray[1]) < 0)
{
perror("Last child's execvp failed");
exit(EXIT_FAILURE);
}
printf("Last child execvp finished\n");
}
}
}
// Only the parent gets here
printf("Only the parent should be here\n");
printf("My pid is %d\n", getpid());
for (i = 0; i < 4; i++)
{
if (close(fildes[i]) < 0)
{
perror("close failed");
exit(EXIT_FAILURE);
}
}
for (;;)
{
pid_t pid;
if (pid = wait(NULL) < 0)
{
perror("wait failed");
exit(EXIT_FAILURE);
}
if (pid == lastChild)
{
printf("Parent is waiting for lastChild\n");
break;
}
}
printf("Parent finished waiting. Returning...\n");
return 0;
}
I did pipe(fildes) before any fork, so that all children and a parent have their copy. Therefore, I must close all file descriptors in each child (after dup2 but before execvp) and the parent. The parent will then wait until it gets the pid of lastChild.
With a lot of printf statements, I have found that no child does the dup2() command (except for dup2(inFD...) and dup2(outFD...) when the flags are appropriate). There is also no error printed.
I printed out my (char) newargv[] and my (int) pipeArray[] and they contain the correct values. It seems to be just the dup2 problem, and I have absolutely no idea what's going wrong with it.
I made a simple text file called test2 containing
ls | sort | cat someString
Where someString is just a file with some text. With all the print statements in the pipeHandler() function my output is:
EDIT: I fixed a couple typos I had. I forgot to lace an extra set of parenthesis on 3 ifs, if ((firstChild = fork()0 < 0)
I now have an infinite loop as the parent is waiting for the lastChild's pid. Here's the output:
Initializing pipes
numpipes = 2
pipe initializing; i is 0
pipe initializing; i is 2
firstChild pid = 20521
firstChild pid = 20522
doing dup2 fildes[writeDes]
middleChild pid = 20521
middleChild pid = 20523
lastChild pid = 20521
Only the parent should be here
My pid is 20521
lastChild pid = 20524
<infinite loop>
I'm still clueless though as to what's going on or what's potentially stopping the child.
#MarkPlotnick you're right! It's not that dup2 isn't executing or anything. Because I did dup2(fildes[1], STDOUT_FILENO), all print statements will be piped.
I fixed the typo mentioned as well. I tried my teacher's test file
< input1 cat|>your.outputc tr a-z A-Z | tr \ q
Which should result with a file called your.outputc. It does, and the contents are input1 with the effects of tr. However, I also have the printf statements at the top of this file.
I assumed the dup2 wasn't working because no printf statement followed, unlike it did in dup2(inFD, STDIN_FILENO), but that's probably because it was STDIN.

Forking within C - infinite loop?

Attempting to learn C forking. It correctly prints the number of times the main loop is suppose to run and the right number of threads but the execution time is off and the program never terminates. Am I making an infinite amount of processes?
After some suggestions here is a cleaner version of the code. The old version is located below. The updated part is still creating to many child processes and never exiting. I'm just not seeing what's going wrong.
Update: Suggestion by John Hascall fixed a formatting and threads running out of order. An infinite number of threads are still generated but now in the correct order. I.e prints thread execution time 1, 2, 3, 4... etc. Don't think the problem is the wait syscall but going to study it and see if I can't find anything.
Update**: I found the solution. The first problem I believe was that I didn't have a wait command and the second is that when putting in the wait I accidentally removed the check for count < argv[1]. I put it back in and it seems to be running correctly! Thanks for the help and style pointers everyone! Working version is below.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "./processes.h"
int main(int argc, char** argv) {
if (argc != 4) {
printf("Wrong number of arguments entered. Usage: #processes sleepTime inputFile.\n");
return 1;
}
if(atoi(argv[1]) <= 0){
printf("Incorrect number of children, must be greater than 0");
return -1;
}
int count = 0;
int index;
Child *child = malloc(sizeof(Child) * atoi(argv[1]));
int childIndex;
int pid;
do{
switch (pid = fork()){
case -1:
printf("Fork failed\n");
exit(1);
case 0:
sleep(atoi(argv[2]) * childIndex);
gettimeofday(&child[childIndex].endTime, NULL);
double elapsed = child[childIndex].endTime.tv_usec - child[childIndex].startTime.tv_usec;
printf("Time for process %d = %f microseconds\n", childIndex, elapsed);
break;
default:
childIndex = count + 1;
gettimeofday(&child[count].startTime, NULL);
child[count].index = count + 1;
child[count].pid = pid;
count++;
}
} while((wait(NULL) != -1) && (count < atoi(argv[1])));
return 1;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "./processes.h"
int main(int argc, char** argv) {
if (argc != 4) {
printf("Wrong number of arguments entered. Try again.");
return 1;
}
if(atoi(argv[1]) <= 0){
printf("Incorrect number of children, must be greater than 0");
return -1;
}
int count;
int index;
Child *child = malloc(sizeof(Child) * atoi(argv[1]));
int pid = 1;
int childIndex;
for (count = 0; count < atoi(argv[1]); count++) {
if (pid != 0) {
childIndex = count + 1;
gettimeofday(&child[count].startTime, NULL);
child[count].index = count + 1;
pid = fork();
if (pid != 0){
child[count].pid = pid;
printf("Main thread loop: %d\n", count);
printf("Child process: %d\n", getpid());
}
}
}
if (pid == 0) {
//this is the child process
sleep(atoi(argv[2]) * childIndex);
gettimeofday(&child[childIndex].endTime, NULL);
double elapsed = child[childIndex].endTime.tv_usec - child[childIndex].startTime.tv_usec;
printf("Time for process %d = %f microseconds\n", childIndex, elapsed);
//printf("This is thread %d reporting in.\n", childIndex);
}
// printf("Testing\n");
return 1;
}
The bigest issue is your child's code:
if (pid == 0) {
....
}
belongs in the same loop (say right after) the parent's code:
if (pid != 0) {
....
}
Also, you never check for pid == -1 (the fork() failed).
A more standard way to write something like this is:
switch (pid = fork()) {
case -1:
/* handle fork error */
exit(1);
case 0:
/* child code goes here */
_exit(0);
default:
/* parent code goes here */
}
/* Also you probably want to look into the `wait()` syscall. */
do {} while (wait(NULL) != -1); /* <--- the very minimum */

Resources