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).