Repasemos rápidamente los archivos del dispositivo: en Linux, los programas de aplicación comunican operaciones rad y de escritura al núcleo a través de descriptores de archivo . Eso funciona muy bien para los archivos, y resultó que la misma API podría usarse para dispositivos de caracteres que producen y consumen secuencias de caracteres y bloquean dispositivos que leen y escriben bloques de tamaño fijo en una dirección de acceso aleatorio, simplemente pretendiendo que estos También son archivos.
Pero se necesitaba una forma de configurar esos dispositivos (establecer velocidades de transmisión, etc.), y para eso, se inventó la llamada ioctl . Simplemente pasa una estructura de datos que es específica para el dispositivo y el tipo de control de E / S utilizado para el núcleo, y recupera los resultados en la misma estructura de datos, por lo que es una API extensible muy genérica y puede usarse para muchas cosas .
Ahora, ¿cómo encajan las operaciones de red? Una aplicación de servidor de red típica quiere vincularse a alguna dirección de red, escuchar en un determinado puerto (por ejemplo, 80 para HTTP o 22 para ssh), y si un cliente se conecta , quiere enviar datos y recibir datos de este cliente. Y las operaciones duales para el cliente.
No es obvio cómo encajar esto con las operaciones de archivo (aunque se puede hacer, vea el Plan 9 ), es por eso que los diseñadores de UNIX inventaron una nueva API: sockets . Puede encontrar más detalles en las páginas del manual para la sección 2 socket
, bind
, listen
, connect
, send
y recv
. Tenga en cuenta que si bien es diferente de la API de E / S de archivo, la socket
llamada también devuelve un descriptor de archivo. Existen numerosos tutoriales sobre cómo usar sockets en la web, google un poco.
Hasta ahora, todo esto es puro UNIX, nadie hablaba de interfaces de red en el momento en que se inventaron los sockets. Y debido a que esta API es realmente antigua, está definida para una variedad de protocolos de red más allá del protocolo de Internet (observe las AF_*
constantes), aunque solo algunos de ellos son compatibles con Linux.
Pero a medida que las computadoras comenzaron a obtener múltiples tarjetas de red, se necesitó cierta abstracción para esto. En Linux, esa es la interfaz de red (NI). No solo se usa para una pieza de hardware, sino también para varios túneles, puntos finales de aplicaciones de usuario que sirven como túneles como OpenVPN, etc. Como se explicó, la API de socket no se basa en archivos (especiales) e independientes del sistema de archivos. Del mismo modo, las interfaces de red tampoco se muestran en el sistema de archivos. Sin embargo, las NI están disponibles en el sistema de archivos /proc
y /sys
(así como en otros ajustes de red ajustables).
Una NI es simple una abstracción del núcleo de un punto final donde los paquetes de red entran y salen del núcleo. Los sockets, por otro lado, se utilizan para comunicar paquetes con aplicaciones. No es necesario involucrar ningún socket con el procesamiento de un paquete. Por ejemplo, cuando el reenvío está habilitado, un paquete puede ingresar en un NI y salir en otro. En ese sentido, los sockets y las interfaces de red son totalmente independientes.
Pero tenía que haber una forma de configurar NI, al igual que necesitabas una forma de configurar dispositivos de bloque y de caracteres. Y dado que los sockets ya devolvieron un descriptor de archivo, era algo lógico permitir un ioctl
descriptor en ese archivo. Esa es la interfaz de netdevice que vinculaste .
Hay muchos otros abusos de las llamadas al sistema de manera similar, por ejemplo, para el filtrado de paquetes, la captura de paquetes, etc.
Todo esto ha crecido pieza tras pieza, y no es particularmente lógico en muchos lugares. Si se hubiera diseñado de una vez, probablemente se podría haber hecho una API más ortogonal.