Link to home
Start Free TrialLog in
Avatar of Maarten Bruins
Maarten Bruins

asked on

Understanding dup2 (C System Call)

dup2(int oldfd, int newfd);

Open in new window


This is an equivalent of:

close(int newfd);
dup(int oldfd);

Open in new window


By closing "newfd" first, it becomes the lowest-numbered unused file descriptor (normally). Because of that the oldfd is copied to newfd (dup system call). So far, everything is clear.

Now see: http://codewiki.wikidot.com/c:system-calls:dup2


dup2 is a system call similar to dup in that it duplicates one file descriptor, making them aliases, and then deleting the old file descriptor.

Actually I don't think the old file descriptor will be deleted?

See: http://man7.org/linux/man-pages/man2/dup.2.html

After a successful return, the old and new file descriptors may be used interchangeably.

If the old file descriptor will be deleted, then they would not say something like that. Is wikidot.com just wrong about this?
Avatar of Martyn Spencer
Martyn Spencer
Flag of United Kingdom of Great Britain and Northern Ireland image

From my reading, dup2 will close newfd if it refers to an open descriptor and then make newfd an alias of oldfd. After this, newfd and oldfd refer to the same file. The primary idea is to make the reuse of newfd immune to a race condition that could occur if you were to close newfd and then call dup.

I guess wikidot is using the term "delete" instead of "close". I am not sure why they refer to "deleting the old file descriptor" unless they are referring to the one that newfd could potentially be referencing but you no longer require. It's unclear to me.
I think the author was trying to keep the description short and didn't have a good word to use instead of "delete".  "delete" isn't 100% accurate, neither is "closed" as more happens than just closing the FD.  "Overwritten" has implications that need clarification.

If you think about it as calling dup2() on STDIN (fd 0), the new entry replaces the old one so that I/O against what used to be STDIN now happens against a different file/device.  That's exactly what redirection does and how the shell implements redirection.

Kent
Avatar of Maarten Bruins
Maarten Bruins

ASKER

I also considered "delete" instead of "close", but only at the beginning one descriptor is closed. The "coming" new one is closed to make it the lowest-numbered unused file descriptor, so it can become the new one. But they are saying:

and then ...

after making them aliases. So when replacing "delete" by "close" it's still seems incorrect.
@Kent: Then how you explan the "and then", after making them aliases? Anyway delete or close or whatever are wrong there. Nothing happens anymore after the dup() call. So after they are aliases, nothing happens anymore. It's just dup2 = close + dup + nothing more. So "and then..." is incorrect anyway if you would ask me (in any form).
I understand your argument.  It might have made the documentation more clear.

When your program starts, it "knows" of 3 FDs, one each for STDIN, STDOUT, and STDERR.  In a terminal environment they typically reference the keyboard and monitor.  In a batch environment they reference other files or devices.  Let's discuss it as a program running in a terminal environment.

The unix kernel maintains a list of all open files and devices.   If you and I are simultaneously running programs on an otherwise idle server it's conceivable that the only entries in the kernel table is for STDIN, STDOUT, and STDERR that are mapped to the terminal devices.  (In reality, there will be other files open, such as system logs, but let's ignore them.)  Let's also assume that STDIN, STDOUT, and STDERR were created in the kernel in that order.  So with your program fd[0] contains a 0, indicating the first kernel entry.  fd[1] contains a 1, and fd[2] contains a 2.

Your program then opens another file.  fd[3] in your program space now contains a 3 as it's the next item in the kernel.
My program now opens another file.  fd[3] in my program space now contains a 4 as the position in the kernel table is slot 4.

That's a long way to explain that an fd is just an integer and how a value is assigned.

Most user programs will call dup().  It would be unusual to develop something that calls dup2().

I found a pretty good example of how any why a program would use dup2().  

  http://www.cs.loyola.edu/~jglenn/702/S2005/Examples/dup2.html

Take a look at the examples.

Kent
But now you're just explaining what dup and dup2 do in general? I think I know pretty well how they are working and that's why I think that wikidot.com is wrong about it, independent of how you look at it (so wrong in any form).

Another example of dup2, see: http://www.rozmichelle.com/pipes-forks-dups/#attachment_7362

But this question is mainly about this sentence:


dup2 is a system call similar to dup in that it duplicates one file descriptor, making them aliases, and then deleting the old file descriptor.

This is just incorrect in any form, right? Or how you're seeing that sentence if you think it's still pretty okay?
Perhaps I am missing something but all I see dup2 as doing is closing and opening a file descriptor in a single atomic operation (as per the docs). On that basis, I think the terminology in wikidot.com is not really accurate,  but I can excuse the term.
I understand it, but like you, think it's not phrased very well.

Look at this part of the redirect.c example

  in = open("scores", O_RDONLY);

  // replace standard input with input file

  dup2(in, 0);

Open in new window


The program opens file "scores" and calls dup2() to assign STDIN such that reads from STDIN read from "scores".

Dup2 duplicates the fd associated with "scores" and deletes the fd that was associated with STDIN prior to the call.  In truth, it simply overwrites the entry after closing the file.  But I can (kind of ) understand why the author used "deleted".
@Kent: You're saying:

Dup2 duplicates the fd associated with "scores" and deletes the fd that was associated with STDIN prior to the call.

This is not true. Dup2 first closes newfd, and then it duplicates oldfd ("in" in your example). So you forgot about "close" before the duplication. After the duplication, nothing will be closed or deleted.
We're getting off track here.  

In trying to explain the use of the word "delete" the call to close() isn't relevant.    The process makes access to the file/device originally referenced by STDIN inaccessible though that fd.  The device or file may still be accessible if another fd references it, but access via STDIN is logically deleted.
Ah okay, now I understand what you mean. In other words, one reference count to that device/file is deleted.

But if they were trying to say that, then it was a really bad try, because the reference count is already one less at the moment the close function is called. Then that device/file already has no access anymore to STDIN, so in the end it is about close() and close() is relevant.
Generally, the linux/unix documentation is pretty good, but it usually looks more like reference material (where you need to know the subject and are looking for a refresher) instead of student materials (where you're learning from scratch).
Why it's so difficult to admit that it's just a mistake and that they actually just have to correct it? Even if it's reference material, it's just bad.
ASKER CERTIFIED SOLUTION
Avatar of Kent Olsen
Kent Olsen
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Ah okay, thanks! Then at least I know for sure now that it's just incorrect.
The man page is correct, the wikidot.com is wrong...
The man page never uses the word delete....

From the gnu manpages:
  dup2()
       The dup2() system call performs the same task as dup(), but instead of using the lowest-numbered unused file descriptor,  it  uses  the  file
       descriptor number specified in newfd.  If the file descriptor newfd was previously open, it is silently closed before being reused.

       The  steps of closing and reusing the file descriptor newfd are performed atomically.  This is important, because trying to implement equiva‐
       lent functionality using close(2) and dup() would be subject to race conditions, whereby newfd might be reused between the two  steps.   Such
       reuse could happen because the main program is interrupted by a signal handler that allocates a file descriptor, or because a parallel thread
       allocates a file descriptor.

       Note the following points:

       *  If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.

       *  If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.

In general accurate man page site: https://linux.die.net/man/2/dup
slightly different description still  

Source of manpage:

COLOPHON
       This page is part of release 4.16 of the Linux man-pages project.  A description of the project, information about reporting  bugs,  and  the
       latest version of this page, can be found at https://www.kernel.org/doc/man-pages/.  (nlu+10

And here you can help maintaining them:
https://www.kernel.org/doc/man-pages/contributing.html