How to make a process ring with fork and mkfifo? - c

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.

Related

Not all of my child processes running the end of my for loop

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);
}

SIGPIPE in bidirectional messaging with two pipes

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).

Why don't I see deadlock (EDEADLK) when several processes lock the same fd with F_SETLKW?

I need to properly handle EDEADLK. In my program I see that both children wait until parent sleep and, and then they apply the lock and right away they leave it. Sorry for my mistakes, I am a Spanish student.
int main(){
pid_t childpid, childpid2;
struct flock cerrojo;
int fd, status;
if ((fd=open("prueba", O_RDWR)) == -1)
perror("apertura fallo");
cerrojo.l_type =F_WRLCK;
cerrojo.l_whence =SEEK_SET;
cerrojo.l_start =0;
cerrojo.l_len =0;
if (fcntl(fd, F_SETLK,&cerrojo) == -1)
perror("cerrojo fallo");
if ((childpid= fork()) == -1) {
printf("Could not create child");
exit(-1);
}
if(childpid){
if ((childpid2= fork()) == -1) {
printf("Could not create child");
exit(-1);
}
if(childpid2){
cerrojo.l_type = F_UNLCK;
cerrojo.l_whence =SEEK_SET;
cerrojo.l_start =0;
cerrojo.l_len =0;
sleep(2);
fcntl(fd, F_SETLKW, &cerrojo);
waitpid(childpid,&status,0);
waitpid(childpid2,&status,0);
}
}
if(!childpid||!childpid2){
printf("Soy el hijo %d\n",getpid());
if(fcntl(fd, F_SETLKW,&cerrojo) == -1){
printf("FCNTL FALLA EN PID: %d\n",getpid());
sleep(1);
}
printf("PID %d va a quitar el cerrojo.\n",getpid());
cerrojo.l_type = F_UNLCK;
cerrojo.l_whence =SEEK_SET;
cerrojo.l_start =0;
cerrojo.l_len =0;
fcntl(fd, F_SETLKW, &cerrojo);
printf("HIJO ACABADO\n");
}
return 0;
}
For deadlock you need at least two locks. I always imagine myself locked in room A, with the key of room B, and someone else in room B, with the key of room A.
In your example there is only one lock: both children try to lock the same big "door" (the whole file). The second who gets there will block until the first releases the lock again, and then sing the same litle song Lock, sleep,... unlock. No deadlock in sight.
Now, in the example below, the parent locks two "doors" - the first and second byte of the file fd points to (btw, is this really necessary for your example?) and then spawns two children. Both children try to lock both bytes, but each begins with a different one. As soon as the parent releases both bytes the children acquire their locks, 4 lock attempts in all, but the last one would cause deadlock, and duly fails with EDEADLK, so that everybody lives happily ever after - thanks to our wise and just kernel.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(){
pid_t childpid, childpid2;
struct flock cerrojo;
int fd, status;
if ((fd=open("prueba", O_RDWR)) == -1)
perror("apertura fallo");
cerrojo.l_type = F_WRLCK;
cerrojo.l_whence = SEEK_SET;
cerrojo.l_start = 0;
cerrojo.l_len = 2; /* lock "doors" (i.e. bytes) 0 and 1*/
if (fcntl(fd, F_SETLK,&cerrojo) == -1)
perror("cerrojo fallo");
if((childpid= fork())){ /* parent */
if ((childpid2= fork())) { /* still parent */
cerrojo.l_type = F_UNLCK;
cerrojo.l_len = 2; /* unlock both doors: let the fun begin :-) */
sleep(2);
printf("Tata va a quitar el cerrojo....\n",getpid());
fcntl(fd, F_SETLKW, &cerrojo);
waitpid(childpid,&status,0);
waitpid(childpid2,&status,0);
}
}
if(!childpid||!childpid2){ /* in child 1 or child 2 */
printf("Soy el hijo %d\n",getpid());
int offset0 = (childpid ? 0 : 1); /* child1 gets 0 and 1, child 2 gets 1 and 0 */
int offset1 = (childpid ? 1 : 0);
cerrojo.l_len = 1;
cerrojo.l_start = offset0; /* lock door 0 (1) as soon as parent lets us*/
printf("PID %d locking byte %d\n", getpid(), offset0);
if(fcntl(fd, F_SETLKW,&cerrojo) == -1){
printf("CERROJO byte %d FALLA EN PID %d (%s)\n",offset0, getpid(), strerror(errno));
}
sleep(1); /* guarantee that the other child has our next door locked ... */
printf("PID %d locking byte %d\n", getpid(), offset1);
cerrojo.l_start = offset1; /* lock door 1 (0). The second of both children who gets here closes the circle and faces deadlock */
if(fcntl(fd, F_SETLKW,&cerrojo) == -1){
printf("CERROJO byte %d FALLA EN PID: %d (%s)\n", offset1, getpid(), strerror(errno));
}
printf("HIJO %d ACABADO (releasing its lock)\n", getpid()); /* Falling off the end of main() will release the lock anyway */
}
}
The output:
[hlub#karpaten] ~ > ./test
Soy el hijo 29711
PID 29711 locking byte 1
Soy el hijo 29712
PID 29712 locking byte 0
Tata va a quitar el cerrojo....
PID 29711 locking byte 0
PID 29712 locking byte 1
CERROJO byte 1 FALLA EN PID: 29712 (Resource deadlock avoided)
HIJO 29712 ACABADO (releasing its lock)
HIJO 29711 ACABADO (releasing its lock)

WHy are my processes behaving like this?

it's some implementation of linux shell in c. Since i have added background process support i have some output that i fail to understand. Here is the code:
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <signal.h>
#include <wait.h>
#define DEFAULT_PROMPT "\nLog710H2014%>"
#define EXIT_CMD "exit"
#define CD_CMD "cd"
#define HOME_ENV_VAR "HOME"
#define NEW_LINE "\n**************************************************\n"
#define BCG_CMD_FLAG "&"
void cd_handler(int argc, char *argv[]);
int lire(char *chaine, int longueur);
char** init_command(int* size,char *str);
int execProg(int *argc, char **argv);
int execProgBg(int *argc, char **argv);
void sigchldHandler(int sig_num);
struct beanProcess {
pid_t pid;
int job_num;
char *command;
};
struct beanProcess *beans;
int jobCount = 1;
int main() {
printf(NEW_LINE);
printf("Bienvenue sur le shell de l'equipe 1");
printf(NEW_LINE);
while(1){
char str[200];
printf(DEFAULT_PROMPT);
lire(str, 200);
int commArgsC = 0, bg = 0;
char** comms = init_command(&commArgsC, str);
if(commArgsC == 0){
printf("Saisie vide, veuillez entrez une commande.");
continue;
}
if(strcmp(comms[commArgsC-1], BCG_CMD_FLAG) == 0){
bg = 1;
comms[commArgsC-1] = 0;
}
if(strcmp(comms[0], CD_CMD) == 0){
cd_handler(commArgsC, comms);
commArgsC = commArgsC -1;
}
else if (strcmp(comms[0], EXIT_CMD) == 0){
exit(0);
}
else {
if(bg){
execProgBg(&commArgsC, comms);
}
else{
execProg(&commArgsC, comms);
}
}
}
exit;
}
void cd_handler(int argc, char *argv[]){
char buff[512];
char * directory;
if(argc < 2){
directory = getenv(HOME_ENV_VAR);
}else if (argc == 2){
directory = argv[1];
}else{
exit(1);
}
if (chdir(directory) == -1) {
printf ("Erreur de changement de repertoire actif", strerror (errno));
}else{
if (getcwd(buff, sizeof(buff)) == NULL)
perror("Impossible d'afficher le repertoire courant");
else
printf("le repertoire courant est: %s\n", buff);
}
}
//Cette fonction est adaptée a partir du code de refp sur http://stackoverflow.com/questions/11198604/c-split-string-into-an-array-of-strings
char** init_command(int* size, char* str){
char ** res = NULL;
char * p = strtok (str, " ");
int n_spaces = 0;
while (p) {
res = realloc (res, sizeof (char*) * ++n_spaces);
if (res == NULL){
exit (-1);
}
res[n_spaces-1] = p;
p = strtok (NULL, " ");
}
res = realloc (res, sizeof (char*) * (n_spaces+1));
res[n_spaces] = 0;
*size = n_spaces;
return res;
}
//cette fonction est tirée d'un exemple de http://fr.openclassrooms.com/informatique/cours/apprenez-a-programmer-en-c/recuperer-une-chaine-de-caracteres
int lire(char *chaine, int longueur)
{
char *positionEntree = NULL;
if (fgets(chaine, longueur, stdin) != NULL)
{
positionEntree = strchr(chaine, '\n');
if (positionEntree != NULL)
{
*positionEntree = '\0';
}
return 1;
}
else
{
return 0;
}
}
int execProg(int *argc, char **argv){
char path[] = "/bin/";
strcat(path,argv[0]);
printf("\nThis is the %d process executing the code in non bg mode\n", getpid());
printf("Voici le resultat de l'execution de votre commande\n");
pid_t pid;
pid = fork();
if (pid < 0) {
perror("Creation de processus avec fork echouee");
exit(-1);
}
else if (pid == 0) {
if(execvp(argv[0], argv) == -1){
printf("\nthis is the child process %d executing the command in non bg mode\n", getpid());
perror("execv");
return EXIT_FAILURE;
}
}
else {
printf("\nthis is the parent process %d showing the stats in non bg mode\n", getpid());
struct rusage rusg;
long temp, tempCpu;
wait (NULL);
getrusage(RUSAGE_CHILDREN, &rusg);
printf("\nStatistique de la commande %s:\n", argv[0]);
temp = (rusg.ru_utime.tv_sec * 1000) + (rusg.ru_utime.tv_usec / 1000);
tempCpu = (rusg.ru_stime.tv_sec * 1000) + (rusg.ru_stime.tv_usec / 1000);
printf("\nLe temps wall-clock (ms): %ld", temp);
printf("\nLe temps CPU (ms) %ld", tempCpu);
printf("\nNB interruptions volontaires: %ld", rusg.ru_nvcsw);
printf("\nNB interruptions involontaires: %ld", rusg.ru_nivcsw);
printf("\nNB defaults de pages: %ld", rusg.ru_majflt);
printf("\nNB defaults de pages satifaits du noyau : %ld", rusg.ru_minflt);
}
return EXIT_SUCCESS;
}
int execProgBg(int *argc, char **argv){
signal(SIGCHLD, sigchldHandler);
printf("\nThis is the %d process executing the code in bg mode\n", getpid());
pid_t pid;
pid = fork();
if (pid < 0) {
perror("Creation de processus avec fork echouee");
return EXIT_FAILURE;
}
else if (pid == 0) {
//printf("This is the pid %d", getpid());
printf("\nthis is the child process %d executing the command in bg mode\n", getpid());
if(execvp(argv[0], argv) == -1){
perror("execvp");
return EXIT_FAILURE;
}
}
else {
printf("\nthis is the parent process %d showing the queue in bg mode\n", getpid());
printf("[%d] %d", jobCount, pid);
jobCount++;
//cleanJobList(childPid);
//ajoutProcess();
}
return EXIT_SUCCESS;
}
void sigchldHandler(int sig_num)
{
int status;
int childPid;
childPid = waitpid(-1, &status, 1);
printf("Hello the process %d has died\n", childPid);
//cleanJobList(childPid);
}
When i execute a bg command like "ls &", here's the output:
**************************************************
Bienvenue sur le shell de l'equipe 1
**************************************************
Log710H2014%>ls &
This is the 23464 process executing the code in bg mode
this is the parent process 23464 showing the queue in bg mode
[1] 23472
Log710H2014%>
this is the child process 23472 executing the command in bg mode
Debug PARTIE3.c
Hello the process 23472 has died
This is the 23464 process executing the code in non bg mode
Voici le resultat de l'execution de votre commande
this is the parent process 23464 showing the stats in non bg mode
Debug PARTIE3.c
Statistique de la commande ls:
Le temps wall-clock (ms): 0
Le temps CPU (ms) 2
NB interruptions volontaires: 2
NB interruptions involontaires: 9
NB defaults de pages: 0
NB defaults de pages satifaits du noyau : 644
Log710H2014%>
Why is that the parent process overlapping the lire() function and going directly to execProg function after the first execution ?
You are in a while loop. When your execProgBg function returns, no matter what it returns, the loop keeps going. If you want to stop you need to break or call exit from execProgBg.
Why is that the parent process overlapping the lire() function and
going directly to execProg function after the first execution
I don't know how you are executing your program but it looks like the second time around fgets fails, which you don't notice since you don't check the return code of the lire function. SO you go on and reuse the buffer from the previous call. It seems likely that you pass EOF to the program - perhaps by hitting ctrl-d.
I decided to run the code and obtained the same result as the OP does by hitting CTRL-D after the first ls &.
It is besides the point but warrants some explanation:
exit;
This evaluates the function exit converting it to a function pointer and throws away the result. It does not call the function. More importantly, calling exit as the last statement of main is pointless, since main is exiting anyway. You should just return some_code to indicate success of failure.

N command pipe " Inter-process "

I have successfully piped the output of one command by using one pipe .
I want to do this with N successive commands where each command represent a process .
This is my attempt at pipelining one command ,So to make a N sequence of command do i need n-1 pipes ? and how the father of all processes can collect the output and print it .
and thank you .
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
int main(int argc ,char *argv[])
{
int i,pid;
int fd[2];//crea i descriptor
char phrase[30][30];//crea il buffer
pipe(fd); /* crea la pipe */
for(i=0;i<argc;i++)
{
if((pid=fork())==0)
{
strcpy(phrase[i], argv[i+1]);
printf("ho scritoo :'%s'\n",phrase);
close(fd[0]); /* chiude in lettura */
write(fd[1],phrase[i],strlen(phrase[i])+1); /* invia anche 0x00 */
close (fd[1]); // chiude in scrittura
// pid=0 -> figlio
usleep(50000*(1+i)); // Ritardo iniziale
printf("Figlio: %d\n",i+1); // Stampa messaggio del figlio
usleep(500000*(1+i)); // Ritardo finale
return(101+i); // Termina con codice di ritorno
} else {
printf("Ho generato il figlio %d con pid %d\n",i+1,pid);
char message[100]; //creare il buffer
memset(message,0,100);
int bytesread;
bytesread = read(fd[0],message,sizeof(message));
printf("ho letto dalla pipe %d bytes: '%s' \n",bytesread,message);
// close(fd[0]);
}
}
close(fd[0]); /* chiude in scrittura */
close(fd[1]); /* chiude in scrittura */
// Attende che dieci processi terminino
for(i=0;i<argc-1;i++)
{
int status;
wait(&status); // Attende termine di un figlio (uno qualunque)
printf("Terminato processo %d\n",WEXITSTATUS(status));
}
return 0;
}
Yes, you will have N-1 pipes for N processes. You could just have all the processes print to stdout. Since each child process will inherit the parent's stdout, they will all print to whatever the parent's stdout is set to. If you start the chain by redirecting stdout to a file, then they will all print to that file. The problem will synchronizing their writes so that they don't write in the middle of another's write.
If you want to pipe all child output back up to the original parent, you could have each child just write to its parent's pipe everything it reads from its own child's pipe. Each child passing it up the chain in this manner.

Resources