Forking in loops - c

Say I have a process. I fork it, then it has a parent and a child.
I want the parent to write from 2 to n to a pipe and the child to read from it.
The child will pass through each value through some conditions, and they don't pass any of the conditions, it will go back to the parent by calling exit().
In the parent, I will need to fork the original process and now the current parent will read 3 into the fd used in the master process and write to the newly created child, which goes through what the previous child went through.
if (pid > 0){ //parent which writes n to fd
close(fd[0]); //close read
for (j = 2; j <= n; j++){
if (write(fd[1], &j, sizeof(int)) == -1){ //write j = 2 to fd
perror("write j");
}
}
close(fd[1]); //close write
int status;
if(wait(&status) != -1){
if (WIFEXITED(status)){
if (WEXITSTATUS(status) == 2){
pid = fork() //should I even be calling fork here?
}
}
}
else{ //CHILD
close(fd[1]); //close write
for (j = 2; j <= n; j++){
if (read(fd[0], &j, sizeof(int)) == -1){ //read j from fd
perror("read j");
}
if (SOME CONDITION){
exit(2);
}
So far this only gets me through value 2, and I'm not sure make the parent send the value 3 into the next child.
Here's a diagram if my explanation was confusing.
Any help would be much appreciated, thanks!

You'll have to leave the child end of the pipe open in the parent process, and pass that to the next child that is created. Sorry, haven't used linux in a while, and I don't have a linux box right now, but I figured since this is kind of psuedocode at this point, it wouldn't hurt too much to try. If you want to be able to hand an arbitrary n, you'll have to either loop or use recursion, with loop being preferably because it won't use stack memory for each iteration.
Also not entirely sure I understand what you are trying to do, so I made a few guesses. So, something like:
int keep_going = 1;
while(keep_going){
pid = fork();
if (pid > 0){ //parent which writes n to fd
/* can't close read, have to pass it to next child
close(fd[0]);*/ //close read
for (j = 2; j <= n; j++){
if (write(fd[1], &j, sizeof(int)) == -1){ //write j = 2 to fd
perror("write j");
}
}
/* can't close write either, since it gets closed in child, so that
would have to be rewritten to check if it is open before
closing. close(fd[1]);*/ //close write
int status;
if(wait(&status) != -1){
if (WIFEXITED(status)){
if (WEXITSTATUS(status) == 2){
continue; /* continues the while loop */
}
/* handle errors */
break;
}
/* handle errors */
break;
}
/* handle more errors */
break;
assert(0); /* shouldn't be here */
}
else{ //CHILD
close(fd[1]); //close write
for(;;) { /* child needs its own loop */
for (j = 2; j <= n; j++){
if (read(fd[0], &j, sizeof(int)) == -1){ //read j from fd
perror("read j");
}
if (SOME CONDITION){
exit(2);
}
/* going to repeat for(;;) loop for next n */
}
}
}
assert(0); /* shouldn't be here */
}

Related

Get pid from brother process

I want to have a parent process and three child processes. I want these child processes to know the pids of the other child processes.
The problem is that when I do fork and then I do it again, the second fork is also executed in the child process creating an extra process (or so I think).
How could I solve it?
Thanks.
The parent should fork three times, the children should not fork. This way, the parent will know the pids of all three children.
After the fork, you'll need some kind of separate communication channel by which the parent can communicate these pids to all children. A simple way would be to open a pipe (see pipe(2)) before forking each child, so the child inherits the pipe's file descriptor (at least the read end) and the parent keeps the write end. Then have the parent send the three pids down each pipe and close it.
Example code (long, but that's the nature of C):
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define NUM_CHILDREN 3
/* Entry point for the child processes */
int child_main(int pipe_read_end) {
pid_t my_pid = getpid();
/* Read child pids from pipe */
int child_pids[NUM_CHILDREN];
unsigned int bytes_read = 0;
while (bytes_read < sizeof(child_pids)) {
ssize_t result = read(pipe_read_end, ((unsigned char *) child_pids) + bytes_read, sizeof(child_pids) - bytes_read);
if (result < 0) {
perror("error reading from pipe");
return 1;
} else if (result == 0) {
fprintf(stderr, "unexpected end of file\n");
return 1;
} else {
bytes_read += result;
}
}
close(pipe_read_end);
/* Do something useful with these child pids */
for (int i = 0; i < NUM_CHILDREN; i++) {
printf("Child %d received sibling pid %d\n", my_pid, child_pids[i]);
}
return 0;
}
/* Entry point for the parent process. */
int main() {
int child_pids[NUM_CHILDREN];
int pipe_write_ends[NUM_CHILDREN];
for (int i = 0; i < NUM_CHILDREN; i++) {
/* Create the pipe for child i */
int pipefd[2];
if (pipe(pipefd)) {
perror("error creating pipe");
return 1;
}
int pipe_read_end = pipefd[0];
int pipe_write_end = pipefd[1];
/* Fork child i */
pid_t child_pid = fork();
if (child_pid < 0) {
perror("error forking");
return 1;
} else if (child_pid == 0) {
printf("Child %d was forked\n", getpid());
close(pipe_write_end);
return child_main(pipe_read_end);
} else {
printf("Parent forked child %d\n", child_pid);
close(pipe_read_end);
pipe_write_ends[i] = pipe_write_end;
child_pids[i] = child_pid;
}
}
/* Send pids down the pipes for each child */
for (int i = 0; i < NUM_CHILDREN; i++) {
unsigned int bytes_written = 0;
while (bytes_written < sizeof(child_pids)) {
ssize_t result = write(pipe_write_ends[i], ((unsigned char *) child_pids) + bytes_written, sizeof(child_pids) - bytes_written);
if (result < 0) {
perror("error writing to pipe");
return 1;
} else {
bytes_written += result;
}
}
close(pipe_write_ends[i]);
}
/* Wait for children to exit */
for (int i = 0; i < NUM_CHILDREN; i++) {
if (waitpid(child_pids[i], 0, 0) < 0) {
perror("error waiting for child");
return 1;
}
}
}
As #PSkocik points out in their answer, you should probably not be doing this. Pids can be reused by the OS, so there's no way for the children to know that their sibling pids still actually refer to their siblings; only the parent can be sure, because it has to wait for each pid before it can be reused.
However, this same mechanism can be used for other forms of IPC (inter-process communication); you could, for example, use it to create pipes between the children directly.
You can use shared memory or some other kind of IPC to communicate the PIDs, but you probably shouldn't even try.
PIDs are subject to recycling and you can only ever know for sure if a PID refers to the process you think it refers to if that PID belongs to a child process of yours (because then you can know if you've waited on it or not).
Otherwise, PIDs (of non-children) are racy references which are basically only usable for hacky debugging.

Multiple pipes and processes

I am trying to communicate with children processes and make them sort a part of a list. My problem is children processes read everything but do nothing after it.
int main(int argc, char *argv[]){
int i;
int num_children;
pid_t pid;
num_children= 3;
int fd[num_children][2]; //PIPES
for (i=0; i<num_children; i++)
{
if (pipe(fd[i]) == -1)
{
printf("couldnt create the pipe\n");
exit(EXIT_FAILURE);
}
}
for (i=0; i<num_children; i++)
{
pid = fork();
if (pid == -1)
{
printf("couldnt create child process %i\n",i);
exit(EXIT_FAILURE);
}
if (pid == 0)
{ //this is child process
close(fd[i][1]); //closing fd[1] the write end of the pipe
int received;
node *list = NULL;
while ( read(fd[i][0], &received, sizeof(int)) > 0)
{
list = insert(list, received);
printf("Process %i got the number: %i\n",i,received); //this part is working perfect
}
printf("Im process %i here is my list: \n",i); //i couldnt get any output from here
printList(list);
close(fd[i][0]);
exit(EXIT_SUCCESS);
}
}
for (i=0; i<num_children; i++) //closing the read end of pipe for parent
{
close(fd[i][0]);
}
int number;
int mod;
FILE *fileIn = fopen ("<file directory>","r");
while(fscanf(fileIn, "%i", &number)>=0)
{
mod = number % num_children;
write(fd[mod][1], &number, sizeof(int));
}
for (int i=0; i<num_children; i++)
{
if(close(fd[i][1])==0)
{
printf("cant close the pipe");
//tried to catch errors, but pipes are closing with no problem i think
}
}
return 0;
I tried to see if children process wait in the while(read) loop, but when i close the write end of pipes from the parent process they should leave the loop.
You're probably thinking that some specific pipe[2] is shared by the parent and it's respective child process. That's true ... However it is also shared by all the other children processes you create along the way - and because it's opened, those other children processes also inherit it as opened.
Doing this at the beginning of your child pid check worked for me:
if (pid == 0) {
int j;
for (j = 0; j < num_children; j++) {
if (j != i) {
close(fd[j][0]);
close(fd[j][1]);
}
}
...
}
I suspect that the reading from the pipe via:
while ( read(fd[i][0], &received, sizeof(int)) > 0)
is being blocked/haulted until data is available on the pipe. If so, this would explain the lack of response from your code after this point.

Infinite pipe insanity

I'm trying to create an infinte set of pipes to traverse from the left process to the right process. I'm using a fd to keep the previous out fd and input it to the new process. Can anyone see where I'm going wrong. It should be pretty simple to see at this point. I documented well.
//Keep the previous out fd for the in of the subsequent process
int prev_out_fd;
for (x = 0; x < prog_count; ++x)
{
//Create a pipe for both processes to share
int pipefd[2];
if (x != prog_count -1)
{
pipe(pipefd);
}
prog_defs[x].pid = fork();
if(prog_defs[x].pid == 0)
{
//If this is the first process we don't need a read end
if (x == 0)
{
close(pipefd[0]);
}
//If this is not the first process, set the input to the output of the previous pipe
if (x != 0)
{
dup2(prev_out_fd, STDIN_FILENO);
//Pipe now garbage. Get rid of it.
close (prev_out_fd);
}
if(x != prog_count - 1)
{
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[0]);
close(pipefd[1]);
}
execvp(prog_defs[x].bin, prog_defs[x].args);
}
if (x != 0)
close(prev_out_fd);
prev_out_fd = pipefd[0];
close(pipefd[1]);
}

Piping Given an Array of UNIX Commands

So I am at the very end of a project to create a basic UNIX shell using C. I have finished a lot of different pieces of the program, but now I would like to conquer piping. I would specifically like to create a program that can handle any number of pipes.
For some reason my code get to s certain line (labeled: //DIES HERE) and then stops and I can't figure out why.
Here is the code that I have so far:
//the contents of args[0] is {"ls","-l","-o"}
//the contents of args[1] is {"wc","-l"}
int pipefd[2];
pipe(&pipefd[0]); // Error check!
fflush(stdout);
for (i = 0; i < commands; i++){
int pid = fork();
if (pid == 0){
int command_no = i;
int prev_pipe = ((command_no - 1) % 2) * 2;
int current_pipe = (command_no % 2) * 2;
printf("\ncmd %d: prev pipe %d, curr pipe %d\n\n", i, prev_pipe, current_pipe);
fflush(stdout);
// If current command is the first command, close the
// read end, else read from the last command's pipe
if (command_no == 0){
close(pipefd[0]);
}
else{
dup2(pipefd[prev_pipe], 0);
close(pipefd[current_pipe]);
}
// If current command is the last command, close the
// write end, else write to the pipe
if (command_no == commands - 1){
close(pipefd[current_pipe + 1]);
}
else{
dup2(pipefd[current_pipe + 1], 1); //DIES HERE
}
// printf("Here?\n\n");
execvp(*args[i], args[i]);
fprintf(stderr, "Failed to exec: %s (%d: %s)\n", arrayOfCommands[i], errno, strerror(errno));
_exit(1);
}
}
Any help is appreciated! :)
The primary issue I see is that pipe() is outside the loop. You're going to need a new pipe() between every pair of processes. The comments on your question make some good points as well.
I wrote a shell many years ago in college and here's the similar loop from my code. I'm sure I'd do it much differently now, but it may be of use to you:
for (i = 0; i < iNumPipes; ++i) {
if (i == iNumPipes - 1) {
/* this is the last command
*/
p[1] = fdOutput;
p[0] = -1;
} else if (-1 == pipe(p)) {
perror("pipe");
exit(1);
}
switch (iPid = fork()) {
case -1:
perror("fork");
exit(1);
case 0:
close(0);
dup2(fdInput, 0);
close(fdInput);
close(1);
dup2(p[1], 1);
close(p[1]);
if (-1 != fdErr) {
close(2);
dup2(fdErr, 2);
close(fdErr);
}
pc = SearchPath(pppcAvs[i][0]);
execve(pc, pppcAvs[i], ppcEnv);
perror(pc);
_exit(-1);
default:
close(fdInput);
close(p[1]);
fdInput = p[0];
}
}

Implementation of multiple pipes in C

I'm trying to implement multiple pipes in my shell in C. I found a tutorial on this website and the function I made is based on this example. Here's the function
void executePipes(cmdLine* command, char* userInput) {
int numPipes = 2 * countPipes(userInput);
int status;
int i = 0, j = 0;
int pipefds[numPipes];
for(i = 0; i < (numPipes); i += 2)
pipe(pipefds + i);
while(command != NULL) {
if(fork() == 0){
if(j != 0){
dup2(pipefds[j - 2], 0);
}
if(command->next != NULL){
dup2(pipefds[j + 1], 1);
}
for(i = 0; i < (numPipes); i++){
close(pipefds[i]);
}
if( execvp(*command->arguments, command->arguments) < 0 ){
perror(*command->arguments);
exit(EXIT_FAILURE);
}
}
else{
if(command != NULL)
command = command->next;
j += 2;
for(i = 0; i < (numPipes ); i++){
close(pipefds[i]);
}
while(waitpid(0,0,0) < 0);
}
}
}
After executing it and typing a command like for example ls | grep bin, the shell just hangs there and doesn't output any result. I made sure I closed all pipes. But it just hangs there. I thought that it was the waitpid that's was the problem. I removed the waitpid and after executing I get no results. What did I do wrong? Thanks.
Added code:
void runPipedCommands(cmdLine* command, char* userInput) {
int numPipes = countPipes(userInput);
int status;
int i = 0, j = 0;
pid_t pid;
int pipefds[2*numPipes];
for(i = 0; i < 2*(numPipes); i++){
if(pipe(pipefds + i*2) < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}
}
while(command) {
pid = fork();
if(pid == 0) {
//if not first command
if(j != 0){
if(dup2(pipefds[(j-1) * 2], 0) < 0){
perror(" dup2");///j-2 0 j+1 1
exit(EXIT_FAILURE);
//printf("j != 0 dup(pipefd[%d], 0])\n", j-2);
}
//if not last command
if(command->next){
if(dup2(pipefds[j * 2 + 1], 1) < 0){
perror("dup2");
exit(EXIT_FAILURE);
}
}
for(i = 0; i < 2*numPipes; i++){
close(pipefds[i]);
}
if( execvp(*command->arguments, command->arguments) < 0 ){
perror(*command->arguments);
exit(EXIT_FAILURE);
}
} else if(pid < 0){
perror("error");
exit(EXIT_FAILURE);
}
command = command->next;
j++;
}
for(i = 0; i < 2 * numPipes; i++){
close(pipefds[i]);
puts("closed pipe in parent");
}
while(waitpid(0,0,0) <= 0);
}
}
I believe the issue here is that your waiting and closing inside the same loop that's creating children. On the first iteration, the child will exec (which will destroy the child program, overwriting it with your first command) and then the parent closes all of its file descriptors and waits for the child to finish before it iterates on to creating the next child. At that point, since the parent has closed all of its pipes, any further children will have nothing to write to or read from. Since you are not checking for the success of your dup2 calls, this is going un-noticed.
If you want to keep the same loop structure, you'll need to make sure the parent only closes the file descriptors that have already been used, but leaves those that haven't alone. Then, after all children have been created, your parent can wait.
EDIT: I mixed up the parent/child in my answer, but the reasoning still holds: the process that goes on to fork again closes all of its copies of the pipes, so any process after the first fork will not have valid file descriptors to read to/write from.
pseudo code, using an array of pipes created up-front:
/* parent creates all needed pipes at the start */
for( i = 0; i < num-pipes; i++ ){
if( pipe(pipefds + i*2) < 0 ){
perror and exit
}
}
commandc = 0
while( command ){
pid = fork()
if( pid == 0 ){
/* child gets input from the previous command,
if it's not the first command */
if( not first command ){
if( dup2(pipefds[(commandc-1)*2], 0) < ){
perror and exit
}
}
/* child outputs to next command, if it's not
the last command */
if( not last command ){
if( dup2(pipefds[commandc*2+1], 1) < 0 ){
perror and exit
}
}
close all pipe-fds
execvp
perror and exit
} else if( pid < 0 ){
perror and exit
}
cmd = cmd->next
commandc++
}
/* parent closes all of its copies at the end */
for( i = 0; i < 2 * num-pipes; i++ ){
close( pipefds[i] );
}
In this code, the original parent process creates a child for each command and therefore survives the entire ordeal. The children check to see if they should get their input from the previous command and if they should send their output to the next command. Then they close all of their copies of the pipe file descriptors and then exec. The parent doesn't do anything but fork until it's created a child for each command. It then closes all of its copies of the descriptors and can go on to wait.
Creating all of the pipes you need first, and then managing them in the loop, is tricky and requires some array arithmetic. The goal, though, looks like this:
cmd0 cmd1 cmd2 cmd3 cmd4
pipe0 pipe1 pipe2 pipe3
[0,1] [2,3] [4,5] [6,7]
Realizing that, at any given time, you only need two sets of pipes (the pipe to the previous command and the pipe to the next command) will simplify your code and make it a little more robust. Ephemient gives pseudo-code for this here. His code is cleaner, because the parent and child do not have to do unnecessary looping to close un-needed file descriptors and because the parent can easily close its copies of the file descriptors immediately after the fork.
As a side note: you should always check the return values of pipe, dup2, fork, and exec.
EDIT 2: typo in pseudo code. OP: num-pipes would be the number of pipes. E.g., "ls | grep foo | sort -r" would have 2 pipes.
Here's the correct functioning code
void runPipedCommands(cmdLine* command, char* userInput) {
int numPipes = countPipes(userInput);
int status;
int i = 0;
pid_t pid;
int pipefds[2*numPipes];
for(i = 0; i < (numPipes); i++){
if(pipe(pipefds + i*2) < 0) {
perror("couldn't pipe");
exit(EXIT_FAILURE);
}
}
int j = 0;
while(command) {
pid = fork();
if(pid == 0) {
//if not last command
if(command->next){
if(dup2(pipefds[j + 1], 1) < 0){
perror("dup2");
exit(EXIT_FAILURE);
}
}
//if not first command&& j!= 2*numPipes
if(j != 0 ){
if(dup2(pipefds[j-2], 0) < 0){
perror(" dup2");///j-2 0 j+1 1
exit(EXIT_FAILURE);
}
}
for(i = 0; i < 2*numPipes; i++){
close(pipefds[i]);
}
if( execvp(*command->arguments, command->arguments) < 0 ){
perror(*command->arguments);
exit(EXIT_FAILURE);
}
} else if(pid < 0){
perror("error");
exit(EXIT_FAILURE);
}
command = command->next;
j+=2;
}
/**Parent closes the pipes and wait for children*/
for(i = 0; i < 2 * numPipes; i++){
close(pipefds[i]);
}
for(i = 0; i < numPipes + 1; i++)
wait(&status);
}
The (shortened) relevant code is:
if(fork() == 0){
// do child stuff here
....
}
else{
// do parent stuff here
if(command != NULL)
command = command->next;
j += 2;
for(i = 0; i < (numPipes ); i++){
close(pipefds[i]);
}
while(waitpid(0,0,0) < 0);
}
Which means the parent (controlling) process does this:
fork
close all pipes
wait for child process
next loop / child
But it should be something like this:
fork
fork
fork
close all pipes (everything should have been duped now)
wait for childs
You only need two pipes alternating like below:
typedef int io[2];
extern int I; //piped command current index
extern int pipe_count; //count of '|'
#define CURRENT 0
#define PREVIOUS 1
#define READ 0
#define WRITE 1
#define is_last_command (I == pipe_count)
bool connect(io pipes[2])
{
if (pipe_count)
{
if (is_last_command || I != 0)
dup2(pipes[PREVIOUS][READ], STDIN_FILENO);
if (I == 0 || !is_last_command)
dup2(pipes[CURRENT][WRITE], STDOUT_FILENO);
}
return (true);
}
void close_(io pipes[2])
{
if (pipe_count)
{
if (is_last_command || I != 0)
close(pipes[PREVIOUS][READ]);
if (I == 0 || !is_last_command)
close(pipes[CURRENT][WRITE]);
}
}
void alternate(int **pipes)
{
int *pipe_current;
pipe_current = pipes[CURRENT];
pipes[CURRENT] = pipes[PREVIOUS];
pipes[PREVIOUS] = pipe_current;
}
Example usage:
#define ERROR -1
#define CHILD 0
void execute(char **command)
{
static io pipes[2];
if (pipe_count && pipe(pipes[CURRENT]) == ERROR)
exit_error("pipe");
if (fork()==CHILD && connect(pipes))
{
execvp(command[0], command);
_exit(EXIT_FAILURE);
}
while (wait(NULL) >= 0);
close_(pipes);
alternate((int **)pipes);
}
static void run(char ***commands)
{
for (I = 0; commands[I]; I++)
if (*commands[I])
execute(commands[I]);
}
I'll leave a link to a full working code for someone who needs it.
Building upon the idea of using a maximum of two pipes at a given time mentioned by Christopher Neylan, I put together pseudocode for n-pipes. args is an array of character pointers of size 'args_size' which is a global variable.
// MULTIPLE PIPES
// Test case: char *args[] = {"ls", "-l", "|", "head", "|", "tail", "-4",
0};// "|", "grep", "Txt", 0};
enum fileEnd{READ, WRITE};
void multiple pipes( char** args){
pid_t cpid;
// declare pipes
int pipeA[2]
int pipeB[2]
// I have done getNumberofpipes
int numPipes = getNumberOfPipes;
int command_num = numPipes+1;
// holds sub array of args
// which is a statement to execute
// for example: cmd = {"ls", "-l", NULL}
char** cmd
// iterate over args
for(i = 0; i < args_size; i++){
//
// strip subarray from main array
// cmd 1 | cmd 2 | cmd3 => cmd
// cmd = {"ls", "-l", NULL}
//Open/reopen one pipe
//if i is even open pipeB
if(i % 2) pipe(pipeB);
//if i is odd open pipeA
else pipe(pipeA);
switch(cpid = fork(){
case -1: error forking
case 0: // child process
childprocess(i);
default: // parent process
parentprocess(i, cpid);
}
}
}
// parent pipes must be closed in parent
void parentprocess(int i, pid_t cpid){
// if first command
if(i == 0)
close(pipeB[WRITE]);
// if last command close WRITE
else if (i == numPipes){
// if i is even close pipeB[WRITE]
// if i is odd close pipeA[WRITE]
}
// otherwise if in middle close READ and WRITE
// for appropriate pipes
// if i is even
close(pipeA[READ])
close(pipeB[WRITE])
// if i is odd
close(pipeB[READ])
close(pipeA[WRITE])
}
int returnvalue, status;
waitpid(cpid, returnvalue, status);
}
void childprocess(int i){
// if in first command
if(i == 0)
dup2(pipeB[WRITE], STDOUT_FILENO);
//if in last command change stdin for
// the necessary pipe. Don't touch stdout -
// stdout goes to shell
else if( numPipes == i){
// if i is even
dup2(pipeB[READ], STDIN_FILENO)
//if i is odd
dup2(pipeA[READ], STDIN_FILENO);
}
// otherwise, we are in middle command where
// both pipes are used.
else{
// if i is even
dup2(pipeA[READ], STDIN_FILENO)
dupe(pipeB[WRITE], STDOUT_FILENO)
// if i is odd
dup2(pipeB[READ], STDIN_FILENO)
dup2(pipeA[WRITE], STDOUT_FILENO)
}
// execute command for this iteration
// check for errors!!
// The exec() functions only return if an error has occurred. The return value is -1, and errno is set to indicate the error.
if(exec(cmd, cmd) < 0)
printf("Oh dear, something went wrong with read()! %s\n", strerror(errno));
}
}
Basically what you wanna do is a recursive function where the child executes the first command and the parent executes the second one if no other commands are left or calls the function again.

Resources