Linux C: what happens to unused file descriptors? - c

(apologies for not taking care of my accepts lately - will do so as soon as I get some time; just wanted to ask this question now that it occurred)
Consider the following C program:
int main(void) {
write(3, "aaaaaa\n", 7);
write(2, "bbbbbb\n", 7);
write(1, "cccccc\n", 7);
return 0;
}
I build and run it from the bash shell like this:
$ gcc -o wtest wtest.c
$ ./wtest 3>/dev/stdout
aaaaaa
bbbbbb
cccccc
The way I see it, in this case, due to the shell redirection of fd 3 to stdout, that file descriptor is now "used" (not sure about "opened", since there is no opening of files, in the C code at least) - and so we get the cccccc string output to terminal, as expected.
If I don't use the redirection, then the output is this:
$ ./wtest
aaaaaa
bbbbbb
Now fd 3 is not redirected - and so cccccc string is not output, again as expected.
My question is - what happened to those cccccc bytes? Did they dissapear in the same sense, as if I redirected fd 3 to /dev/null? (as in:
$ ./wtest 3>/dev/null
)
In addition, assuming that in a particular case I'd like to "hide" the fd 3 output: would there be a performance difference between redirecting "3>/dev/null" vs. not addressing fd 3 in the shell at all (in terms of streaming data; that is, if fd 3 outputs a really long byte stream, would there be an instruction penalty per byte written in the "3>/dev/null" case, as opposed to not addressing fd 3)?
Many thanks in advance for any answers,
Cheers!

My question is - what happened to those cccccc bytes?
nothing. you failed to capture the return code of write, it should tell you that there was an error and errno should tell you what the error was
You also seem to have a questionable concept of what is persistent, the "bytes" are still sitting in the string literal where the compiler put them from the beginning. write copies byte to a stream.

Jens is right. If you run your program under strace on both situations, you'll see that when you redirect, the write works - because the shell called pipe() on your behalf before fork'ing your executable.
When you look at the strace without the redirection:
write(3, "aaaaaa\n", 7) = -1 EBADF (Bad file descriptor)
write(2, "bbbbbb\n", 7bbbbbb) = 7
write(1, "cccccc\n", 7cccccc) = 7
Which reminds us of the best practice - always check your return values.

Related

Meaning of read and syscall with arguments

I'm playing with strace and ltrace tools to get information about a program which contains a prompt for a user entry.
With strace, after the read from the prompt is called, there is an openat of a specific file in readOnly:
openat(AT_FDCWD, "file", O_RDONLY) = 3
read(3, "22d72c", 6) = 6
I know that the second argument for read is supposed to be a buffer and that read starts at the buffer, but what exatly does it mean here? Does it means it starts at the 22d72c bit? or is 22d72c a key and it reads it's value?
As for syscall, I'm getting that when I use ltrace, after a scanf for the prompt and a
fopen to open the file, it returns similar syscall such as:
syscall(0, 3, 0x56127f5c96c0, 6)
What is the meaning of syscall third argument here? (0x56127f5c96c0)
No. "22d72c" are the 6 characters that read read from your file... just check the beginning of file.
Indeed if you read from STDIN_FILENO using read (or for example use fgets; strace will output
read(0,
and stop there waiting for the read to complete so that it can print out the characters read!
As for
syscall(0, 3, 0x56127f5c96c0, 6)
that output is from a program that doesn't know how to decode the system call parameters for system call 0 (read), so it just displays some sensible value - all small numbers in decimal. 0x56127f5c96c0 is the pointer to the first character of the buffer you're reading into.
0x56127f5c96c0 is the pointer passed to read. It's not very useful to you is it? strace was nice enough to decode the system call, notice that's a pointer argument, and show you what it points to instead.

C & bash redirection processus communication

look at this bash :
mkfifo fifo
./processA <fifo | processB >fifo
In my process A, i generate a file which is send by process B. Then I want to process the result of processB.
So in A I just send info to B with printfs into std out. Then I create a thread who just read(stdin). After creating this thread, I send infos to B via printf.
I do not understand why this whole sh block. The read never recieive anything. Why? the two process are tested and work fine separatly. The whole sh also work perfectly (dont block) if I dont read (but then I cant process B output).
Can somebody explain me what i am understanding wrong?
Sorry for my approximative English. I am also intersted by your clean solution if you have one (but it would prefer understanding why this one is not working).
//edit
Here is the main (process A):
//managing some arguments threatment, constructing object...
pthread_t thread;//creation of the thread supposed to read
if(pthread_create(&thread, NULL,IsKhacToolKit::threadRead, solver) != 0) {
fprintf(stderr,"\nSomething went wrong while creating Reader thread\n" );
}
solver->generateDimacFile();//printing to stdout
pthread_exit(0);
}
the function executed by the thread is just supposed to read stdin and printing into stderr the string obtened (for now). Nothing is printed in stderr right now.
generateDimacFile print a char* into stdout (and flush(stdout) at the end) that processB use. The process B is that one: http://www.labri.fr/perso/lsimon/glucose/
here ise the function executed by the thread now :
char* satResult=(char*)malloc(sizeof(char)* solutionSize);
for (int i=0; i<2; i++){
read(0, satResult, solutionSize );
fprintf(stderr, "\n%s\n", satResult);
}
DEBUGFLAG("Getting result from glucose");
Ok so now thanks to Maxim Egorushkin, I discovered that the first read dont block, but the next one block using that bash instead:
./processA <fifo | stdbuf -o0 ./processB >fifo
and if I use that one :
stdbuf -o0 ./processA <fifo | stdbuf -o0 ./processB >fifo
Most of the time I can read twice whitout blocking (some time it block). I still can't read 3 times. I dont understand why it change anything because I flush stdout in generateDimacFile.
Look at what's actually printed when it dont block(reading twice) in stderr:
c
c This is glucose 4.0 -- based on MiniSAT (Many thanks to MiniSAT team)
c
c This is glucose 4.0 -- based on MiniSAT (Many thanks to MiniSAT team)
c
c Reading from standard input... Use '--help' for help.
s to MiniSAT team)
c
The coresponding expected result:
c
c This is glucose 4.0 -- based on MiniSAT (Many thanks to MiniSAT team)
c
c Reading from standard input... Use '--help' for help.
c | |
s UNSATISFIABLE
You have a potentially blocking race condition. If the processB needs to read a large amount of data before it produces anything, then it is possible that processA will be data starved before it produces enough data. Once that happens, there's a deadlock. Or, if processA never generates any data until it reads something, then both processes will just sit there. It really depends on what processA and processB are doing.
If the processes are sufficiently simple, what you are doing should work. For instance:
$ cat a.sh
#!/bin/sh
echo "$$"
while read line; do echo $(($line + 1 )); echo $$ read: $line >&2; sleep 1; done
$ ./a.sh < fifo | ./a.sh > fifo
26385 read: 26384
26384 read: 26385
26385 read: 26386
26384 read: 26385
26385 read: 26386
26384 read: 26387
26385 read: 26388
26384 read: 26387
^C
Using | or > in bash makes the process block-buffered, so that it does not output anything until the buffer is full or fflush is invoked.
Try disabling all buffering with stdbuf -o0 ./processA <fifo | stdbuf -o0 processB >fifo.
stderr does not get redirected in your command line, I am not sure why you write into it. Write into stdout.
Another issue is that
read(0, satResult, solutionSize);
fprintf(stderr, "\n%s\n", satResult);
is incorrect, satResult is not zero-terminated and the errors are not handled. A fixL
ssize_t r = read(0, satResult, solutionSize);
if(r > 0)
fwrite(satResult, r, 1, stdout);
else
// Handle read error.

how to use a GDB input file for multiple input

EDIT: GDB was not the issue. Bugs in my code created the behaviour.
I am wondering how GDB's input works.
For example I created the following small c program:
#include <stdlib.h>
#include <stdio.h>
int main(){
setbuf(stdout,NULL);
printf("first:\n");
char *inp;
size_t k = 0;
getline(&inp, &k, stdin);
printf("%s",inp);
free(inp);
// read buffer overflow
printf("second:\n");
char buf[0x101];
read(fileno(stdin),buf,0x100);
printf("%s",buf);
printf("finished\n");
}
It reads two times a string from stdin and prints the echo of it.
To automate this reading I created following python code:
python3 -c 'import sys,time; l1 = b"aaaa\n"; l2 = b"bbbb\n"; sys.stdout.buffer.write(l1); sys.stdout.buffer.flush(); time.sleep(1); sys.stdout.buffer.write(l2); sys.stdout.buffer.flush();'
Running the c programm works fine. Running the c program with the python input runs fine, too:
python-snippet-above | ./c-program
Running gdb without an input file, typing the strings when requested, seems also fine.
But when it comes to using an inputfile in gdb, I am afraid I am using the debugger wrongly.
Through tutorials and stackoverflow posts I know that gdb can take input via file.
So I tried:
& python-snippet > in
& gdb ./c-program
run < in
I expected that gdb would use for the first read the first line of the file in and for the second read the second line of in.
in looks like (due to the python code):
aaaa
bbbb
But instead gdb prints:
(gdb) r < in
Starting program: /home/user/tmp/stackoverflow/test < in
first:
aaaa
second:
finished
[Inferior 1 (process 24635) exited with code 011]
Observing the variable buf after read(fileno(stdin),buf,0x100) shows me:
(gdb) print buf
$1 = 0x0
So i assume that my second input (bbbb) gets lost. How can I use multiple input inside gdb?
Thanks for reading :)
I am wondering how GDB's input works.
Your problem doesn't appear to have anything to with GDB, and everything to do with bugs in your program itself.
First, if you run the program outside of GDB in the same way, namely:
./a.out < in
you should see the same behavior that you see in GDB. Here is what I see:
./a.out < in
first:
aaaa
second:
p ��finished
So what are the bugs?
The first one: from "man getline"
getline() reads an entire line from stream, storing the address
of the buffer containing the text into *lineptr.
If *lineptr is NULL, then getline() will allocate a buffer
for storing the line, which should be freed by the user program.
You did not set inp to NULL, nor to an allocated buffer. If inp didn't happen to be NULL, you would have gotten heap corruption.
Second bug: you don't check return value from read. If you did, you'd discover that it returns 0, and therefore your printf("%s",buf); prints uninitialized values (which are visible in my terminal as ��).
Third bug: you are expecting read to return the second line. But you used getline on stdin before, and when reading from a file, stdin will use full buffering. Since your input is small, the first getline tries to read BUFSIZ worth of data, and reads (buffers) all of it. A subsequent read (naturally) returns 0 since you've already reached end of file.
You have setbuf(stdout,NULL);. Did you mean to disable buffering on stdin instead?
Fourth bug: read does not NUL-terminate the string, you have to do that yourself, before you can call printf("%s", ...) on it.
With the bugs corrected, I get expected:
first:
aaaa
second:
bbbb
finished

how to use execl corrrectly? [duplicate]

This question already has answers here:
Output redirection using fork() and execl()
(2 answers)
Closed 8 years ago.
I was trying to redirect the output from an arduino ( USB ) to some file at the computer using the next code:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main()
{
pid_t pid;
pid = fork();
if (pid == 0) {
execl("/bin/cat","cat /dev/cu.usbmodem1421 - 9600 >> data.txt",NULL);
}
printf("Cuando desee terminar la recolección de datos presione cualquier tecla: ");
getchar();
kill(pid, SIGKILL);
return 0;
}
Using ps to verify if everything is fine, i can see the process running behind my main program. After stoping the program the data file has nothing on it. I tried to use system() which is a little bit nasty because i need to kill the program manually using OSX terminal. I think maybe the syntaxis is wrong and all i need is to add another parameter but nothing seems to work.
As written, your code executes /bin/cat with the name (argv[0]) of:
cat /dev/cu.usbmodem1421 - 9600 >> data.txt
and no other arguments, so it reads from its standard input and writes to its standard output and sits around until it detects EOF on its standard input (or you kill it).
The simplest option is to use system() to run the command.
Failing that, you will need to split up the string into separate arguments, and handle the I/O redirection in the child. Note that the code would read from 3 files:
/dev/cu.usbmodem1421
-
9600
The second would be interpreted as standard input again. If the 9600 is meant to be a modem speed or something, cat is the wrong command.
Seems like you're doing a fork/execl to do something you could do using simple file I/O.
That being said... execl syntax is, you pass each parameter separately, followed by a null pointer. So, something like this (taking your command literally...):
execl("/bin/cat", "/dev/cu.usbmodem1421", "-", "9600", ">>", "data.txt", (char *) NULL);

Dynamic generation of file contents (poor man's proc file)

I'm trying to make a simple userspace program that dynamically generates file contents when a file is read, much like a virtual filesystem. I know there are programs like FUSE, but they seem a bit heavy for what I want to do.
For example, a simple counter implementation would look like:
$ cat specialFile
0
$ cat specialFile
1
$ cat specialFile
2
I was thinking that specialFile could be a named pipe, but I haven't had much luck. I was also thinking select may help here, but I'm not sure how I would use it. Am I missing some fundamental concept?
#include <stdio.h>
int main(void)
{
char stdoutEmpty;
char counter;
while (1) {
if (stdoutEmpty = feof(stdout)) { // stdout is never EOF (empty)?
printf("%d\n", counter++);
fflush(stdout);
}
}
return 0;
}
Then usage would be something like:
shell 1 $ mkfifo testing
shell 1 $ ./main > testing
shell 2 $ cat testing
# should be 0?
shell 2 $ cat testing
# should be 1?
You need to use FUSE. A FIFO will not work, because either your program keeps pushing content to stdout (in which case cat will never stop), or it closes stdout, in which case you obviously can't write to it anymore.

Resources