I was working on a little program I had to make some time ago, and I wanted to short it up.
This little program was about one thread creating several child who would redirect their standard output/input one to another with pipes in order, except for the last child, who won't redirect it's standard output, like this.
Parent pipe child1 pipe child2 pipe last child
__ __ __
O-----|__|-----O-----|__|-----O-----|__|-----O -> Stdout
First time I face this code, I made a matrix with a dimension of [n_child][2], and made a pipe from every position of that matrix, so it was very easy to connect every pipe to every child when needed. But now I want to try it only with 2 pipes, and "playing" with inheritance.
Maybe I'm not explaining myself really well, so I think everything would be better understood with my code,so here we go.
piping_function(int number_of_child){
int i;
int olddesc[2];
int newdesc[2]; //Here I create the descriptors of the pipes I'll use
int *olddir;
int *newdir;
if(pipe(olddesc)<0 || pipe(newdesc) < 0){ //I create the pipes
//Error
}
olddir = olddesc;
newdir = newdesc; //And attach the directions to the array's direction (we will see later why)
for(i = 0; i < number_of_child; i++){
chaddr[i] = fork();
switch(chaddr[i]){
case -1:
//Error trace
case 0:
dup2(olddesc[0],0); //Here I redirect the pipe who connect the previous child's pipe to the standard input
if(i != number_of_child - 1)
dup2(newdesc[1],1); //And here, except from the last child, I redirect the standard output to the pipe who will connect to the standard input of the next child
close(olddesc[0]);
close(newdesc[1]); //I close the descriptors I don't need
//Several child operations with standard input-output (end up returning 0/1 after the pipeline is connected, so no child will create any child)
default:
if(i == 0)
dup2(olddesc[1], 1); //I want the standard output of the principal proccess only on the first pipe
olddir = newdir; //Here I would want the direction of the "old" pipe to be the direction of the "new" pipe, in order to achieve the pipeline
if(pipe(newdesc)<0)
//Error
break;
}//End of switch
}//End of for
close(olddesc[0]);close(olddesc[1]);close(newdesc[0]);close(newdesc[1]); //I don't need these descriptors anymore, as they must be redirected to the standard's input/output of the process they need.
}//End of function
Well, there is my code. I think I can see the mistake I'm doing, when I make olddir be newdir, and create the pipe, I'm doing olddir to be also that new pipe right? So here comes my question:
Is there any way to achieve that change? I mean, the thing I want is to equal olddir (who is the address of olddesc, right? so if I change that address olddesc's address will be also changed, right?) to newdir, in order to continue with the pipe I created before to redirect the standard output of the "next" child, but I also want newdir to be a NEW pipe.
I don't really know if I explained myself right, I'm not a native speaker and it's a bit difficult to explain these kind of ideas in other language. Feel free to correct any grammar mistake, I'd appreciate it, and to ask any question about the code, as maybe I'm not giving the point I wanted to.
Thanks.
Well, I finally made it, but changing the point of view and not working with any address overwrite, only playing with the iterations, in order always keep the record of one of the two pipes. I didn't really solve the address problem, but it works so, I'm satisfied.
piping_function(int number_of_child){
int i;
int olddesc[2];
int newdesc[2]; //Here I create the descriptors of the pipes I'll use
//Now we'll start from the last pipe, so we can connect the parent to the first one and not connect the last child's output.
for(i = number_of_child - 1; i >= 0; i--){
//Instead of changing addresses, I'll play with the "i"
if(i%2==0){
close(newdesc[0]); //We close the last descriptors
close(newdesc[1]);
pipe(newdesc); //And create a new pipe!
}
else{
close(olddesc[0]);
close(olddesc[1]);
pipe(oldesc);
}
chaddr[i] = fork();
switch(chaddr[i]){
case -1:
//Error trace
case 0:
if(i%2==0){
dup2(newdesc[0],0); //Here I redirect the pipe who connect the previous child's pipe to the standard input
if(i != number_of_child - 1)
dup2(olddesc[1],1); //And here, except from the last child, I redirect the standard output to the pipe who will connect to the standard input of the next child
close(newdesc[1]);
close(olddesc[0]); //I close the descriptors I don't need
}
else{
dup2(olddesc[0],0); //Here I redirect the pipe who connect the previous child's pipe to the standard input
if(i != number_of_child - 1)
dup2(newdesc[1],1); //And here, except from the last child, I redirect the standard output to the pipe who will connect to the standard input of the next child
close(olddesc[1]);
close(newdesc[0]); //I close the descriptors I don't need
}
//Several child operations with standard input-output (end up exiting 0/1 after the pipeline is connected, so no child will create any child)
default:
if(i == 0)
dup2(newdesc[1], 1); //I want the standard output of the principal proccess only on the first pipe
break;
}//End of switch
}//End of for
close(olddesc[0]);close(olddesc[1]);close(newdesc[0]);close(newdesc[1]); //I don't need these descriptors anymore, as they must be redirected to the standard's input/output of the process they need.
}//End of function
*Edit:
I don't think it's good to close always the descriptors, even without creating anything, I saw I left them this way on this piece of code, but in order to make it work correctly we should only close the descriptors when created so an if would be needed on every close.
Related
I want to read a file using different processes but when i try that first created child read all file so other processes cannot read the file . For example i create 3 different process with 101,102 and 103 process ids.
a read from = 101.
b read from = 101.
c read from = 101.
d read from = 101.
But I wanted to read like that
a read from = 101.
b read from = 103.
c read from = 102.
d read from = 103.
I tried to solve it using semaphore and mutex but I couldn't do that. Could you help me, please?
int i=0, pid;
char buffer[100];
for(i=0; i<3; i++){
pid = fork();
if(pid == 0){
sem_wait(&mutex); // sem_t mutex is global.
while(read(fd,&buffer[j],1) == 1){
printf("%c read from = %d\n",buffer[j],getpid());
j++;
}
sem_post(&mutex);
exit(0);
}
else{
wait(NULL);
}
}
The problem is that even though each process has its own file descriptor, those file descriptors all share the same open file description ('descriptor' != 'description'), and the read position is stored in the file description, not the file descriptors. Consequently, when any of the children reads the file, it moves the file pointer for all the children.
For more information about this, see the POSIX specifications for:
open()
dup2()
fork()
No mutex or other similar gadget is going to fix this problem for you — at least, not on its own. The easiest fix is to reopen the file in the child processes so that each child has a separate open file description as well as its own file descriptor. Alternatively, each child will have to use a mutex, rewind the file, read the data, and release the mutex when done. It's simpler to (re)open the file in the child processes.
Note that the mutex must be shared between processes for it to be relevant. See the POSIX specification for pthread_mutexattr_setpshared(). That is not set with the default mutex attribute values.
You have two problems that prevent what you appear to want and will result in the entire file being read by (just) the first child
the parent process waits for each child immediately after creating it, before forking any more children. So after creating the first child, it will wait for that child to exit looping and creaing a second child. To fix that, you need have two loops parent -- the first just creates children and the second waits for them:
for (...) {
if (fork() == 0) {
// run the child
exit(0); } }
for (...)
wait(NULL); // wait for a child
your reading loop is inside the sem_wait/sem_post. So the first child will get the mutex, then proceed to read the entire file before releasing the mutex. Subsequent children will not get the mutex until the file is fully read, so they'll see they're at the EOF and exit. To fix this you need to move the sem_wait/sem_post calls inside the while loop:
while (!done) {
sem_wait(&mutex);
if (read(...) == 1) { ...
} else {
done = true; }
sem_post(&mutex); }
You might not even need the semaphore at all -- the kernel will synchronize reads between the different processes, so each byte will be read by exactly one child. This would allow the children to proceed in parallel (processing the bytes) rather than only allowing one child at a time to run.
Of course, even with the above, one child may process many bytes before another child starts to run and process them, so if the processing is fast and there are few bytes, they might still all be consumed by the first child.
I've been working on this school assignment forever now, and I'm super close to finishing.
The assignment is to create a bash shell in C, which sounds basic enough, but it has to support piping, IO redirect, and flags within the piped commands. I have it all working except for one thing; the | piping child isn't getting any of the data written to the pipe by the user command process child. If I were to remove the child fork for pipechild, and have everything from if(pipe_cmd[0] != '\0') run as the parent, it would work just fine (minus ending the program because of execlp). If I were to use printf() inside the pipe section, the output would be in the right file or terminal, which just leaves the input from the user command process child not getting to where it needs to be as a culprit.
Does anyone see an issue on how I'm using the pipe? It all felt 100% normal to me, given the definition of a pipe.
int a[2];
pipe(a);
//assume file_name is something like file.txt
strcat(file_name, "file.txt");
strcat(pipe_cmd, "wc");
if(!fork())
{
if(pipe_cmd[0] != '\0') // if there's a pipe
{
close(1); //close normal stdout
dup(a[1]); // making stdout same as a[1]
close(a[0]); // closing other end of pipe
execlp("ls","ls",NULL);
}
else if(file_name[0] != '\0') // if just a bare command with a file redirect
{
int rootcmd_file = open(file_name, O_APPEND|O_WRONLY|O_CREAT, 0644);
dup2(rootcmd_file, STDOUT_FILENO);
execlp("ls","ls",NULL); // writes ls to the filename
}
// if no pipe or file name write...
else if(rootcmd_flags[0] != '\0') execlp("ls","ls",NULL)
else execlp("ls","ls",NULL);
} else wait(0);
if(pipe_cmd[0] != '\0') // parent goes here, if pipe.
{
pipechild = fork();
if(pipechild != 0) // *PROBLEM ARISES HERE- IF THIS IS FORKED, IT WILL HAVE NO INFO TAKEN IN.
{
close(0); // closing normal stdin
dup(a[0]); // making our input come from the child above
close(a[1]); // close other end of pipe
if(file_name[0] != '\0') // if a filename does exist, we must reroute the output to the pipe
{
close(1); // close normal stdout
int fileredir_pipe = open(file_name, O_APPEND|O_WRONLY|O_CREAT, 0644);
dup2(fileredir_pipe, STDOUT_FILENO); //redirects STDOUT to file
execlp("wc","wc",NULL); // this outputs nothing
}
else
{
// else there is no file.
// executing the pipe in stdout using execlp.
execlp("wc","wc",NULL); // this outputs nothing
}
}
else wait(0);
}
Thanks in advance. I apologize for some of the code being withheld. This is still an active assignment and I don't want any cases of academic dishonesty. This post was risky enough.
} else wait(0);
The shown code forks the first child process and then waits for it to terminate, at this point.
The first child process gets set up with a pipe on its standard output. The pipe will be connected to the second child process's standard input. The fatal flaw in this scheme is that the second child process isn't even started yet, and won't get started until the first process terminates.
Pipes have limited internal buffering. If the first process generates very little output chances are that its output will fit inside the tiny pipe buffer, it'll write its output and then quietly terminate, none the wiser.
But if the pipe buffer becomes full, the process will block and wait until something reads from the pipe and clears it. It will wait as long as it takes for that to happen. And wait, and wait, and wait. And since the second child process hasn't been started yet, and the parent process is waiting for the first process to terminate it will wait, in vain, forever.
This overall logic is fatally flawed for this reason. The correct logic is to completely fork and execute all child processes, close the pipe descriptors in the parent (this is also important), and then wait for all child processes to terminate. wait must be the very last thing that happens here, otherwise things will break in various amazing and mysterious ways.
So I'm trying to create program that accepts user input (price for example 50) and then first child passes it to second, second one add 10 (price is now 60), third one then 50 (price is now 110) and 4 one just prints/returns final price. I have fork in loop and I'm creating pipes, but price is always the same, only 10 is added in each child. What is wrong or how to fix so that it's going to work as I want to.
My code:
int main(int argc,char *argv[])
{
int anon_pipe[2];
int n,N=4;
char value_price[100];
if(argc>1)
{
int price=atoi(argv[1]);
printf("%d\n",price);
if(pipe(anon_pipe)==-1){
perror("Error opening pipe");
return -1;
}
for(n = 0; n < N; n++){
switch(fork()){
case -1:
perror("Problem calling fork");
return -1;
case 0:
close(anon_pipe[1]);
read(anon_pipe[0],value_price,100);
price+=10;
sprintf(value_price,"%d \n",price);
printf("Price: %d\n",atoi(value_price));
write(anon_pipe[1],value_price,sizeof(value_price));
_exit(0);
}
}
close(anon_pipe[0]);
sleep(1);
close(anon_pipe[1]);
}
return 0;
}
You seem to think that forking makes the child start from the beginning of the program. This is not the case, forking makes the child start at the same line when the fork() was called
For instance look at this code here:
read(anon_pipe[0],value_price,100);
price+=10;
sprintf(value_price,"%d \n",price);
printf("Price: %d\n",atoi(value_price));
See you increase the value of price but you never read that value form the pipe. So all children will always output +10 to their respective pipe.
You should check the return values of your function calls for error codes. If you had done, you would have detected the error arising from this combination of calls:
close(anon_pipe[1]);
// ...
write(anon_pipe[1],value_price,sizeof(value_price));
Very likely, you would also have detected that many of these calls ...
read(anon_pipe[0],value_price,100);
... signal end-of-file without reading anything. At the very least, you need read()'s return value to determine where to place the needed string terminator (which you fail to place before using the buffer as a string).
As a general rule, it is mandatory to handle the return values of read() and write(), for in addition to the possibility of errors / EOF, these functions may perform short data transfers instead of full ones. The return value tells you how many bytes were transferred, which you need to know to determine whether to loop to attempt to transfer more bytes.
Moreover, you have all of your processes using the same pipe to communicate with each other. You might luck into that working, but it is probable that at least sometimes you'll end up with garbled communication. You really ought to create a separate pipe for each pair of communicating processes (including the parent process).
Furthermore, do not use sleep() to synchronize processes. It doesn't work reliably. Instead, the parent should wait() or waitpid() for each of its child processes, but only after starting them all and performing all needed pipe-end handling. Waiting for the child processes also prevents them from remaining zombies for any significant time after they exit. That doesn't much matter when the main process exits instead of proceeding to any other work, as in this case, but otherwise it constitutes a resource leak (file descriptors). You should form the good habit of waiting for your child processes.
Of course, all of that is moot if you don't actually write the data you mean to write; #SanchkeDellowar explains in his answer how you fail to do that.
I am unsure if I am correctly going about this, I am trying to create 7 processes total via...
void err_sys(const char* x)
{
perror(x);
exit(1);
}
for(i = 1; i < 7; i++){
switch(parent = fork()){
case -1:
err_sys("fork error");
case 0: //child
printf("I'm the child, number %d(%d)\n", i, getpid());
break;
default: //parent
printf("I'm the parent(%d)\n", getpid());
break;
}
if(parent)
break; //loop break
}
When I run it with prog | cat > file I get 6 outputs of "I am the parent", followed with various amounts of children each. However, there are 6 children made with unique pids. The other parent pids, other than the first match a child pid. Is this just some problem that comes with output due to forking?
Your code is not incorrect a priori; it does what it does. The issue is 'does it do what you want it to do', and that is not clear because you've not clearly stated what you are trying to do.
One reason you get 6 processes is that you have a loop:
for (i = 1; i < 7; i++)
This counts 1, 2, 3, 4, 5, 6 and stops. You need to use one of two idioms:
for (i = 0; i < 7; i++) // Idiomatic C
for (i = 1; i <= 7; i++) // Less idiomatic C
Assuming that err_sys() does not return when called (if it did, you'd need a break; after it), then:
The process forks. If the fork fails, the process exits via err_sys().
If the process is the child (so the misnamed variable parent is set to 0), then the code prints "I'm the child" and then leaves the switch, and iterates around the loop again.
If the process is the parent (so the misnamed variable parent is set to a non-zero value), it prints "I'm the parent" and then leaves both the switch and the loop.
Only the child from the first fork gets to re-execute the loop. However, each child except the last gets to identify itself as the parent on the next iteration of the loop.
Buffered I/O
Note that buffered I/O complicates things. If the output of the program is written to the terminal, you will get line buffered output by default. Each newline causes the data to be written to the terminal. However, when the output goes to a pipe, it is fully buffered; the data only gets written to the terminal when the buffer is full, or the process is exiting (or closes standard output for some other reason). Thus, when the output goes to a pipe, the first child has the output from just its operations, but the second child has the data printed, but not flushed, by the first child in its buffer as well as what it wrote. The third child has 3 process's worth of output, and so on.
You could add fflush(0) or fflush(stdout) after the print statements (or just after the end of the switch, before the if) and see what a difference it makes. You can also run the code without redirecting to a pipe and see what a difference that makes.
I've been messing around in C trying to figure out how to do this. Let's say I have my main program, the parent process. The parent creates three child processes, each of which will eventually run programs (but that's not important right now). What I'd like to do is make it so that the first child's stdout will be received by the second child's stdin. The second child's stdout will then be received by the third child's stdin.
The parent process's stdin/stdout aren't messed with at all.
So far, what I've got is
pipe(procpipe);
parentPid = getpid();
for(i = 0; i < 3; i++)
{
if(getpid() == parentPid)
{
child[i] = fork();
if(child[i] == 0)
{
mynumber = i+1;
}
}
}
But from there I'm kind of stuck as to how to use dup2 to properly assign my pipes, and in which section of my code to insert it. There are lots of examples on Google and this website of how to pipe from a parent to a child, but I'm yet to see one that will tell me exactly how to connect a child's stdout to another child's stdin.
Edit:
Forgot to mention: assume all my variables are properly initialised. The int 'mynumber' is so a child process knows upon creation which number it is, so I can give it instructions via
if(mynumber == whatever)
So you have a loop that creates several child processes. Each of these child processes will be using two pipes: read from previous and write to the next. To set up a pipe for the reading end you need to close the write end of the pipe, and dup2 the read end into the stdin. Similar for the pipe where the process will be writing.
void set_read(int* lpipe)
{
dup2(lpipe[0], STDIN_FILENO);
close(lpipe[0]); // we have a copy already, so close it
close(lpipe[1]); // not using this end
}
void set_write(int* rpipe)
{
dup2(rpipe[1], STDOUT_FILENO);
close(rpipe[0]); // not using this end
close(rpipe[1]); // we have a copy already, so close it
}
When you fork each children you need to attach the pipes to it.
void fork_and_chain(int* lpipe, int* rpipe)
{
if(!fork())
{
if(lpipe) // there's a pipe from the previous process
set_read(lpipe);
// else you may want to redirect input from somewhere else for the start
if(rpipe) // there's a pipe to the next process
set_write(rpipe);
// else you may want to redirect out to somewhere else for the end
// blah do your stuff
// and make sure the child process terminates in here
// so it won't continue running the chaining code
}
}
With this in hand you can now write a loop that continuously forks, attaches the pipes, and then reuses the output pipe as the input pipe for the next one. Of course, once both ends of a pipe have been connected to child processes, the parent should not leave it open for itself.
// This assumes there are at least two processes to be chained :)
// two pipes: one from the previous in the chain, one to the next in the chain
int lpipe[2], rpipe[2];
// create the first output pipe
pipe(rpipe);
// first child takes input from somewhere else
fork_and_chain(NULL, rpipe);
// output pipe becomes input for the next process.
lpipe[0] = rpipe[0];
lpipe[1] = rpipe[1];
// chain all but the first and last children
for(i = 1; i < N - 1; i++)
{
pipe(rpipe); // make the next output pipe
fork_and_chain(lpipe, rpipe);
close(lpipe[0]); // both ends are attached, close them on parent
close(lpipe[1]);
lpipe[0] = rpipe[0]; // output pipe becomes input pipe
lpipe[1] = rpipe[1];
}
// fork the last one, its output goes somewhere else
fork_and_chain(lpipe, NULL);
close(lpipe[0]);
close(lpipe[1]);
The closing bits are very important! When you fork with an open pipe, there will be four open file descriptors: two on the parent process, and two others on the child process. You have to close all of those you won't be using. That's why the code above always closes the irrelevant ends of the pipes in the child processes, and both ends on the parent.
Also note that I am giving special treatment to the first and the last processes, because I don't know where the input for the chain will come from, and where the output will go to.