The resource manager library provides another convenient service for us: it knows how to handle dup() messages.
Suppose that the client executed code that eventually ended up performing:
fd = open ("/dev/sample", O_RDONLY); … fd2 = dup (fd); … fd3 = dup (fd); … close (fd3); … close (fd2); … close (fd);
Our resource manager would get an _IO_CONNECT message for the first open(), followed by two _IO_DUP messages for the two dup() calls. Then, when the client executed the close() calls, we would get three _IO_CLOSE messages.
Since the dup() functions generate duplicates of the file descriptors, we don't want to allocate new OCBs for each one. And since we're not allocating new OCBs for each dup(), we don't want to release the memory in each _IO_CLOSE message when the _IO_CLOSE messages arrive! If we did that, the first close would wipe out the OCB.
The resource manager library knows how to manage this for us; it keeps count of the number of _IO_DUP and _IO_CLOSE messages sent by the client. Only on the last _IO_CLOSE message will the library synthesize a call to our _IO_CLOSE_OCB handler.