GNU/Linux >> Tutoriales Linux >  >> Linux

¿Es posible pedirle a Linux que bloquee los bytes durante una lectura de socket?

Hay un tl;dr al final.

En mi comentario, te sugerí mmap() el /dev/null dispositivo. Sin embargo, parece que el dispositivo no es mapeable en mi máquina (err 19 :No such device ). Parece /dev/zero aunque es mapeable. Otra pregunta/respuesta sugiere que es equivalente a MAP_ANONYMOUS lo que hace que el fd argumento y su asociado open() innecesario en primer lugar. Mira un ejemplo:

#include <iostream>
#include <cstring>
#include <cerrno>
#include <cstdlib>

extern "C" {
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
}

template <class Type>
struct iovec ignored(void *p)
{
    struct iovec iov_ = {};
    iov_.iov_base = p;
    iov_.iov_len = sizeof(Type);
    return iov_;
}

int main()
{
    auto * p = mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if ( MAP_FAILED == p ) {
        auto err = errno;
        std::cerr << "mmap(MAP_PRIVATE | MAP_ANONYMOUS): " << err << ": " << strerror(err) << std::endl;
        return EXIT_FAILURE;
    }

    int s_[2] = {-1, -1};
    int result = socketpair(AF_UNIX, SOCK_STREAM, 0, s_);
    if ( result < 0 ) {
        auto err = errno;
        std::cerr << "socketpair(): " << err << ": " << strerror(err) << std::endl;
        return EXIT_FAILURE;
    }

    int w_[3] = {1,2,3};
    ssize_t nwritten = 0;
    auto makeiov = [](int & v){
        struct iovec iov_ = {};
        iov_.iov_base = &v;
        iov_.iov_len = sizeof(v);
        return iov_;
    };
    struct iovec wv[3] = {
        makeiov(w_[0]),
        makeiov(w_[1]),
        makeiov(w_[2])
    };

    nwritten = writev(s_[0], wv, 3);
    if ( nwritten < 0 ) {
        auto err = errno;
        std::cerr << "writev(): " << err << ": " << strerror(err) << std::endl;
        return EXIT_FAILURE;
    }

    int r_ = {0};
    ssize_t nread = 0;
    struct iovec rv[3] = {
        ignored<int>(p),
        makeiov(r_),
        ignored<int>(p),
    };

    nread = readv(s_[1], rv, 3);
    if ( nread < 0 ) {
        auto err = errno;
        std::cerr << "readv(): " << err << ": " << strerror(err) << std::endl;
        return EXIT_FAILURE;
    }

    std::cout <<
        w_[0] << '\t' <<
        w_[1] << '\t' <<
        w_[2] << '\n' <<
        r_ << '\t' <<
        *(int*)p << std::endl;

    return EXIT_SUCCESS;
}

En el ejemplo anterior, puede ver que creo un privado (las escrituras no serán visibles para los niños después de fork() ) mapeo de memoria anónimo (no respaldado por un archivo) de 4KiB (un solo tamaño de página en la mayoría de los sistemas). Luego se usa dos veces para proporcionar un destino de escritura para dos enteros; el último entero sobrescribe al anterior.

Eso no exactamente resuelve tu pregunta:cómo ignorar los bytes. Ya que estás usando readv() , investigué su función hermana, preadv() que a primera vista parece hacer lo que usted quiere que haga:omitir bytes. Sin embargo, parece que no es compatible con los descriptores de archivos de socket. El siguiente código da preadv(): 29: Illegal seek .

rv = makeiov(r_[1]);
nread = preadv(s_[1], &rv, 1, sizeof(int));
if ( nread < 0 ) {
    auto err = errno;
    std::cerr << "preadv(): " << err << ": " << strerror(err) << std::endl;
    return EXIT_FAILURE;
}

Entonces parece que incluso preadv() usa seek() debajo del capó que, por supuesto, no está permitido en un enchufe. No estoy seguro de si hay (¿todavía?) una forma de decirle al sistema operativo que ignore/elimine los bytes recibidos en un flujo establecido. Sospecho que es porque @geza tiene razón:el costo de escribir en el destino final (ignorado) es extremadamente trivial para la mayoría de las situaciones que he encontrado. Y, en las situaciones en las que el costo de los bytes ignorados no trivial, debería considerar seriamente usar mejores opciones, implementaciones o protocolos.

tl;dr:

La creación de una asignación de memoria privada anónima de 4KiB es prácticamente indistinguible de los contenedores de asignación contigua (existen diferencias sutiles que probablemente no sean importantes para ninguna carga de trabajo fuera del rendimiento de muy alto nivel). Usar un contenedor estándar también es mucho menos propenso a errores de asignación:fugas de memoria, punteros salvajes, etc. Así que diría KISS y solo haría eso en lugar de respaldar cualquier del código que escribí arriba. Por ejemplo:std::array<char, 4096> ignored; o std::vector<char> ignored{4096}; y solo configura iovec.iov_base = ignored.data(); y establece el .iov_len al tamaño que necesite ignorar (dentro de la longitud del contenedor).


Linux
  1. Introducción al comando chmod de Linux

  2. ¿Es posible desarrollar aplicaciones DirectX en Linux?

  3. ¿Es posible abortar el apagado en Linux?

  4. portapapeles de linux leer/escribir en C

  5. Linux:¿hay una lectura o recepción desde el socket con tiempo de espera?

Linux Mint KDE todavía es posible

¿Biblioteca C para leer la versión EXE de Linux?

¿Por qué se usa select en Linux?

Linux, sockets, conexión sin bloqueo

¿Cómo leer las páginas man de Linux?

Cómo hacer sockets con nombre de Linux