I have a problem with this exercise:
Write a program in C that creates a child and between father and child, there will be two-way communication using pipes. His father would read a file (whose name will be given the user) and will send letters to the child.
The child will count the number of words starting from with 'a'; if the number is, whether X of words, X, starting with 'a' is greater than 5, then the child will create his own child (grandchild). The grandchild of the establishment will send, by whatever means you deem possible, the grandfather * value of X number and exit. [By inference: if the number X is less than 6, then the child simply exits, and the 'would be grandparent' also exits.]
Note: Grandfather = initial process of the father, that father of his father's grandchild
Here is what I have done till now; please help me...
#include <stdio.h>
#include<string.h>
#include <stdlib.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd1[2], fd2[2], pid, status,sum=0, i;
char gram[100], x;
char buff[100];
char rev[1000];
FILE *fp;
if (pipe(fd1) == -1) { /* Create a pipe */
perror("pipe");
exit(1);
}
pid = fork();
switch (pid)
{
case -1:
perror ("Fork error\n");
exit(99); // in case of error
case 0:
close(fd1[1]);//Close the writing side
rev[1000]= read(fd1[0], buff, 1000); /* Read from the pipe */
/*for (i=0; i< 1000 ; i++)
{
rev[i] = read(fd1[0], buff, sizeof(buff));
}*/
while( rev[i] != '\0')
{
if (buff[i] == 'a' )
{ sum++;
i++;
}
if (rev[i] == "")
{
if (rev[i+1]) //elenxei to epomeno tou kenou
{
sum++;
i++;
}
}
i++;
}
printf("%d", sum);
exit(0);
default:
printf("dwse arxeio\n");
scanf("%s", gram);
close(fd1[0]);//Close the reading side
fp = fopen (gram,"r");
getc(fp);
fclose(fp);
write(fd1[1], buff, sizeof(buff)+1);
close(fd1[1]);//Close the writing side
wait(&status); // waits till the child process ends
}
}
You probably want to look at popen.
It starts a child process and returns a FILE * you can use to read/write to the childs stdin/stdout.
Let's call the three processes GP (for grandparent), PP (for parent process), and GC (for grandchild). In outline, I think what you need to do is:
GP creates two pipes (4 file descriptors) designated RP (read pipe) and WP (write pipe). That decribes how GP will use them; PP and GC will write on RP and read on WP.
GP forks, creating PP.
GP will close the write end of RP and the read end of WP.
GP will open the file.
GP will write whatever subset of the file is appropriate to PP via WP.
GP will close WP when there is no more data to transfer.
GP should also close the file it opened.
GP will then read from RP, probably stashing the data until it gets EOF.
If it gets any information back, GP will echo that information to standard output.
GP can then terminate.
Meanwhile, step 2 above created PP, who has to do some work:
PP needs to close the read end of RP and the write end of WP.
PP sits in a loop, reading data from WP, counting whatever is relevant.
When PP gets an EOF on WP, it can decide what it needs to do.
PP can now close the read end of WP.
If its counter X is bigger than 5, then (for reasons that only make sense in homework) it will fork to create GC; it can then exit.
If its counter X does not reach the threshold, then as far as the specification goes, it can terminate immediately. For debugging, you'll probably have it print something to stdout about what it did and why.
Now you have GP and GC around; remember that GC is an almost exact copy of PP, and (in particular) GC knows the value X just as well as PP did. So, GC does some work, too:
GC formats X as a string (probably - you could do binary data transfer if you prefer, but that passes the formatting buck to GP, that's all).
GC writes the formatted X on the write end of RP.
GC closes the write end of RP.
GC exits.
GC's step 3 ensures that GP wakes up from its step 8.
Judged as an industrial design, there is no point to creating GC; PP could perfectly well do what GC does. As a homework exercise, it passes muster. The key insight is in the comments above:
The crux of the matter is how to communicate from grandchild to grandfather. The good news is, the grandchild inherits the pipes open in the child.
The other key steps are closing the unused ends of the pipes and closing the pipes when there's nothing more to write. Without those closes, processes are apt to get stuck in deadlock. If GP fails to close the write end of RP, for example, it will never get EOF when reading from RP, because there is a process that could still write to RP - and that process is GP!
rev[1000]= read(fd1[0], buff, 1000); /* Read from the pipe */
What are you trying to accomplish with the lvalue here?
First, rev is declared as having 1000 elements, so rev[1000] is a buffer overrun...
Second, I suggest you look at the "Return value" section of the manual page for read(). It returns the number of bytes received (which may be smaller than the third parameter you specified), or 0 on end-of-file, or negative on failure. It will fill in the contents of buff with the actual data. I am not sure from your code what you are expecting the system call to act like but it doesn't seem to me like you are using it correctly.
You want to be doing something like this:
int r;
r = read(fd1[0], buff, sizeof(buff));
if (r < 0) { /* TODO: Handle error */ }
else if (r == 0) { /* TODO: Handle EOF */ }
else { /* TODO: Handle the fact that buff now contains 'r' bytes */ }
Related
So my program needs to pipe multiple processes and read the number of bytes each process output has.
The way I implemented it, in a for loop, we have two children:
Child 1: dups output and executes the process
Child 2: reads the output and writes it for the next input
Currently, child 1 executes the process and the child 2 reads its output, but it doesn't seem to write it in the right place because in the second loop iteration it prints the output to the screen and blocks.
for (int i = 0; i < processes; i++) {
int result = socketpair(PF_LOCAL, SOCK_STREAM, 0, apipe[i]);
if (result == -1) {
error_and_exit();
}
int pid;
int pid2;
pid = fork_or_die();
// child one points to STDOUT
if (pid == FORK_CHILD) {
if (dup2(apipe[i][1], STDOUT_FILENO) == -1)
error_and_exit();
if (close(apipe[i][1]) == -1)
error_and_exit();
if (close(apipe[i][0]) == -1)
error_and_exit();
if (execlp("/bin/sh", "sh", "-c", tabCommande[i], (char *)NULL) == -1)
error_and_exit();
}
pid2 = fork_or_die();
//CHILD 2 reads the output and writes if for the next command to use
if(pid2 == FORK_CHILD){
FILE *fp;
fp = fopen("count", "a");
close(apipe[i][1]);
int count=0;
char str[4096];
count = read(apipe[i][0], str, sizeof(str)+1);
close(apipe[i][0]);
write(STDIN_FILENO, str, count);
fprintf(fp, "%d : %d \n ", i, count);
fclose(fp);
}
}
Your second child does “write(STDIN_FILENO, …); that’s not a conventional way of using standard input.
If standard input is a terminal, then the device is usually opened for reading and writing and the three standard I/O channels are created using dup() or dup2(). Thus you can read from the outputs and write to the input — but only if the streams are connected to a login terminal (window). If the input is a pipe, you can't successfully write to it, nor can you read from the output if it is a pipe. (Similarly if the input is redirected from a file or the output is redirected to a file.) This terminal setup is done by the process that creates the terminal window. It is background information explaining why writing to standard input appears on the terminal.
Anyway, that's what you're doing. You are writing to the terminal via standard input. Your minimum necessary change is to replace STDIN_FILENO with STDOUT_FILENO.
You are also going to need a loop around the reading and writing code. In general, processes generate lots of output in small chunks. The close on the input pipe will be outside the loop, of course, not between the read() and write() operations. You should check that the write() operations write all the data to the output.
You should also have the second child exit after it closes the output file. In this code, I'd probably open the file after the counting loop (or what will become the counting loop), but that's mostly a stylistic change, keeping the scope of variables to a minimum.
You will probably eventually need to handle signals like SIGPIPE (or ignore it so that the output functions return errors when the pipe is closed early). However, that's a refinement for when you have the basic code working.
Bug: you have:
count = read(apipe[i][0], str, sizeof(str)+1);
This is a request to the o/s to give you a buffer overflow — you ask it to write more data into str than str can hold. Remove the +1!
Minor note: you don’t need to check the return value from execlp() or any of that family of functions. If the call succeeds, it doesn’t return; if it returns, it failed. Your code is correct to exit after the call to execlp(), though; that's good.
You said:
I replaced STDIN_FILENO to STDOUT_FILENO in the second child but it doesn't seem to solve the issue. The output is still shown in the terminal and there's a pipe blockage after.
That observation may well be correct, but it isn't something that can be resolved by studying this code alone. The change to write to an output stream is necessary — and in the absence of any alternative information, writing to STDOUT_FILENO is better than writing to STDIN_FILENO.
That is a necessary change, but it is probably not a sufficient change. There are other changes needed too.
Did you set up the inputs and outputs for the pair of children this code creates correctly? It is very hard to know from the code shown — but given that it is not working as you intended, it's a reasonable inference that you did not get all the plumbing correct. You need to draw a diagram of how the processes are intended to operate in the larger context. At a minimum, you need to know where the standard input for each process comes from, and where its standard input goes. Sometimes, you need to worry about standard error too — most likely though, in this case, you can quietly ignore it.
This is what I think your code could look like — though the comments in it describe numerous possible variants.
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
/* The code needs these declarations and definition to compile */
extern _Noreturn void error_and_exit(void);
extern pid_t fork_or_die(void);
extern void unknown_function(void);
static ssize_t copy_bytes(int fd1, int fd2);
#define FORK_CHILD 0
int processes;
int apipe[20][2];
char *tabCommande[21];
void unknown_function(void)
{
for (int i = 0; i < processes; i++)
{
int result = socketpair(PF_LOCAL, SOCK_STREAM, 0, apipe[i]);
if (result == -1)
error_and_exit();
int pid1 = fork_or_die();
// child one points to STDOUT
if (pid1 == FORK_CHILD)
{
if (dup2(apipe[i][1], STDOUT_FILENO) == -1)
error_and_exit();
if (close(apipe[i][1]) == -1)
error_and_exit();
if (close(apipe[i][0]) == -1)
error_and_exit();
execlp("/bin/sh", "sh", "-c", tabCommande[i], (char *)NULL);
error_and_exit();
}
//CHILD 2 reads the output and writes if for the next command to use
int pid2 = fork_or_die();
if (pid2 == FORK_CHILD)
{
close(apipe[i][1]);
ssize_t count = copy_bytes(apipe[i][0], STDOUT_FILENO);
FILE *fp = fopen("count", "a");
if (fp == NULL)
error_and_exit();
/*
** Using %zd for ssize_t is a reasonable guess at a format to
** print ssize_t - but it is a guess. Alternatively, change the
** type of count to long long and use %lld. There isn't a
** documented, official (fully standardized by POSIX) conversion
** specifier for ssize_t AFAIK.
*/
fprintf(fp, "%d : %zd\n ", i, count);
fclose(fp);
exit(EXIT_SUCCESS);
}
/*
** This is crucial - the parent has all the pipes open, and the
** child processes won't get EOF until the parent closes the
** write ends of the pipes, and they won't get EOF on the inputs
** until the parent closes the read ends of the pipe.
**
** It could be avoided if the first child creates the pipe or
** socketpair and then creates the second child as a grandchild
** of the main process. That also alters the process structure
** and reduces the number of processes that the original parent
** process has to wait for. If the first child creates the
** pipe, then the apipe array of arrays becomes unnecessary;
** you can have a simple int apipe[2]; array that's local to the
** two processes. However, you may need the array of arrays so
** that you can chain the outputs of one process (pair of
** processes) to the input of the next.
*/
close(apipe[i][0]);
close(apipe[i][1]);
}
}
static ssize_t copy_bytes(int fd1, int fd2)
{
ssize_t tbytes = 0;
ssize_t rbytes;
char buffer[4096];
while ((rbytes = read(fd1, buffer, sizeof(buffer))) > 0)
{
ssize_t wbytes = write(fd2, buffer, rbytes);
if (wbytes != rbytes)
{
/*
** There are many possible ways to deal with this. If
** wbytes is negative, then the write failed, presumably
** irrecoverably. The code could break the loop, reporting
** how many bytes were written successfully to the output.
** If wbytes is zero (pretty improbable), it isn't clear
** what happened. If wbytes is positive, then you could add
** the current value to tbytes and try to write the rest in
** a loop until everything has been written or an error
** occurs. You pays your money and takes your pick.
*/
error_and_exit();
}
tbytes += wbytes;
}
if (tbytes == 0 && rbytes < 0)
tbytes = rbytes;
return tbytes;
}
You could add #include <signal.h> and signal(SIGPIPE, SIG_IGN); to the code in the second child.
Blow is an implementation of Sieve of Eratosthenes in C
#include "kernel/stat.h"
#include "kernel/types.h"
#include "user/user.h"
void cull(int p) {
int n;
while (read(0, &n, sizeof(n))) {
// n is not prime
if (n % p != 0) {
write(1, &n, sizeof(n));
}
}
}
void redirect(int k, int pd[]) {
close(k);
dup(pd[k]);
close(pd[0]);
close(pd[1]);
}
void right() {
int pd[2], p;
// read p
if (read(0, &p, sizeof(p))) {
printf("prime %d\n", p);
pipe(pd);
if (fork()) {
// parent
redirect(0, pd);
right();
} else {
redirect(1, pd);
cull(p);
}
}
}
int main(int argc, char *argv[]) {
int pd[2];
pipe(pd);
if (fork()) {
redirect(0, pd);
right();
} else {
redirect(1, pd);
for (int i = 2; i < 36; i++) {
write(1, &i, sizeof(i));
}
}
exit(0);
I am not quite following the logic here:
1). Why does redirect need to close pd1?
2). cull() is reading from the file descriptor 0, but in right() the child process will close 0. Why doesn't this cause any problem?
3). why it is necessary to use redirect here when we only want read from 0 and write to 1?
Update:
1). I found this implementation in the following blog post:
https://abcdlsj.github.io/post/mit-6.828-lab1xv6-and-unix-utilities/
2). I think the idea is borrowed from the inventor of this algorithm
3). I think the reason that the header is so is because it is implemented in a toy operating system for educational purpose.
The code is an adaptation of the paper Coroutine prime number sieve by M. Douglas McIlroy to the Xv6 operating system, a re-implementation of Version 6 Unix used for teaching. The technique is from 1968 as an experiment in co-routines via pipes. The paper explains the algorithm and rationale.
The program implements the sieve of Eratosthenes as a kind of production line, in which each worker culls multiples of one prime from a passing stream of integers, and new workers are recruited as primes are discovered.
When Unix came to be, my fascination with coroutines led me to badger its author, KenThompson, to allow writes in one process to go not only to devices but also to matching reads in another process.
...the coroutine sieve has been a standard demo for languages or systems that support interprocess communication. Implementations using Unix processes typically place the three coroutines—source, cull and sink—in distinct executable files.The fact that the whole program can be written as a single source file, in a language that supports neither concurrency nor IPC, is a tribute not only to Unix’s pipes, but also to its clean separation of program initiation into fork for duplicating address spaces and exec for initializing them.
I believe using stdin and stdout is an artifact of its origins in the early days of Unix when piping stdin and stdout between processes was first introduced. It makes a lot more sense in shell.
#!/bin/bash
source() {
seq 2 1000000
}
cull() {
while true
do
read n
(($n % $1 != 0)) && echo $n
done
}
sink() {
read p
echo $p
cull $p | sink &
}
source | sink
In C, as we'll see, it's simpler to skip the redirection and pass around pipes.
First, what's going on?
redirect is redirecting stdin and stdout to a pipe. 0 is stdin and 1 is stdout. This can be made more clear by using STDIN_FILENO and STDOUT_FILENO.
main makes a pipe.
main forks.
The child redirects stdout to the pipe.
The child streams numbers to the pipe via stdout.
The first number must be 2.
main redirects stdin to the pipe.
main calls right.
right reads the first prime, 2, from stdin which is a pipe to the number stream.
[number stream] ->(2) [right]
After the initial condition, a switcheroo happens inside right.
right makes a pipe.
right forks.
The child redirects its stdout to the pipe.
The child's stdin is still reading from the number stream.
The child calls cull.
cull reads from stdin (the number stream) and writes to stdout (right).
right redirects its stdin to the pipe, reading from cull.
right recurses.
[number stream] ->(n) [cull] ->(p) [right]
After the first call right is reading primes from cull and writing them to the real stdout. cull reads candidates from the number stream and writes primes to right.
When the number stream loop ends the process ends and closes its pipe to cull. Once all the numbers have been read from the pipe, cull to reads EOF ending its loop and its process, closing its pipe to right. right reads EOF and returns back to main which exits.
To explain redirect we need to understand redirection in C.
First, a simple one-way pipe.
int pd[2];
pipe(pd);
//parent
if (fork()) {
// Parent must close the input side else reading from pd[0] will
// continue to try to read from pd[1] even after the child closes
// their pipe.
close(pd[1]);
int p;
while( read(pd[0], &p, sizeof(p)) ) {
printf("p = %d\n", p);
}
fprintf(stderr, "parent done reading\n");
}
// child
else {
// Not strictly necessary, but the child will not be reading.
close(pd[0]);
for (int i = 2; i < 10; i++) {
write(pd[1], &i, sizeof(i));
}
// Tell the parent we're done writing to the pipe.
// The parent will get EOF on its next read. If the child
// does not close the pipe, the parent will hang waiting to read.
close(pd[1]);
fprintf(stderr, "child done writing\n");
// Pipes are closed automatically when a process exits, but
// let's simulate the child not immediately exiting to
// illustrate why it's important to explicitly close pipes.
sleep(1);
}
The parent and child share a pipe. The parent reads from one end, the child writes to the other. The child closes their write end when they're done so the parent doesn't hang trying to read. The parent closes their write end immediately so their pipe doesn't try to read from it.
Instead of passing the pipe around, redirect is redirecting the parent's half to stdin and the child's half to stdout. Let's do that in our simple example using dup2. dup2 duplicates a descriptor to another, first closing the target.
int pd[2];
pipe(pd);
if (fork()) {
// Redirect pd[0] to stdin.
dup2(pd[0], STDIN_FILENO);
// Parent still has to close its input side.
close(pd[1]);
int p;
while( read(STDIN_FILENO, &p, sizeof(p)) ) {
printf("p = %d\n", p);
}
fprintf(stderr, "parent done reading\n");
} else {
// Redirect pd[1] to stdout.
dup2(pd[1], STDOUT_FILENO);
// Close the original pd[1] so the parent doesn't try to read from it.
close(pd[1]);
for (int i = 2; i < 10; i++) {
write(STDOUT_FILENO, &i, sizeof(i));
}
// Tell the parent we're done writing.
close(STDOUT_FILENO);
fprintf(stderr, "child done writing\n");
sleep(1);
}
The final twist is dup. dup duplicates pd[k] to the lowest numbered descriptor currently not in use by the process. redirect(0, pd) closes descriptor 0 and then copies pd[0] to the lowest numbered descriptor: 0.
redirect(1, pd) closes descriptor 1 and then copies pd[1] to what it hopes is the lowest numbered descriptor: 1. If something else closed 0, redirect(1, pd) will copy pd[1] to descriptor 0 and the code will not work. This can be avoided by using dup2 which makes it explicit which file descriptor you're copying to.
// close(k) and dup(pd[k]) to k safely and explicitly.
dup2(pd[k], k);
redirect can be rewritten as:
void redirect(int k, int pd[]) {
dup2(pd[k], k);
close(pd[0]);
close(pd[1]);
}
Note that is all for a one-way pipe. cull uses bi-directional pipes, but the idea is the same.
By redirecting its pipe to stdin and stdout the program can use the pipe without having to pass the pipe around. This lets right read the first prime from the number generator and then let cull read the rest. It could also be done explicitly.
With some simpler examples in place, now we can answer the questions.
1). Why does redirect need to close pd[1]?
The parent must close the input side of its pipe, even after it's been duplicated or closed by the child, else the pipe will remain open and the parent will hang trying to read from it.
cull() is reading from the file descriptor 0, but in right() the child process will close 0. Why doesn't this cause any problem?
right closes its 0 and then copies pd[0] to 0. cull does not close 0, it closes pd[0]. cull reads from the original 0 which is the number generator.
Why it is necessary to use redirect here when we only want read from 0 and write to 1?
Because we need 0 and 1 to be different things at different times. We don't really want to read from 0 and write to 1. We want to read and write from pipes which happen to be attached to 0 and 1. The program is redirecting its pipes to 0 and 1 to demonstrate how Unix pipes and redirection works internally.
It took a dabbler like me some time to figure out how this program worked, it would have been a lot easier if I'd read the original paper and seen the shell script version first.
It can be rewritten to use explicit pipes. This avoids action at a distance, is easier to understand, and still demonstrates pipes and co-routines, but it no longer illustrates redirection.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void cull(int p, int read_d, int write_d) {
int n;
while (read(read_d, &n, sizeof(n))) {
if (n % p != 0) {
write(write_d, &n, sizeof(n));
}
}
}
void right(int read_d) {
int p;
if (read(read_d, &p, sizeof(p))) {
printf("prime %d\n", p);
int cull_pipe[2];
pipe(cull_pipe);
if (fork()) {
// right() reads from cull().
close(cull_pipe[1]);
right(cull_pipe[0]);
} else {
// cull() reads from the number generator and writes to right().
close(cull_pipe[0]);
cull(p, read_d, cull_pipe[1]);
close(cull_pipe[1]);
}
}
}
int main(int argc, char *argv[]) {
int pd[2];
pipe(pd);
if (fork()) {
// The first call to right() reads from the number generator.
close(pd[1]);
right(pd[0]);
} else {
close(pd[0]);
for (int i = 2; i < 6; i++) {
write(pd[1], &i, sizeof(i));
}
close(pd[1]);
}
exit(0);
}
Other notes:
The headers can be replaced with standard headers.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
I have a project called "consumers and producers".
In it, my main program creates given numbers of consumer and producer
child processes, as specified via command-line arguments (./main number_of_producents number_of_consumers).
Every producer generates a random number of random printable characters, writes these both to a process-specific file and to a pipe designated by the main process. Every consumer reads all characters from the pipe specified to it by the main process, and writes them to its own process-specific file. After the main program finishes, the combined number of characters in the producers' files should be the same as the combined number of characters in the consumers' files.
I have a problem with how to inform consumers about the end of the products. I've tried to do this by having the producers each write a null character after their data, but it doesn't solve my problem, because then when I create more producers than consumers not all products will be read by consumers (each finishes its work when it reads a null character). When the number of consumers is bigger than the number of producers, on the other hand, some of the consumers never do read a null character, and therefore never finish.
I think that I need to a different mechanism to signal the end of each producer's data, but I don't have any other idea how to inform consumers about the end of pipe. I've read something about fcntl, but I don't know how to use it in my code.
Here is the code:
MAIN:
int n;
int i,w,x;
int pipeT[2];
if(pipe(potok) != 0) exit(EXIT_FAILURE);
printf("[MAIN] Creating %d producers!\n", atoi(argv[1]));
for(i = 0; i < atoi(argv[1]); i++) {
switch(fork()) {
case -1:
exit(3);
break;
case 0:
if((n = dup(pipeT[1])) == -1) exit(5);
close(pipeT[0]);
if((execl("./producer","producer", &n, NULL)) == -1) exit(4);
break;
default:
break;
}
}
printf("[MAIN] Creating %d consumers!\n", atoi(argv[2]));
for(i = 0; i < atoi(argv[2]); i++) {
switch(fork()) {
case -1:
exit(3);
break;
case 0: //Proces potomny
if((n = dup(pipeT[0])) == -1) exit(5);
close(pipeT[1]);
if((execl("./consumer","consumer", &n, NULL)) == -1) exit(4);
break;
default:
break;
}
}
close(pipeT[0]);
close(pipeT[1]);
for (i = 0; i < atoi(argv[1]) + atoi(argv[2]); i++) {
w = wait(&x);
if (w == -1) exit(6);
}
PRODUCER
int main(int argc, char *argv[]) {
srand(getpid());
int k,t,n;
n = *argv[1];
char sign;
char nameOfFile[20];
sprintf(nameOfFile, "P/p_%d", getpid());
FILE* f = fopen(nameOfFile, "w");
if(f == NULL) exit (1);
int i;
int numberOfSigns = rand()%100;
for(i = 0; i < numberOfSigns; i++) {
sign = rand()%94 + 32;
if (write(n, &sign, sizeof(char)) == -1) exit(EXIT_FAILURE);
fprintf(f, "%c", sign);
}
sign = 0;
write(n, &sign, sizeof(char));
fclose(f);
return 0;
}
CONSUMER:
int main(int argc, char *argv[]) {
char sign;
char nameOfFile[20];
int n = *argv[1];
sprintf(nameOfFile, "K/k_%d", getpid());
FILE* f = fopen(nameOfFile, "w");
if(f == NULL) exit (1);
do {
if ((read(n, &sign, sizeof(char))) == -1) exit(EXIT_FAILURE);
if(sign != 0) fprintf(f, "%c" , sign);
} while(sign != 0);
fclose(f);
return 0;
}
How can I signal the end of the data to consumers such that the consumers among them read all the data and all of them recognize the end?
You're making this harder than it needs to be.
The natural way for the consumers to recognize the end of the data is as end-of-file on the pipe. They will each see this in the form of read() returning 0, which will happen after all copies of the write end of the pipe are closed and no more data are available to be read from it. Since the consumers and producers all share the same pipe, this will also provide some natural balancing of the data among the consumers.
Note well, however, that I said all copies of the write end of the pipe must be closed. This includes the copy in the main program, as well as the copies in each producer and consumer. It turns out that you do close all these, but you are relying on termination of the producers for closure of one copy in each of them. That's a bit unclean, and it's certainly situation-specific. It would be better style for the producers to explicitly close these when they are finished with them.
Additionally, your code exhibits some additional problems and oddities, among them:
Your uses of dup() are pointless. Since you're passing the file descriptor numbers to the children anyway, you could just as well let them use the original pipe-end file descriptors. The usual use of dup() (or better: dup2()) in circumstances like yours is to make one or more specific file descriptors be copies of the ones you are duping. Those of the standard streams, for example. And note that if you do use the standard streams then they have well-known file descriptor numbers, so you don't need to tell the children which FDs to use.
The arguments to execl() and the elements of every process's argv are pointers to strings, so type char *, terminated by a NULL pointer of that type (which is not counted in argc). You are specifying some pointers of type int * in your execl() calls, and your child processes are assuming that they receive pointers of that type. It may be that this happens to work for you, but it is incorrect, in the sense that the behaviors of parent and child are both undefined as a result.
You should probably think harder about how you're trying to solve this. For example, what if one of the producers stops much earlier than the others, should that stop one of the consumers? If not, then the producers shouldn't send a code to the consumers, only the main program should care that they've terminated.
The way I would solve this, is that the consumers should be terminated by the main program, not by the producers. The easiest way to do that is by closing the sending end of the pipe. The reading end would then get an end-of-file, i.e., the size returned by read() would be 0.
In your code, this would probably mean
1) don't send 0 from the producers
2) make the main program wait for all the producers to terminate first, and close the pipe when they've all terminated (before waiting for the consumers)
3) make the consumers exit when read() returns 0
(Also note that there are other problems with your code. For example, you can't pass pointers to integer through execl, only strings.)
What I know about PIPE is that it is used for a unidirectional communication and it helps to communicate between two related process. I have got the below PIPE programming code example from a book. I am trying to understand the code using printf and printing out all the points after every line of the code. But I am not understanding how the program is running after each and every line. My code is below:
PIPE:
//using PIPE to communicate with a child process
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
/* Write COUNT copies of MESSAGE to filename, pausing for a second
between each. */
void writer (const char* message, int count, FILE* filename)
{
for (; count > 0 ; -- count) {
printf("point 13\n");
/* Write the message to the filename, and send it off immediately.*/
fprintf (filename, "%s\n", message);
printf("point 14\n");
fflush (filename);
printf("point 15\n");
/* Snooze a while. */
sleep (1);
}
}
/* Read random strings from the filename as long as possible.
*/
void reader (FILE* filename)
{
char buffer[1024];
/* Read until we hit the end of the filename. fgets reads until
either a newline or the end-of-FILE. */
printf("point 16\n");
while (!feof (filename) && !ferror (filename) && fgets (buffer, sizeof (buffer), filename) != NULL)
fputs (buffer, stdout);
printf("point 17\n");
}
int main ()
{
int fds[2];
pid_t pid;
printf("point 1\n");
/* Create a pipe. FILE descriptors for the two ends of the pipe are
placed in fds. */
pipe (fds);
printf("point 2\n");
/* Fork a child process. */
pid = fork ();
printf("point 3\n");
if (pid == (pid_t) 0)
{
FILE* filename;
printf("point 4\n");
/* This is the child process. Close our copy of the write end of
the FILE descriptor. */
close (fds[1]);
printf("point 5\n");
/* Convert the read FILE descriptor to a FILE object, and read
from it. */
filename = fdopen (fds[0], "r");
printf("point 6\n");
reader (filename);
printf("point 7\n");
close (fds[0]);
printf("point 8\n");
}
else
{
/* This is the parent process. */
FILE* filename;
/* Close our copy of the read end of the FILE descriptor. */
close (fds[0]);
printf("point 9\n");
/* Convert the write FILE descriptor to a FILE object, and write
to it. */
filename = fdopen (fds[1], "w");
printf("point 10\n");
writer ("I want to learn c programming!", 5, filename);
printf("point 11\n");
close (fds[1]);
printf("point 12\n");
}
return 0;
}
I really need to understand the code. If I run my code then I am getting output like below in the Linux terminal but I am not sure why after the point 3,the point 9 is coming. Again after point 9 why point 3,4,5,10. Details explanation will help me.
point 1
point 2
point 3
point 9
point 3
point 4
point 5
point 10
point 13
point 14
point 15
point 6
point 16
I want to learn c programming!
point 13
point 14
point 15
I want to learn c programming!
point 13
point 14
point 15
I want to learn c programming!
point 13
point 14
point 15
I want to learn c programming!
point 13
point 14
point 15
I want to learn c programming!
point 11
point 12
point 17
point 7
point 8
pipe(int[]) function returns an array of size two integers. You need to understand the basic concepts here. A pipe can be used for unidirectional communication same as a water pipe. One thing you need to understand here is pipe is internally a file. So, as we use file descriptors for reading and writing content from/into a file similarly we need descriptors for reading/writing from/to a pipe. Now in your program when pipe(fds) is executed, a pipe is created and two descriptors are created for reading and writing into this pipe. These descriptors are fds[0](read end) and fds[1](write end).
Now, you need to understand about fork() here, this function created a new process(child process) by duplicating the existing process. Immediately after calling fork(), in child if is executed as fork returns 0 in child process and in parent the if condition fails as fork returns pid of child in parent process, hence else is executed. Rest you can observe your code.
Inside if you are closing fds[1], this will now enable your child process to only have read file descriptor for pipe and you are closing fds[0] in parent, this will enable your parent to have only write descriptor for pipe. Now, your child is able to only read from pipe and parent is only able to write from the pipe and you can observe the communication is from parent to child(parent is writing, child is reading). If you want a reverse communication than you need to create a new pipe. So, one pipe can be used for communication in one direction.
Now, you can understand rest of the program simply by understanding how a parent and child process will execute after fork. Now whichever process gets CPU time that process is executed, in this way you get an interleaved execution of child and parent process leaving you with the above output.
I am trying to implement a client-server type of communication system using the poll function in C. The flow is as follows:
Main program forks a sub-process
Child process calls the exec function to execute some_binary
Parent and child send messages to each other alternately, each message that is sent depends on the last message received.
I tried to implement this using poll, but ran into problems because the child process buffers its output, causing my poll calls to timeout. Here's my code:
int main() {
char *buffer = (char *) malloc(1000);
int n;
pid_t pid; /* pid of child process */
int rpipe[2]; /* pipe used to read from child process */
int wpipe[2]; /* pipe used to write to child process */
pipe(rpipe);
pipe(wpipe);
pid = fork();
if (pid == (pid_t) 0)
{
/* child */
dup2(wpipe[0], STDIN_FILENO);
dup2(rpipe[1], STDOUT_FILENO);
close(wpipe[0]); close(rpipe[0]);
close(wpipe[1]); close(rpipe[1]);
if (execl("./server", "./server", (char *) NULL) == -1)
{
fprintf(stderr, "exec failed\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
else
{
/* parent */
/* close the other ends */
close(wpipe[0]);
close(rpipe[1]);
/*
poll to check if write is good to go
This poll succeeds, write goes through
*/
struct pollfd pfds[1];
pfds[0].fd = wpipe[1];
pfds[0].events = POLLIN | POLLOUT;
int pres = poll(pfds, (nfds_t) 1, 1000);
if (pres > 0)
{
if (pfds[0].revents & POLLOUT)
{
printf("Writing data...\n");
write(wpipe[1], "hello\n", 6);
}
}
/*
poll to check if there's something to read.
This poll times out because the child buffers its stdout stream.
*/
pfds[0].fd = rpipe[0];
pfds[0].events = POLLIN | POLLOUT;
pres = poll(pfds, (nfds_t) 1, 1000);
if (pres > 0)
{
if (pfds[0].revents & POLLIN)
{
printf("Reading data...\n");
int n = read(rpipe[0], buffer, 1000);
buffer[n] = '\0';
printf("child says:\n%s\n", buffer);
}
}
kill(pid, SIGTERM);
return EXIT_SUCCESS;
}
}
The server code is simply:
int main() {
char *buffer = (char *) malloc(1000);
while (scanf("%s", buffer) != EOF)
{
printf("I received %s\n", buffer);
}
return 0;
}
How do I prevent poll calls from timing out because of buffering?
EDIT:
I would like the program to work even when the execed binary is external, i.e., I have no control over the code - like a unix command, e.g., cat or ls.
There seem to be two problems in your code. "stdout" is by default buffered,
so the server should flush it explicitly:
printf("I received %s\n", buffer);
fflush(stdout);
And the main program should not register for POLLOUT when trying to read
(but you may want register for POLLERR):
pfds[0].fd = rpipe[0];
pfds[0].events = POLLIN | POLLERR;
With these modifications you get the expected output:
$ ./main
Writing data...
Reading data...
child says:
I received hello
Generally, you should also check the return value of poll(), and repeat the call if
necessary (e.g. in the case of an interrupted system call or timeout).
You need, as I answered in a related answer to a previous question by you, to implement an event loop; as it name implies, it is looping, so you should code in the parent process:
while (1) { // simplistic event loop!
int status=0;
if (waitpid(pid, &status, WNOHANG) == pid)
{ // clean up, child process has ended
handle_process_end(status);
break;
};
struct pollpfd pfd[2];
memset (&pfd, 0, sizeof(pfd)); // probably useless but dont harm
pfd[0].fd = rpipe[0];
pfd[0].events = POLL_IN;
pfd[1].fd = wpipe[1];
pfd[0].event = POLL_OUT;
#define DELAY 5000 /* 5 seconds */
if (poll(pfd, 2, DELAY)>0) {
if (pfd[0].revents & POLL_IN) {
/* read something from rpipe[0]; detect end of file;
you probably need to do some buffering, because you may
e.g. read some partial line chunk written by the child, and
you could only handle full lines. */
};
if (pfd[1].revents & POLL_OUT) {
/* write something on wpipe[1] */
};
}
fflush(NULL);
} /* end while(1) */
you cannot predict in which order the pipes are readable or writable, and this can happen many times. Of course, a lot of buffering (in the parent process) is involved, I leave the details to you.... You have no influence on the buffering in the child process (some programs detect that their output is or not a terminal with isatty).
What an event polling loop like above gives you is to avoid the deadlock situation where the child process is blocked because its stdout pipe is full, while the parent is blocked writing (to the child's stdin pipe) because the pipe is full: with an event loop, you read as soon as some data is polled readable on the input pipe (i.e. the stdout of the child process), and you write some data as soon as the output pipe is polled writable (i.e. is not full). You cannot predict in advance in which order these events "output of child is readable by parent" and "input of child is writable by parent" happen.
I recommend reading Advanced Linux Programming which has several chapters explaining these issues!
BTW my simplistic event loop is a bit wrong: if the child process terminated and some data remains in its stdout pipe, its reading is not done. You could move the waitpid test after the poll
Also, don't expect that a single write (from the child process) into a pipe would trigger one single read in the parent process. In other words, there is no notion of message length. However, POSIX knows about PIPE_MAX .... See its write documentation. Probably your buffer passed to read and write should be of PIPE_MAX size.
I repeat: you need to call poll inside your event loop and very probably poll will be called several times (because your loop will be repeated many times!), and will report readable or writable pipe ends in an unpredictable (and non-reproducible) order! A first run of your program could report "rpipe[0] readable", you read 324 bytes from it, you repeat the event loop, poll says you "wpipe[1] writable", you can write 10 bytes to it, you repeat the event loop, poll tells that "rpipe[0] readable", you read 110 bytes from it, you repeat the event loop, poll tells again "rpipe[0] readable", you read 4096 bytes from it, etc etc etc... A second run of the same program in the same environment would give different events, like: poll says that "wpipe[1] writable", you write 1000 bytes to it, you repeat the loop, poll says that "rpipe[0] readable, etc....
NB: your issue is not the buffering in the child ("client") program, which we assume you cannot change. So what matters is not the buffered data in it, but the genuine input and output (that is the only thing your parent process can observe; internal child buffering is irrelevant for the parent), i.e. the data that your child program has been able to really read(2) and write(2). And if going thru a pipe(7), such data will become poll(2)-able in the parent process (and your parent process can read or write some of it after POLL_IN or POLL_OUT in the updated revents field after poll). BTW, if you did code the child, don't forget to call fflush at appropriate places inside it.