GNU/Linux >> Znalost Linux >  >> Linux

Je možné požádat Linux o blackhole bajty během čtení soketu?

Na konci je tl;dr.

Ve svém komentáři jsem vám navrhl mmap() /dev/null přístroj. Zdá se však, že zařízení není na mém počítači mapovatelné (chyba 19 :No such device ). Vypadá to jako /dev/zero je však zmapovatelný. Další otázka/odpověď naznačuje, že je ekvivalentní MAP_ANONYMOUS což dělá fd argument a jeho přidružený open() v první řadě zbytečné. Podívejte se na příklad:

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

Ve výše uvedeném příkladu můžete vidět, že vytvářím soukromé (zápisy nebudou viditelné pro děti po fork() ) anonymní (nepodporované souborem) mapování paměti 4KiB (ve většině systémů jedna velikost stránky). Poté se použije dvakrát k poskytnutí cíle zápisu pro dva int – pozdější int přepíše dřívější.

To není přesně vyřešit vaši otázku:jak ignorovat bajty. Protože používáte readv() , podíval jsem se na její sesterskou funkci preadv() který na první pohled vypadá, že dělá to, co chcete:přeskakovat bajty. Zdá se však, že to není podporováno na deskriptorech souborů soketu. Následující kód dává 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;
}

Takže to vypadá dokonce jako preadv() používá seek() pod kapotou, což samozřejmě není povoleno na zásuvku. Nejsem si jistý, jestli existuje (zatím?) způsob, jak říct OS, aby ignoroval/zahodil bajty přijaté v zavedeném streamu. Mám podezření, že je to proto, že @geza má pravdu:náklady na zápis do konečného (ignorovaného) cíle jsou extrémně triviální pro většinu situací, se kterými jsem se setkal. A v situacích, kdy náklady na ignorované bajty není triviální, měli byste vážně zvážit použití lepších možností, implementací nebo protokolů.

tl;dr:

Vytváření anonymního mapování soukromé paměti o velikosti 4 kB je efektivně nerozeznatelné od kontejnerů pro souvislou alokaci (existují jemné rozdíly, které pravděpodobně nebudou důležité pro jakoukoli pracovní zátěž mimo velmi špičkový výkon). Použití standardního kontejneru je také mnohem méně náchylné k alokačním chybám:úniky paměti, divoké ukazatele a další. Takže bych řekl KISS a prostě to udělal místo schvalování jakéhokoli kódu, který jsem napsal výše. Například:std::array<char, 4096> ignored; nebo std::vector<char> ignored{4096}; a stačí nastavit iovec.iov_base = ignored.data(); a nastavte .iov_len na jakoukoli velikost, kterou potřebujete ignorovat (v rámci délky kontejneru).


Linux
  1. Úvod do linuxového příkazu chmod

  2. Je možné vyvíjet aplikace DirectX v Linuxu?

  3. Je možné přerušit vypnutí na Linuxu?

  1. linuxová schránka pro čtení/zápis v C

  2. Linux:existuje čtení nebo recv ze socketu s časovým limitem?

  3. zkontrolujte všechny otevřené zásuvky v linuxovém OS

  1. Správa oddílů v Linuxu pomocí fdisk

  2. Je možné vyvinout modul linuxového jádra v CLion?

  3. Omezení délky názvu souboru na linuxu?