GNU/Linux >> Linux Esercitazione >  >> Linux

È possibile chiedere a Linux di eseguire il blackhole dei byte durante la lettura di un socket?

C'è un tl;dr alla fine.

Nel mio commento, ti ho suggerito mmap() il /dev/null dispositivo. Tuttavia sembra che il dispositivo non sia mappabile sulla mia macchina (err 19 :No such device ). Sembra /dev/zero è mappabile però. Un'altra domanda/risposta suggerisce che è equivalente a MAP_ANONYMOUS che fa il fd argomento e il relativo open() associato inutile in primo luogo. Guarda un esempio:

#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;
}

Nell'esempio sopra puoi vedere che creo un privato (le scritture non saranno visibili dai bambini dopo fork() ) mappatura della memoria anonima (non supportata da un file) di 4 KiB (una singola dimensione di pagina sulla maggior parte dei sistemi). Viene quindi utilizzato due volte per fornire una destinazione di scrittura per due int:l'int successivo sovrascrive quello precedente.

Questo non esattamente risolvi la tua domanda:come ignorare i byte. Dato che stai usando readv() , ho esaminato la sua funzione gemella, preadv() che a prima vista sembra fare quello che vuoi che faccia:saltare i byte. Tuttavia, sembra che non sia supportato sui descrittori di file socket. Il codice seguente restituisce 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;
}

Quindi sembra anche preadv() utilizza seek() sotto il cofano che, ovviamente, non è consentito su una presa. Non sono sicuro che esista (ancora?) Un modo per dire al sistema operativo di ignorare/eliminare i byte ricevuti in un flusso stabilito. Sospetto che sia perché @geza ha ragione:il costo per scrivere alla destinazione finale (ignorata) è estremamente banale per la maggior parte delle situazioni che ho incontrato. E, nelle situazioni in cui il costo dei byte ignorati non banale, dovresti prendere seriamente in considerazione l'utilizzo di opzioni, implementazioni o protocolli migliori.

tl;dr:

La creazione di una mappatura della memoria privata anonima di 4 KiB è effettivamente indistinguibile dai contenitori di allocazione contigua (ci sono sottili differenze che probabilmente non sono importanti per qualsiasi carico di lavoro al di fuori delle prestazioni di fascia alta). L'utilizzo di un contenitore standard è anche molto meno soggetto a bug di allocazione:perdite di memoria, puntatori jolly e così via. Quindi direi KISS e lo farei invece di approvare qualsiasi del codice che ho scritto sopra. Ad esempio:std::array<char, 4096> ignored; o std::vector<char> ignored{4096}; e basta impostare iovec.iov_base = ignored.data(); e imposta .iov_len a qualsiasi dimensione tu debba ignorare (entro la lunghezza del contenitore).


Linux
  1. È possibile sviluppare app DirectX in Linux?

  2. È possibile interrompere l'arresto su Linux?

  3. appunti di linux leggi/scrivi in ​​C

  4. Possibile utilizzare un .dll su Linux

  5. Libreria C per leggere la versione EXE da Linux?

Linux Mint KDE è ancora possibile

Comando Linux (come cat) per leggere una quantità specificata di caratteri

Linux:c'è una lettura o una ricezione dal socket con timeout?

Perché select è usato in Linux

controlla tutti i socket aperti nel sistema operativo Linux

Linux denominato socket howto