C Read from pipe blocks until child is terminated - c

Parent process creates N children each one replaces itself with exec. There is a communication between parent and exec through an array of pipes (int pipefd[N][2];)
The exec writes to the pipe with these commands:
char msg[50];
sprintf( msg, "\tsent from pid: %d, pi= %f", getpid(), pi);
printf("%s\n",msg);
write(i1, msg, strlen(msg)+1);
and the parent reads with these:
for (i=0;i<N;i++) {
close(pipefd[i][1]); // close the write end of the pipe in the parent
read(pipefd[i][0], buffer, sizeof(buffer));
printf("\n-C-\n");
if (buffer[0] == '\t'){
printf("%s\n",buffer);
}
int j;
for (j=0; j<100; j++) {
buffer[j]='\n';
}
close(pipefd[i][0]);
}
Now the problem is that only after the child is terminated the read gets unblocked and prints the buffer.
What I want to do is print the buffer immediately after the exec writes to the pipe.
Below is the all the code:
Parent File:
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#define N 5
pid_t *pid;
int pipefd[N][2];
int flag = 0;
int count_ctrl_c = 0;
void signal_handler(int sig){
signal(sig, SIG_IGN);
printf("\n");
flag = 1;
signal(SIGINT, signal_handler);
}
int main(int argc, char *argv[]) {
int i;
for (i = 0; i < N; i++) {
pipe(pipefd[i]);
}
int parent_pid = getpid();
pid= (pid_t *)malloc(N * sizeof(pid_t));
for (i = 0; i < N; i++) {
pid[i] = fork();
if (pid[i] == 0) //The parent process will keep looping
{
char b[50];
sprintf( b, "%d", i+1);
char i0[50];
sprintf( i0, "%d", pipefd[i][0]);
char i1[50];
sprintf( i1, "%d", pipefd[i][1]);
char par_id[50];
sprintf( par_id, "%d", parent_pid);
execl("***the/path/to/exec/calculate***", b,i0,i1,par_id,NULL);
}
}
if (parent_pid == getpid()) {
signal(SIGINT, signal_handler);
while(1){
if (flag){
printf("\n-A-\n");
char buffer[100];
int i;
for (i=0;i<N;i++) {
// Apostellei to shma SIGUSR2 se ola ta paidia tou
kill(pid[i], SIGUSR2);
}
printf("\n-B-\n");
for (i=0;i<N;i++) {
close(pipefd[i][1]); // close the write end of the pipe in the parent
read(pipefd[i][0], buffer, sizeof(buffer));
printf("\n-C-\n");
if (buffer[0] == '\t'){
printf("%s\n",buffer);
}
int j;
for (j=0; j<100; j++) {
buffer[j]='\n';
}
close(pipefd[i][0]);
}
//exit(0);
printf("finished reading\n");
flag = 0;
count_ctrl_c++;
if (count_ctrl_c == 2) {
exit(0);
}
}
}
}
}
calculate.c
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#define N 5
int i0,i1,parent_pid;
int flag = 0;
int time_to_term = 0;
double pi;
void signal_handler2(int sig);
void signal_handler(int sig);
void signal_handler2(int sig){
signal(sig, SIG_IGN);
signal(SIGALRM, SIG_IGN);
flag = 1;
signal(SIGUSR2, signal_handler2);
signal(SIGALRM, signal_handler);
}
void signal_handler(int sig){
signal(sig, SIG_IGN);
pid_t pid = getpid();
printf("time: %d, pid: %d, pi: %1.10f\n", time_to_term, pid, pi);
exit(0);
}
int main(int argc, char *argv[]) {
int pid;
signal(SIGINT, SIG_IGN);
signal(SIGUSR2, signal_handler2);
signal(SIGALRM, signal_handler);
time_to_term = atoi(argv[0]);
alarm(time_to_term);
i0 = atoi(argv[1]);
i1 = atoi(argv[2]);
parent_pid = atoi(argv[3]);
double mul = 1.0;
double par = 2.0;
pi = 3.0;
while(1){
pi = pi + (mul * (4.0 / (par * (par + 1.0) * (par + 2.0))));
mul = mul * (-1.0);
par += 2;
sleep(1);
if (flag) {
signal(SIGALRM, SIG_IGN);
close(i0);
char msg[50];
sprintf( msg, "\tsent from pid: %d, pi= %f", getpid(), pi);
printf("%s\n",msg);
write(i1, msg, strlen(msg)+1);
close(i1);
flag = 0;
signal(SIGALRM, signal_handler);
//exit(0);
}
}
}

General tip for troubleshooting this: Run both sides of the pipeline using the strace tool (you can use strace -f to follow forks) so that you can verify what is actually written / read from the pipe.
I suspect that in this case, nothing is written by the child! This is because the stdio layer that you use (printf()) checks whether it is writing to a terminal or to, well, anything else. In case of terminal, it flushes the output after each newline, but in other cases, it flushes only after a large-ish block of data is written (8KiB on GNU systems). Try flushing the output manually using fflush() after printf(). If that helps, you can adjust stdout buffering mode using setvbuf().

IMHO, your design is not really good as all child processes inherit all pipes you created and this is a waste of system resources. The right thing to do would be:
In the parent process:
dup fd's #0 and #1 to preserve for later use by the parent process; use fcntl with F_DUPFD_CLOEXEC on these new fd's to prevent inheritance on exec
close fd #0
close fd #1
create a pipe
prevent inheritance of the read fd of the pipe as said above
dup2 write fd of the pipe to make it fd #1; close the original write fd
fork & exec the child process
repeat steps 3 through 7 for all necessary children
dup2 stored duplicates of original fd's #0 and #1 back to #0 and #1 to restore the printf/scaf functionality
use select to poll read fd's of all pipes and possibly #0 if you expect any input on #0
If two-way communication is required then at step 4 create two pipes with appropriate adjustments to the described procedure and repeat steps 2 through 7 to create children.
In the child process (after exec)
Do all processing as required. Write to the parent either using fd #1 or printf or whatever.
Child process may always obtain its parent PID with getppid()

Your operating system is probably buffering the write.
Try using ioctl and FLUSHW to explicitly flush the pipe after the call to write. Also, check your return values in case something insidious is happening. See http://pubs.opengroup.org/onlinepubs/7908799/xsh/ioctl.html for more reading on ioctl.

Related

Can't use system call write() correctly

The assignment is
Write a program in C/Posix (Linux environment) that synchronizes a parent process and his child process (using signals) that reads and write one by one numbers from 1 to n (given by the user) in the first position of a txt file.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
int n = atoi(argv[1]);
int i;
int fd = open("exercise.txt", O_RDWR | O_CREAT, 0777);
pid_t pid = fork();
if (!pid) {
for (i = 1; i <= (n / 2); i++) {
pause();
char c[2];
sprintf(c, "%d", i + 1);
write(fd, c, sizeof(c));
kill(pid, SIGUSR1);
}
}
else {
for (i = 1; i <= (n / 2); i++) {
char c[2];
sprintf(c, "%d", i);
write(fd, c, sizeof(c));
kill(pid, SIGUSR1);
pause();
}
}
return 0;
}
For each number I pass to my program I always find a txt file with just "1" written in it instead of 1234...n.
What did I do wrong?
The child process will immediately pause upon entering the first iteration of the loop, waiting to receive a signal.
In the first iteration of its loop, the parent process will write two bytes of data to the file, send the SIGUSR1 signal to the child process, and then itself pause, waiting to receive a signal.
The default action when receiving SIGUSR1 is to terminate the process.
pause() returns only when a signal was caught and the signal-catching function returned.
Thus the child process never does anything but possibly wait for a signal and certainly terminate almost immediately.
The parent process remains paused, waiting for a signal.
Some notes:
signal(7) for an overview of signals.
The instructions were, as far as I can tell, to write in one process, and read in the other. Your program attempts to write in both processes.
char c[2];
sprintf(c, "%d", i); /* or i + 1 */
c is only large enough to store strings of, at most, length 1 (optionally one character, plus the null-terminating byte, always). This risks Undefined Behaviour, as there will not be enough room in the buffer if the third argument to sprintf is outside the range [0, 9].
In the child process, pid will be 0 in kill(pid, SIGUSR1);. From kill(2):
If pid equals 0, then sig is sent to every process in the process group of the calling process.
Opening the file before forking, means the two processes share the same file description. Changing the file offset (by reading, writing, or seeking) in one process will change it in the other.
In any case, the instructions are to always write and read at the start of the file (lseek(2)).
You need to catch the delivery of SIGUSR1, or the default action of process termination will occur.
Additionally, the pattern of kill and then pause is not sufficient to avoid a race condition wherein a signal is delivered before the process pauses to wait for signals. This answer covers the problem, and a solution. The method detailed is to use sigprocmask(2) to normally delay (block) the delivery of signals and then sigsupend(2) to temporarily handle those signals.
Here is a cursory example, lacking error handling, wherein the parent process writes and the child process reads.
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
void pdie(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
void handler(int sig)
{
signal(sig, handler);
}
int main(int argc, char const *argv[])
{
if (argc < 2)
return EXIT_FAILURE;
int fd = open("exercise.txt", O_RDWR | O_CREAT | O_TRUNC, 0777);
if (-1 == fd)
pdie("open");
int n = atoi(argv[1]);
if (n < 1)
n = 10;
sigset_t set, old;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigprocmask(SIG_BLOCK, &set, &old);
signal(SIGUSR1, handler);
pid_t pid = fork();
if (-1 == pid)
pdie("fork");
for (int i = 0; i < n; i++) {
char buf[32];
if (pid) {
/* parent writes */
int len = snprintf(buf, sizeof buf, "%d", i + 1);
lseek(fd, 0, SEEK_SET);
write(fd, buf, (size_t) len);
kill(pid, SIGUSR1);
sigsuspend(&old);
} else {
/* child reads */
sigsuspend(&old);
memset(buf, 0, sizeof buf);
lseek(fd, 0, SEEK_SET);
ssize_t bytes = read(fd, buf, sizeof buf - 1);
printf("Child read %zd bytes: %s\n", bytes, buf);
kill(getppid(), SIGUSR1);
}
}
close(fd);
if (pid)
waitpid(pid, NULL, WUNTRACED);
}

pipe stdout of a child to stdin of another child in c

This is what I'm trying to do: a parent process creates two child processes, then pipes stdout of one to stdin of another. Then parent waits for 5 secs and kills the first child.
This is how I approached it: First I create a pipe. Then fork to create the first child (gen in code) and dup2 to duplicate pipe's write onto stdout. Another fork for second child (called cons in code), dup2 to duplicate read end onto stdin. Cons just prints the data. Parent sends SIGTERM to first child, second child reads until EOF, so it closes on it's own.
Nothing but my error output (here used for debugging) is printed. Gen generates two random numbers, but loop in cons doesn't get executed. So I suppose there's nothing on stdin of cons.
I consulted Google and followed this How do I chain stdout in one child process to stdin in another child in C?, but didn't manage to figure out what I messed up. Would appreciate any help. Thanks
Compilation: gcc -std=c99 -Wall -Werror main.c -o main on Bash in Windows 10
Code:
#define _POSIX_SOURCE
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <signal.h>
#define READ_END 0
#define WRITE_END 1
#define BUF_SIZE 10
int main(int argc, char* argv[])
{
pid_t gen, cons;
int fd[2];
if (pipe(fd) < 0) {
// pipe error
exit(1);
}
gen = fork();
if (gen < 0) {
// fork error
close(fd[READ_END]);
close(fd[WRITE_END]);
exit(2);
} else if (gen == 0) {
// gen child
close(fd[READ_END]);
time_t t;
srand((unsigned)time(&t));
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[WRITE_END]);
while(1) {
int a = rand() % 1000;
int b = rand() % 1000;
printf("gen %d %d\n", a, b);
fprintf(stderr, "err, gen %d %d\n", a, b);
sleep(1);
}
}
else {
cons = fork();
if (cons < 0) {
// fork error
close(fd[READ_END]);
close(fd[WRITE_END]);
kill(gen, SIGKILL);
exit(2);
} else if (cons == 0) {
// cons child
close(fd[WRITE_END]);
dup2(fd[READ_END], STDIN_FILENO);
close(fd[READ_END]);
char line[BUF_SIZE];
while (fgets(line, sizeof(line), stdin)) {
printf("cons received: %s\n", line);
fprintf(stderr, "cons lives!\n");
}
} else {
// parent
close(fd[READ_END]);
close(fd[WRITE_END]);
sleep(5);
kill(gen, SIGTERM);
}
}
return 0;
}
Standard output is by default buffered, so your gen child only queues output for later sending. So you must fflush it to force your messages to be immedialely delivered:
...
printf("gen %d %d\n", a, b);
fprintf(stderr, "err, gen %d %d\n", a, b);
fflush(stdout); // <= force immediate send
sleep(1);
...

sending synchronized signal among 3 processes

I'm working on signal project get a file with 0, increase to the input (ex ./count 300 sample.txt) by using sync signals p1->p2->p3->p1, after they increase number by 1, the fall in to sleep and call next one.
but I got stuck with two problems
how to and where to implement increasing number process, in signal handling or main( 0 -> 1 -> 2 ... input )
don't know how to implement with sigwait() or sigprocmask() what's the difference? . can i choose either one to guarantee that they are synchronized? or should I just use sleep?
belows are code that I've been working on so far.
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
void sig_usr1(int signo)
{
char a;
sigset_t sigset, oldset;
sigemptyset (&oldset);
sigemptyset (&sigset);
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigset, &oldset);
fd =open("./sample.txt",O_RDWR);
pread (fd, a, sizeof(a));
if (argv[0]>a)
{a ++;
truncate ("./sample.txt", 0);
write(fd, a, sizeof(a));
}
}
int main (int argc, char** argv)
{
int fd;
int num=0;
struct sigaction usrsig ;
if(!(argv[0]>0))
printf("insert positive integer");
fd = open("./sample.txt",O_RDWR|O_CREAT|O_TRUNC);
write(fd, num ,sizeof(num);
pid_t child[3];
usrsig.sa_handler =sig_usr; // Parent
sigemptyset(&usrsig.sa_mask);
usrsig1.sa_flags = 0;
sigaction(SIGUSR1,&usrsig, 0);
for ( i=0; i<3; i++)
{
child[i] = fork();
if(child[i] == 0)
break;
}
pid_t prev;
if(i ==0) prev = getppid();
else prev = child[i-1];
kill(pid_prev, SIGUSR1)
}

Synchronization of parent and child with SIGUSR signal in C. Make parent and child read one after the other

I have created a two way communication between parent and child processes using two pipes. Parent and child write data and I was able to make them read the data from each other. Parent writes numbers 1 to 5, and child writes numbers from 6 to 10. But I want parent to start reading data the first, and then reading continues in this order switching from parent to child until all the data are read: 6,1,7,2,8,3,9,4,10,5. I have tried to synchronize the reading with SIGUSR1 but when the parent is reading for the second time the program stops. I have searched a lot to find where the problem can be, and tried some tips and alike working examples, but nothing seems to help. Here is my code:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
void paction(int dummy)
{
printf("P*************************************************\n");
}
void caction(int dummy)
{
printf("C*************************************************\n");
}
int main()
{
int pfd[2];
int pfd2[2];
pid_t cfork, pfork;
if (pipe(pfd) == -1 || pipe(pfd2) == -1) {
fprintf(stderr,"Pipe failed");
exit(1);
}
cfork = fork();
signal(SIGUSR1, paction);
if (cfork == -1) {
printf("Fork Failed\n");
exit(1);
}
else if (cfork > 0) { /*parent process*/
int numbers[] = {1, 2,3, 4, 5};
int numbers2[] = { 6, 7,8, 9, 10 };
close(pfd[0]); /*close read end, write and then close write end*/
/*write part*/
int limit = 5;
int i;
for (i = 0; i < limit; i++) {
printf("Parent sends: %d\n", numbers[i]);
write(pfd[1], &numbers[i], sizeof(numbers[i]));
printf("Child sends: %d\n", numbers2[i]);
write(pfd2[1], &numbers2[i], sizeof(numbers2[i]));
}
printf("***************************************************\n");
close(pfd[1]);
close(pfd2[1]);
/*read part/////////////////////////////////////////*/
int temp;
int reads = 5;
int j;
for (j = 0; j < reads; j++) {
sleep(1);
read(pfd2[0], &temp, sizeof(temp));
printf("Parent gets: %d\n", temp);
kill(cfork, SIGUSR1);
pause();
}
/*printf("***************************************************\n");*/
kill( cfork, SIGUSR1 );
close(pfd2[0]);
}
else { /*child process*/
signal(SIGUSR1, caction);
close(pfd[1]);
int temp;
int reads = 5;
int j;
pfork = getppid();
for (j = 0; j < reads; j++) {
sleep(1);
read(pfd[0], &temp, sizeof(temp));
printf("Child gets: %d\n", temp);
kill(getppid(), SIGUSR1);
pause();
}
/*printf("***************************************************\n");*/
close(pfd[0]);
close(pfd2[0]);
}
return 0;
}
My output looks like this:
> Parent sends:1
> Child sends:6
> Parent sends:2
> Child sends:7
> Parent sends:3
> Child sends:8
> Parent sends:4
> Child sends:9
> Parent sends:5
> Child sends:10
> **************************************************************
Parent gets:6
> C************************************************************
> Child gets:1
> P*************************************************************
> Parent gets:7
And here is when it stops.
If someone can help me I would really appreciate it because I really want to know where the problem is, and since I am a beginner in C programming and processes!
Thank you in advance
printf() is not an async-safe function. Calling printf() in both normal code and a signal handler will cause undefined behavior. In particular, printf() may need to take a lock on the output-stream, while taking locks in signal-handlers is very inadvisable (risk of self-deadlock).
Maybe it is a bad idea to use signals, but I had a task in which it was assigned to use SIGUSR1. I solved the issue by adding:
static struct sigaction pact, cact;
/* set SIGUSR1 action for parent */;
pact.sa_handler = p_action;
sigaction(SIGUSR1, &pact, NULL);
After the parent was assigned the first action, it worked fine.
Thank you:)

New to IPC, can't get my pipe to work

Sorry for the length of this post... I've encountered about a zillion problems in this. Up front I'll say I'm a student and my professor is a worthless resource. So, all I want to to do is have producer fork, then the parent producer will count some stuff in a file and send two ints to consumer, which was launched by the child process. I've tested everything, the fork and the file stuff works and I have printf statements all over the place so I know what is being done and where the code is at.
When I added the
if (pipe(pipefd) == -1) {
perror("pipe");
}
it caused my parent to just terminate. It reaches "parent pipe open" but then it dies. I checked with $ ps to see if it was just hung, but it's not there; it just dies. If I take that snippet out, it runs to the end but I presume if that code isn't there, then it's not actually aware that pipefd is a pipe... right?
I did search on this site and found another example of this and followed what he did as well as the answer and mine just refuses to work. I'm pretty sure it's a trivially easy thing to fix but I've run out of ideas of what to try :(
I don't really want to post all my code because it'll be a huge wall of text but I don't want to accidentally cut something out that turns out to be important either.
producer.c
#include <stdio.h> /* printf, stderr, fprintf */
#include <sys/types.h> /* pid_t */
#include <unistd.h> /* _exit, fork, execl */
#include <stdlib.h> /* exit */
#include <errno.h> /* errno */
#include <string.h> /* strlen */
#include <sys/wait.h> /* wait */
#define SLEEP_TIME 8
int main (int argc, char *argv[]){
//PID
pid_t local_pid;
local_pid = fork();
//Logic to determine if the process running is the parent or the child
if (local_pid == -1) {
/* Error:
* When fork() returns -1, an error happened
* (for example, number of processes reached the limit).
*/
fprintf(stderr, "can't fork, error %d\n", errno);
exit(EXIT_FAILURE);
} else if (local_pid == 0) {
//Child specific code
int child;
char *temp[] = {NULL};
printf("Child PID found\n");
child = execv("./consumer", temp);
_exit(0);
} else {
//Parent specific code
printf("Parent running\n");
//open file
FILE * randStrings;
randStrings = fopen("randStrings.txt", "r");
int file_length;
int num_of_e = 0;
int c; //using this as a char
//until eof
while (feof(randStrings) == 0) {
c = fgetc(randStrings);
//calculate length of file
file_length++;
//count e chars
if (c == 'e') {
num_of_e++;
}
}
//close file
fclose(randStrings);
//send bundle to child
int a[2];
a[0] = num_of_e;
a[1] = file_length;
printf("num of e = %i\n", a[0]);
printf("len = %i\n", a[1]);
//set up parent pipe
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
printf("x\n");
}
printf("parent pipe open\n");
close(pipefd[0]); //close the read end
write(pipefd[1], &a[0], sizeof(int));
write(pipefd[1], &a[1], sizeof(int));
close(pipefd[1]);
printf("parent pipe closed\n");
//wait for child to finish running
wait(NULL);
printf("parent out\n");
//terminate
}
}
and consumer.c
#include <stdio.h> /* printf, stderr, fprintf */
#include <sys/types.h> /* pid_t */
#include <unistd.h> /* _exit, fork, execl */
#include <stdlib.h> /* exit */
#include <errno.h> /* errno */
#define SLEEP_TIME 5
int main (int argc, char *argv[]){
sleep(SLEEP_TIME);
printf("Child program launched\n");
//receive bundle
int pipefd[2];
int buf[2];
if (pipe(pipefd) == -1) {
perror("pipe");
printf("child x\n");
}
close(pipefd[1]); //child closes write end
buf[0] = 0;
buf[1] = 0;
/*int i = 0; // i dont like this
while (read(pipefd[0], &buf[i], sizeof(int)) > 0) {
i++;
}*/
printf("child reading pipe\n");
read(pipefd[0], &buf[0], sizeof(int));
read(pipefd[0], &buf[1], sizeof(int));
close(pipefd[0]);
//buf should have the stuff in it
int num_of_e = buf[0];
int file_length = buf[1];
printf("child num of e = %i\n", num_of_e);
printf("child len = %i\n", file_length);
//open file
FILE * resultStrings;
resultStrings = fopen("resultStrings.txt", "w");
for (int i = 0; i < num_of_e; i++) {
//write num_of_e e chars
fputc('e', resultStrings);
}
//or if no e chars, write - chars
if (num_of_e == 0) {
for (int i = 0; i < file_length; i++) {
//write file_length '-' chars
fputc('-', resultStrings);
}
}
//close file
fclose(resultStrings);
printf("child out\n");
}
if you're still here after all that, you deserve a thank you just due to the length of this.
You're doing it wrong. The whole mechanism works because a child process inherits the parent's open file descriptors.
It should go like this:
Open the pipe with pipe(pipefd)
fork()
Parent (producer):
closes the read side (pipefd[0])
writes to the write side (pipefd[1])
Child (consumer):
closes the write side (pipefd[1])
reads from the read side (pipefd[0]) or calls exec
You are opening distinct pipes in both the parent and child process (after you've forked.) It needs to happen before you fork.
Now since you're execing, the new process needs to be aware of read-only pipe. There are a couple ways you could do this:
Pass it the file descriptor number (pipefd[0]) on the command line
dup2(1, fd) it to be the stdin of the newly exec'd process

Resources