This question already has an answer here:
Flushing buffers in C
(1 answer)
Closed 3 years ago.
I am trying to simulate a race condition (is this a correct term?) in order to fix it with semaphores afterward.
I have a master.c process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
for(int i = 0; i < 100; ++i)
{
if (fork() == 0)
{
char str[12];
sprintf(str, "%d", i%10);
execl("./slave", "slave", str, (char *)0);
}
}
return 0;
}
And a slave.c process which prints into a file:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
FILE *p_file;
p_file = fopen("out.txt", "a");
for (int i = 0; i < 100; ++i)
{
usleep(100000); // I tried using this but it changes nothing
fprintf(p_file, "%s", argv[1]);
}
fprintf(p_file, "\n");
return 0;
}
The output file out.txt looks like this:
https://pastebin.com/nU6YsRsp
The order is "random", but no data is not corrupted for some reason. Why is that?
Because stdio uses buffered output by default when writing to a file, and everything you're printing in each process fits into a single buffer. The buffer doesn't get flushed until the process exits, and then it's written as a single write() call, which is small enough to be written atomically to the file.
Call fflush(p_file); after each fprintf() and you'll get more mixed up results. Or call setvbuf() to disable buffering.
Related
I am having trouble communicating with the child process. I am trying to make quick.c simply get an input from stdin and send it to sand.c to capitialise it and send it back to the parent and then print it to stdout. Right now the program asks for an input twice instead of only asking once.
this is quick.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int fds[2];
int test[2];
int pid;
pid_t child_a;
char buffer[50], buff[50];
if(pipe(fds)) {
perror("Pipe:");
exit(0);
}
if(pipe(test)) {
perror("Pipe:");
exit(0);
}
child_a = fork();
if (child_a == 0) {
//Child
FILE *f = fdopen(fds[0], "r");
FILE *e = fdopen(test[1], "w");
close(fds[1]);
close(test[0]);
//dup2(fds[0],0); causes infinite loop
dup2(test[1],1);
execlp("./sand", "sand", NULL);
fclose(e);
fclose(f);
} else {
// Parent
// Wrap the pipes
FILE *f = fdopen(fds[1], "w");
FILE *e = fdopen(test[0], "r");
close(fds[0]);
close(test[1]);
fgets(buffer,50, stdin);
fprintf(f,"%s",buffer);
while(fgets(buff, 50, e)) {
printf("Parent receive %s", buff);
}
fflush(stdout);
fclose(f);
fclose(e);
wait(NULL);
}
return 0;
}
This method is sand.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, char *argv[]) {
char buffer[50];
int i = 0;
fgets(buffer,50,stdin);
char chr;
// Loop
while (buffer[i]) {
buffer[i] = toupper(buffer[i]);
i++;
}
fprintf(stdout,"%s",buffer);
return 0;
}
On running the code in my machine, the commented dup2 line does not loop to infinity. That may be because pipe programs run differently on different machines. However, the program terminates after taking input. Here are the things that are wrong with your code:
You aren't waiting for the child to write data to test pipe before printing in the parent. You must put the wait statement after taking input.
You've used file pointers for handling pipes. Pipes are accessed with file descriptors and cause unexpected results when handled with file pointers. Instead of fgets and fprintf, use read and write methods to work with file descriptors.
Error in the execlp command which I've commented.
There are errors regarding buffers, I've commented them in the code where they occur.
This is quick.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int fds[2];
int test[2];
int pid;
pid_t child_a;
char buffer[50], buff[50];
if(pipe(fds)) {
perror("Pipe:");
exit(0);
}
if(pipe(test)) {
perror("Pipe:");
exit(0);
}
child_a = fork();
if (child_a == 0) {
//Child
//CHANGED: No need to open file pointers here. Pipes are already open and accessed by file descriptor instead of file pointer. File pointers create trouble when used with pipes. 0 is file descriptor of stdin, 1 for stdout.
close(fds[1]);
close(test[0]);
dup2(fds[0],0);
dup2(test[1],1);
//CHANGED: There was an error with the command you wrote.
//That's because ./sand arg will look for a 'sand' directory which doesn't exist
//This line will throw warnings because execlp requires needs a command as the second argument, but in this case the filename is the command.
//NOTE: before running quick.c, compile sand.c as sand.out and not a.out
execlp("./sand.out",NULL);
printf("Exec Error\n"); //this will only execute if execlp didn't run. Always have this line in your code to know what's happening.
}
else
{
// Parent
// Wrap the pipes
//Got rid of the file pointers
close(fds[0]);
close(test[1]);
//CHANGE: fgets is only used with file pointers. While handling pipes, we work with file descriptors, with which read and write methods are used
int n = read(0,buffer,50); //If this is new to you, I strongly recommend reading manual pages for read and write, but for right now
// The signature should be enough to understand - read/write(int file_descriptor, char *buffer, int number_of_bytes)
write(fds[1],buffer,n);
//MOST IMPORTANT: You need to wait for child after this point. Because test pipe doesn't have data yet which will be received by child.
wait(NULL);
//CHANGE: printf statements do not work well with buffere, because buffers are not terminated with null
//%s specifier will always look for a null or print garbage
//If you still want to use printf, look into $man bzero
while((n = read(test[0],buff, 50))>0)
{
write(1,buff,n);
}
fflush(stdout);
}
return 0;
}
This is sand.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
char buffer[50];
int i = 0;
int n = read(0,buffer,sizeof(buffer)); //changed fgets to read, to get number of bytes read.
char chr;
// Loop
// we have number of bytes. So change while to for
for (i=0;i<n;i++)
{
buffer[i] = toupper(buffer[i]);
}
write(1,buffer,n); //Changed fprintf to write to get rid of %s problem.
//Again, to fill remaining places of buffer with null, look up bzero.
//The reason I haven't done that is to not confuse you with so many changed methods.
return 0;
}
Let me know, if the solution also helps you find the source of the infinity loop.
I need to read output of a program that uses carriage returns (\r) to make its output.
I have an old utility with output like this:
#include <stdio.h>
#include <unistd.h>
int main() {
setvbuf(stdout, (char*)NULL,_IONBF, 0);
fprintf(stdout, "Start\n");
for (auto i = 0; i < 100; ++i) {
fprintf(stdout, "\r%2d", i);
usleep(100000);
}
fprintf(stdout, "\nEnd\n");
return 0;
}
So I execute it with popen from my program and I need to trace a progress:
#include <memory>
#include <stdio.h>
int main(int argc, char* argv[]) {
if (argc != 2) {
return -1;
}
std::unique_ptr<FILE, int(*)(FILE*)> filePtr( popen(argv[1], "r"), &pclose );
int c = fgetc(filePtr.get());
while (c != EOF); {
printf("%c", c);
c = fgetc(filePtr.get());
}
return 0;
}
This work fine only without \r in stdout. Does anybody know is it possible to execute a process and read all output even with carriaage return symbols?
The problem here is that the carriage-return '\r' isn't really anything special, it's how you handle it that makes it different.
A terminal program uses it to control cursor position, but that's only done inside the terminal program itself, it's not "built-in" in the '\r' character.
If you want to handle it in a special way then you need to create your program that way.
The manpage for popen says "reading from a "popened" stream reads the command's standard output".
However, I can't seem to get the subprocess output in the trivial program below. The "reader" parent process blocks on the read (whether using fgets or fread)
What am I missing?
Attaching to the pinger program with gdb shows it is looping and calling printf to output text. Just nothing detected by fgets on the parent's side...
PINGER.C
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int
main(int argc, char **argv)
{
int i = 0;
while (1)
{
printf("stdout %d\n", i++);
sleep(1);
}
}
POPENTEST.C
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
int
main(int argc, char **argv)
{
char *cmd = "./pinger";
printf("Running '%s'\n", cmd);
FILE *fp = popen(cmd, "r");
if (!fp)
{
perror("popen failed:");
exit(1);
}
printf("fp open\n");
char inLine[1024];
while (fgets(inLine, sizeof(inLine), fp) != NULL)
{
printf("Received: '%s'\n", inLine);
}
printf("feof=%d ferror=%d: %s\n", feof(fp), ferror(fp), strerror(errno));
pclose(fp);
}
OUTPUT
$ ./popenTest
fp open
By default, C buffers writes to the stdout when stdout is not connected to a tty. This means that from the OS' perspective, the program has not written anything to stdout until either the buffer is full or you manually flushed the output:
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int
main(int argc, char **argv)
{
int i = 0;
while (1)
{
printf("stdout %d\n", i++);
fflush(stdout);
sleep(1);
}
}
When connected to a tty, the stdout is automatically flushed on every newline. But this automatic flushing does not happen when the stdout is connected to a pipe.
This question already has answers here:
Implementing pipe in C
(2 answers)
How to send a simple string between two programs using pipes?
(7 answers)
Closed 3 years ago.
I want to learn how to use pipes in C, and tried to do basic things like for example cloning the behaviour of | in shell.
This is my first try:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
FILE *stdin_tmp;
stdin_tmp = stdin;
stdin = stdout;
system("cat /tmp/test.txt");
system("less");
stdin = stdin_tmp;
return 0;
}
This is what I want to do (written in shell):
cat /tmp/test.txt |less
The behaviour is obviously not what I expected. less isn't receiving the output of cat.
How is it done correctly?
Try the popen() function.
Here's the prototype for it:
FILE *popen(const char *command, const char *type);
And here's a correct way to use it:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int ch;
FILE *input;
FILE *output;
input = popen("cat /tmp/test.txt", "r");
output = popen("less", "w");
if (!input || !output)
return EXIT_FAILURE;
while( (ch = fgetc(input)) != EOF )
fputc(ch, output);
pclose(input);
pclose(output);
return 0;
}
I have a program where I need to communicate with another program on its standard input and output. I can use a pipe to send it input and redirect its output to a specified file to be read when it is done, and that works fine. I am not using pipe and dup2 because I would like my program to at least somewhat work on windows as well as linux.
My issue is that I want to use the pipes to stream data.
I tried the following snippet:
#include <stdio.h>
int main()
{
while (!feof(stdin)) {
int x;
scanf("%d", &x);
printf("%x ", x);
fflush(0);
}
return 0;
}
in conjunction with:
#include <unistd.h>
#include <stdio.h>
int main()
{
int x;
FILE *p = popen("./test > tmp.datafile", "w");
FILE *f = fopen("tmp.datafile", "r");
for (int i = 0; i < 100; ++i) {
fprintf(p, "%d", i);
sleep(1);
x = fgetc(f);
printf("%c ", x);
fflush(0);
}
fclose(f);
pclose(p);
return 0;
}
However no matter what I do, I just keep getting nulls out of the file. I suspect that perhaps there are concurrency issues, in that I try to read the file before the test executable finishes flushing it, however I am not sure how to fix this.
Is there a better way to communicate with a program via standard streams in c?
fgetc() won't read past EOF once EOF has been reached — which may happen on the very first read. Doing something like
if(feof(f)) {
clearerr(f);
sleep(1);
} else {
printf("%c ", x);
}
should sort-of solve the issue with a busy loop.
A better idea would be to wait for the file to change once EOF has been reached, but that would require system-specific calls.
Beware of another race condition: fopen may happen before the file has been created by the command started via popen.
while(!(f = fopen(..., "r"))
sleep(1);
It's another busy loop and overall not a good solution that should only be used if dup2() is not available.
One of the issues in your code is that you are not writing any white space to the input of p, because of which the scanf() call in ./test is waiting for the input to finish so that it could read the value. Because of which the stdout of ./test remains empty. Fix it by something like this:
fprintf(p, "%d\n", i);
Also, consider using unbuffered I/O functions inside the ./test program. printf is part of the buffered I/O family of functions and the output might not be written immediately to the file.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
while (!feof(stdin)) {
int x;
char s;
scanf("%d", &x);
sprintf(&s, "%x", x);
write(STDOUT_FILENO, &s, 1);
fflush(stdout);
}
return 0;
}