How processes work sequentially with C pipe? - c

I want to do that 2 child processes will put their names and wait until other process put his name. For instance, if there are first and second process, first will put her name and will wait for other's name in screen. So I want to work with processes and I wanna to see they are working sequentially.
Output:
first
second
first
second
first
second
I just tried something about C(linux).
int main(void)
{
pid_t child_a, child_b;
int pipe1[2], pipe2[2];
char mesazhi1[] = "first";
char mesazhi2[] = "second";
char buf[1024];
int first_pipe = pipe(pipe1);
pipe(pipe2);
if(first_pipe == -1){
perror("pipe");
exit(1);
}
child_a = fork();
if (child_a == 0)
{
/* Child A code */
int i;
for (i = 0; i < 3; i++)
{
write(pipe1[1],mesazhi1, strlen(mesazhi1) + 1);
//printf("first\n");
int a = read(pipe2[0], buf, strlen(mesazhi2) + 1);
printf("%s - %d\n", buf, a);
}
}
else
{
child_b = fork();
if (child_b == 0)
{
int i;
for (i = 0; i < 3; i++)
{
write(pipe2[1],mesazhi2, strlen(mesazhi2) + 1);
//printf("second\n");
int a = read(pipe1[0], buf, strlen(mesazhi1) + 1);
printf("%s - %d\n", buf, a);
}
}
else
{
/* Parent Code */
int returnStatusA,returnStatusB;
waitpid(child_a, &returnStatusA, 0); // Parent process waits here for child to terminate.
waitpid(child_b, &returnStatusB, 0); // Parent process waits here for child to terminate.
if (returnStatusA == 0 && returnStatusB == 0) // Verify child process terminated without error.
{
printf("%s\n", "The child processes terminated normally.\n");
}
if (returnStatusA == 1 && returnStatusB == 1)
{
printf("%s\n", "The child processes terminated with an error!. \n" );
}
}
}
}
It is putting name randomly. I mean that I think, sometimes second process works faster than first. Output like that:
first
second
second
first
second
...
So why second process doesn't wait for first one, because I think that read() function should wait until there is something in pipe1.

In the posted code, both processes write to their respective pipes, and then read. After that, it's a race to see which process gets to print first.
For a more controlled situation, have child B call read and printf before calling write. That way B has to wait for A before printing, and vice versa.
if (child_b == 0)
{
int i;
for (i = 0; i < 3; i++)
{
int a = read(pipe1[0], buf, strlen(mesazhi1) + 1);
printf("%s - %d\n", buf, a);
write(pipe2[1],mesazhi2, strlen(mesazhi2) + 1);
}
}

Related

Value modification inside child doesn't update

Im trying to modify an int inside a child process when fork(); but I cant get the program to update it more than once, my idea is to add a counter that displays to screen that shows how many bg where (when & is at the end of the line), but I don't know why it's not working. Now I'm trying this but it doesn't work either, maybe I should modify the value in the parent process? This it the code:
void execute(char **tokens, int token_Size, int *blk){
pid_t pid, wpid;
int status;
int result;
int flag;
int isPipe;
int output;
int input;
int isAmper;
pid = fork();
if (pid == -1)
{
perror("Fork:");
exit(1);
}
else if (pid == 0)
{
isAmper = needs_amper(tokens, token_Size);
output = needs_out_redir(tokens, token_Size);
input = needs_in_redir(tokens, token_Size);
isPipe = needs_pipe(tokens, token_Size);
static int bloq = 1;
if (isAmper != -1)
{
*blk +=1;
printf("[%d] %d \n", *blk, getppid()); //-> [blk] is the job number asigned to the job
tokens[isAmper] = NULL;
}
if (strcmp(tokens[0], "echo") == 0)
{
for (int i = 1; tokens[i]; i++)
{
printf("%s ", tokens[i]);
}
}
flag = 0;
if (output != -1)
{
redirect_output(tokens, output);
tokens[output] = NULL;
flag = 1;
}
if (input != -1)
{
redirect_input(tokens, input);
tokens[input] = NULL;
flag = 1;
}
if (isPipe != -1)
{
create_pipe(tokens, output, input, isPipe);
}
if (flag || isPipe == -1)
{
execvp(tokens[0], tokens);
perror("Unkown Command:");
exit(1);
}
// exit(0);
}
else // Main (parent) process after fork succeeds
{
while ((wpid = wait(&status)) > 0)
; // this way, the father waits for all the child processes
result = waitpid(pid, &status, 0);
if (result == 1) //If the process terminated correctly, result returns 0.
{
printf("The child process terminated with an error!.\n");
}
}}
I'm trying to modify the blk value passed to the execute function, I tried using an inside value also, but doesnt work either.
To make my self clearer. I want that when I type in my custom shell something with an '&' at the end it return something like this:
ivo#ivo-Surface-Pro-6:/home/ivo/Documents/SO1/soi-myshell-Ivoo25$ echo hola &
[1] 10853
hola
And the next time I type something with the & at the end..
[2] 10853
main.c main.o Makefile myshell myShell.c myShell.h myShell.o README.md
I think that what Im trying to do cant be done because the pid is the same in every execution, maybe is that?
I managed to get it, I added the same function
needs_amper(tokens, token_Size)
but outside the child process, before the fork inside an if function veryfing that it returns a value higher than 0

Pipe's related arguments to pass to a function?

I'm a beginner in C programming and I started learning about pipes today.
I need them because my program has to run up to 4 processes at the time, so to avoid creating more processes than those required, I have to use a shared variable between all of them to keep track how may can still be created.
I tried to simplify my program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void forking(int p, int pid);
int main(int argc, char *argv[])
{
int fd[2];
int p = 4; // Max number of processes that can run at the same time
int pid;
if(pipe(fd) == -1)
{
perror("pipe: ");
return 0;
}
//It will try the function forking 10 times to execute SOME CODE that
// changes everytime something operates on it
for(int i = 0; i < 10; i++)
{
forking(p, pid);
}
return 0;
}
void forking(int p, int pid)
{
if (p > 0) //We can create another process
{
p -= 1; // update the p before creating a child process
write(fd[1], &p, (sizeof(int)*3)); //Tell everyone about the update
pid = fork();
if (pid == 0)
{
//The child process turn to elaborate SOME CODE
// SOME CODE
// Then there will be a point where
// we will need to check if the p has been modified!
read(fd[0], &p, sizeof(int)*3);
//So that forking can decide whether we can create another process
// to operate on SOME OTHER CODE
forking(p, pid);
//Once we are done, we can terminate the child
//but first we'll need to update the process n° p
p += 1;
write(fd[1], &p, (sizeof(int)*3));
exit(0);
}
else if(pid > 1) //Father time
{
// check the updated value
//the father will do nothing
// since a process it's already on it (on the SOME CODE part)
return;
}
}
else
{
//else the father does SOME CODE itself
// SOME CODE
}
return;
}
My 2 doubts is whether I should pass something else to the function "forking" (which can be recursive), like "fd", or if it is okay to just leave the code like this, and whether this will have the desired result.
Hopefully I made myself clear enough.
EDIT 1:
void forking(int p, int pid, int *fd)
{
if (p > 0) //We can create another process
{
p -= 1; // update the p before creating a child process
write(fd[1], &p, (sizeof(int)*3)); //Tell everyone about the update
pid = fork();
if (pid == 0)
{
//The child process turn to elaborate SOME CODE
// SOME CODE
// Then there will be a point where
// we will need to check if the p has been modified!
read(fd[0], &p, sizeof(int)*3);
//So that forking can decide whether we can create another process
// to operate on SOME OTHER CODE
forking(p, pid, fd);
//Once we are done, we can terminate the child
//but first we'll need to update the process n° p
p += 1;
write(fd[1], &p, (sizeof(int)*3));
exit(0);
}
else if(pid > 1) //Father time
{
// check the updated value
//the father will do nothing
// since a process it's already on it (on the SOME CODE part)
return;
}
}
else
{
//else the father does SOME CODE itself
// SOME CODE
}
return;
}
Passing fd resulted as a success, now I'm wondering whether I should add pipe(fd) at the start of the forking program like so . . .
void forking(int p, int pid, int *fd)
{
if(pipe(fd) == -1)
{
perror("pipe: ");
return;
}
//Rest of the code
}

Multi-level pipes in C

I have to create a multi-level pipe in C that interprets Linux commands just like the Unix console. The code I have works for a 2-level pipe but I have to implement more (up to 16). The main problem here is I'm not sure how to take the output of the first two commands and then reroute it to be the input of the third command (etc. for the other levels). I know I need to use "1" of the first pipe and "0" of the second pipe since those are stdout and stdin respectively but I am unsure how to implement this in practice.
// tokenize is a separate function that uses strtok repeatedly on cmdline, setting the array
// segments to the strings separated by | and numTokens to the number of commands
char* segments[MAX_PIPE_SEGMENTS];
int x = 0;
int* numTokens = &x;
tokenize(segments, cmdline, numTokens, "|");
// the code for one command
if (*numTokens == 1) {
pid_t pid = fork();
if (pid == 0) {
char* strings[MAX_SEGMENT_LENGTH];
int y = 0;
int* numStrings = &y;
tokenize(strings, segments[0], numStrings, " ");
execvp(strings[0], strings);
} else {
wait(0);
return;
}
}
pid_t pid = fork();
if (pid == 0) {
int i = 0;
for (i = 0; i < *numTokens-1; i++) {
pid_t pid2 = fork();
if (pid2 == 0) {
int ps[2];
pipe(ps);
pid_t pid3 = fork();
if (pid3 == 0) {
close(1);
dup2(ps[1], 1);
close(ps[0]);
char* strings[MAX_SEGMENT_LENGTH];
int y = 0;
int* numStrings = &y;
tokenize(strings, segments[i], numStrings, " ");
execvp(strings[0], strings);
} else {
close(0);
dup2(ps[0], 0);
close(ps[1]);
wait(0);
char* strings[MAX_SEGMENT_LENGTH];
int y = 0;
int* numStrings = &y;
tokenize(strings, segments[i+1], numStrings, " ");
execvp(strings[0], strings);
}
} else {
wait(0);
}
}
} else {
wait(0);
return;
}
You see, treating the first and second command in different if/else blocks is not generalizable to more than two commands. Feasible is to treat the pipe commands and operations identically as far as possible and only add special conditions for the first and last:
int i, in, out = dup(1); // save standard output descriptor
for (i = 0; i < x; i++)
{
int ps[2];
if (i < x-1) pipe(ps); // if not last in line, make a pipe
pid_t pid = fork();
if (pid == 0)
{
// if not first in line, connect standard input to pipe
if (i) dup2(in, 0), close(in);
// if not last in line, connect standard output to pipe
if (i < x-1) dup2(ps[1], 1), close(ps[1]);
// if last in line, restore standard output to original
else dup2(out, 1), close(out);
char* strings[MAX_SEGMENT_LENGTH];
int y = 0;
int* numStrings = &y;
tokenize(strings, segments[i], numStrings, " ");
execvp(strings[0], strings);
exit(1);
}
if (i) close(in);
close(ps[1]);
in = ps[0]; // the current pipe's read end is the new input
}
close(out);
do ; while (wait(0) > 0);

Using pipes to pass data between child processes in C

My homework task is to write a C program that creates 4 child processes and each child has to do something with an integer number and send it to the next child that does something else with it and the last one has to print the changed value. I have to use anonymous pipes to communicate between children. Parent process has no other work than to open pipes for children. I have written the program, but the problem is I get funny output when I try to print the number using the last child. It prints out 8 numbers instead of one (actually one of them is the right one). The part of code is following:
int pipe1[2];
int pipe2[2];
int pipe3[2];
pipe(pipe1);
pipe(pipe2);
pipe(pipe3);
int j;
close(pipe1[1]); //close pipes for writing on parent process
close(pipe2[1]);
close(pipe3[1]);
for (j = 0; j < 4; j++) {
switch(fork()) {
case 0:
if (j == 0) {
int value = 100;
write(pipe1[1], &value, sizeof(int));
close(pipe1[1]);
}
else if (j == 1) {
int value;
read(pipe1[0], &value, sizeof(int));
close(pipe1[0]);
value = value * 10;
write(pipe2[1], &value, sizeof(int));
close(pipe2[1]);
}
//and so on until the last process
else if (j == 3) {
int value;
read(pipe3[0], &value, sizeof(int));
char buf[4] = {0};
memset(buf, 0, sizeof(buf));
snprintf(buf, sizeof(value), "%d ", value);
write(1, buf, strlen(buf));
}
break;
}
}
close(pipe1[0]);
close(pipe2[0]);
close(pipe3[0]);
sleep(1);
int k;
for (k = 0; k < 4; k++) {
wait(0);
}
What do I have to do for this case, just to get one (and the proper one) output?
What do you think happens when one of your child processes executes the break statement in the code you presented? Hint: it doesn't exit.
Additionally, I'm doubtful that your pipes can work quite like that. You close the write ends of two of them (one of those twice) before forking any children. Those will not magically be reopened for the children, so their whole pipes are useless.

Concurrent program in C(sequential issue)

I am trying to find a way to make this algorithm run concurrent and be simultaneous. So far it has only 1 for-loop that reads each file and then makes a process for each file.
I believe this algorithm runs sequentially which is not what I want...
I thought about creating an outer for-loop where I put the wait(null) and read command. But when I tried it did not work, didn't produce output. Currently I have the wait(null) command in the parent process.
Any suggestions?
Code:
int main(int argc, char *argv[])
{
// ATTRIBUTES //
int freq[argc-1], ipc[argc][2], nbytes, i;// freq of words, pipes, counters
ssize_t errorfi;
char readbuffer[9999];
char** k = malloc(50);
char** op = malloc(50);
if(argc == ONE) { // there are no files given, throw error and close
fprintf(stderr, "There are no files found from %s\n", argv[0]);
exit(0);
}
for(i = 1; i < argc; i++) // creates pipes for ipc
pipe(ipc[i]);
pid_t pids[argc-1]; // array of pids
for(i = 1; i < argc; i++) { // reads input after position 0(a.out)
pid_t pid = fork(); // creates process
pids[i-1] = pid;
if( pid < 0 ) { // bad fork process: error
perror("bad fork");
exit(0);
}
else if(pid > 0) { //parent process
close(ipc[i][1]);
wait(NULL);
nbytes = read(ipc[i][0], readbuffer, sizeof(readbuffer));
if(nbytes > 0)
printf("%s\n", readbuffer);
}
else if(pid == 0) { // child process
close(ipc[i][0]);
k = inputReader(argv[i]); // finds filename,w1,w2,w3,uniqueWords
char info[50] = "";
strcat(info, k[0]);
strcat(info, " ");
strcat(info, k[1]);
strcat(info, " ");
strcat(info, k[2]);
strcat(info, " ");
strcat(info, k[3]);
strcat(info, " ");
strcat(info, k[4]);
int uniqueWordint = atoi(k[4]);
freq[i-1] = uniqueWordint; // freq of all uniqueWords
errorfi = write(ipc[i][1], info, strlen(info)+1); // writes info to pipe i
if (errorfi < 0 ) {
fprintf(stderr, "error found when writing in pipe errofi: %d\n", errorfi);
exit(0);
}
exit(0); // close process
} // closes child process
} // closes for-loop for each process
for(j = 0; j < argc-1; j++) {
wait(2); // if i put read command here it won't work
}
return(0); // close main
}
This is pretty much a sequential execution indeed. The parent-process enters a loop, forks a child-process, and then it won't continue to the next loop until that child-process is done.
What you could do is create a pid_t array of size argc, to store each fork()'s return value.
Also create a new loop after "for-loop for each process", where the parent-process would wait for all of his children, using wait(2) or waitpid(2), depending on whether you need to process each child's result in the specific order or not, and continue processing them (reading or whatever needed.

Resources