Related
I have a project where I need to create N child processes and to do an election between them. So I've made so far a function to generate the random number (for the election, they have to compare the number they were attributed then the lower number is elected). Then, I'm stuck to make them communicate with pipes to compare the number. My problem is that only my first child process run the end of this for loop I really don't understand why. Here's all of my code so far :
void comparerIdElection(int (*pipes)[2], int numProc)
{
for (int i = 0; i < NBPROC; ++i)
{
if (numProc!=i)
{
close(pipes[i][0]);
printf("je suis %d et je ferme la pipe [%d][0]\n",getpid(),i );
}else printf("je suis %d et je laisse la pipe [%d][0] \n",getpid(),i );
if (numProc +1 != i)
{
close(pipes[i][1]);
printf("je suis %d et je ferme la pipe [%d][1]\n",getpid(),i );
}else printf("je suis %d et je laisse la pipe [%d][1] \n",getpid(),i );
}
//Generate a random number for my child process
int GenererIdElection ()
{
srand(time(NULL)^getpid()<<16);
return rand()%NBPROC;
}
int main(int argc, char const *argv[])
{
int N = NBPROC, idElection, numProc= -1;
int pere = getpid();
/*Creation du nombre de pipe adequat */
int pipes[N][2];
int pids[N];
/* Indique l'etat du processus C = Candidat, B =B battu , E = Elu*/
char etat ;
printf("le nombre de processus que je crée est : %d\n", N);
// Creation d'un nombre de N processus fils par le même pere
for (int i = 0; i < N; ++i)
{
if(getpid()==pere){
fork() ;
numProc ++ ;
}
}
if (getpid() != pere)
{
pids[numProc] = getpid();
etat = 'C';
while(etat == 67)
{
idElection = GenererIdElection();
printf("je suis %d et j'ai eu %d, mon num proc est %d \n",getpid(), idElection, numProc);
comparerIdElection(pipes, numProc);
printf("hop\n");
etat ='B';
}
}
if (getpid()==pere)
{
/*On attend la fin de chaque processus
pour mettre un terme au processu pere*/
for (int i = 0; i < N; ++i)
{
wait(&pids[i]);
}
}
exit(0);
}
And here is what happens once executed (I translated all of printf) :
[terminal][1]
Edit : As I can't post image due to a lack of reputation I copy/pasta :
I'm 1854 i got 1, my numproc is : 0
I'm 1854 i don't close pipe [0][0]
I'm 1854 i close [0][1]
I'm 1854 i close pipe [1][0]
I'm 1854 i don't close pipe [1][1]
I'm 1854 i close pipe [2][0]
I'm 1854 i close [2][1]
hop
I'm 1855 i got 1, my numproc is : 1
I'm 1855 i close pipe [0][0]
I'm 1855 i close [0][1]
I'm 1855 i don't close pipe [1][0]
I'm 1856 i got 1, my numproc is : 2
I'm 1856 i close pipe [0][0]
I'm 1856 i close [0][1]
I'm 1856 i close pipe [1][0]
NB : NBPROC = 3 in this case, but no matter the Number of value it does not work except for the very first child processes.
I seem to have a plausible explanation for the behavior of your program. As I've already pointed out in the comments, there is an obvious flaw in the program that the int pipes[N][2]; weren't set up which would end up in the close() APIs actually just leading to EBADF being returned and probably not achieving what had been intended.
As for your program's behavior, that can be explained by inspecting the actual values being populated in the array int pipes[N][2];. Since the values aren't being set explicitly in the original question, the values are essentially undefined but...
Let's try setting them to something more sane values using:
int pipeVal = INT32_MIN;
if(argc == 2) {
pipeVal = atoi(argv[1]);
}
for(int i = 0; i < N; ++i) {
if(pipeVal != INT32_MIN) {
pipes[i][0] = pipes[i][1] = pipeVal;
}
printf("%d: Pipe pair %d-%d\n",getpid(), pipes[i][0], pipes[i][1]);
}
Passing in 0 yields the output:
61962: Number of process is : 3
61962: Pipe pair 0-0
61962: Pipe pair 0-0
61962: Pipe pair 0-0
61963: Election Id 0, numProc 0
61963: Exiting while-loop
61964: Election Id 0, numProc 1
61964: Exiting while-loop
61965: Election Id 0, numProc 2
61965: Exiting while-loop
This seems to align with what's expected: fork 3 processes which just exit eventually. Let's try with the value of 1
62033: Number of process is : 3
62033: Pipe pair 1-1
62033: Pipe pair 1-1
62033: Pipe pair 1-1
62034: Election Id 0, numProc 0
62035: Election Id 2, numProc 1
62036: Election Id 0, numProc 2
Now this appears to be weird since none of the processes actually exit out of the while loop, yet the main program terminates successfully. To actually check the exit status of the child programs, I added the following code:
if (getpid()==pere)
{
/*On attend la fin de chaque processus
pour mettre un terme au processu pere*/
for (int i = 0; i < N; ++i)
{
errno = 0;
int stat_val;
wait(&stat_val);
#if DBG == 1
printf("%d: WIFEXITED(%d),WEXITSTATUS(%d),WIFSIGNALED(%d),WTERMSIG(%d),WIFSTOPPED(%d),WSTOPSIG(%d),WIFCONTINUED(%d)\n", getpid(), WIFEXITED(stat_val),WEXITSTATUS(stat_val),WIFSIGNALED(stat_val),WTERMSIG(stat_val),WIFSTOPPED(stat_val),WSTOPSIG(stat_val),WIFCONTINUED(stat_val));
#endif
}
}
Here's the output on passing in 1 as the argument:
62215: WIFEXITED(1),WEXITSTATUS(0),WIFSIGNALED(0),WTERMSIG(0),WIFSTOPPED(0),WSTOPSIG(0),WIFCONTINUED(0)
62215: WIFEXITED(1),WEXITSTATUS(0),WIFSIGNALED(0),WTERMSIG(0),WIFSTOPPED(0),WSTOPSIG(0),WIFCONTINUED(0)
62215: WIFEXITED(1),WEXITSTATUS(0),WIFSIGNALED(0),WTERMSIG(0),WIFSTOPPED(0),WSTOPSIG(0),WIFCONTINUED(0)
This output indicates that all the processes exited normally without being stopped abnormally by any signal whatsoever.
The clue is in the actual fds these processes are trying to close. In Linux (at least) a process once created has 3 file descriptors 0 for stdin, 1 for stdout, and 2 for stderr. When 0 is passed as an argument to the program, the processes just end up closing stdin (close(0)) which isn't being used anyway, hence no issues, and they exit comparerIdElection() and the while-loop subsequently. When 1 is passed in as a parameter to the program, the processes end up closing stdout (close(1)), hence whatever is supposed to be written to the output is not seen on the console anymore, yet they still do exit comparerIdElection() and the while-loop subsequently and thus the WIFEXITED(1) from each process.
Now coming to the actual program shared, what are the actual values that end up getting populated is unspecified, but if even one of the N*2 fds in the array is set to 1, the behavior as above (passing in 1) shall be exhibited i.e. even though the printfs won't be written to the console, the processes do actually exit the while-loop and the main parent process is successfully able to wait for the processes. On my PC (and I suspect on yours too), the actual value of at least one of those array elements is always set to 1.
Values of pipe fd pairs on my Mac:
61120: Pipe pair -296602944-32766
61120: Pipe pair 23978080-1
61120: Pipe pair 64-0
Their values on TryItOnline:
25090: Pipe pair 0-0
25090: Pipe pair 68079952-32708
25090: Pipe pair 1-0
Their values on OnlineGDB (need to add the compiler flag -DNBPROC=3):
5612: Pipe pair 1505064448-32611
5612: Pipe pair 1507242440-32611
5612: Pipe pair 1-0
This analysis should hold irrespective of the value of NBPROC since till there's 1 amongst the pipe fds, that process will end up closing its own stdout and exhibit similar behavior.
Here's the complete program with some instrumentation that can be enabled by passing in DBG=1 and some PID-tagging to figure out which process is the log from :
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
void comparerIdElection(int (*pipes)[2], int numProc)
{
for (int i = 0; i < NBPROC; ++i)
{
if (numProc!=i)
{
#if DBG == 1
printf("%d: Before close pipe [%d][0](%d)\n",getpid(),i , pipes[i][0]);
#endif
errno = 0;
close(pipes[i][0]);
#if DBG
printf("%d: closed pipe [%d][0](%d) %s\n",getpid(),i, pipes[i][0], strerror(errno));
#endif
} else {
#if DBG == 1
printf("%d: no action on pipe [%d][0](%d) \n",getpid(),i, pipes[i][0] );
#endif
}
if (numProc +1 != i)
{
#if DBG == 1
printf("%d: Before close pipe [%d][1](%d)\n",getpid(),i, pipes[i][1] );
#endif
errno = 0;
close(pipes[i][1]);
#if DBG == 1
printf("%d: close pipe [%d][1](%d) %s\n",getpid(),i, pipes[i][1], strerror(errno) );
#endif
} else {
#if DBG == 1
printf("%d: no action pipe [%d][1](%d) \n",getpid(),i, pipes[i][1] );
#endif
}
}
}
//Generate a random number for my child process
int GenererIdElection ()
{
srand(time(NULL)^getpid()<<16);
return rand()%NBPROC;
}
int main(int argc, char const *argv[])
{
int N = NBPROC, idElection, numProc= -1;
int pere = getpid();
int pipes[N][2];
int pids[N];
char etat ;
printf("%d: Number of process is : %d\n", getpid(), N);
int pipeVal = INT32_MIN;
if(argc == 2) {
pipeVal = atoi(argv[1]);
}
for(int i = 0; i < N; ++i) {
if(pipeVal != INT32_MIN) {
pipes[i][0] = pipes[i][1] = pipeVal;
}
printf("%d: Pipe pair %d-%d\n",getpid(), pipes[i][0], pipes[i][1]);
}
for (int i = 0; i < N; ++i)
{
if(getpid()==pere){
pid_t pid = fork() ;
numProc++;
if(pid == 0) {
// sleep(10);
#if DBG == 1
printf("%d: After fork in the child got fork-pid %d\n", getpid(), pid);
#endif
} else {
pids[numProc] = pid;
#if DBG == 1
printf("%d: After fork in the parent got fork-pid %d numProc %d pids[%d] = %d\n",
getpid(), pid, numProc, numProc, pids[numProc]);
#endif
}
}
}
if (getpid() != pere)
{
etat = 'C';
while(etat == 67)
{
idElection = GenererIdElection();
printf("%d: Election Id %d, numProc %d \n",getpid(), idElection, numProc);
comparerIdElection(pipes, numProc);
etat ='B';
}
printf("%d: Exiting while-loop\n", getpid());
}
if (getpid()==pere)
{
/*On attend la fin de chaque processus
pour mettre un terme au processu pere*/
for (int i = 0; i < N; ++i)
{
errno = 0;
int stat_val;
wait(&stat_val);
#if DBG == 1
printf("%d: WIFEXITED(%d),WEXITSTATUS(%d),WIFSIGNALED(%d),WTERMSIG(%d),WIFSTOPPED(%d),WSTOPSIG(%d),WIFCONTINUED(%d)\n", getpid(), WIFEXITED(stat_val),WEXITSTATUS(stat_val),WIFSIGNALED(stat_val),WTERMSIG(stat_val),WIFSTOPPED(stat_val),WSTOPSIG(stat_val),WIFCONTINUED(stat_val));
#endif
}
}
printf("%d: Exiting process\n", getpid());
exit(EXIT_SUCCESS);
}
I want to write a code using pipes. This code is about the rock, scissor, paper game, so I need to ask and receive messages all the time until one of two players score 3. So far, now I have the following code, but I don´t know how to iterate this code, could be with a while iteration?
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define READ_END 0 /* index pipe extremo escritura */
#define WRITE_END 1 /* index pipe extremo lectura */
int main(int argc, char* argv[])
{
int fd1[2],fd2[2];
int status, pid;
pipe(fd1); /* comunica ls y grep */
pid = fork();
if(pid == 0) /* hijo 1*/
{
close(fd1[READ_END]); /* cerrar extremo no necesario */
dup2(fd1[WRITE_END], STDOUT_FILENO);
close(fd1[WRITE_END]);
}
else /* padre */
{
close(fd1[WRITE_END]); /* extremo no necesario ya */
pipe(fd2); /* comunica grep y wc */
pid = fork();
if(pid == 0) /* hijo 2 */
{
close(fd2[READ_END]); /* cerrar extremo no necesario */
dup2(fd1[READ_END], STDIN_FILENO);
close(fd1[READ_END]);
dup2(fd2[WRITE_END], STDOUT_FILENO);
close(fd2[WRITE_END]);
}
else /* padre */
{
close(fd1[READ_END]); /* cerrar extremo no necesario */
close(fd2[WRITE_END]); /* cerrar extremo no necesario */
}
}
close(fd2[READ_END]); /* cerrar extremo que queda abierto en el padre */
/* wait para cada hijo */
wait(&status);
wait(&status);
return 0;
}
Currently I'm learning C and I'd like to make a ring of n childs process with forks and pipes where n is a number entered in argument. Each child could communicate with the next child in two directions like this:
p1 --->--- p2
---<---
|| ||
^v ^v
|| ||
--->---
p4 ---<--- p3
I tried to do it where each child send to the next child its pid or a message, but I don't get what I want for instance. When I run the program, I've got two issues:
the first issue is that if I enter an argument over 5, I get the Segmentation fault (core dumped) error
the second issue is that when a process sends its pid inside the ring, it seems to be the only one that reads it.
What's going wrong?
I'm not really comfortable with multiple fifos in a loop.
#define _XOPEN_SOURCE 500
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MIN_PROC 2 // Nombre minimum de processus
#define MAX_PROC 20 // Nombre maximum de processus
int main (int argc,char ** argv)
{
int n; // Nombre de processus dans l'anneau
/* Lecture du paramètre */
if (argc<2 || (n = (int)strtol(argv[1],NULL,0))<MIN_PROC || n>MAX_PROC)
{
fprintf (stderr,"Usage : %s <nombre>\n"
"Avec <nombre> compris entre %d et %d.\n",
argv[0],MIN_PROC,MAX_PROC);
exit(1);
}
else
{
char tubes[2*n];
pid_t pid;
// Réception du PID lors de fork()
int p[n][4]; // Place pour n paires de descripteurs (VLA)
int i,pos =0,k; // Indice de boucle
/* Indication du nombre de processus à engendrer */
for (int i = 0; i < n*2; i++)
{
pos += sprintf(&tubes[pos],",tube%d",i);
}
printf("String is now:%s\n",tubes);
printf ("Nombre de processus à engendrer : %d\n",n);
/* Création des n tubes anonymes. */
/* On considère que l'appel réussit toujours. */
for (k=0;k<n;k++){
for (int i = 0; i < 4; i++)
{
p[k][i] = mkfifo(tubes[i],0666);
}
}
/* Génération des n processus fils, qui vont communiquer */
/* entre eux. Le processus père reste en superviseur. */
for (i=0;i<n;i++)
{
pid = fork();
if (pid>0)
{
printf ("Création du processus fils #%d : PID %d\n",i,pid);
}
else if (!pid)
{
int in[2]; /* Descripteurs d'entrée */
int out[2]; /* Descripteurs de sortie */
int data; /* Donnée à émettre */
int j; /* Autre indice de boucle (puisque i reste */
/* de fait notre numéro de processus fils. */
/* Temporisation pour que la boucle du processus père ait */
/* le temps de se terminer avant de commencer le traitement */
sleep(1);
for (int i = 0; i < n; i++)
{
if (i%2 == 0)
{
for (int i = 0; i < 2; ++i)
{
in[i]= open(tubes[i],O_RDONLY);
out[i]= open(tubes[i],O_WRONLY);
}
}
else {
for (int i = 0; i < 2; ++i)
{
in[i]= open(tubes[i],O_WRONLY);
out[i]= open(tubes[i],O_RDONLY);
}
}
}
/* Récupération des descripteurs adéquats */
in[0] = p[ i ][0];
out[0] = p[(i+1) % n][1];
in[1] = p[ i ][2];
out[1] = p[(i+1) % n][3];
/* Fermeture des descripteurs inutiles */
for (j=0;j<n;++j)
{
if (p[j][0] != in[0]) close(p[j][0]);
if (p[j][1] != out[0]) close(p[j][1]);
if (p[j][2] != in[1]) close(p[j][2]);
if (p[j][3] != out[2]) close(p[j][3]);
}
/* Récupération et émission de notre PID */
data = (int)getpid();
printf ("Processus #%d : émission de %d\n",i,data);
write (out[0],&data,sizeof data);
close (out[0]);
/* Réception de la donnée de l'autre processus */
data = (int)getpid();
read (in[0],&data,sizeof data);
printf ("Processus #%d : réception de %d\n",i,data);
close (in[0]);
data = (int)getpid();
printf ("Processus #%d : émission de %d\n",i,data);
write (out[1],&data,sizeof data);
close (out[1]);
/* Réception de la donnée de l'autre processus */
data = (int)getpid();
read (in[1],&data,sizeof data);
printf ("Processus #%d : réception de %d\n",i,data);
close (in[1]);
/* En fin de traitement, un break pour quitter la boucle */
/* démarrée par le processus père. */
break;
}
else perror ("Erreur à l'appel de fork()");
}
/* Si PID est non nul à l'issue de la boucle, c'est qu'on est */
/* toujours dans le processus père. On en profite pour faire n */
/* wait() successifs pour attendre tous nos fils. */
if (pid>0)
for (i=0;i<2*n;i++) {
wait(NULL);
unlink(tubes[i]);
}
}
return 0;
}
Here's an implementation that works. It required some care to get the connections established, as outlined in comments and not repeated in this answer. The writing sequence also has to match the open sequence:
P0 will write forward, read forward, read backward, write backward.
PN will read backward, write backward, write forward, read forward.
Note that the code with the children is basically synchronous — there is not much resequencing that can happen. The nanosleep() call simply ensures that startup messages are sequenced. Only the reporting output may be messier if you drop it.
I've used my Standard Error Reporting package, "stderr.h" and stderr.c, to handle much of the messaging from the code. (At the moment, if you use stderr.c, you'll also need kludge.h and kludge.c from the same directory.) The package has features that make it convenient (using err_setlogopts() to include the PID and micro-second timing but omit the program name, and err_settimeformat() to only print the time and not the date component of the information).
I also used a number of functions — I'm not clever enough to write the code in a single function. Very often, for a simple parent-child relationship, I'll have a pair of functions be_childish() and be_parental() to encapsulate the work done by child and parent processes. In this code, I didn't really need a separate parental function.
There are about 200 lines in the code below - including the blank lines and comments.
#include "stderr.h"
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#define MIN_PROC 2
#define MAX_PROC 20
enum { MAX_TUBENAMELEN = 20 };
enum { MAX_MSGLEN = 256 };
enum Mode { M_WF, M_RF, M_WB, M_RB };
/*
** You have to split the connections into 'forward' and 'backward'
** around the loop. P0 (zeroth child) needs to connect forward first,
** then connect backward; all other children need to connect backward
** first, then connect forward. Although the order is arbitrary, when
** a child connects backward, it should open the read FIFO before the
** write FIFO; when it connects forward, it should open the write FIFO
** before the read FIFO.
**
** Child process Pn (for n = 0; n < N; n++) connects forward to tube
** T[(2n)%(2N)] for writing, T[(2n+1)%(2N)] for reading; and connects
** backward to T[(2n-2+2N)%(2N)] for reading, and to T[(2n-1+2N)%(2N)]
** for writing. The +2N terms ensure that the LHS of % is not
** negative. This sequencing should ensure no blockage
**
** When it comes to reading and writing, the rules will be similar.
** P0 will write forward, read forward, read backward, write backward.
** PN will read backward, write backward, write forward, read forward.
*/
static inline int tube_index(int num, int max)
{
int idx = (num + 2 * max) % (2 * max);
return idx;
}
static int open_fifo(enum Mode mode, int max, int num, char tubes[][MAX_TUBENAMELEN])
{
int fd;
int idx = 0;
switch (mode)
{
case M_WF: idx = tube_index(2 * num + 0, max); break;
case M_RF: idx = tube_index(2 * num + 1, max); break;
case M_RB: idx = tube_index(2 * num - 2, max); break;
case M_WB: idx = tube_index(2 * num - 1, max); break;
default: assert(0);
}
const char *fifoname = tubes[idx];
int o_mode = O_RDONLY;
if (mode == M_WF || mode == M_WB)
o_mode = O_WRONLY;
err_remark("Opening FIFO %s with mode %d\n", fifoname, o_mode);
if ((fd = open(fifoname, o_mode)) < 0)
err_syserr("Failed to open %s with mode %d: ", fifoname, o_mode);
err_remark("Opened FIFO %s with mode %d - fd %d\n", fifoname, o_mode, fd);
return fd;
}
static inline void recv_info(int num, int fd)
{
char buffer[MAX_MSGLEN];
int nbytes;
if ((nbytes = read(fd, buffer, sizeof(buffer))) <= 0)
err_syserr("P%d failed to read anything on fd %d: ", num, fd);
err_remark("P%d received %d bytes: [%.*s]\n", num, nbytes, nbytes, buffer);
}
static inline void send_info(int num, int fd, const char *dir)
{
char buffer[MAX_MSGLEN];
int buflen = snprintf(buffer, sizeof(buffer), "P%d (PID %d) sent this message %s",
num, (int)getpid(), dir);
int nbytes;
if ((nbytes = write(fd, buffer, buflen)) != buflen)
err_syserr("Failed to write properly on fd %d (%d vs %d wanted): ", fd, nbytes, buflen);
err_remark("P%d sent %d bytes: [%.*s]\n", num, nbytes, nbytes, buffer);
}
static void be_childish(int max, int num, char tubes[][MAX_TUBENAMELEN])
{
int wf; /* Descriptor for writing forwards */
int wb; /* Descriptor for writing backwards */
int rf; /* Descriptor for reading forwards */
int rb; /* Descriptor for reading backwards */
if (num == 0)
{
/* Child zero connects forwards then backwards */
wf = open_fifo(M_WF, max, num, tubes);
rf = open_fifo(M_RF, max, num, tubes);
rb = open_fifo(M_RB, max, num, tubes);
wb = open_fifo(M_WB, max, num, tubes);
send_info(num, wf, "forwards");
recv_info(num, rf);
recv_info(num, rb);
send_info(num, wb, "backwards");
}
else
{
/* Other children connect backwards then forwards */
rb = open_fifo(M_RB, max, num, tubes);
wb = open_fifo(M_WB, max, num, tubes);
wf = open_fifo(M_WF, max, num, tubes);
rf = open_fifo(M_RF, max, num, tubes);
recv_info(num, rb);
send_info(num, wb, "backwards");
send_info(num, wf, "forwards");
recv_info(num, rf);
}
close(wf);
close(wb);
close(rf);
close(rb);
}
int main(int argc, char **argv)
{
int n;
err_setarg0(argv[0]);
err_setlogopts(ERR_NOARG0|ERR_PID|ERR_MICRO);
err_settimeformat("%H:%M:%S");
if (argc < 2 || (n = (int)strtol(argv[1], NULL, 0)) < MIN_PROC || n > MAX_PROC)
{
fprintf(stderr, "Usage : %s <nombre>\n"
"Avec <nombre> compris entre %d et %d.\n",
argv[0], MIN_PROC, MAX_PROC);
exit(1);
}
char tubes[2 * n][MAX_TUBENAMELEN];
pid_t pid;
pid_t pids[n];
for (int i = 0; i < n * 2; i++)
{
snprintf(tubes[i], sizeof(tubes[i]), "tube%d", i);
printf("Fifo %d: [%s]\n", i, tubes[i]);
}
printf("Nombre de processus à engendrer : %d\n", n);
for (int k = 0; k < 2*n; k++)
{
printf("Create fifo: %s\n", tubes[k]);
if (mkfifo(tubes[k], 0666) != 0)
err_syserr("Failed to create FIFO %s: ", tubes[k]);
}
fflush(0);
for (int i = 0; i < n; i++)
{
pid = fork();
if (pid > 0)
{
pids[i] = pid;
err_remark("Création du processus fils #%d : PID %d\n", i, (int)pid);
}
else if (pid == 0)
{
usleep((i + 1) * 100000); // Tenths of a second
err_remark("Child process #%d (PID %d) at work\n", i, (int)getpid());
be_childish(n, i, tubes);
int status = (i + 1) * 16;
err_remark("Child process #%d (PID %d) exiting with status 0x%.2X\n", i, (int)getpid(), status);
exit(status);
}
else
{
err_sysrem("Failed to fork child %d: ", i);
for (int j = 0; j < i; j++)
{
err_remark("Killing %d\n", pids[j]);
kill(SIGTERM, pids[j]);
}
for (int j = 0; j < 2 * n; j++)
unlink(tubes[j]);
err_error("Terminating!\n");
}
}
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
err_remark("Child %d died with status 0x%.4X\n", corpse, status);
for (int j = 0; j < 2 * n; j++)
unlink(tubes[j]);
return 0;
}
Example output:
Fifo 0: [tube0]
Fifo 1: [tube1]
Fifo 2: [tube2]
Fifo 3: [tube3]
Fifo 4: [tube4]
Fifo 5: [tube5]
Nombre de processus à engendrer : 3
Create fifo: tube0
Create fifo: tube1
Create fifo: tube2
Create fifo: tube3
Create fifo: tube4
Create fifo: tube5
16:19:57.312293 - pid=89807: Création du processus fils #0 : PID 89810
16:19:57.314294 - pid=89807: Création du processus fils #1 : PID 89811
16:19:57.314500 - pid=89807: Création du processus fils #2 : PID 89812
16:19:57.413772 - pid=89810: Child process #0 (PID 89810) at work
16:19:57.415148 - pid=89810: Opening FIFO tube0 with mode 1
16:19:57.515290 - pid=89811: Child process #1 (PID 89811) at work
16:19:57.515558 - pid=89811: Opening FIFO tube0 with mode 0
16:19:57.515771 - pid=89810: Opened FIFO tube0 with mode 1 - fd 3
16:19:57.515788 - pid=89810: Opening FIFO tube1 with mode 0
16:19:57.515764 - pid=89811: Opened FIFO tube0 with mode 0 - fd 3
16:19:57.515883 - pid=89811: Opening FIFO tube1 with mode 1
16:19:57.516011 - pid=89810: Opened FIFO tube1 with mode 0 - fd 4
16:19:57.516020 - pid=89810: Opening FIFO tube4 with mode 0
16:19:57.516010 - pid=89811: Opened FIFO tube1 with mode 1 - fd 4
16:19:57.516120 - pid=89811: Opening FIFO tube2 with mode 1
16:19:57.615230 - pid=89812: Child process #2 (PID 89812) at work
16:19:57.615451 - pid=89812: Opening FIFO tube2 with mode 0
16:19:57.615582 - pid=89812: Opened FIFO tube2 with mode 0 - fd 3
16:19:57.615593 - pid=89811: Opened FIFO tube2 with mode 1 - fd 5
16:19:57.615678 - pid=89812: Opening FIFO tube3 with mode 1
16:19:57.615747 - pid=89811: Opening FIFO tube3 with mode 0
16:19:57.615852 - pid=89811: Opened FIFO tube3 with mode 0 - fd 6
16:19:57.615881 - pid=89812: Opened FIFO tube3 with mode 1 - fd 4
16:19:57.615986 - pid=89812: Opening FIFO tube4 with mode 1
16:19:57.616078 - pid=89810: Opened FIFO tube4 with mode 0 - fd 5
16:19:57.616090 - pid=89810: Opening FIFO tube5 with mode 1
16:19:57.616071 - pid=89812: Opened FIFO tube4 with mode 1 - fd 5
16:19:57.616153 - pid=89812: Opening FIFO tube5 with mode 0
16:19:57.616240 - pid=89810: Opened FIFO tube5 with mode 1 - fd 6
16:19:57.616277 - pid=89810: P0 sent 41 bytes: [P0 (PID 89810) sent this message forwards]
16:19:57.616236 - pid=89812: Opened FIFO tube5 with mode 0 - fd 6
16:19:57.616312 - pid=89811: P1 received 41 bytes: [P0 (PID 89810) sent this message forwards]
16:19:57.616444 - pid=89810: P0 received 42 bytes: [P1 (PID 89811) sent this message backwards]
16:19:57.616437 - pid=89811: P1 sent 42 bytes: [P1 (PID 89811) sent this message backwards]
16:19:57.616530 - pid=89811: P1 sent 41 bytes: [P1 (PID 89811) sent this message forwards]
16:19:57.616535 - pid=89812: P2 received 41 bytes: [P1 (PID 89811) sent this message forwards]
16:19:57.616660 - pid=89812: P2 sent 42 bytes: [P2 (PID 89812) sent this message backwards]
16:19:57.616665 - pid=89811: P1 received 42 bytes: [P2 (PID 89812) sent this message backwards]
16:19:57.616772 - pid=89812: P2 sent 41 bytes: [P2 (PID 89812) sent this message forwards]
16:19:57.616881 - pid=89810: P0 received 41 bytes: [P2 (PID 89812) sent this message forwards]
16:19:57.616893 - pid=89810: P0 sent 42 bytes: [P0 (PID 89810) sent this message backwards]
16:19:57.616817 - pid=89811: Child process #1 (PID 89811) exiting with status 0x20
16:19:57.617243 - pid=89810: Child process #0 (PID 89810) exiting with status 0x10
16:19:57.617501 - pid=89812: P2 received 42 bytes: [P0 (PID 89810) sent this message backwards]
16:19:57.617726 - pid=89807: Child 89811 died with status 0x2000
16:19:57.618114 - pid=89812: Child process #2 (PID 89812) exiting with status 0x30
16:19:57.618313 - pid=89807: Child 89810 died with status 0x1000
16:19:57.618635 - pid=89807: Child 89812 died with status 0x3000
You can find this code at GitHub.
Hi I've to develop this program that create 4 children and, sequentially, make 'em do a simple operation. The first will do the sum, the second the rest, the third the multiplication and the fourth the division. The father will write on the socket the string with the two number he wants his children to "calculate", and every children should read this string, extract the numbers and to the operations. Obviously, being two pipes, it's needed that the father writes every time the string, because of the read in the child. I don't really understand why at the second iteration, i receive a SIGPIPE on the write of the father. Can someone explain me why? I losed 3 days on debugging, but I didn't find anything. Thank you very much.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
/*
fd_0 padre escribe y hijo lee ==== padre cierra fd_0[0] y hijo cierra fd_0[1]
fd_1 hijo escribe y padre lee === padre cierra fd_1[1] y hijo cierra fd_1[0]
*/
int main (int argc, char * argv[]){
char * str = malloc(100*sizeof(char));//hijo
char readbuffer_h[150];
char * stringa = malloc(100*sizeof(char));//padre
char readbuffer_p[150];
int a,b;
int x,y;
int n = 4;
int i,status,nbytes, pipe_status;
int pid, ppid,yo,padre;
int fd_0[2], fd_1[2] ;
pipe_status=pipe(fd_0);
if(pipe_status==- 1) {
perror("Error creando la tuberia 0\n");
exit(EXIT_FAILURE);
}
pipe_status=pipe(fd_1);
if(pipe_status== -1) {
perror("Error creando la tuberia 1 \n");
exit(EXIT_FAILURE);
}
for(i=0; i< n; i++){
if ((pid=fork()) <0 ){
printf("Error al emplear fork\n");
exit(EXIT_FAILURE);
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/
else if (pid ==0){// soy el hijo
yo = getpid();
padre = getppid();
printf("HIJO: %d, mi padre es: %d\n", yo, padre);
close(fd_0[1]);
close(fd_1[0]);
//TODO
nbytes = read(fd_0[0], readbuffer_h, sizeof(readbuffer_h));
sscanf(readbuffer_h, "%d,%d", &x, &y);
switch(i) {
case 0 :
//TODO
sprintf(str, "Datos enviados a través de la tuberia por el proceso hijo: %d. Primero operando: %d, segundo operando: %d. La suma es %d", yo,x,y,(x+y));
break;
case 1 :
//TODO
sprintf(str, "Datos enviados a través de la tuberia por el proceso hijo: %d. Primero operando: %d, segundo operando: %d. La resta es %d", yo,x,y,(x-y));
break;
case 2 :
//TODO
sprintf(str, "Datos enviados a través de la tuberia por el proceso hijo: %d. Primero operando: %d, segundo operando: %d. El producto es %d", yo,x,y,(x*y));
break;
case 3 :
//TODO
sprintf(str, "Datos enviados a través de la tuberia por el proceso hijo: %d. Primero operando: %d, segundo operando: %d. El cociente es %d", yo,x,y,(x/y));
break;
}
write(fd_1[1], str, strlen(str));
exit(EXIT_SUCCESS);
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/
else{ //soy el padre
yo = getpid();
printf("PADRE:%d\n", yo);
a = 3; b = 4;
close(fd_0[0]);
close(fd_1[1]);
sprintf(stringa,"%d,%d",a,b);
printf("Stringa padre : %s\n", stringa);
fflush(stdout);
write(fd_0[1],stringa,strlen(stringa)); // questa write non va a buon fine
wait(&status);
read(fd_1[0], readbuffer_p, sizeof(readbuffer_p));
printf("%s\n",readbuffer_p);
fflush(stdout);
}
}
close(fd_0[0]);
close(fd_0[1]);
close(fd_1[0]);
close(fd_1[1]);
return 0;
}
You're getting yourself into trouble by trying to use the same pipes to communicate with each child.
You create two pipes at the beginning of the program. On the first iteration of the loop, the parent forks, and the child inherits all the parent's open file descriptors. The child closes the pipe ends it doesn't need, and the parent closes the pipe ends it doesn't need. Communication happens as intended (I imagine) -- all well and good so far.
But now consider the second iteration of the loop. You fork again, and the child again inherits the parent's open file descriptors. But now, the file descriptors the child wants to use were closed by the parent in the previous iteration of the loop. I'm a bit surprised that the child then gets an EPIPE instead of an EBADF when it tries to use those file descriptors, but I'm not at all surprised that its read attempt fails.
The cleanest thing to do would be to create a new pair of pipes for each child, instead of trying to reuse one set of pipes. If you want to make it work with just the one pair, then the parent process must avoid closing any of the pipe ends (though the child processes may close their copies, if you wish).
I want to have one parent with 2 children.
The parent reads from file "a.txt" and sends trough pipe to first child; the first child reads the chars and sends to the second child the lower letter chars.
The second child prints in "b.txt" each distinct char and number of appearances(per line) and then sends trough a pipe to the parent the number of distinct chars. The parent prints the result from the second child.
I've done the pipe from parent to 1 child and to test I've put a pipe back to the parent.
What I can't figure it out is how to make the pipe go to the second child. I've been searching for information on dup2 but I don't get on how to make it work.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
void main()
{
int pfd1[2], pfd2[2], pid1, pid2, pfin, status, fd;
char *c = (char *)malloc(sizeof(char));
if (pipe(pfd1) < 0) {
printf("Eroare la crearea pipe-ului\n");
exit(1);
}
if (pipe(pfd2) < 0) {
printf("Eroare la crearea pipe-ului\n");
exit(1);
}
if ((pid1 = fork()) < 0) {
printf("Eroare la fork\n");
exit(1);
}
if (pid1 == 0) { /*child */
close(pfd1[1]);
while (read(pfd1[0], c, 1) > 0) {
//printf("%s",c);
if (islower(*c)) {
close(pfd2[0]);
//inchid capul de citire; scriu in pipe
write(pfd2[1], c, 1);
////dup??????
}
}
printf("\n");
write(pfd[1], buff, len);
close(pfd1[0]);
close(pfd2[1]);
exit(0);
}
if ((pid2 = fork()) < 0) {
printf("Eroare la fork\n");
exit(1);
}
if (pid2 == 0) {
printf("second child");
exit(0);
}
/* parent */
close(pfd1[0]);
close(pfd2[1]);
fd = open("date.txt", O_RDONLY);
while (read(fd, c, 1) > 0) {
write(pfd1[1], c, 1);
}
close(pfd1[1]); /* la sfarsit inchide si capatul utilizat */
close(pfin);
while (read(pfd2[0], c, 1) > 0)
printf("%s", c);
close(pfd2[0]);
printf("%d", wait(&status));
printf("%d", wait(&status));
}
I have a few specific comments on your code:
char *c = (char *)malloc(sizeof(char));
While there is nothing wrong with this, there is also no need for this char to be allocated from the heap. The more idiomatic approach would use char c; here and then pass &c to read(2) and write(2) system calls. (Even more idiomatic would be to use the C Standard IO facility; freopen(3), getchar(3), and putchar(3) -- but do not make that transition until you have this code working exactly as you want it to, because it is an additional complication with the problem you're trying to solve.)
if ((pid1 = fork()) < 0) {
printf("Eroare la fork\n");
exit(1);
}
By using your own error message, you are missing out on important error information. You should use perror(3) to print an error message instead. This will give you and your users an actual cause for errors that they can search for. fork(2) can fail if your user is running into the setrlimit(2) NPROC maximum process limit, the system-wide process limit, out of kernel memory.
if ((pid1 = fork()) < 0) {
perror("Eroare la fork");
exit(1);
}
You should also check the return value from open(2) calls. (You're supposed to also check the return values from write(2) and close(2) for errors, but handling these errors is harder. Simply printing the error and quitting is a good start for most programs.)
while (read(pfd1[0], c, 1) > 0) {
//printf("%s",c);
if (islower(*c)) {
close(pfd2[0]);
This is the wrong location for the close(2) call -- you shouldn't be closing this filedescriptor over and over for every input character. If you were checking the close(2) return value, you would notice errno is set to EBADF because the file descriptor is no longer valid on the second and subsequent calls.
Now, onto the problem you came here for: the sequence of fork(), pipe(), and dup2() calls that will hook up all your processes in a pipeline and send data back to the parent process. Since pipe(2) creates uni-directional pipe(7)s, you need to call pipe(2) four times -- for both directions between parent and children. If you store the pipe endpoints in arrays with names that mean something to you, they will be easier to keep track of. Perhaps create arrays named to_ for writing into and from_ for reading from:
int to_child[2];
int from_parent[2];
int to_parent[2];
int from_child[2];
for (int i=0; i<2; i++) {
int p[2];
if (pipe(p) == -1) {
perror("pipe");
exit(1);
}
/* from parent to child */
to_child[i] = p[1];
from_parent[i] = p[0];
if (pipe(p) == -1) {
perror("pipe");
exit(1);
}
/* from child to parent */
to_parent[i] = p[1];
from_child[i] = p[0];
}
Note that you don't actually need to use dup2(2) to re-arrange the filedescriptors unless you want to execute other programs to handle the "filter" task. Just read(2) using the from_parent[...] or from_child[...] descriptors and write(2) to the to_child[...] and to_parent[...] descriptors.
Maybe the entire thing would be easier with socketpair(2) using AF_UNIX to create bi-directional sockets, which can then be read from and written to in the same fashion as any other BSD-style socket. See socket(7) and unix(7) for an overview.
You do not need dup(). Simply open the pipes in the parent and then in each process close() the endpoints you don't need and just use the endpoints you do need.
Note that this means that the parent will close both endpoints of the pipe for child-child communication. Each of the endpoints of this pipe will be used by one of the children. Each child should also close the endpoint it doesn't use.
Thank you for your answers.
Here is the code for the whole problem.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
void main()
{
int pfd1[2],pfd2[2],pfd3[2],pid1,pid2,status,fd,fo;
char letter[32][2];
int letternumber=0,i,sw=0;
char* c = (char*)malloc(sizeof(char));
//pipe creation
if(pipe(pfd1)<0){
perror("Eroare la crearea pipe-ului");
exit(1);
}
if(pipe(pfd2)<0){
perror("Eroare la crearea pipe-ului\n");
exit(1);
}
if(pipe(pfd3)<0){
perror("Eroare la crearea pipe-ului\n");
exit(1);
}
//make first child
if((pid1=fork())<0){
perror("Eroare la fork\n");
exit(1);
}
if(pid1==0) //child process
{
if(close(pfd1[1])<0) {perror("Eroare close");exit(1);} // close write end; process will read from pipe
if(close(pfd2[0])<0) {perror("Eroare close");exit(1);} //close read end; write in pipe
while (read(pfd1[0], c, 1) > 0){
if(islower(*c)) write(pfd2[1],c,1);//writing in pipe
}
if(close(pfd1[0])<0) {perror("Eroare close");exit(1);} /* close other ends */
if(close(pfd2[1])<0) {perror("Eroare close");exit(1);}
if(close(pfd3[0])<0) {perror("Eroare close");exit(1);}
if(close(pfd3[1])<0) {perror("Eroare close");exit(1);}
exit(0);
}
//make second child
if((pid2=fork())<0){
perror("Eroare la fork");
exit(1);
}
if(pid2==0){ /* second child*/
if(close(pfd1[0])<0) {perror("Eroare close");exit(1);}
if(close(pfd1[1])<0) {perror("Eroare close");exit(1);}
if((fo=open("statistica.txt", O_CREAT | O_TRUNC | O_RDWR,
S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IROTH))==-1)
{perror("Eroare open");exit(1);}
if(close(pfd2[1])<0) {perror("Eroare close");exit(1);}
letter[0][0]='A';
letter[0][1]=0;
while(read(pfd2[0],c,1)>0) {
for(i=0;i<=letternumber;i++)
if(letter[i][0]==*c) {letter[i][1]++;sw=1;}
if (sw==0){
letter[letternumber][0]=*c;
letter[letternumber++][1]=1;
}
sw=0;
}
printf("\n");//why won't it write to file without it;
//wihtout it, it writes to screen?
if(close(pfd2[0])<0) {perror("Eroare close");exit(1);}
dup2(fo,1);
for(i=0;i<letternumber;i++)
printf("%c %d\n",letter[i][0],letter[i][1]);
if(close(fo)<0) {perror("Eroare close");exit(1);}
if(close(pfd3[0])<0) {perror("Eroare close");exit(1);} //close read end; going to write in pipe
dup2(pfd3[1],1);
printf("%d",letternumber);
if(close(pfd3[1])<0) {perror("Eroare close");exit(1);}
exit(0);
}
/* parent process */
if(close(pfd1[0])<0) {perror("Eroare close");exit(1);} // close read end; write in pipe
if(close(pfd2[0])<0) {perror("Eroare close");exit(1);}
if(close(pfd2[1])<0) {perror("Eroare close");exit(1);}
if(close(pfd3[1])<0) {perror("Eroare close");exit(1);}
if((fd=open("date.txt",O_RDONLY))==-1)
{perror("Eroare open");exit(1);}
while(read(fd,c,1)>0)
write(pfd1[1],c,1); //write in pipe
if(close(pfd1[1])<0) {perror("Eroare close");exit(1);}
//dup2(pfd3[0],0);
while(read(pfd3[0],c,1)>0) printf("%c",*c);
printf("\n");
if(close(pfd3[0])<0) {perror("Eroare close");exit(1);}
wait(&status);
wait(&status);
}