Thread: printf and system call write

  1. #1
    Registered User blob84's Avatar
    Join Date
    Jun 2010
    Posts
    46

    printf and system call write

    Code:
    #include<stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    int main()
    { int fd;
     fd = open("file", O_WRONLY);
     close(1);
     if (dup(fd) >= 0)
     {
     printf(" hello\n");
     write(1,"world",4);
     }
    }
    This code print to stdout "world" before "hello" why?
    After closing stdout with close(1), the function dup assign the first free file descriptor to fd, in this case 1(stdout).
    Last edited by blob84; 07-05-2011 at 06:14 AM.

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    Well I would have to guess it has something to do with when the stream gets flushed.

    This seems to do what you want though
    printf(" hello\n");fflush(stdout);

    Though I did think that \n was sufficient, but apparently it isn't.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Perhaps the dup changed the buffering on 1 from "line-oriented" to "block-oriented" or whatever the term is for file-oriented instead of screen-oriented output. Just a guess, though.

  4. #4
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    You have stepped into a nasty place. I can't say for sure if POSIX guarantees what you're doing is "undefined behavior", but it's certainly bad code that you shouldn't use or trust either way. I sincerely hope this is a simple experiment, and not something you're planning on using in the real world. Here's what that code does in plain English:
    1. Open a file, "file" for writing. This is given a fd, most likely 3 (the lowest available so far).
    2. Close file descriptor 1, which was stdout.
    3. Duplicate the file handle to "file", using the lowest numbered file descriptor, which should now be 1.
    4. Print some contents using printf, which internally is using a FILE * that was initialized to stdout file at program startup (stdout was fd 1 at startup, and the FILE * still refers to fd 1 under the hood, hence the output of printf going to the file and not to the actual standard output device).
    5. Print some more contents to fd 1 (now a dup of fd 3, pointing to "file").

    Note that your printf and your write both ultimately go to the same place, but get there from different paths. There's definitely some synchronization issues with the way printf and write are accessing the buffer, hence the out of order problem. If both of those calls were printf or both were write, you shouldn't have this problem. write is a lower-level call, so that may be why it comes out first, despite being called later. It's also possible that, despite the new line, printf saves a little time by not flushing the output until it really needs it, like the next printf call or the end of program (which forces a flush). The details of exactly what's happening probably depend on your libc version for printf specifics and your Linux (Unix, BSD, etc) kernel version for the write specifics. You would have to read the source code of the relevant libc and kernel code to figure out for sure what's happening though.

    EDIT: Removed race condition comment. Noticed the code only wrote 4 bytes from "world".

  5. #5
    Registered User blob84's Avatar
    Join Date
    Jun 2010
    Posts
    46
    Yes anduril462, this is only a test, i will never use this code!
    I tried new example like this code:
    Code:
    #include<stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    
    int main() {
    	int c, fd;
    	fd = open("test", O_WRONLY);
    	close(1);
    	dup(fd);
    	printf("dog\n");
    	printf("The other buffers represented are associated with the standard streams,	and for efficiency are only allocated on first use of these streams. There are 3 standard streams (stdin, stdout, stderr) created automatically for nearly all unix programs by the C library (libc) at the start of program execution, 	and new streams can be created to connect to files, sockets, pipes, ...To control how data is read/written from these buffers one can control 	both the buffer sizes and modes (Unbuffered, Buffered, Line Buffered). To determine the characteristics of the buffering automatically applied to the standard streams I used this program, which indicated the following: The other buffers represented are associated with the standard streams,	and for efficiency are only allocated on first use of these streams. There are 3 standard streams (stdin, stdout, stderr) created automatically for nearly all unix programs by the C library (libc) at the start of program execution, 	and new streams can be created to connect to files, sockets, pipes, ...To control how data is read/written from these buffers one can control 	both the buffer sizes and modes (Unbuffered, Buffered, Line Buffered). To determine the characteristics of the buffering automatically applied to the standard streams I used this program, which indicated the following:The other buffers represented are associated with the standard streams,	and for efficiency are only allocated on first use of these streams. There are 3 standard streams (stdin, stdout, stderr) created automatically for nearly all unix programs by the C library (libc) at the start of program execution, 	and new streams can be created to connect to files, sockets, pipes, ...To control how data is read/written from these buffers one can control 	both the buffer sizes and modes (Unbuffered, Buffered, Line Buffered). To determine the characteristics of the buffering automatically applied to the standard streams I used this program, which indicated the following:The other buffers represented are associated with the standard streams,	and for efficiency are only allocated on first use of these streams. There are 3 standard streams (stdin, stdout, stderr) created automatically for nearly all unix programs by the C library (libc) at the start of program execution, 	and new streams can be created to connect to files, sockets, pipes, ...To control how data is read/written from these buffers one can control 	both the buffer sizes and modes (Unbuffered, Buffered, Line Buffered). To determine the characteristics of the buffering automatically applied to the standard streams I used this program, which indicated the following:The other buffers represented are associated with the standard streams,	and for efficiency are only allocated on first use of these streams. There are 3 standard streams (stdin, stdout, stderr) created automatically for nearly all unix programs by the C library (libc) at the start of program execution, 	and new streams can be created to connect to files, sockets, pipes, ...To control how data is read/written from these buffers one can control 	both the buffer sizes and modes (Unbuffered, Buffered, Line Buffered). To determine the characteristics of the buffering automatically applied to the standard streams I used this program, which indicated the following: The other buffers represented are associated with the standard streams,	and for efficiency are only allocated on first use of these streams. There are 3 standard streams (stdin, stdout, stderr) created automatically for nearly all unix programs by the C library (libc) at the start of program execution, 	and new streams can be created to connect to files, sockets, pipes, ...To control how data is read/written from these buffers one can control 	both the buffer sizes and modes (Unbuffered, Buffered, Line Buffered). To determine the characteristics of the buffering automatically applied to the standard streams I used this program, which indicated the following:The other buffers represented are associated with the standard streams,	and for efficiency are only allocated on first use of these streams. There are 3 standard streams (stdin, stdout, stderr) created automatically for nearly all unix programs by the C library (libc) at the start of program execution, 	and new streams can be created to connect to files, sockets, pipes, ...To control how data is read/written from these buffers one can control 	both the buffer sizes and modes (Unbuffered, Buffered, Line Buffered). To determine the characteristics of the buffering automatically applied to the standard streams I used this program, which indicated the following:The other buffers represented are associated with the standard streams,	and for efficiency are only allocated on first use of these streams. There are 3 standard streams (stdin, stdout, stderr) created automatically for nearly all unix programs by the C library (libc) at the start of program execution, 	and new streams can be created to connect to files, sockets, pipes, ...To control how data is read/written from these buffers one can control 	both the buffer sizes and modes (Unbuffered, Buffered, Line Buffered). To determine the characteristics of the buffering automatically applied to the standard streams I used this program, which indicated the following:The other buffers represented are associated with the standard streams,	and for efficiency are only allocated on first use of these streams. There are 3 standard streams (stdin, stdout, stderr) created automatically for nearly all unix programs by the C library (libc) at the start of program execution, 	and new streams can be created to connect to files, sockets, pipes, ...To control how data is read/written from these buffers one can control 	both the buffer sizes and modes (Unbuffered, Buffered, Line Buffered). To determine the characteristics of the buffering automatically applied to the standard streams I used this program, which indicated the following:The other buffers represented are associated with the standard streams,	and for efficiency are only allocated on first use of these streams. There are 3 standard streams (stdin, stdout, stderr) created automatically for nearly all unix programs by the C library (libc) at the start of program execution, 	and new streams can be created to connect to files, sockets, pipes, ...To control how data is read/written from these buffers one can control 	both the buffer sizes and modes (Unbuffered, Buffered, Line Buffered). To determine the characteristics of the buffering automatically applied to the standard streams I used this program, which indicated the following:END!!\n");
    		printf("hello\n");
    		write(1, "WORLD\n", 6);	
    	  return 1;
    }
    When the printf buffer is full, it is flushed to stdout printing the buffer that in this case contains "dog" and the <big text>, next it prints "WORLD", next it prints the rest of the <big text> and"hello":
    the output is:
    Code:
    dog
    <big text>
    WORLD
    <big text>
    hello
    Without the big text it will print:
    Code:
    WORLD
    dog
    <little big text>
    hello
    Cause printf fill the buffer before to print to stdout, instead the syscall write straight print to stdout.
    Results was different if we used stdout as really stdout because of '\n' at the end of the string, stdout is line buffered.
    But if printf works in this way, do it always wait to next instruction to flush the buffer?
    Last edited by blob84; 07-06-2011 at 03:44 AM.

  6. #6
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by blob84 View Post
    But if printf works in this way, do it always wait to next instruction to flush the buffer?
    It is not printf() that determines that. Printf() will always send the data immediately to stdout. However, stdout may be buffered. How stdout is buffered is system dependent. As Salem indicated earlier, if you want to flush a buffer immediately, use fflush().

    Further, how and what stdout works or does after you have closed its file descriptor is almost certainly undefined. You are of course welcome to look for a definition somewhere, but I very much doubt anyone will have bothered to create a definition for a closed file descriptor beyond: IT'S CLOSED. There is no reason to write to a closed file descriptor. If you do it anyway, don't expect anything other than:

    1) An explicit error.
    2) Some form of undefined behaviour.

    If you get strange things happening when you PURPOSELY engage in undefined behaviour, DO NOT ASK FOR AN EXPLANATION. Why? Because the definition of undefined behaviour is: behaviour that is not defined.

    Undefined behaviour is not called that because "We are still trying to define it". It is called that because it there is no reason to define it. Searching for rules about it is pointless. It is that way on purpose, you should never try to invoke or exploit it. The C standard exists to provide rules about and definitions of behaviour that will occur when you follow the rules and definitions. If you don't, no one can promise you anything, so there is no point in asking "why"? You are wasting your time. The only real thing you can explore here is the source code for your specific compiler,* which WRT to undefined behaviour, that may change at the next release version so again: you CANNOT count on it, therefore you CANNOT use it to do anything, therefore DON'T BOTHER.

    * Meaning: yes, it does something. It might even do it consistently. But since it does not have to adhere to any standard WRT to undefined behaviour, this cannot be discussed in the context of standard compliant C programming. Five different compilers might do five different things in this case. Why do you want to learn to write code based on that?
    Last edited by MK27; 07-06-2011 at 05:04 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    And back to what tabstop said about buffering modes.
    Code:
    $ cat foo.c
    #include<stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    int main()
    {
      int fd;
      fd = open("file", O_WRONLY | O_CREAT, 0600);
      close(1);
      if (dup(fd) >= 0) {
        static char buff[BUFSIZ];
        setvbuf(stdout, buff, _IOLBF, BUFSIZ);  /* reset line buffering on stdout */
        printf(" hello\n");
        write(1, "world", 4);
      }
    }
    $ gcc foo.c
    $ ./a.out 
    $ cat file
     hello
    worl$
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  8. #8
    Registered User blob84's Avatar
    Join Date
    Jun 2010
    Posts
    46
    Quote Originally Posted by MK27 View Post
    It is not printf() that determines that. Printf() will always send the data immediately to stdout. However, stdout may be buffered. How stdout is buffered is system dependent. As Salem indicated earlier, if you want to flush a buffer immediately, use fflush().
    ...
    It is an exercise that ask why this piece of code get this output.
    I don't want to learn to write code like this.
    So the asnwer is only that printf print to stdout and stdout is buffered.
    Stop.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. printf() or write(1,...) when using fork?
    By HaitiBoy in forum C Programming
    Replies: 5
    Last Post: 02-03-2011, 08:50 AM
  2. Doesn't write in file when call GetSaveFileName()
    By randall81 in forum Windows Programming
    Replies: 1
    Last Post: 03-28-2009, 01:34 PM
  3. system call
    By kastrup_carioca in forum C Programming
    Replies: 7
    Last Post: 01-30-2006, 03:05 PM
  4. C system call and library call
    By Coconut in forum C Programming
    Replies: 6
    Last Post: 08-22-2002, 11:20 AM
  5. Can I not call fstream to write to two files sequentially?
    By tigeress in forum C++ Programming
    Replies: 1
    Last Post: 01-20-2002, 01:26 PM