Here’s what my program is supposed to do:
create 4 child processes:
process 0 is reading 1 byte at a time from STDIN, then writing it into FIFO
process 1 is reading this 1 byte from fifo and write its value as HEX into shared memory
process 2 is reading HEX value from shared memory and writing it into pipe
finally process 3 is reading from pipe and writing into STDOUT (in my case: terminal)
I can’t change communication channels. FIFO, then shared memory, then pipes are the only option.
My problem: Program stops at random moments when some file is directed into stdin (for example:./program < /dev/urandom). Sometimes after writing 5 HEX values, sometimes after 100. Weird thing is that when it is working and in another terminal I write “pstree -c” there is 1 main process with 4 children processes (which is what I want), but when I write “pstree -c” after it stopped writing (but still runs) there are only 3 child processes. For some reason 1 is gone even though they all have while(1) in them..
I think I might have problem with synchronization here, but I am unable to spot it (I’ve tried for many hours).
Here’s the code:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <string.h>
#include <signal.h>
#define BUFSIZE 1
#define R 0
#define W 1
// processes ID
pid_t p0, p1, p2, p3;
// FIFO variables
int fifo_fd;
unsigned char bufor[BUFSIZE] = {};
unsigned char bufor1[BUFSIZE] = {};
// Shared memory variables
key_t key;
int shmid;
char * tab;
// zmienne do pipes
int file_des[2];
char bufor_pipe[BUFSIZE*30] = {};
void proces0()
{
ssize_t n;
while(1)
{
fifo_fd = open("/tmp/fifo",O_WRONLY);
if(fifo_fd == -1)
{
perror("blad przy otwieraniu kolejki FIFO w p0\n");
exit(1);
}
n = read(STDIN_FILENO, bufor, BUFSIZE);
if(n<0)
{
perror("read error w p0\n");
exit(1);
}
if(n > 0)
{
if(write(fifo_fd, bufor, n) != n)
{
perror("blad zapisu do kolejki fifo w p0\n");
exit(1);
}
memset(bufor, 0, n); // czyszczenie bufora
}
close(fifo_fd);
}
}
void proces1()
{
ssize_t m, x;
char wartosc_hex[30] = {};
while(1)
{
if(tab[0] == 0)
{
fifo_fd = open("/tmp/fifo", O_RDONLY); // otwiera plik typu fifo do odczytu
if(fifo_fd == -1)
{
perror("blad przy otwieraniu kolejki FIFO w p1\n");
exit(1);
}
m = read(fifo_fd, bufor1, BUFSIZE);
x = m;
if(x < 0)
{
perror("read error p1\n");
exit(1);
}
if(x > 0)
{
// Konwersja na HEX
if(bufor1[0] < 16)
{
if(bufor1[0] == 10) // gdy enter
{
sprintf(wartosc_hex, "0x0%X\n", bufor1[0]);
}
else
{
sprintf(wartosc_hex, "0x0%X ", bufor1[0]);
}
}
else
{
sprintf(wartosc_hex, "0x%X ", bufor1[0]);
}
// poczekaj az pamiec bedzie pusta (gotowa do zapisu)
strcpy(&tab[0], wartosc_hex);
memset(bufor1, 0, sizeof(bufor1)); // czyszczenie bufora
memset(wartosc_hex, 0, sizeof(wartosc_hex)); // przygotowanie tablicy na zapis wartosci hex
x = 0;
}
close(fifo_fd);
}
}
}
void proces2()
{
close(file_des[0]); // zablokuj kanal do odczytu
while(1)
{
if(tab[0] != 0)
{
if(write(file_des[1], tab, strlen(tab)) != strlen(tab))
{
perror("blad write w p2");
exit(1);
}
// wyczysc pamiec dzielona by przyjac kolejny bajt
memset(tab, 0, sizeof(tab));
}
}
}
void proces3()
{
ssize_t n;
close(file_des[1]); // zablokuj kanal do zapisu
while(1)
{
if(tab[0] == 0)
{
if((n = read(file_des[0], bufor_pipe, sizeof(bufor_pipe))) > 0)
{
if(write(STDOUT_FILENO, bufor_pipe, n) != n)
{
perror("write error w proces3()");
exit(1);
}
memset(bufor_pipe, 0, sizeof(bufor_pipe));
}
}
}
}
int main(void)
{
key = 5678;
int status;
// Tworzenie plikow przechowujacych ID procesow
int des_pid[2] = {};
char bufor_proces[50] = {};
mknod("pid0", S_IFREG | 0777, 0);
mknod("pid1", S_IFREG | 0777, 0);
mknod("pid2", S_IFREG | 0777, 0);
mknod("pid3", S_IFREG | 0777, 0);
// Tworzenie semaforow
key_t klucz;
klucz = ftok(".", 'a'); // na podstawie pliku i pojedynczego znaku id wyznacza klucz semafora
if(klucz == -1)
{
perror("blad wyznaczania klucza semafora");
exit(1);
}
semafor = semget(klucz, 1, IPC_CREAT | 0777); // tworzy na podstawie klucza semafor. 1 - ilosc semaforow
if(semafor == -1)
{
perror("blad przy tworzeniu semafora");
exit(1);
}
if(semctl(semafor, 0, SETVAL, 0) == -1) // ustawia poczatkowa wartosc semafora (klucz, numer w zbiorze od 0, polecenie, argument 0/1/2)
{
perror("blad przy ustawianiu wartosci poczatkowej semafora");
exit(1);
}
// Tworzenie lacza nazwanego FIFO
if(access("/tmp/fifo", F_OK) == -1) // sprawdza czy plik istnieje, jesli nie - tworzy go
{
if(mkfifo("/tmp/fifo", 0777) != 0)
{
perror("blad tworzenia FIFO w main");
exit(1);
}
}
// Tworzenie pamieci dzielonej
// Lista pamieci wspoldzielonych, komenda "ipcs"
// usuwanie pamieci wspoldzielonej, komenta "ipcrm -m ID_PAMIECI"
shmid = shmget(key, (BUFSIZE*30), 0666 | IPC_CREAT);
if(shmid == -1)
{
perror("shmget");
exit(1);
}
tab = (char *) shmat(shmid, NULL, 0);
if(tab == (char *)(-1))
{
perror("shmat");
exit(1);
}
memset(tab, 0, (BUFSIZE*30));
// Tworzenie lacza nienazwanego pipe
if(pipe(file_des) == -1)
{
perror("pipe");
exit(1);
}
// Tworzenie procesow potomnych
if(!(p0 = fork()))
{
des_pid[W] = open("pid0", O_WRONLY | O_TRUNC | O_CREAT); // 1 - zapis, 0 - odczyt
sprintf(bufor_proces, "Proces0 ma ID: %d\n", getpid());
if(write(des_pid[W], bufor_proces, sizeof(bufor_proces)) != sizeof(bufor_proces))
{
perror("blad przy zapisie pid do pliku w p0");
exit(1);
}
close(des_pid[W]);
proces0();
}
else if(p0 == -1)
{
perror("blad przy p0 fork w main");
exit(1);
}
else
{
if(!(p1 = fork()))
{
des_pid[W] = open("pid1", O_WRONLY | O_TRUNC | O_CREAT); // 1 - zapis, 0 - odczyt
sprintf(bufor_proces, "Proces1 ma ID: %d\n", getpid());
if(write(des_pid[W], bufor_proces, sizeof(bufor_proces)) != sizeof(bufor_proces))
{
perror("blad przy zapisie pid do pliku w p1");
exit(1);
}
close(des_pid[W]);
proces1();
}
else if(p1 == -1)
{
perror("blad przy p1 fork w main");
exit(1);
}
else
{
if(!(p2 = fork()))
{
des_pid[W] = open("pid2", O_WRONLY | O_TRUNC | O_CREAT); // 1 - zapis, 0 - odczyt
sprintf(bufor_proces, "Proces2 ma ID: %d\n", getpid());
if(write(des_pid[W], bufor_proces, sizeof(bufor_proces)) != sizeof(bufor_proces))
{
perror("blad przy zapisie pid do pliku w p2");
exit(1);
}
close(des_pid[W]);
proces2();
}
else if(p2 == -1)
{
perror("blad przy p2 fork w main");
exit(1);
}
else
{
if(!(p3 = fork()))
{
des_pid[W] = open("pid3", O_WRONLY | O_TRUNC | O_CREAT); // 1 - zapis, 0 - odczyt
sprintf(bufor_proces, "Proces3 ma ID: %d\n", getpid());
if(write(des_pid[W], bufor_proces, sizeof(bufor_proces)) != sizeof(bufor_proces))
{
perror("blad przy zapisie pid do pliku w p3");
exit(1);
}
close(des_pid[W]);
proces3();
}
else if(p3 == -1)
{
perror("blad przy p3 fork w main");
exit(1);
}
else
{
// proces macierzysty
waitpid(p0, &status, 0);
waitpid(p1, &status, 0);
waitpid(p2, &status, 0);
waitpid(p3, &status, 0);
//wait(NULL);
unlink("/tmp/fifo");
shmdt(tab); // odlaczenie pamieci dzielonej
shmctl(shmid, IPC_RMID, NULL); // usuwanie pamieci wspoldzielonej
printf("\nKONIEC PROGRAMU\n");
}
}
}
}
exit(0);
}
after using “strace ./projekt_wlasciwy < /dev/urandom”
I’ve got something like this:
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb7e2e728) = 26916
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb7e2e728) = 26917
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb7e2e728) = 26918
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb7e2e728) = 26919
waitpid(26916, 0xAA 0xC1 0x84 0x9C 0x4E 0x0D 0x54 0xB2 0xE7 0x19 0x22 0xA0 0x2E 0xF0 0x1C 0xC4 0xDF 0x21 0xA2 0xB4 0x3B 0xEE [{WIFSIGNALED(s) && WTERMSIG(s) == SIGPIPE}], 0) = 26916
--- SIGCHLD (Child exited) @ 0 (0) ---
waitpid(26917, 0x40 0xbfb083a8, 0) = ? ERESTARTSYS (To be restarted)
--- SIGWINCH (Window changed) @ 0 (0) ---
waitpid(26917, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGTERM}], 0) = 26917
--- SIGTERM (Terminated) @ 0 (0) ---
+++ killed by SIGTERM +++
There is probably a bug in your code which makes one of the children crash. I suggest to add some debug output (write it to the console or to a log file) so you can see what data comes in and what goes out.
I’m also a bit worried by the output.
ERESTARTSYSmeans there is a system call which can be restarted (= just try again). Not sure where that comes from.Also
SIGTERMmeans someone killed the process. But that could bestracetelling you that you stopped it. If it’s the output of one of the children, then someone killed your process.