dup2 / dup: ¿por qué debería duplicar un descriptor de archivo?

85

Estoy tratando de entender el uso de dup2y dup.

Desde la página del manual:

DESCRIPTION

dup and dup2 create a copy of the file descriptor oldfd.
After successful return of dup or dup2, the old and new descriptors may
be used interchangeably. They share locks, file position pointers and
flags; for example, if the file position is modified by using lseek on
one of the descriptors, the position is also changed for the other.

The two descriptors do not share the close-on-exec flag, however.

dup uses the lowest-numbered unused descriptor for the new descriptor.

dup2 makes newfd be the copy of oldfd, closing newfd first if necessary.  

RETURN VALUE

dup and dup2 return the new descriptor, or -1 if an error occurred 
(in which case, errno is set appropriately).  

¿Por qué necesitaría esa llamada al sistema? ¿De qué sirve duplicar el descriptor de archivo?

Si tengo el descriptor de archivo, ¿por qué querría hacer una copia?

Le agradecería si pudiera explicarme y darme un ejemplo de dónde se necesita dup2/ dup.

Gracias

ENE
fuente
¿Cómo implementaría la funcionalidad de tubería de las carcasas sin dupo dup2? pipe(2)dupSTDIN_FILENO
Debe
1
Posible duplicado de ejemplos prácticos use dup o dup2
DrTyrsa

Respuestas:

46

La llamada al sistema dup duplica un descriptor de archivo existente y devuelve uno nuevo que hace referencia al mismo objeto de E / S subyacente.

Dup permite que los shells implementen comandos como este:

ls existing-file non-existing-file > tmp1  2>&1

El 2> & 1 le dice al shell que dé al comando un descriptor de archivo 2 que es un duplicado del descriptor 1. (es decir, stderr y stdout apuntan al mismo fd).
Ahora, el mensaje de error para llamar a ls en un archivo no existente y la salida correcta de ls en un archivo existente se muestran en el archivo tmp1 .

El siguiente código de ejemplo ejecuta el programa wc con una entrada estándar conectada al extremo de lectura de una tubería.

int p[2];
char *argv[2];
argv[0] = "wc";
argv[1] = 0;
pipe(p);
if(fork() == 0) {
    close(STDIN); //CHILD CLOSING stdin
    dup(p[STDIN]); // copies the fd of read end of pipe into its fd i.e 0 (STDIN)
    close(p[STDIN]);
    close(p[STDOUT]);
    exec("/bin/wc", argv);
} else {
    write(p[STDOUT], "hello world\n", 12);
    close(p[STDIN]);
    close(p[STDOUT]);
}

El niño duplica el final de la lectura en el descriptor de archivo 0, cierra los descriptores de archivo en p y ejecuta wc. Cuando wc lee de su entrada estándar, lee de la tubería.
Así es como se implementan las tuberías usando dup, bueno, un uso de dup ahora usa tubería para construir otra cosa, esa es la belleza de las llamadas al sistema, construye una cosa tras otra usando herramientas que ya están allí, estas herramientas se construyeron a su vez usando algo más y así sucesivamente .. Al final, las llamadas al sistema son las herramientas más básicas que obtienes en el kernel

Salud :)

Pensamiento profundo
fuente
1
Entonces, ¿ dupes útil para la persona que llama y no el lsprograma en sí? ¿Hay algún beneficio de haberlo duputilizado en un programa como el mismo ls si ya tiene acceso al archivo? Aquí, por ejemplo, lsescribe errores en los 2que está codificado, por lo que tengo una forma de anularlo como consumidor de ls. Creo que es un punto sutil, ¿no?
Nishant
2
Su programa de ejemplo parece tener un error; estás llamando dup(p[STDIN])pero luego tirando el resultado. ¿Quisiste usar dup2(p[STDIN], 0)?
Quuxplusone
1
@Quuxplusone dupdevuelve el "descriptor con el número más bajo que actualmente no está siendo utilizado por el proceso". Dado que fd 0 se acaba de cerrar, dupdebería devolver 0. dup2es explícito sobre qué fd se debe usar, en lugar de simplemente usar el fd libre más bajo, por lo que preferiría eso.
miércoles
@Wodin: Ah, apuesto a que tienes razón sobre lo que OP estaba pensando. Sin embargo, ¿también tengo razón en que "recién cerrado" es relativo, y el código de OP podría romperse en presencia de, por ejemplo, hilos concurrentes que también podrían estar abriendo archivos?
Quuxplusone
@Quuxplusone Sospecho que tienes razón, pero no estoy seguro. Pero en ese caso tendrás otros problemas. Si cierra stdin porque desea leer desde otro lugar y luego otro hilo abre un archivo antes que usted, obtendrá fd 0. Si luego usa dup2, cerrará el fd que abrió el otro hilo, por lo que el otro hilo ahora leerá (¿y escribirá?) el archivo que abra. De todos modos, si mal no recuerdo, no debería llamar exec*desde un proceso multiproceso. Pero no soy un experto en subprocesos :)
Wodin
18

Otra razón para duplicar un descriptor de archivo es usarlo con fdopen. fclosecierra el descriptor de archivo al que se le pasó fdopen, por lo que si no desea que se cierre el descriptor de archivo original, dupprimero debe duplicarlo .

R .. GitHub DEJA DE AYUDAR A ICE
fuente
fdopen()Parece que no duplica un descriptor de archivo, solo crea un búfer en el espacio del usuario.
Eric Wang
3
Leíste mal mi respuesta. El punto es que es posible que desee dupacceder al fd antes de pasarlo, fdopenya que fcloselo cerrará.
R .. GitHub DEJA DE AYUDAR A ICE
1
@ theferrit32: si asigna un FILEidentificador para acceder a un archivo abierto preexistente a través de las interfaces stdio, debe llamar fclosepara desasignar ese FILEidentificador. Si desea seguir usando el archivo abierto subyacente, o si la arquitectura de su software es tal que el código de "propietario" original para el descriptor de archivo lo hará close, el hecho de que fclosetambién cierre el descriptor de archivo subyacente que entregó fdopenes un problema. Puede evitar este problema utilizando duppara crear un nuevo descriptor de archivo para que pase el mismo archivo abierto fdopen, de modo que fcloseno cierre el original.
R .. GitHub DEJA DE AYUDAR A ICE
1
El punto es que fdopen () mueve la propiedad del fd al FILE, en lugar de copiarlo . Eso es algo de lo que los usuarios deben ser conscientes. Los consumidores que necesiten conservar un fdidentificador utilizable además del FILEobjeto deben duplicar el fd. Eso es todo.
Conrad Meyer
1
@ConradMeyer: Sí, esa es una muy buena forma de decirlo, con una nota de que no hay ninguna operación para "mover la propiedad" FILEuna vez que la transfieras.
R .. GitHub DEJA DE AYUDAR A ICE
4

dup se utiliza para poder redirigir la salida de un proceso.

Por ejemplo, si desea guardar la salida de un proceso, duplica la salida (fd = 1), redirige el fd duplicado a un archivo, luego bifurca y ejecuta el proceso, y cuando el proceso finaliza, redirige nuevamente el guardado fd en la salida.

alinsoar
fuente
4

Algunos puntos relacionados con dup / dup2 se pueden anotar por favor

dup / dup2: técnicamente, el propósito es compartir una entrada de la tabla de archivos dentro de un solo proceso mediante diferentes identificadores. (Si estamos bifurcando, el descriptor se duplica de forma predeterminada en el proceso hijo y la entrada de la tabla de archivos también se comparte).

Eso significa que podemos tener más de un descriptor de archivo que posiblemente tenga diferentes atributos para una sola entrada de tabla de archivo abierta usando la función dup / dup2.

(Aunque parece que actualmente solo la bandera FD_CLOEXEC es el único atributo para un descriptor de archivo).

http://www.gnu.org/software/libc/manual/html_node/Descriptor-Flags.html

dup(fd) is equivalent to fcntl(fd, F_DUPFD, 0);

dup2(fildes, fildes2); is equivalent to 

   close(fildes2);
   fcntl(fildes, F_DUPFD, fildes2);

Las diferencias son (para el último) - Aparte de algunos valores de errno entre dup2 y fcntl close seguido de fcntl pueden generar condiciones de carrera ya que hay dos llamadas de función involucradas.

Los detalles se pueden consultar en http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html

Un ejemplo de uso -

Un ejemplo interesante al implementar el control de trabajos en un shell, donde se puede ver el uso de dup / dup2 ... en el enlace de abajo

http://www.gnu.org/software/libc/manual/html_node/Launching-Jobs.html#Launching-Jobs

Tanmoy Bandyopadhyay
fuente