C fork program explanation - c

I have a code as below
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
printf("a\n");
fork();
printf("b\n");
if(fork() == 0) {
printf("c\n");
exit(0);
}
printf("d\n");
return 0;
}
Output :
a
b
d
a
b
c
a
b
d
a
b
c
I don't know why the output duplicated many times.

I don't know why the output duplicated many times
Because printf() is buffered.
When a process calls fork(), the resulting child process obtains a copy of the parent's output buffer.
You can empty this output buffer by placing fflush(stdout) just before each call to fork(). In that case output should be:
a
b
b
d
c
d
c
Note that if the output refered to a terminal, it would be line-buffered by default, i.e.: the buffer would be dumped every time a \n is sent to the output. Not so if you are redirecting the output to a file.

When you call fork() it gets a copy of output buffer of the calling process. Buffering is enabled by default, so you get this behavior.
You can use
fflush(stdout);
before a call to fork(). Or, you can also disable buffering using
setbuf(stdout, NULL);
You can read more about fork here. Let me know if you need any more help.

The answer is already in the comments. You are calling fork() twice. So the solution is to just call it once and save the result in a variable like this int pid = fork(). Also, you should check if the fork-call failed (if it does, it returns a negative value).
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
printf("a\n");
int pid = fork();
if (pid < 0)
{
fprintf(stderr, "Can't fork!");
exit(1);
}
printf("b\n");
if(pid == 0)
{
printf("c\n");
}
else
{
printf("d\n");
}
return 0;
}

Related

Redirection of stdin and stdout via pipes in C works for external programmes but not for recursive call

I am trying to communicate with forked child processes via pipe redirection of stdin and stdout in C. I already managed to get this to work for shell commands (like ls, for example) executed in child processes. However, I wasn't able to recursively execute the same program and redirect the output (printed by printf(), fprintf() to stdout, ...) via the pipes from the child process to the parent (in this test to stdout of the parent), although this works fine for ls or similar commands.
Here's how I tried to approach this:
I create a pipe, the reading end is for the parent, the child process should write to the writing end.
The Process forks, both processes close the unused end, respectively.
The writing end of the pipe is redirected to STDOUT_FILENO and closed
The child process executes the program recursively (it is called ./to2)
As mentioned, this does work if I execute ls in the child process, but not if I try to call the same program recursively. Here's my test program where I tried to get this to work:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <netdb.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <fcntl.h>
static void usage(void){
fprintf(stderr,"RIP");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]){
if(argc > 1){
dprintf(STDOUT_FILENO,"Please work\n");
printf("\n THIS IS A MESSAGE FROM THE CHILD \n");
fputs("Pretty Please!\n",stdout);
fflush(stdout);
exit(EXIT_SUCCESS);
}
int p1[2];
if(-1 == pipe(p1)) {
fprintf(stderr,"pipe\n");
fprintf(stderr,"%s\n",strerror(errno));
usage();
}
int f = fork();
if(f == 0){
close(p1[0]);
if(dup2(p1[1],STDOUT_FILENO) < 0){
fprintf(stderr,"dup2\n");
usage();
}
close(p1[1]);
//I want this to work:
//execlp("./to2", "./to2", "-e");
//This works fine:
execlp("ls", "ls");
exit(EXIT_SUCCESS);
} else if (f == -1) {
usage();
} else {
close(p1[1]);
int w = -1;
if(-1 == wait(&w)) usage();
char b[12];
memset(b,0,12);
read(p1[0],&b,12);
char reading_buf[1];
while(read(p1[0], reading_buf, 1) > 0){
write(1, reading_buf, STDOUT_FILENO);
}
close(p1[0]);
}
}
For testing purposes, the function is called recursively with additional arguments, while the parent program is called without additional arguments (hence the if(argc>1)).
In the final program, endless recursion is being avoided by other means.
Did I understand something wrongly? I am pretty confused by the fact that the only thing that doesn't seem to work is redirecting the output of my own
program...
Thank you very much in advance, any help or ideas are greatly appreciated.
The primary problem is precisely as outlined in the comments — you are not calling execlp() correctly (nor ls in the alternative). You must make the last argument on those function calls into an explicit null pointer, as shown in this code, which is a mostly mildly edited version of what's in the question:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static void usage(void)
{
fprintf(stderr, "RIP\n");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
if (argc > 1)
{
dprintf(STDOUT_FILENO, "Please work\n");
printf("THIS IS A MESSAGE FROM THE CHILD\n");
fputs("Pretty Please!\n", stdout);
fflush(stdout);
exit(EXIT_SUCCESS);
}
int p1[2];
if (-1 == pipe(p1))
{
fprintf(stderr, "pipe: %s\n", strerror(errno));
usage();
}
int f = fork();
if (f == 0)
{
close(p1[0]);
if (dup2(p1[1], STDOUT_FILENO) < 0)
{
fprintf(stderr, "dup2: %s\n", strerror(errno));
usage();
}
close(p1[1]);
execlp(argv[0], argv[0], "-e", (char *)0);
fprintf(stderr, "failed to exec %s again\n", argv[0]);
exit(EXIT_FAILURE);
}
else if (f == -1)
{
usage();
}
else
{
close(p1[1]);
char b[13];
memset(b, 0, 13);
if (read(p1[0], &b, 12) < 0)
{
fprintf(stderr, "Failed to read from pipe (%s)\n", strerror(errno));
exit(EXIT_FAILURE);
}
int len = strcspn(b, "\n");
printf("M1 [%.*s]\n", len, b);
char reading_buf[1];
while (read(p1[0], reading_buf, 1) > 0)
{
write(1, reading_buf, STDOUT_FILENO);
}
close(p1[0]);
int w = -1;
if (-1 == wait(&w))
usage();
}
return 0;
}
Two important changes should be highlighted:
This code echoes the first line of data — the one written by dprintf() — whereas the original code just read it and discarded it.
The wait() call is after the input, not before. If the child had more data to write than a set of fixed messages, it could block waiting for the parent to read some of the data, while the parent is blocked waiting for the child to exit. This would be a deadlock.
The usage() function is not appropriately named — it doesn't report how to run the program. I also exit with a failure status, not success, if the child process fails the execlp().
Under peculiar circumstances, the wait() call might report on the exit status from some child other than the one that was forked. It is generally best to use a loop to reap such children. However, the circumstances required are extremely peculiar — the process which launched the parent with an exec*() function must have previously created some children for which it didn't wait, so that they are inherited by the parent process (because the PID doesn't change across an exec*() call).

Logging results of fork() in C to file to see results

I'm trying to get the results of each run of the program (both the parent and child). The results print once on the screen and only once in a file. I can't seem to get two unique files created (one representing parent and one representing child). I'm not sure if getpid() is the effective way to separate parent and child identification. What could I be doing wrong?
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static char *app1="/path/to/app1";
static char *app;
static pid_t forky=-1;
void otherfunction(){
int aflag=1;
//do something
if (aflag==1){
//run app 1
app=app1;
printf("Starting fork\n");
forky=fork();
}
}
int main(){
char dat[40000],test[10000];
sprintf(dat,"Exec start\nFORKY = %d\nPID = %d\nPPID = %d\n",forky,getpid(),getppid());
sprintf(test,"/TEST%d",getpid());
int h=open(test,O_WRONLY|O_CREAT);
write(1,dat,strlen(dat));
write(h,dat,strlen(dat));
close(h);
otherfunction();
return 0;
}
You're creating the file before you call fork. The fork is the last thing you do and then both processes just return 0.
As specified in fork's man page, the process created by calling fork is a copy of the parent process except for some specific differences, and this child process starts execution as if resuming from after the call to fork. So, it's kind of like you get two returns from fork, one for the parent and one for the child. So, it looks like you ask two questions here:
How to differentiate parent and child
Again, the man page mentions that fork will return the child's pid in the parent process and 0 for the child process so the following code sample will get you distinguished output from both:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
pid_t pid = fork();
if (pid == 0)
printf("Hello from child!!!\n");
else if(pid > 0)
printf("Hello from parent!!!\n");
else
printf("Wow, fork failed!!!");
return 0;
}
Obtaining separate files for each process
As mentioned above, both processes resume from after the call to fork, so the files must be created after calling to fork. In your example you are calling otherfunction last in main, so fork is pretty much the last call in both processes.
The following is an example that will give you different files with different content for each process, as well as print in stdout for each process. The usage of getpid here is just so you can actually check what the man page says.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv)
{
pid_t pid = fork();
int h;
char *test;
char dat[100];
if (pid == 0)
test = "child";
else if(pid > 0)
test = "parent";
else
test = "failed";
h = open(test,O_WRONLY|O_CREAT);
sprintf(dat, "%s | fork returned = %d | my_pid = %d\n", test, pid, getpid());
write(1,dat,strlen(dat));
write(h,dat,strlen(dat));
close(h);
}

Child shell process bidirectional redirection to parent process

Hello stackoverflow I tried to create a program which execute a son shell process and redirect his I/O to a pipe in order to communicate with his father process.
I can execute command via the write pipe (wpipefd) but I can't get the response from the shell process on the read pipe (rpipefd).
I had 3 errors so far according to Strace : First the read function was blocking the program so I made ​​the read fd of the reading pipe non-blocking (rpipe[0]). Then I had an EAGAIN error with the read function... Finally I got an EPIPE error when I close the read fd from rpipe (close(rpipefd[0])) in the forked process just after the use of dup2() .
I don't understand what I did wrong. Here's what I did so far :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#define BUF_SIZE 1024
int main(int argc, char **argv)
{
int rpipefd[2], wpipefd[2], pid;
pipe(rpipefd);
pipe(wpipefd);
char buffer[BUF_SIZE] = {0};
int flags = fcntl(rpipefd[0], F_GETFL, 0);
fcntl(rpipefd[0], F_SETFL, flags | O_NONBLOCK);
pid = fork();
if(pid == 0)
{
close(rpipefd[0]);
dup2(rpipefd[1],1);
dup2(rpipefd[1],2);
close(wpipefd[1]);
dup2(wpipefd[0],0);
close(rpipefd[1]);
close(wpipefd[0]);
execl("/bin/sh","/bin/sh",NULL);
}
close(wpipefd[0]);
write(wpipefd[1],"echo helloWorld",strlen("echo helloWorld"));
close(rpipefd[1]);
read(rpipefd[0],buffer,BUF_SIZE);
//perror("read()");
printf("%s",buffer);
exit(0);
}
Please help !
The main issue doesn't come from the code itself: the command passed to the shell is incomplete, you missed the final '\n' and thus the child process (your shell) is waiting for the rest of the command.
The non-blocking part is not a good idea (or at least, you should spin around you pipe in order to retrieve its content.)
Once you're done with your command, you should close the output pipe so the shell get the end-of-file on its input.
Other remarks: you should wait for the child termination (using wait(2)), you should leave after your execl in the child process (use with err(3) for the error message) to handle exec errors. And, seriously, calling strlen on string literal ? I know that gcc is replacing it at compile time, but …
Here is a modified version of your code:
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#define BUF_SIZE 1024
int main(int argc, char **argv)
{
int rpipefd[2], wpipefd[2], pid;
pipe(rpipefd);
pipe(wpipefd);
char buffer[BUF_SIZE] = {0};
pid = fork();
if(pid == 0)
{
close(rpipefd[0]);
dup2(rpipefd[1],STDOUT_FILENO);
dup2(rpipefd[1],STDERR_FILENO);
close(wpipefd[1]);
dup2(wpipefd[0],STDIN_FILENO);
close(rpipefd[1]);
close(wpipefd[0]);
execl("/bin/sh","/bin/sh",NULL);
err(1, "execl()");
}
close(wpipefd[0]);
close(rpipefd[1]);
write(wpipefd[1], "echo helloWorld\n", 16);
close(wpipefd[1]); // we're done, say it to the shell
int r;
while ( (r = read(rpipefd[0],buffer,BUF_SIZE)) )
{
if (r == -1)
{
if (errno == EAGAIN || errno == EINTR) continue;
err(1, "read()");
}
write(STDOUT_FILENO, buffer, r);
}
wait(NULL);
return 0;
}

Share a file descriptor between parent and child after fork and exec

I have two processes on Linux, A & B.
I want to share the file descriptor from process A with process B, now I just serialize it to a char* and pass it to the execl parameters, but that doesn't work.
A.c looks like this:
union descriptor{
char c[sizeof(int)];
int i;
} fd;
pid_t pid;
fd.i = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Perform other socket functions
pid = fork();
if(pid == 0){
// Read data from socket
if(execl("./B", fd.c, NULL) < 0){
exit(EXIT_FAILURE);
}else(
exit(EXIT_SUCCESS);
}
}else if(pid < 0){
exit(EXIT_FAILURE);
}else{
waitpid(pid, NULL, 0);
}
B.c looks like this:
union descriptor{
char c[sizeof(int)];
int i;
} fd;
memcpy(&fd.c[0], argv[0], sizeof(int));
write(fd.i, "TEST", 4);
close(fd.i);
But this doesn't work, and I don't really understand why not. How can I make this work? And if it works, is it the best solution to share a file descriptor between a parent and a child after a fork and a exec?
Update
The problem is unrelated to the question I asked, it is caused by a wrong way of passing an integer as pointed out by #OliCharlesworth. Please close this question.
File descriptors are always passed between a parent and child process
When you fork a process, the file descriptors that are open in the parent(at the time of fork()) are implicitly passed on to the child. There is no need to send them explicitly.
For example:
The pseudo-code looks as follows:
In process A:
fd = open_socket_or_file;
char str_fd[3];
str_fd[0]=fd;
str_fd[1]=fd;
str_fd[2]=0;
if(fork()==0)
{
execl("./B",str_fd,NULL);
}
In the child process B you can do:
int fd = argv[1][0];
/* now do whatever you want with the fd...*/
EDIT:
In case the processes are different, you need to pass the file descriptor explicitly. This is generally done using UNIX-Domain Sockets(If you are using Linux Flavors). For code related to this, you can see this answer
Yes that is true that file descriptors remain open even after fork or exec or fork and exec.You only need to know the value of fd in the new process image that was replaced using exec else put that fd on the one which is already known to that process(ex:0,1,2). So you can do this in two ways:
Placing the fd on either one of standard file descriptors using dup2(note:as far as i know you will be unable to reset that standard file descriptor for which it was actually known for)
Passing the fd as string argument for one of 6 exec functions does the job
Therefore I suggest you to use second method in case if you want standard fds remain
These are the two methods of implementation:
P1.c(using argument passing)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
void main()
{
printf("Hello this is process 1\n");
int fd=open("./foo",O_RDONLY);
char buf[255];
//int n=read(fd,buf,255);
int h=fork();
if(h==0)
{
char *fname="./p2";
char *arg[3];
char targ[10];
sprintf(targ,"%d",fd);
arg[0]=fname;
arg[1]=targ;
arg[2]=NULL;
execvp(fname,arg);
}
else
{
printf("This is from p1 process\n");
//write(1,buf,strlen(buf));
//do some process with p1
printf("This is end of p1 process\n");
}
}
P1.c(using dup2 with 0)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
void main()
{
printf("Hello this is process 1\n");
int fd=open("./foo",O_RDONLY);
int h=fork();
if(h==0)
{
dup2(fd,0);//note we will be loosing standard input in p2
execvp(fname,NULL);
}
else
{
printf("This is from p1 process\n");
//write(1,buf,strlen(buf));
//do some process with p1
printf("This is end of p1 process\n");
}
}
P2.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main(int argc,char *argv[])
{
int fd=atoi(argv[1]); //here fd=0 in case dup2 in process ps1.c
char buf[1024];
int n=read(fd,buf,1024);
buf[n]='\0';
printf("This is from p2\n");
write(1,buf,strlen(buf));
}

Using fork() for split process- what's wrong with the program

I'd really love your help with understanding why doesn't the process reach the "son process" after using fork() command. I'm trying to write a program that runs another program, but It seems that the program dosen't even reach the son process. I can tell that since "son process" is not being printed to the screen, and I really wonder why.
Here's a sketch of the code- I can't even check if it is alright since as I said, it doesn't even reaching the son process, I always get "son exited with error".
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <assert.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <time.h>
#define MAXARGV 5;
int main() {
char* cmd;
int child_status;
char* s;
char** argv;
int counter;
cmd= (char*) calloc( 5, sizeof(char)*20);
s=(char*) calloc(1,sizeof(char)*20);
argv=(char**) calloc(5, sizeof(char*)*20);
printf("Please write a command\n");
gets(cmd);
counter = 0;
while (strcmp(cmd, "exit") != 0) {
int pid = fork();
if (pid == 0) {
printf("son process");
while (sscanf(cmd, "%s", s) == 1) {
strcpy(argv[counter], s);
counter++;
}
execv(argv[0], argv);
printf("the command is not legal");
assert(0);
}
else {
if (wait(&child_status) == -1) {
printf("error waiting for pid=%d\n", pid);
exit(-1);
}
if(WIFEXITED(child_status)!=0)
printf("son status=%d\n", WEXITSTATUS(child_status));
else
printf("son exited with error\n");
}
printf("Please write a command");
gets(cmd);
}
free(s);
free(cmd);
free(argv);
printf("here as well");
return 1;
}
The program reaches the printf("son process") just fine, but that just puts the string in a buffer inside the process and since you didn't fflush() it, it doesn't make it to the screen and is discarded with the rest of the process' memory in the exec call. Note, that stdout is normally line-buffered, so if you had newline there, it would auto-flush. Also stderr is by default unbuffered and more suitable for debug prints (fprintf(stderr, "child process")).
You are trying to assemble the command read from standard input in argv, but it only has memory for the actual arguments given to you, so you overrun this memory and get segmentation fault.
if WIFEXITED gives zero, you should use WIFSIGNALED and WTERMSIG to confirm that the error is indeed SIGSEGV.
assert(0) is not a good way to terminate process after error. exit(1) is. Assertions are only for conditions that indicate bug in the code itself if they happen and are often eliminated (by defining NDEBUG) from production code.

Resources