I am learning about multi process and know that when using fork() child process is created, and the child obtains copies of the parent’s stack, data, heap, and text segments.
So why this code below does not print two "hello"?
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
static int idata = 111; /* Allocated in data segment */
int main(int argc, char *argv[])
{
int istack = 222; /* Allocated in stack segment */
pid_t childPid;
idata *= 2;
istack *= 2;
printf("hello\n");
switch (childPid = fork()) {
case -1:
printf("fork fail\n");
exit(0);
case 0:
idata *= 3;
istack *= 3;
break;
default:
sleep(3); // Give child a chance to execute
break;
}
/* Both parent and child come here */
printf("PID=%ld %s idata=%d istack=%d\n", (long) getpid(),
(childPid == 0) ? "(child) " : "(parent)", idata, istack);
exit(0);
}
The result is
hello
PID=591 (child) idata=666 istack=1332
PID=590 (parent) idata=222 istack=444
why this code does not print two "hello"?
printf("hello\n"); happens before fork().
When the output is a terminal, stdout is line-buffered by default, it outputs 1 hello because stdout gets flushed on \n.
When the output is redirected into a file or a pipe, stdout is block-buffered by default, it outputs 2 hellos because both the parent and child processes have hello buffered and the buffers get flushed on exit().
you're making printf("hello\n"); before the call to the fork() function .
Trace the program flow. First there is only one process executing this program. This process prints "hello". Next, there is a fork system call inside the switch statement. Now there are two processes, parent and child. The child is a clone of the parent. Everything, except the process id, is the same for both. Even the next instruction to be executed is the same for both. Both, the parent and child execute the switch statement next. The child never had a chance to print "hello".
Related
I am current reading OSTEP and come across this piece of code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int
main(int argc, char *argv[])
{
printf("hello world (pid:%d)\n", (int) getpid());
int rc = fork();
if (rc < 0) {
// fork failed; exit
fprintf(stderr, "fork failed\n");
exit(1);
} else if (rc == 0) {
// child (new process)
printf("hello, I am child (pid:%d)\n", (int) getpid());
char *myargs[3];
myargs[0] = strdup("wc"); // program: "wc" (word count)
myargs[1] = strdup("p3.c"); // argument: file to count
myargs[2] = NULL; // marks end of array
execvp(myargs[0], myargs); // runs word count
printf("this shouldn't print out");
} else {
// parent goes down this path (original process)
int wc = wait(NULL);
//WHY IS THIS LINE PRINTED OUT??? Call to exec() already
//overwrites the original code segment
printf("hello, I am parent of %d (wc:%d) (pid:%d)\n",
rc, wc, (int) getpid());
}
return 0;
}
And here is the output:
hello world (pid:22206)
hello, I am child (pid:22207)
32 123 966 p3.c
hello, I am parent of 22207 (wc:22207) (pid:22206)
$
The author says: "What it does: given the name of an executable (e.g.,wc), and some arguments (e.g.,p3.c), it loads code (and static data) from that executable and over-writes its current code segment (and current static data) with it; the heap and stack and other parts of the memory space of the program are re-initialized. Then the OS simply runs that program, passing in any arguments as the argv of that process. Thus, it does not create a new process; rather, it transforms the currently running program (formerly p3) into a different running program (wc). After the exec()in the child, it is almost as if p3.c never ran; a successful call to exec()never returns."
The bolded part is where I feel so confused. From my understanding, the parent process stops at line int wc = wait(NULL);, and the child process starts from int rc = fork(); until it reaches execvp(myargs[0], myargs);. After that, the codes from that called executable are loaded and overwrites the current code segment, and the stack and heap is re-initialized, transforming the original program to a different running program, but why hello, I am parent of 22207 (wc:22207) (pid:22206) still got printed out after the original code segment has been overwritten and call to exec() never returns?
When the authors say:
"(...) Thus, it does not create a new process; rather, it transforms
the currently running program (formerly p3) into a different running
program (wc). After the exec()in the child, it is almost as if p3.c
never ran; a successful call to exec()never returns."
They mean that the child process, not the parent process, will be overwritten by the exec() command. That's why the reaming code in the child process after the exec is not executed. Remember that the child process code is only those lines inside the else if (rc == 0) clause:
else if (rc == 0) {
// child (new process)
printf("hello, I am child (pid:%d)\n", (int) getpid());
char *myargs[3];
myargs[0] = strdup("wc"); // program: "wc" (word count)
myargs[1] = strdup("p3.c"); // argument: file to count
myargs[2] = NULL; // marks end of array
execvp(myargs[0], myargs); // runs word count
printf("this shouldn't print out");
}
Remember also that the child process is itself a new process, with its own memory segments. Therefore an exec() command will not affect its parent (or even have access to its parent) not even other child processes from the same parent.
I have an app that spawns a child process. That child process outputs information about what it's doing by printing to stdout. The parent process does the same (i.e. prints to stdout).
In the child process I can write to stdout with some text prepended, but I have to add that to every single location I print across many source files.
I thought it might be smarter to have the parent process prepend output from the child process that it forks/exec's. I don't want to redirect the output because seeing the output inline with the parent process is beneficial. How do I do this? I'm using fork/exec in the parent.
Do I have to read the output and prepend each line manually or is there a simpler approach?
Update:
Thanks to Barmar. Here is how I'm doing it. I also could read byte by byte in the parent process from the pipe until line end. But I chose not to use that approach for reasons of complexity in my single threaded lua+C app.
// Crude example of output filtering using sed to
// prepend child process output text
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>
#include <string.h>
pid_t spawn(int fd[2], const char* path)
{
printf("Create child\n");
pid_t pid = fork();
switch(pid){
case -1:
printf("Create process failed");
return -1;
case 0:
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
execl(path, path, NULL);
return 0;
default:
return pid;
}
}
pid_t spawnOutputFilter(int fd[2])
{
printf("Create sed\n");
pid_t pid = fork();
switch(pid){
case -1:
printf("Create sed failed");
return -1;
case 0:
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execlp("sed", "sed", "s/^/Engine: /", (char *)NULL);
return -1;
default:
return pid;
}
}
int main(int argc, char* argv[])
{
if (argc > 1){
int options;
int fd[2];
pipe(fd);
pid_t pid = spawn(fd, argv[1]);
pid_t sed_pid = spawnOutputFilter(fd);
close(fd[0]);
close(fd[1]);
waitpid(pid, NULL, 0);
waitpid(sed_pid, NULL, 0);
}
return 0;
}
You could create a second child process that performs
execlp("sed", "sed", "s/^/PREFIX: /", (char *)NULL);
Connect the first child's stdout to this process's stdin with a pipe.
I thought it might be smarter to have the parent process prepend output from the child process.
I guess it depends on how you judge "smart". It might be simpler to just make the child prepend the desired text to its outputs.
I don't want to redirect the output because seeing the output inline with the parent process is beneficial. What's the best way to do this?
When two processes share an open file, both access it independently, regardless of the nature of the relationship between those processes. Thus, if your child inherits the parent's stdout, the parent has no mechanism even to notice that the child is sending output, much less to modify that output.
If you want the parent to handle this, you would need to pass the child's output through the parent. You could do that by creating a pipe, and associating the child's stdout with the write end of that pipe. The parent would then need to monitor the read end, and forward suitably-modified outputs to its own stdout. The parent would probably want to create a separate thread for that purpose.
Additionally, if the child sometimes produces multi-line outputs that you want prefixed as a group, rather than per-line, then you'd probably need to build and use some kind of protocol for demarcating message boundaries, which would make the whole parent-moderation idea pretty pointless.
Couldn't you define #define printf(a) printf("your text:" a).
Other alternative I can think of is using dup
You open the same log file in your child process and dup your stdout to new file descriptor.
I'm trying to learn UNIX programming and came across a question regarding fork() and I couldn't interpret the output of the 2 programs below.
I understand that fork() creates an identical process of the currently running process, but where does it start? For example, if I have these two programs below, what will be the output and how does it work ?
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main (int argc, char **argv)
{
int retval;
printf ("This is most definitely the parent process\n");
// now here fork will create a child process
// i need to know from which line child process starts execution
retval = fork ();
printf ("Which process printed this?\n");
return (0);
}
What will be the difference in above program and the one below with
respect to child process execution:
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main (int argc, char **argv)
{
int retval;
printf ("This is most definitely the parent process\n");
fflush (stdout);
// how does fflush change the output of above program and why ?
// even though no string operations are being used
retval = fork ();
printf ("Which process printed this?\n");
return (0);
}
I think both of them should print :
This is most definitely the parent process
Which process printed this?
Which process printed this?
But the first one is printing:
This is most definitely the parent process
Which process printed this?
This is most definitely the parent process
Which process printed this?
I understand that fork() creates an identical process of the currently
running process, but where does it start?
If fork(2) is successful (i.e. does not return -1), it starts in the line that calls fork(2). fork(2) returns twice: it returns 0 in the child, and a positive number C in the parent, where C is the process ID of the newborn child.
The reason you're seeing This is most definitely the parent process twice is related to stdio's buffering. Stdio buffers output in userspace buffers that are flushed only when some condition occurs (for example, the buffer becomes full). The buffering mode dictates when and how buffers are flushed.
Usually, if output is being written to an interactive device such as a terminal (or pseudoterminal), stdio is line-buffered, which means that the buffers are flushed when a newline is found or fflush(3) is called.
OTOH, if output is redirected to a file or other non-interactive devices (for example, output is redirected to a pipe), stdio is fully-buffered, which means that buffers are flushed only when they become full or fflush(3) is called.
So, without fflush(3), executing the code in a terminal device will print this:
This is most definitely the parent process
Which process printed this?
Which process printed this?
Which is expected. However, if you pipe it through cat(1), you will see this (or some other variant, depends on execution order):
This is most definitely the parent process
Which process printed this?
This is most definitely the parent process
Which process printed this?
This is because output is fully buffered when redirected to a pipe. The string This is most definitely the parent process isn't enough to fill and flush the buffer, so when the parent forks, the child (which gets a copy of the parent's memory space) will get a copy of the output buffer, which already contains the string This is most definitely the parent process. So both processes end up printing that string.
If you always call fflush(3) before forking, this won't happen because the buffer is empty when the parent's memory space is copied to the child.
Execution will continue at (or just after) the fork call. You can use the return value to check if you're the parent or the child process:
RETURN VALUE
On success, the PID of the child process is returned in the parent, and 0 is returned
in the child. On failure, -1 is returned in the parent, no child process is created,
and errno is set appropriately.
(Source: man fork)
For example, if you have the following program:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv) {
printf("Foo.\n");
int retval = fork();
printf("Bar from %s (%d).\n", (retval == 0) ? "child" : "parent", retval);
return 0;
}
The output would be something like:
Foo.
Bar from parent (18464).
Bar from child (0).
...assuming output is line buffered.
when calling fork() there are three possible return conditions.
Do read the man page for fork()
the three return conditions are
-1 -- the fork() failed
0 -- the child is executing
some positive number -- the pid of the child, the parent is executing
The code needs to be something like this:
pid_t pid;
pid = fork();
if ( 0 > pid )
{ // then handle error
}
else if ( 0 == pid )
{ // then child executing
}
else // if ( 0 < pid )
{ // then parent executing
}
So this is simple program of creating two process: parent and child. So what I did is have the greeting inside the parent and the name inside the child process. For some reason my child process is not printing despite that I called wait() inside the parent. What should I do?
GOAL OUTPUT: "Hello Sam"
OUTPUT I"M GETTING: "Hello"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
char *greeting = "Hello";
char *name;
pid_t pid;
pid = fork();
if(pid == 0)
{
name = "Sam";
exit(0);
}
else if(pid == -1)
printf("Fail\n");
else{
wait(0);
printf("%s %s", greeting, name);
}
}
When you make a call to fork(), the child process will receive a copy of the parent process' address space (variables etc...). This means that in the child, "name" is defined, from the parent. However, "name" in the child process is just a copy. So modifying it in the child process does not affect the parent.
To get the behavior that I sense you're after, replace fork() with vfork(). For the purposes of this discussion, the only difference is that:
The address space is shared instead of copied. Editing "name" in the child process will be reflected in the parent process
The parent process is suspended while the child process executes. I assume that this is OK, because you are already calling wait() in the parent process
Edit:
I forgot to add that if you go the vfork route, change exit() to _exit() in the child process
Child process once forked will run independantly of the parent process. So when you set name="Sam" in child it is in different program, than the printf. So you are not able to see the message.
In the child process, you assigned to name and then exit.
if(pid == 0)
{
name = "Sam";
exit(0);
}
The printf("%s %s", greeting, name); line is executed only in the else branch, i.e, the parent process. It's actually undefined behavior because name is uninitliazed.
I'm trying to create a child process in another process. I am writing both the programs in C language. First I write a dummy process which will be the child process. What it is doing is only to write a string on the screen. It works well on its own. Then I write another program which will be the parent process. However, I can't make it happen. I'm trying to use fork and execl functions together, but I fail. I also want the child process does not terminate until the parent process terminates.
How should I write the parent process?
Thanks.
Here is the code for the child process:
#include <stdio.h>
int main(void) {
while(1) {
printf("*");
sleep(1);
}
}
And here is the parent process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
if (fork()) {
while(1) {
printf("-\n");
sleep(5);
}
} else {
execl("./", "dummy", (char *)0);
}
}
The fork() system call may return three different statuses: failure (<0), parent process (>0) or child process (==0). You must test the return value properly.
int pid = fork();
if (pid < 0) {
/* handle error */
perror("fork");
exit(1);
} else if (pid > 0) {
/* parent code */
} else {
/* child code */
}
Your execl() system call is wrong. The first argument is the path to the program you want to execute, "./" is not valid, it should be something like "./dummy" at least. The next argument is by convention the command name (argv[0] in the executed program), which may be a repetition of the first argument. So:
execl("./dummy", "dummy", NULL);
Also, note that the printf("*") statement in the child program will probably buffer and you won't see anything on the terminal. You must either add a "\n" to the end or call fflush(stdout) to flush the standard output.
Basic use of fork in C
int PID = fork();
if( PID < 0 ) {
//fail
return PID;
}
else if( !PID ) {
//child process
return exec( prog, args );
}
else {
//parent process
return 0;
}
There is no way to force the child process to "not terminate" when it's done (you'll still be able in the parent to wait for it to get info on how it terminated, but that's about it). Apart from that, any of the many examples of fork/exec on the web, such as this one, should work -- why don't you try it and see if it performs as you wish (in which case you'll just need to change whatever you were doing differently in your own attempt). If it doesn't work as desired (except for the impossibility per the first sentence in this A;-), please edit your to add copious detail about how the code behaves differently than you expect it to.