I'm completly new to the C language and Unix systems. I might formulate myself badly or wrong. I have a C program where two processes increments a global variable up to 30. I am aware that order of output is random with parallel processing. But when using the | more pipe, the output is always the same: Child 1-30 -> Parent 1-30. How come?
I've been told that when using printf to print a variable, the variable content is sent to a buffer before printing to screen. And apparently you can use new line (\n) to flush the buffer right away. Is this information connected?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
int g_ant = 0;
void writeloop(char *text)
{
long i = 0;
while (g_ant < 30)
{
if (++i % 100000 == 0)
printf("%s: %d\n", text, ++g_ant);
}
}
int main(void)
{
pid_t pid;
pid = fork();
if (pid == 0)
{ /* child */
writeloop("Child");
exit(0);
}
writeloop("Parent"); /* parent */
waitpid(pid, NULL, 0);
return 0;
}
Your information is basically correct.
When the output goes to the terminal, the output is line buffered. When the output goes to a pipe, the output is fully buffered, so one of the processes finishes its output first. You could add fflush(stdout) after each iteration in writeloop(), or use setvbuf() to make it line buffered, or even unbuffered output.
Incidentally, note that the global variable is not shared between the child and the parent process; they each have their own copy of the variable.
Related
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
int main(){
char buf[10];
pid_t pid = fork();
if (pid < 0){
printf("error fork\n");
exit(1);
}
if (pid == 0){
fgets(buf,5,stdin);
printf("Child : %s\n",buf);
}
else{
wait(NULL);
char* r = fgets(buf,5,stdin);
if (r == NULL){
printf("Parent : eof = %i\n",feof(stdin));
}
else {
printf("Parent : %s\n",buf);
}
}
return 0;
}
My program is very simple : a process is forked; the child process reads 4 characters from stdin and when it finishes, the parent process reads 4 characters from stdin.
Normally, if I write characters in stdin (before the fork) the child process should read the first 4 characters and then the parent process should read the next 4 characters. It seems quit logical as fork() duplicates the parent process, including the file descriptors and opened files.
But, when I execute
echo 'aaaaaaaaa' | ./my_program
I get
Child : aaaa Parent : eof = 1
It seems that stdin has been emptied by the child process when it finished.
I having hard time explaining this behavior.
Can you help me ? :)
Standard input is usually (Is stdout line buffered, unbuffered or indeterminate by default?) line buffered by default. Check this answer to see what exactly this entails.
If you want your program to work as expected, explicitly set your standard input to be unbuffered (before the fork() call). This can be done like so:
setbuf(stdin, NULL);
Also see Should I set stdout and stdin to be unbuffered in C? for more implications of setting stdin to be unbuffered.
Try comment out after "else{"
//wait(NULL);
I would like to run an external command from a C program. Let's say, as minimal working example, that I want to run the 'cat' command. I use use fork() and execl() to spawn the new process, and I communicate with it via pipes.
Now that's where my problem is. In a terminal I would tell 'cat' that I am done with my input by pressing CTRL-D. Here I am trying to do so by closing the file descriptor -- see the line with close(outpipefd[1]) in the code below -- but this does not seem to work. My code stalls as 'cat' is waiting for more input.
My code is as follows... What am I doing wrong? Thanks in advance!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
int main(void)
{
pid_t pid=0;
int inpipefd[2];
int outpipefd[2];
/*
We create the pipes for communicating with the child process
*/
pipe(inpipefd);
pipe(outpipefd);
if((pid=fork())==0)
{
/*
Child
*/
dup2(outpipefd[0],STDIN_FILENO);
dup2(inpipefd[1],STDOUT_FILENO);
dup2(inpipefd[1],STDERR_FILENO);
/*
We spawn the process
*/
execl("/bin/cat","cat",(char *)(NULL));
/*
Nothing below this line should be executed by child process.
If so, it means that the execl function wasn't successfull, so lets exit!
*/
exit(1);
}
/*
Parent.
Close unused pipe ends.
*/
close(outpipefd[0]);
close(inpipefd[1]);
/*
Now we can write to outpipefd[1] and read from inpipefd[0]
*/
char *greeting="Hello world!\n";
write(outpipefd[1],greeting,strlen(greeting));
/*
Here I believe that closing the pipe should be equivalent to
pressing CTRL-D in a terminal, therefore terminating the cat command...
This is unfortunately not the case!
*/
close(outpipefd[1]);
while(1)
{
char buf[256];
for(int c=0;c<256;c++)
buf[c]=0;
if(read(inpipefd[0], buf, 256)<=0)
break;
printf("OUTPUT: %s\n", buf);
}
/*
Send SIGKILL signal to the child process
*/
int status;
kill(pid, SIGKILL);
waitpid(pid, &status, 0);
return 0;
}
The child still has both ends of both pipes opened, because you never closed any of your FDs in it. Until every FD referring to the write end of a pipe is closed, it won't return EOF.
You have also to close the unused pipe ends in the child, or there will be still things open that block the other end. close what you don't use in parent and child, and you will get the EOFs.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char **argv) {
FILE *file;
file = fopen(argv[1], "r");
char buf[600];
char *pos;
pid_t parent = fork();
if(parent == 0) {
while (fgets(buf, sizeof(buf), file)) {
pid_t child = fork();
if(child == 0) {
/* is there a function I can put here so that it waits
once the parent is exited to then run?*/
printf("%s\n", buf);
return(0);
}
}
return(0);
}
wait(NULL);
return(0);
}
The goal here to print out the line of a file all at the same time, parallel.
For example:
Given a file
a
b
c
$ gcc -Wall above.c
$ ./a.out file
a
c
b
$ ./a.out file
b
c
a
As in the processes ran at the exact same time. I think I can get this to work if there was a wait clause that waits for the parent to exit then start running the child. As shown in the comments above. Once the parent exits then all the processes would start at the print statement as wanted.
If you had:
int i = 10;
while (i > 0)
{
pid_t child = fork();
if(child == 0) {
printf("i: %d\n", i--);
exit(0);
}
}
then the child processes are running concurrently. And depending on the number of cores and your OS scheduler, they might even run literally at the same time. However, printf is buffer, so the order in which the lines appear on screen cannot be determined and will vary between executions of your program. And because printf is buffered, you will most likely not see lines overlapping other other. However if you were using write directly to stdout, then the outputs might overlap.
In your scenario however, the children die so fast and because you are reading
from a file (which might take a while to return), by the time the next fork is executed,
the previous child is already dead. But that doesn't change the fact, that if
the children would run long enough, they would be running concurrently and the
order of the lines on screen cannot be determined.
edit
As Barmar points out in the comments, write is atomic. I looked up in my
man page and in the BUGS section it says this:
man 2 write
According to POSIX.1-2008/SUSv4 Section XSI 2.9.7 ("Thread Interactions with Regular File Operations"):
All of the following functions shall be atomic with respect to each other in the effects specified in POSIX.1-2008 when they operate on regular files or symbolic links: ...
Among the APIs subsequently listed are write() and writev(2). And among the effects that should be atomic across threads (and pro‐
cesses) are updates of the file offset. However, on Linux before version 3.14, this was not the case: if two processes that share an
open file description (see open(2)) perform a write() (or writev(2)) at the same time, then the I/O operations were not atomic with
respect updating the file offset, with the result that the blocks of data output by the two processes might (incorrectly) overlap.
This problem was fixed in Linux 3.14.
Sever years ago I observed this behaviour of write on stdout with concurrent
children printing stuff, that's why I wrote that with write, the lines may
overlap.
I am not sure why you have an outer loop. You could rewrite as follows. Once you create the child processes, they could run in any order. So you might seem the output in "order" but in another run you might see different order. It depends on the process scheduling by your OS and for your purpose, it's all running in "parallel". So you really don't need to ensure parent process is dead.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char **argv)
{
if (argc != 2) {
printf("Incorrect args\n");
exit(1);
}
char buf[1024];
FILE *file = fopen(argv[1], "r");
while (fgets(buf, sizeof buf, file)) {
pid_t child = fork();
if(child == 0) {
write(STDOUT_FILENO, buf, strlen(buf));
_exit(0);
}
}
/* Wait for all child processes. */
while (wait(NULL) != -1);
}
I have the following code:
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h> // may not be needed
#include <sys/stat.h> // may not be needed
#include <stdlib.h>
#include <string.h>
typedef struct {
int pid;
char arg[100];
int nr;
} Str;
int main() {
int c2p[2];
pipe(c2p);
int f = fork();
if (f == 0) {
Str s;
s.pid = 1234;
strcpy(s.arg, "abcdef");
s.nr = 1;
close(c2p[0]);
write(c2p[1], &s, sizeof(Str));
close(c2p[1]);
exit(0);
}
wait(0);
close(c2p[1]);
Str s;
read(c2p[0], &s, sizeof(Str));
printf("pid: %d nr: %d arg: %s", s.pid, s.nr, s.arg);
close(c2p[0]);
return 0;
}
I have to say that it worked just fine until now (pid, nr and arg were never altered), but:
When the child process is done, is the memory segment (used by the child) destroyed (marked as free)?
If so, is there the risk that between the time of writing and the time of reading to lose the acces to that segment or the data to be altered?
(The original question was this: Sending structure through pipe without losing data )
Although the child process' memory is given back to the operating system when the process exits, I suspect this is not what you're really asking about.
You are more likely concerned about what happens to the data that was written to the pipe after the child process exits. As the pipe(2) man page states:
Data written to the write end of the pipe is buffered by the kernel
until it is read from the read end of the pipe.
So your data will arrive, even if the process that wrote it has already exited.
This is a quiz from my class, and it invovles concept around fork and pipe. I just have a several confusions about this code.
1) What does if((pid = fork() == 0) means? is it just checking fork using pid(process id), why does loop start with this?
2)close (p[1]); what does this part mean? closing the first integer of array P?
3)The while loop start after close, does it mean it read into p[0]'s size if it is not zero?
4.The two write lines, what does that mean, and why are they both named 1? are they happening at the same time?
#include <stdio.h>
#include <stdlib>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int p[2];
int i, pid, status;
char buffer[20];
pipe(p);
if((pid = fork() == 0) {
close (p[1]);
while (( i = read (p[0], buffer, sizeof("abcdefghi"))) != 0)
{ buffer [i] = '\0';
printf("read %d bytes: %s\n", i, buffer);
}
close(p[0]);
exit (0);
}
write(p[1], "abcdefghi', sizeof("abcdefghi"));
write(p[1], "123456789', sizeof("123456789"));
close(p[0]);
close(p[1]);
while(wait(&status)!= pid);
return(0);
}
You really should RTFM but :-
fork() creates an identical copy of the current procedure running from the same line of code. The only difference between the two copies is the return code from fork(). This will be 0 if you are in the newly created copy or the process id of the newly created copy if you are in the original executable (or -1 if something went wrong).
pipe(p) creates a pipe and returns two file handles in the array "p". the first handle is the output from the pipe opened for reading, the second handle is the input to the pipe open for writing. So close(p[1]) closes the input to the pipe ( this is in the new process which reads from the pipe, it is considered good practice to close the file descriptor you are not using!)
The while loop is checking "i" the return code from the read from the pipe file, this will return 0 when there is nothing to read.