I have to use fork(2) to make as many children as inputted by the user.
Then I need them to split up the work reading a txt file of coordinate points comparing the distance between them to an inputted distance.
Then they add their count of how many points are within the distance given. Each child has to write their count to the pipe and the parent has to read each count and add it to the total and then print it out. Here is my code:
int main( int argc, char *argv[] ) {
int distance = atoi(argv[1]);
if ( argc != 3 || sscanf( argv[ 1 ], "%d", &distance ) != 1 )
fail( "usage: pairs <distance>" );
readPoints();
int workers = atoi(argv[2]);
// Compute the square of the distance bound, since that's what we'll
// need to compare against.
int dsq = distance * distance;
// Count up the number of nearby pairs of points.
int total = 0;
int fd[2]; // pipe
if ( pipe( fd ) != 0 ){
fail( "Can't create pipe" );
}
int pid; // child
int chNum; // child's number
int c;
for( chNum = 0; chNum < workers; chNum++){
c = 0;
pid = fork();
if ( pid == -1 ){ //failure
fail( "Can't create child process" );
}
if( pid ==0 ){ // it's a child
for ( int i =chNum; i < ptCount; i+=workers)
for ( int j = i + 1; j < ptCount; j++ ) {
// Check the squared distance.
int dx = ptList[ i ].x - ptList[ j ].x;
int dy = ptList[ i ].y - ptList[ j ].y;
if ( dx * dx + dy * dy <= dsq )
c++;
}
close(fd[READ]);
lockf(fd[WRITE], F_LOCK,0);
write(fd[WRITE], &c, sizeof(c));
lockf(fd[WRITE], F_ULOCK,0);
close(fd[WRITE]);
exit(0);
}
else if(pid>0){ // this is parent
int d;
close(fd[WRITE]);
read(fd[READ], &d, sizeof(d));
close(fd[READ]);
total = total + d;
}
}
if(pid>0){
wait(NULL);
printf( "Total: %d\n", total );
}
return 0;
}
I use a for loop to make the children with fork(2), and then I have them calculate the count and send it to the pipe to be read by the parent. The parent reads into d and adds it to total. I was wondering if I am using the pipe correctly to send each child's count to the parent and/or if I am forking correctly so it only comes from one parent. I am getting the wrong total count when I use more than 1 child.
If I use 1 child, the total result is 166428, which is correct, but when I use 4 for example, it gives me 164908. Can someone help me?
You're not doing pipe handling properly.
First off, you don't need to lock / unlock to write to and read from the pipe: writes that are less than PIPE_BUF bytes are guaranteed to be atomic. POSIX.1-2001 requires that PIPE_BUF is at least 512 bytes; since you're only writing sizeof(int) bytes at a time, you're safe (unless sizeof(int) is greater than or equal to 512, which is nonsense). See man limits.h, under Pathname Variable Values:
{PIPE_BUF}
Maximum number of bytes that is guaranteed to be atomic
when writing to a pipe. Minimum Acceptable Value: {_POSIX_PIPE_BUF}
That by itself simplifies the code and reduces unnecessary locking / unlocking overhead.
But the real issue is here:
else if (pid > 0) { // this is parent
int d;
close(fd[WRITE]);
read(fd[READ], &d, sizeof(d));
close(fd[READ]);
total = total + d;
}
You can't close fd[WRITE] inside the loop: consider what happens in the next iteration, when you fork the next process. The child process in the next loop will attempt to write to a file descriptor that has already been closed, so an error occurs (and write(2) fails with EBADF, but you never check the return value of write(2) so the code happily ignores the error). Plus, you attempt to close fd[WRITE] again and again, so close(2) will also return an error (which again, you ignore).
Similarly for read(2): if you close fd[READ], you can't read the results out of the pipe in the next iteration; read(2) will return an error and close(2) too.
(So the lesson is: do not ignore errors. If you had done error handling properly, you would have a pretty strong clue at what was going wrong)
You don't need to close. The child processes write exactly workers integers to the pipe; the parent process reads exactly workers integers from the pipe, so this is enough:
for (chNum = 0; chNum < workers; chNum++) {
c = 0;
pid = fork();
if (pid == -1)
fail("Can't create child process");
if (pid == 0) { // it's a child
for (int i = chNum; i < ptCount; i += workers) {
for (int j = i + 1; j < ptCount; j++) {
// Check the squared distance.
int dx = ptList[i].x - ptList[j].x;
int dy = ptList[i].y - ptList[j].y;
if (dx*dx + dy*dy <= dsq) {
c++;
}
}
}
ssize_t written = write(fd[WRITE], &c, sizeof(c));
if (written == -1)
perror("write error");
if (written != sizeof(c))
fail("Write failed on pipe");
exit(0);
}
else {
int d;
if (read(fd[READ], &d, sizeof(d)) != sizeof(d))
fail("Read error on pipe");
total += d;
}
}
The key point is to understand that you need to keep fd[READ] and fd[WRITE] open as long as you plan to fork new processes that will use the pipe.
Now, that fixes the problem, but you get a false sense of parallelism: reads in a pipe will block by default if no data is available. This means that on each iteration, the parent will not make progress until the corresponding child writes to the pipe. So you're not really parallelizing anything; the effect is the same as having the parent fork, wait for the child to terminate, read the result and add it to total, and then fork the next child (and repeat the cycle).
If you want true parallelism, you have to fork every process and only then start reading from the pipe. Something like this:
for (chNum = 0; chNum < workers; chNum++) {
c = 0;
pid = fork();
if (pid == -1)
fail("Can't create child process");
if (pid == 0) { // it's a child
for (int i = chNum; i < ptCount; i += workers) {
for (int j = i + 1; j < ptCount; j++) {
// Check the squared distance.
int dx = ptList[i].x - ptList[j].x;
int dy = ptList[i].y - ptList[j].y;
if (dx*dx + dy*dy <= dsq) {
c++;
}
}
}
ssize_t written = write(fd[WRITE], &c, sizeof(c));
if (written == -1)
perror("write error");
if (written != sizeof(c))
fail("Write failed on pipe");
exit(0);
}
}
if (close(fd[WRITE]) < 0)
fail("Error closing pipe's write channel");
int d;
ssize_t r;
while ((r = read(fd[READ], &d, sizeof(d))) > 0) {
if (r != sizeof(d))
fail("read error");
total += d;
}
Note that here we have to explicitly close the pipe's write channel before starting the reads; this is to avoid having the parent hang when no more child processes are actively writing to the pipe. Remember that a read will block as long as there is at least one process with the pipe's write channel open. If the parent process kept the write channel open, read(2) would never return because there is a chance that the parent himself could write to the pipe (even though we know it won't). So we have to close fd[WRITE].
Alternatively, since we know that there are exactly workers numbers to read from the pipe, we could just do this after the loop instead of closing the write channel:
int d;
int i;
for (i = 0; i < workers; i++) {
if (read(fd[READ], &d, sizeof(d)) != sizeof(d))
fail("Failed to read from pipe");
total += d;
}
A couple of other (unrelated) remarks:
The error message when the wrong arguments are given does not agree with the code. The code shows that distance is in argv[1] and workers is in argv[2], yet the error message passed to fail() seems to say that distance is in argv[2].
argv[1] is parsed twice as an integer: with atoi(3) and with sscanf(3). I'd stick to sscanf(3) since you can check the return value to make sure that parsing was successful.
workers is not validated and is converted with atoi(3). Errors are ignored. I'd suggest parsing it with sscanf(3) just like you did with distance and make sure that it is successful.
The correct type to store a pid is pid_t, not int. Please use the correct type (you might have to include sys/types.h in addition to unistd.h).
Here's the final version with all of this sorted out:
int main(int argc, char *argv[]) {
int distance;
int workers;
if (argc != 3 || sscanf(argv[1], "%d", &distance) != 1 || sscanf(argv[2], "%d", &workers) != 1)
fail("usage: <distance> <workers>");
readPoints();
// Compute the square of the distance bound, since that's what we'll
// need to compare against.
int dsq = distance * distance;
// Count up the number of nearby pairs of points.
int total = 0;
int fd[2]; // pipe
if (pipe(fd) != 0)
fail("Can't create pipe");
pid_t pid;
int chNum; // child's number
int c;
for (chNum = 0; chNum < workers; chNum++) {
c = 0;
pid = fork();
if (pid == -1)
fail("Can't create child process");
if (pid == 0) { // it's a child
for (int i = chNum; i < ptCount; i += workers) {
for (int j = i + 1; j < ptCount; j++) {
// Check the squared distance.
int dx = ptList[i].x - ptList[j].x;
int dy = ptList[i].y - ptList[j].y;
if (dx*dx + dy*dy <= dsq) {
c++;
}
}
}
ssize_t written = write(fd[WRITE], &c, sizeof(c));
if (written == -1)
perror("write error");
if (written != sizeof(c))
fail("Write failed on pipe");
exit(0);
}
}
if (close(fd[WRITE]) < 0)
fail("Error closing pipe's write channel");
int d;
ssize_t r;
while ((r = read(fd[READ], &d, sizeof(d))) > 0) {
if (r != sizeof(d))
fail("read error");
total += d;
}
printf("Total: %d\n", total);
return 0;
}
Related
Im trying to replicate the multiple pipes execution of bash, and Im getting no output from terminal. First I create the number of pipes that my program will need depending on the number of commands, then in a while loop I launch each process executing each command in the child proccess and working with the correct FDs of pipes, in case it is first command, I dont read from any pipe, on the other hand, in case it is last command, I dont redirect the output to any pipe. I printed each fd to check if my program is doing well and it looks everything okay but as I said I dont get any output of the last commands.
Here is a snippet of my code about how I deal with the process and the pipes, if you need something more just let me know.
int i;
int *pipes;
i = 0;
pipes = ft_calloc(sizeof(int), gdata->n_pipes * 2);
while (i < gdata->n_pipes)
{
pipe(pipes + (i * 2));
i++;
}
int cc = 0; //command count
int r = 0;
int m;
pid_t pid;
while (gdata->cmds[r]) //double pointer array that contains the commands to execute ex: [["ls -l"], ["grep i"], ["wc -l"]]
{
pid = fork();
if (pid < 0)
{
perror("Fork: ");
exit(EXIT_FAILURE);
}
if (pid == 0)
{
if (r > 0)
{
if (dup2(pipes[(cc - 1) * 2], STDIN_FILENO) < 0)
{
perror("dup");
exit(EXIT_FAILURE);
}
}
if (r < gdata->commands - 1)
{
if (dup2(pipes[cc * 2 + 1], STDOUT_FILENO) < 0)
{
perror("dup");
exit(EXIT_FAILURE);
}
}
int k = 0;
while (k < gdata->n_pipes * 2)
{
close(pipes[k]);
k++;
}
handle_path(gdata->cmds[r], gdata->envp); // function that calls execve to execute commands
}
waitpid(pid, &m, 0);
r++;
cc++;
}
int y = 0;
while (y < gdata->n_pipes * 2)
{
close(pipes[y]);
y++;
}
Do you have any idea what I'm doing wrong, Thanks for the help!
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);
}
}
I have this assignment where I have to create two processes and each process has to generate 50 integers which are odd or even.
Write a simple sequence-number system through which two processes, P1 and P2, can each obtain 50 unique integers, such that one receives all the odd and the other all the even numbers. Use the fork() call to create P1 and P2. Given a file, F, containing a single number, each process must perform the following steps:
a. Open F.
b. Read the sequence number N from the file.
c. Close F.
d. Output N and the process' PID (either on screen or test file).
e. Increment N by 1
f. Open F.
g. Write N to F.
h. Flush F.
i. Close F
As suggested by SO user I have created a loop in each process and ran the steps as mentioned above. But I am not sure if this approach is correct. I have asked my Teaching assistant for help and he suggested to do the same(using sleep call and waiting for a valid integer). But the thing is I can obtain the same results without using the sleep call. So I am not sure if I am applying the logic properly to code. Can someone please help?
This is my implementation:
void getUniqueNumbers() {
struct process p1;
struct process p2;
int numberFromFile;
pid_t pid = fork();
// Process 1
if (pid == 0) {
int p1Counter = 0;
p1.processId = getpid();
while(p1Counter < numLimit) {
numberFromFile = getNumberFromFile();
if (numberFromFile % 2 == 0) { // even
p1.numbers[p1Counter] = numberFromFile;
printf("N: %d, PID: %d\n", numberFromFile, p1.processId);
numberFromFile++;
writeNumberToFile(numberFromFile);
p1Counter++;
}
else {
sleep(1);
}
}
}
// Process 2
else if (pid > 0 ) {
int p2Counter = 0;
p2.processId = getpid();
while(p2Counter < numLimit) {
numberFromFile = getNumberFromFile();
if (numberFromFile % 2 != 0) { // odd
p2.numbers[p2Counter] = numberFromFile;
printf("N: %d, PID: %d\n", numberFromFile, p2.processId);
numberFromFile++;
writeNumberToFile(numberFromFile);
p2Counter++;
}
else {
sleep(1);
}
}
}
else {
printf("Error: Could not create process\n");
}
}
Read/Write functions:
// Returns the number included in user provided file
int getNumberFromFile() {
FILE *fp = fopen(fileName, "rb");
int num = 0;
if (fp != 0) {
char line[10];
if (fgets(line, sizeof(line), fp) != 0)
num = atoi(line);
fclose(fp);
}
return num;
}
// Writes a given number to the user provided file
void writeNumberToFile(int num) {
FILE *fp = fopen(fileName, "w");
if (fp != 0) {
fprintf(fp, "%d", num);
fclose(fp);
}
}
The code looks ok. It can be simplified a lot though.
void getUniqueNumbers()
{
struct process p; // We need only 1 structure
size_t counter = 0; // sample counter
int oddEven; // flag if we are parent
pid_t pid = fork(); // Fork here
if (-1 == pid)
{
abort(); // simply die on error
}
oddEven = 0 == pid ? 0 : 1;
p.processId = getpid(); // We are either child or parent.
while (counter < numLimit)
{
int numberFromFile = getNumberFromFile();
if ((numberFromFile & 1) == oddEven)
{
p.numbers[counter++] = numberFromFile;
printf("N: %d, PID: %ld\n", numberFromFile, (long)p.processId);
numberFromFile++;
writeNumberToFile(numberFromFile);
}
sleep(1); // sleep in both cases
// Extra check for parent: if child has died, we are in infinite
// loop, so check it here
if (0 != pid && counter < numLimit)
{
int status = 0;
if (waitpid(pid, &status, WNOHANG) > 0)
{
printf("Child exited with 0x%08X status\n", status);
break;
}
}
}
// wait till child process terminates
if (0 != pid)
{
int status = 0;
waitpid(pid, &status, 0);
printf("Child exited with 0x%08X status\n", status);
}
}
Also, the file reading/writing either should use file lock operations, or atomic file change. It is important to prevent potential errors like one thread is writing number 40006, and another one manages to read 400. Should not happen in real life though.
File locks are needed to prevent concurrent access to the same contents. It can be exclusive lock, or shared read exclusive write.
Atomic modifications are feature that enables to replace file contents atomically, regardless of how many operations it took to write the data. It is an alternative to keep data consistent.
I am writing on a pipe 10 integers, so i call write 10 times and then i want to call read pipe only once and store the written integers into an array of size 10 and after that add all the integers from the array into a total sum. The problem is that i get only 9 integers after reading. What i am doing wrong?
int main()
{
int fd[2];
int total = 0;
int result;
int nbytes;
int child;
int subVector;
int written;
static int readSum[P];
int partialSum;
if(pipe(fd) < 0){
perror("pipe");
}
for(child = 0; child < P; child++){
if((pid[child] = fork()) < 0){
perror("fork");
exit(1);
}
else if(pid[child] == 0){
close(fd[0]);
partialSum = getSubvectorSum(elementsList,child,P,SIZE);
//printf("Partial sum: %d by child #%d\n",partialSum,getpid());
written = write(fd[1],&partialSum,sizeof partialSum);
//printf("Child #%d has written: %d bytes.\n",getpid(),written);
if(written == 0){
printf("Writting not performed.");
}
close(fd[1]);
exit(0);
}
}
close(fd[1]);
int status = 0;
nbytes = read(fd[0],&readSum,sizeof readSum);
printf("Parent reads %d bytes\n",nbytes);
if(nbytes > 0){
for(child =0;child<P;child++){
total += readSum[child];
printf("Partial sum in father: %d\n",readSum[child]);
}
}
else{
printf("Failed to read.");
}
}
You are ignoring the wisdom of the sage Rolling Stones and not accepting that you can't always get what you want but sometimes you get what you need.
(1) There is no guarantee all your children have run and written to the pipe before the parent tries to read.
(2) There is no guarantee even if (1) did take place that your read would return all 10 integers in one read. read can (and often will) return less than you ask for.
One way to cover this is to have your parent wait on its children so you know they completed and then to read in a loop until you read everything you need.
http://linux.die.net/man/2/read
Read returns available data, not the requested amount, use cycle and check return value on each iteration.
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.