fork() and write commands - c

#include<stdio.h>
int main()
{
if (fork())
{
wait();
printf("Now I am showing you ls -l"); // why can't we see this ?
execlp("ls","ls", "-l", 0); //gets printed second
}
else
{
printf("We are in the child process."); // gets printed first
}
}
I have this simple peace of code. My question is why don't we see on the console the first print, in the parent process?

The printf function provides buffered IO. When printing to stdout, as you are here, the buffer will normally get flushed when you print a newline \n, which you have not done, so your string is sitting in a buffer waiting to be printed.
The next thing you are doing is calling execlp. The exec family of functions replace the current process with the new one you have specified. The buffer is lost without ever being flushed, because the whole process is replaced.
If you add \n to the strings you are printing, you will probably see the output.

you have to flush stdout before the execlp.
if you put a \n at the end of the printf (or you call fflush(stdout)) you will get the correct result

In the interactive case, standard output is line buffered by default. And that execlp() will replace its memory image, therefore that output line may be dropped before it is written to console.
Change that print() statement to
printf("Now I am showing you ls -l\n");
to fix this problem.

There is 2 problems with your code:
first you need to flush your outputs, either by adding \n in the strings or by calling fflush(stdout)
But it should be the job of the son to exec(), of course you can always exec in the parent, but this is probably not what you want.
A more common fork/exec sample looks like :
int main() {
if (fork()) { // parent
wait(NULL); // wait his child
printf("Ok, my children finished the job\n");
exit(0);
}
else { // child
printf("We are in the child process.\n"); // gets printed first
printf("Now I am showing you ls -l\n");
execlp("ls","ls", "-l", 0); // never return from this
}
}

Related

Why does the child process behave differently when exec is supplied an invalid command?

pid_t pid;
pid = fork(); //Two processes are made
const char* ptr = secondlinecopy;
if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
{
int status;
if (waitpid(pid, &status, 0) == pid && WIFEXITED(status))
{
printf("Exitstatus [");
for (int i = 0; i < noOfTokens; i++)
{
printf("%s ", commands[i]);
}
printf("\b] = %d\n", WEXITSTATUS(status));
}
}
else if (pid == 0) //Child process. Executes commands and prints error if something unexpected happened
{
printf("why does this only print when an invalid command is supplied?");
if (runBGflag==1) insertElement(getpid(),ptr);
execvp(commands[0], commands);
printf ("exec: %s\n", strerror(errno));
exit(1);
}
In the code excerpt we see a process creation via fork(). When execvp is supplied a real command, such as "ls" for example, we get the output.
/home/kali/CLionProjects/clash/cmake-build-debug: ls
clash CMakeCache.txt cmake_install.cmake Testing
clash.cbp CMakeFiles Makefile
Exitstatus [ls] = 0
However, if we supply an invalid command, the output will be:
/home/kali/CLionProjects/clash/cmake-build-debug: sd
why does this only print when an invalid command is supplied?exec: No such file or directory
Exitstatus [sd] = 1
Why is that the case? Shouldnt the process always call printf("Why does ...") first and then run exec?
Why is that the case? Shouldnt the process always call printf("Why does ...") first and then run exec?
Let's say it works like this:
printf(...) --> internal buffer --(fflush? newline? max_buffer_size?)--> output
Usually stdout is line buffered and your printf has no newline. The data to-be-printed are stored inside some internal buffer. When exec-ing the stdout is not fflushed and the parent process is replaced by child process as it is as a whole - so all the data stored in parent process, including some internal stdout state, are removed. When exec fails, stdout is flushed when you printf(...\n" (or after calling exit() when stdout is block buffered) and the data show up. Research: studio buffering modes and setvbuf() function.
Why is that the case? Shouldnt the process always call printf("Why does ...") first and then run exec?
Yes, it should, and you've not presented any reason to think that it doesn't.
printf directs output to the standard output stream, and that defaults to being line buffered when it is connected to an interactive device, or to being block buffered otherwise. When execvp() succeeds, it replaces the whole program image with that of a new program, including the contents of any I/O buffers. Any data that have been buffered but not flushed to the underlying device are lost.
When execvp() fails and the program thereafter terminates normally (regardless of its exit status) all then-buffered buffered data is automatically flushed to the relevant output device.
You would see different behavior if you appended a newline to the message you are printing, or if you called fflush(stdout) between the printf and execvp calls, or if you printed to stderr instead of to stdout, or if you turned off buffering of stdout.

Why is main called twice?

I've just learnt about fork, and as I understand it the child process starts execution from the call to fork (otherwise fork would be recursive?).
However in this code (ideone link):
int main() {
printf("%d: Common code1\n", getpid());
if (fork() != 0) {
printf("%d: Parent code\n", getpid());
} else {
printf("%d: Child code\n", getpid());
}
printf("%d: Common code\n", getpid());
}
The output is:
27380: Common code1
27380: Parent code
27380: Common code
27380: Common code1
27383: Child code
27383: Common code
I don't understand why the 4th line is printed? I could understand if it was printed from the child process and fork called main but it's printed from the parent, and fork doesn't call main.
Good question! I was a little bit confused at first.
When you use printf, the output is buffered. That means that things printed by printf won't be actually flushed to the console until a newline is reached, or even until the program terminates if stdout is redirected.
In this case. the parent PID's stdout buffer is being copied to the child during the fork. Then, both the parent and child write to their buffers before terminating. This results in duplicated data being printed.
Here's an ideone link that includes fflush(stdout); before fork() is called.
The problem comes from buffering. When stdout is not associated with a terminal, it isn't line buffered. The strings you write are short enough to stay inside the buffer and are only written out once the program terminates. Thus, you first see process 27380 dumping its buffers and then process 27383 dumping its buffers. The line 27380: Common code1 hasn't been flushed out before the fork, so it's in the buffer of both the original and the forked process. Call fflush() right before forking to fix this problem.
Buffering. Printf does not (usually) write anything to stdout. It merely updates some internal data structures. Those data structures are duplicated in the child, and when the stream is flushed the data is written. You should flush(stdout) before you fork.

Forking a process- execution before the system call (execution point)

"When we call fork() on a process and its child is created, it is said that the point of execution starts from the point next to the fork() call in both the processes. But when I checked,
main() {
printf("hello");
val =fork();
if(val==0){
printf("child");
}
if(val>0){
printf("parent");
}
}
This program printed hello also twice. I am a little confused. Please help me out.
When you do printf("hello");, the output is line buffered for STDOUT which is not flushed. Now when the buffer still containing data, fork() call makes both parent and child process inherit data present in the buffer making it print twice.
Ideally you should be flushing the standard output as below:
printf("hello");
fflush(stdout);
fork();

Need to know how fork works [duplicate]

I am trying the following C code:
int main()
{
printf("text1\n");
fork();
printf("text2\n");
return 0;
}
I was expecting to get the output where i get two "text1" and two "text2", like:
text1
text1
text2
text2
But, i am, instead, getting:
text1
text2
text2
only one "text1"???
Ok, if child process executes from the fork(), then why do i get two "text1" for following:
int main()
{
printf("text1");
fork();
printf("text2\n");
return 0;
}
the output now is:
text1text2
text1text2
If the child process starts after the fork, output should be:
text1
text2
text2
fork() creates a new process by copying everything in the current process into the new process. That typically includes everything in memory and the current values of the CPU registers with some minor adjustments. So in effect, the new process gets a copy of the process's instruction pointer as well so it resumes at the same point where the original process would continue (the instruction following the fork()).
To address your update, printf() is buffered. Normally the buffer is flushed when it encounters a newline character at the end, '\n'. However since you have omitted this, the contents of the buffer stays and is not flushed. In the end, both processes (the original and the child) will have the output buffer with "text1" in it. When it eventually gets flushed, you'll see this in both processes.
In practice, you should always flush files and all buffers (that includes stdout) before forking to ensure that this does not happen.
printf("text1");
fflush(stdout);
fork();
The output should look like this (in some order):
text1text2
text2
The forked process gets a copy of the variable memory, and at the time of the fork the output buffer has yet to be flushed. No output has been written to the console when you fork, only buffered up. Both processes thus continues with text1 already in the buffer, and thus both print it.
fork clones the current process. The new process will "start" at the fork call, not at the start of main as you seem to expect. Thus when you print the first time there is 1 process, then when you fork there are two.
Since you are forking after printing "text1", it is only printed once.
In the second example the duplicated output is due to output buffering - printf doesn't actually output anything to the screen until it is flushed or it hits a newline ('\n').
Consequently the first call to printf actually just wrote data to a buffer somewhere, the data was then copied into the second process' address space, and then the second call to printf would have flushed the buffer, complete with "text1" in both buffers.
It is because forked process starts after fork, not from very beginning. exec starts process from entry point and will print what you expect.
The child process will start from the position of the fork(), so you are getting the correct output.
Problem 1 : the output as
text1
text2
text2
This is because fork() create exact copy (child) of parent process and both processes start their execution right after the system call fork().
Problem 2 : the output as
text1text2
text1text2
This is all about buffering. Refer this link and learn about fork() basics.
http://www.csl.mtu.edu/cs4411.ck/www/NOTES/process/fork/create.html
from man 2 fork: fork returns 0 to the child process.
value = fork();
if( value == -1 ) {
printf( "fork failed\n" );
exit(1);
}
if( value ) {
printf( "test1\n" );
} else {
printf( "test2\n" };
}

readline clash with child printf?

In Linux, readline() in an infinite loop repeatdly reads text\n. However, as soon as child processes start printing to the screen, readline no longer reads new lines. Even if I repeatdly press enter, readline() doesn't return.
Anyone know what's wrong?
Code sample as requested:
char* input;
int cpid;
while(1)
{
input = readline(">>>");
strcpy(tempInput, input); //...does some stuff w/ tempInput
//....
cpid = fork();
if(cpid == 0){
printf("..."); printf("...");
execl("ls", "ls", "-l", (char*) NULL); //sample execl parameters
}
else{
//do some stuff...
printf("...");
}
free(input);
}
//readline(">>>") works the first time and doesn't return on subsequent calls
The stacktrace of where the code hangs (forever):
Thread [1] (Suspended : Signal : SIGINT:Interrupt)
__read_nocancel() at ../sysdeps/unix/syscall-template.S:82 0x7ffff78f0490
rl_getc() at 0x7ffff7bc3727
rl_read_key() at 0x7ffff7bc3c90
readline_internal_char() at 0x7ffff7baf25f
readline() at 0x7ffff7baf795
main() at /home/.../.../xxx.c:95 0x4010a1
Edit: This probably sounds like total technobabble to experienced unix developers, but could the child process have somehow 'captured' stdin and fail to release it somehow?
Your child processes, even after exec, still has its standard input connected to your terminal. Either use wait() to wait for the child process to finish, or reopen the child's standard input as /dev/null:
close(STDIN_FILENO) && open("/dev/null", "O_WRONLY");
After you fork(), your child process executes the 2 printf calls, then goes on to execute the while(1) loop, so what you are doing is creating 1 extra process after every newline.
What you need to do, is kill the child immediately after it prints the 2 ellipses (...)
Change your code to this:
if(cpid == 0){
printf("..."); printf("..."); exec(...); exit(0);
}
Bear in mind, exit() is called only if the exec() fails.
Edit:
If you intend to accept input for some kind of command line interpreter, readline isn't a very good option. Its official man page says:
BUGS:
It’s too big and too slow.
I could suggest an alternative way of getting the input string:
char c;
inputString = calloc(0,sizeof(char));
inputLength = 0;
c = getchar();
while(c!='\n')
{
inputString = realloc(inputString,(inputLength+1)*sizeof(char));
inputString[inputLength++] = c;
c = getchar();
}
inputString[inputLength] = '\0';
Now you have the string in inputString & its length in inputLength. You could very well do:
execlp(inputString,inputString,0);
to execute the required functionality.
This piece of code is not working because your call to execl() is failing, therefore the child process will resume at the loop code, which will also be using readline().
You must pass the full path to the executable when using execl(), or use the execlp() variant (which search into PATH environment variable) for it to work.
Even for the simplest syscalls, it's a good practice to always check for their return codes.

Resources