I am using these two programs of this answer. This answer uses named-pipes and not pipes, am I correct?
I have written main.c, which is actually the code of my actual project, minimized to this specific question (that's why I have a for loop for example).
#include <unistd.h>
#include <sys/wait.h>
#include <stddef.h>
#include <limits.h>
#include <stdio.h>
int main(void) {
pid_t pid;
int i;
for(i = 0; i < 2; ++i) {
pid = fork();
if (pid == -1) {
// error, failed to fork()
perror("failed to fork()");
return 1;
} else if (pid == 0) {
// child code
if(i < 1) {
// the writer.c
char* arg_list[] = { "w", NULL };
execv( "w", arg_list );
printf("exec FAILED\n");
} else {
// the reader.c
char* arg_list[] = { "r", NULL };
execv( "r", arg_list );
printf("exec FAILED\n");
}
}
}
// parent code
int status;
// wait for all children to terminate
while ((pid = wait(&status)) > 0) {
if (status == 1) {
printf("The child process terminated with an error!\n");
return -1;
}
}
printf("All children are done\n");
return 0;
}
The problem is that sometimes, the reader receives garbage (or most likely nothing) and it hangs up.
Sample output:
Received: Hi
All children are done
samaras#samaras-A15:~/test$ ./m
Received: Hi
All children are done
samaras#samaras-A15:~/test$ ./m
Received: <----------------- This is garbage, that is not reproducible
^C
So, what am I missing?
No need to read below that point.
My guesses are (not checked, so if I am correct, I still need clarification):
The reader runs before writer, that's why it has garbage, but then why it hangs?
or
I need to write a wrapper function read_all() (and one for the write case as well?) that collects all the data that the pipe spits, but then why if I replace "Hi" with "H", I have the same behaviour?
EDIT:
In case my first guess is the case, I put a loop for reading, but it will execute forever in the case that reader starts first.
In the case that I see garbage, after running with strace -f I got this:
...
[pid 3326] read(-1, 0xbfddd80c, 1024) = -1 EBADF (Bad file descriptor)^C
Process 3324 resumed
Process 3325 detached
Process 3326 detached
Your loops (or lack of) have nothing to do with it. When your reader opens (open()) the pipe for reading before writer creates the pipe, then the file descriptor your readers waits on is invalid (-1). So even when writer writes something later on, reader just waits on an invalid fd (-1) and is never going to read anything. Trivially, you could solve it with:
while( (fd = open(myfifo, O_RDONLY)) == -1);
in reader so that it waits until pipe is available. I am actually wondering if there can be a better approach than this. One other way I can think of is a loop over access(), but it's not massively different to this...
Related
I'm practicing writing fork processes and I got stuck in the area where parent passes the process off to the child. I seem to get a bit lost there.
I was thinking the child opens /dev/null and then writes a lot of data to this file. Each iteration of the loop writes 10 bites to /dev/null.
More specifically I want to write a 10-character array and run the loop a bunch of times like 100,000,000 iterations!
In the meantime the parent will be in standby mode until the child process has finished. This is what I currently have:
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
int fd = open("/dev/null",O_WRONLY);
pid_t newPid = fork();
if (newPid <0) {
perror("fork() failed");
exit(1);
}
if (newPid > 0) { //Parent
pid[procIdx] = newPid;
} else { //Child
//write 10 bytes to /dev/null
write(fd, "ABCDEFGHIJ", 10)
exit(0);
close (fd);
}
// Wait for processes to complete
for (int i = 0; i < // unsure what to write here; i++) {
if (waitpid(pid[i], NULL, 0) == -1) {
perror("waitpid() failed");
exit(1);// process termination status value 1
}
}
exit(0); // process termination status value 0
}
In the code below, is it safe to rely on read() failure to detect termination of child?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
int pipefd[2];
pipefd[0] = 0;
pipefd[1] = 0;
pipe(pipefd);
pid_t pid = fork();
if (pid == 0)
{
// child
close(pipefd[0]); // close unused read end
while ((dup2(pipefd[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {} // send stdout to the pipe
while ((dup2(pipefd[1], STDERR_FILENO) == -1) && (errno == EINTR)) {} // send stderr to the pipe
close(pipefd[1]); // close unused write end
char *argv[3];
argv[0] = "worker-app";
argv[1] = NULL;
argv[2] = NULL;
execvp("./worker-app", argv);
printf("failed to execvp, errno %d\n", errno);
exit(EXIT_FAILURE);
}
else if (pid == -1)
{
}
else
{
// parent
close(pipefd[1]); // close the write end of the pipe in the parent
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
while (1) // <= here is it safe to rely on read below to break from this loop ?
{
ssize_t count = read(pipefd[0], buffer, sizeof(buffer)-1);
printf("pipe read return %d\n", (int)count);
if (count > 0)
{
printf("child: %s\n", buffer);
}
else if (count == 0)
{
printf("end read child pipe\n", buffer);
break;
}
else if (count == -1)
{
if (errno == EINTR)
{ continue;
}
printf("error read child pipe\n", buffer);
break;
}
}
close(pipefd[0]); // close read end, prevent descriptor leak
int waitStatus = 0;
waitpid(pid, &waitStatus, 0);
}
fprintf(stdout, "All work completed :-)\n");
return EXIT_SUCCESS;
}
Should I add something in the while(1) loop to detect child termination? What specific scenario could happen and break this app ?
Some thoughts of improvements below. However would I just waste CPU cycles?
Use kill with special argument 0 that won't kill the process but just check if it is responsive:
if (kill(pid, 0)) { break; /* child exited */ };
/* If sig is 0, then no signal is sent, but error checking is still performed; this can be used to check for the existence of a process ID or process group ID. https://linux.die.net/man/2/kill */
Use waitpid non-blocking in the while(1) loop to check if child has exited.
Use select() to check for pipe readability to prevent read() from possibly hanging?
Thanks!
Regarding your ideas:
If the child spawns children of its own, the read() won't return 0 until all of its descendants either die or close stdout and stderr. If it doesn't, or if the child always outlives all of its descendants, then just waiting for read() to return 0 is good enough and won't ever cause a problem.
If the child dies but the parent hasn't yet wait(2)ed on it, then kill(pid, 0) will succeed as if the child were still alive (at least on Linux), so this isn't an effective check from within your parent program.
A non-blocking waitpid() on its own would appear to fix the problem with the child having children of its own, but would actually introduce a subtle race condition. If the child exited right after the waitpid() but before the read(), then the read() would block until the rest of the descendants exited.
On its own, if you used select() in a blocking way, it's no better than just calling read(). If you used select() in a non-blocking way, you'd just end up burning CPU time in a loop.
What I'd do:
Add a no-op signal handler function for SIGCHLD, just so that it causes EINTR when it occurs.
Block SIGCHLD in the parent before you start looping.
Use non-blocking reads, and use pselect(2) to block to avoid spinning the CPU forever.
During the pselect, pass in a sigset_t that doesn't have SIGCHLD blocked, so that it's guaranteed to cause an EINTR for it when it eventually gets sent.
Somewhere in the loop, do a non-blocking waitpid(2), and handle its return appropriately. (Make sure you do this at least once after blocking SIGCHLD but before calling select for the first time, or you'll have a race condition.)
My program is a rudimental little shell.
It allow you to run programs in PATH as ls, cd..also with arguments.
To run the program type from terminal "./myshell2" then it starts and you can insert how many commands you want.
It starts a child process, runs execvp,it returns and restarts so you can type a new command.
When typed "Q" or "q" all the entire program should terminates.
The problem is that I don't know how to stop it,the code is below.
My idea is, when typed "Q" or "q", to kill the child process created and send a signal to comunicate its bad termination(of child process).
So the final status(from parent) 'll be not 1 and the function returns.
I commented some parts of the code hoping that it's easier to understand.
It works the problem is that to stop it I need of ctrl C.
I would like to say to child process that he must ends with a non-zero value.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <signal.h>
int main(int argc, char * argv[]) {
while(1)
{
pid_t pid = fork();
if (pid == -1) {
perror("fork error");
exit(EXIT_FAILURE);
}
if (pid == 0) { // child process
printf("type the command to start (and arguments if required) \n"
"Q to quit\n");
char *dest[10]; // allow you to insert
char line[4096];//commands from terminal
if (fgets(line,sizeof(line),stdin)==0) return 1;
int i;
line[strcspn(line, "\n")] = '\0';
char *st = line;
for (i=0; i< 10 && (dest[i]=strsep(&st," "))!=NULL;i++)
continue;//now you typed the command
if ( ( memcmp(dest[0],"Q",1)==0 ) // if Q or q the program
|| (memcmp(dest[0],"q",1)==0) ) //must end
{
printf("got it!\n");
if (kill(getpid(),SIGSEGV)==-1) printf("kill error\n");
//in theory the process should terminates with bad status
// and the value of the variable "status" 'll be not 0
// I think that the problem is in this part of the code
}
if( strcmp(dest[0]," ")!=0 )
{
int res = execvp(dest[0], dest);
}
else
{ int res= execvp(dest[1],dest+1);}
perror("execvp error");
exit(EXIT_FAILURE);
}
int status;
pid_t child = wait(&status);
if (child == -1) {
perror("wait error");
exit(EXIT_FAILURE);
}
if (status==1)
break; //so it can exit from the loop that creates new process
setenv("WAIT","TRUE",0); //dont' worry about
//perror("setenv error\n");
if (memcmp("TRUE",getenv("WAIT"),4) == 0 ) //these 6 lines
printf("WAIT=TRUE\n");
else if(memcmp("FALSE",getenv("WAIT"),4) == 0 )
printf("WAIT=FALSE\n");
printf("end current process (status=%d, child=%d)\n", WEXITSTATUS(status), son);
}
return EXIT_SUCCESS;
}
You're printing out WEXITSTATUS() for all cases, but that isn't right. You need to check if the status returned by wait is an exit status or not using WIFEXITED(). If it's non-zero then the child exited normally. Otherwise, you can use WIFSIGNALED() to see if the child was terminated and you'll get the signal from WTERMSIG()
if(WIFEXITED(status))
{
printf("end current process (status=%d, child=%d)\n", WEXITSTATUS(status), son);
}
else if(WIFSIGNALED(status))
{
printf("end current process (signal=%d, child=%d)\n", WTERMSIG(status), son);
}
You really should have the parent process handle the inputting of the command and leave the child process to run it though.
As explained in this answer, I'd be expecting the reader process to catch the EOF right after the writer process closes all related file descriptors.
But that doesn't happen and this program ends up stuck in an endless loop.
Parent waits for it's child to finish & child waits for EOF signalizing closed pipe.
Why the reader process doesn't receive EOF?
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <wait.h>
#define STRING_TO_SEND "Hello, world!\n"
int main() {
int fd[2], i = 0;
__pid_t pid;
char _char;
ssize_t nbytes;
pipe(fd);
pid = fork();
if (pid == -1) {
// Error
perror("Error forking!");
return EXIT_FAILURE;
} else if (pid == 0) {
// Child
close(fd[1]);
while ((nbytes = read(fd[0], &_char, 1)) != EOF) {
if (nbytes == 0)
continue;
putchar(_char);
}
close(fd[0]);
} else {
// Parent
close(fd[0]);
for(;;) {
_char = STRING_TO_SEND[i++];
write(fd[1], &_char, 1);
if (_char == '\0')
break;
}
close(fd[1]);
close(STDOUT_FILENO);
while (wait(NULL)>0) {}
}
return 0;
}
You simply misunderstood the "end of file" indication of read() which simply means nothing more to read for read() (read() returns 0 in that case). But read() doesn't actually return the value EOF. So your condition should be:
while ((nbytes = read(fd[0], &_char, 1)) > 0) {
Also __pid_t is an internal type of your C library. You shouldn't use that; just use pid_t.
See read(2)'s man page for details.
EOF is a constant usually defined to -1 that stdio (the C library buffering layer around the raw system calls) uses to signal end of file in functions like getchar() which conflate the returned character with an end-of-file signal.
read signals end-of-file by simply returning 0. Note that it can also return -1 if there's an error (e.g., you can get EINTR if the read is interrupted by a signal handler before it read anything).
Consequently, what you want is something like:
while ((nbytes = read(fd[0], &_char, 1)) > 0){ /*...*/ }
if (0>nread) { /*report error (or maybe repeat if it's EINTR)*/ }
Manpages (read(2)) or the POSIX spec for read document all this.
I need help with this sample application. When I run it, it gets stuck after the child process prints "Child sending!".
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#define INPUT 0
#define OUTPUT 1
int main()
{
int fd1[2];
int fd2[2];
int pid;
if (pipe(fd1) < 0)
exit(1);
if (pipe(fd2) < 0)
exit(1);
if ((pid = fork()) < 0)
{
perror("fork");
exit(1);
}
else if (pid == 0)
{
close(fd1[INPUT]);
close(fd2[OUTPUT]);
char *str = "Hello World!";
printf("Child sending!\n");
write(fd1[OUTPUT], str, strlen(str));
char *bufferc = (char *)malloc(1000);
char *readbufferc = (char *)malloc(80);
int rdc;
int gotdata = 0;
while (gotdata == 0)
while ((rdc = read(fd2[INPUT], readbufferc, sizeof(readbufferc))) > 0)
{
strncat(bufferc,readbufferc,rdc);
gotdata = 1;
}
printf("Child received: %s",bufferc);
free(readbufferc);
free(bufferc);
exit(0);
}
else
{
close(fd1[OUTPUT]);
close(fd2[INPUT]);
int rd;
char *buffer = (char *)malloc(1000);
char *readbuffer = (char *)malloc(80);
int gd = 0;
while (gd == 0)
while ((rd = read(fd1[INPUT],readbuffer, sizeof(readbuffer))) > 0)
{
strncat(buffer, readbuffer,rd);
gd = 1;
}
printf("Parent received: %s\n",buffer);
free(readbuffer);
printf("Parent sending!");
write(fd2[OUTPUT], buffer, strlen(buffer));
free(buffer);
}
return 0;
}
On a side note, is there a way to debug when I use fork because gdb automatically goes to the parent process
After the child writes to the parent, it must close the write end of the pipe so the parent knows it has reached EOF.
There are many bugs in your code. Why are you using fd2 without initializing it? Remove it.
Now its stuck at "Child sending" because pipe read is a blocking call and you are putting it in a while loop which will never return. Please refer to man page of pipe.
If you want to break that while loop, close all write ends of that pipe.
Also to debug child process, use gdb command follow-fork-mode as child before call to fork() while debugging.
Several things wrong:
fd2 is just never initialized.
The parent will never exit this:
while ((rd = read(fd1[INPUT],readbuffer, sizeof(readbuffer))) > 0)
{
strncat(buffer, readbuffer,rd);
gd = 1;
}
If there is no data to read, read will block and just not return. The only thing that would make it exit is if the connection was closed and the child doesn't close it.
You are calling read() in the expectation that if there is nothing to read, it will return with zero bytes read. However, what you are seeing is because read() is waiting for some data before returning. To address this, you need to do one of two things:
set your socket to do non-blocking reads (not recommended)
use select() or poll() to see whether there is some data to read before you read it
Also, several other points:
don't cast the returns from malloc()
check that malloc() does not return NULL
replace the whole gotdata thing with a break instruction