Jak mogę nałożyć maskę bitową na / dev / zero, aby uzyskać bajty inne niż zero?

20

Jak mogę nałożyć maskę bitową, /dev/zeroaby mieć źródło nie tylko dla 0x00, ale także dla dowolnego bajtu między 0x01 a 0xFF?

Eduard Florinescu
źródło
8
Dlaczego pytasz? Edytuj pytanie, aby je motywować.
Basile Starynkevitch,
1
Możesz użyć tej odpowiedzi jako odniesienia: stackoverflow.com/questions/12634503/how-to-use-xor-in-bash
Romeo Ninov
Odpowiedziałem na to pytanie, ale czytając je ponownie, myślę, że go nie zrozumiałem. Czy chcesz przetłumaczyć każdą 0x00z nich na określoną wartość lub na losową wartość z tego 0x00-0xFFzakresu?
Kos
1
@kos do określonej wartości, takiej jak 444444...nieprzypadkowa
Eduard Florinescu,

Odpowiedzi:

18

Poniższy bashkod jest ustawiony do pracy z bajtem reprezentowanym w formacie binarnym . Można go jednak łatwo zmienić, aby obsługiwał ósemkowy , dziesiętny lub szesnastkowy , po prostu zmieniając wartość radix na odpowiednio , lub odpowiednio i ustawiając odpowiednio. r281016b=

r=2; b=01111110
printf -vo '\\%o' "$(($r#$b))"; </dev/zero tr '\0' "$o"

EDYCJA - Obsługuje pełny zakres wartości bajtów: hex 00 - FF (kiedy napisałem 00-7F poniżej, rozważałem tylko jednobajtowe znaki UTF-8).

Jeśli na przykład chcesz tylko 4 bajty (znaki w zakresie szesnastkowym 00-7F tylko w formacie UTF-8 „ASCII”) , możesz potokować go do głowy :... | head -c4

Wyjście (4 znaki):

~~~~

Aby zobaczyć wynik w formacie 8-bitowym, potokuj go do xxd(lub dowolnego innego zrzutu bajtów 1 i 0 *):
np. b=10000000i orurowanie do:... | head -c4 | xxd -b

0000000: 10000000 10000000 10000000 10000000                    ....
Peter.O
źródło
1
Miałeś na myśli pisać o=$(printf ...)dla drugiej linii?
jwodder
1
@jwodder: Nie, drugi wiersz jest poprawny, jak pokazano. Printf opcja -vpowoduje tthe wyjście bezpośrednio ustawić zmienną o nazwie bezpośrednio po nim; w tym przypadku nazwa zmiennej to o( ósemkowa ) - zwróć uwagę, że -vopcja dotyczy wersji wbudowanej w powłokęprintf (nie wersji / usr / bin / printf )
Peter.O
2
@jwodder Ponadto, ogólnie rzecz biorąc, -vopcja zapewnia, że ​​zmienna zostanie ustawiona dokładnie tak, jak podałeś. $(...)najpierw przekształca dane wyjściowe. Dlatego o=$(printf '\n')nie będzie miał oczekiwanego efektu, podczas gdy printf -vo '\n'ma. (Nie ma to tutaj znaczenia, ponieważ dane wyjściowe są w formie, na którą taka transformacja nie ma wpływu, ale jeśli nie byłeś świadomy tej -vopcji, warto to wiedzieć.)
hvd
18

Nie możesz tego łatwo zrobić.

Możesz rozważyć napisanie własnego modułu jądra zapewniającego takie urządzenie. Nie polecam tego.

Mógłbyś napisać mały program C zapisujący nieskończony strumień tych samych bajtów na jakimś potoku (lub on stdout) lub FIFO.

Możesz użyć tr (1) do czytania /dev/zeroi tłumaczenia każdego 0 bajtu na coś innego.

Możesz użyć może tak (1) , przynajmniej jeśli możesz sobie pozwolić na posiadanie nowego wiersza (lub wpuszczaj go do tr -d '\n'...)

Basile Starynkevitch
źródło
10
Lub użyj yes 1 | tr -d $'\n'do tego.
kojiro
3
@kojiro: to się nie powiedzie, jeśli spróbujesz przesłać yesstrumień \nznaków. Alternatywą, która obsługuje \n: yes '' | tr '\n' "$c"- gdzie $cmoże być dowolny znak pełnego zakresu znaków ASCII.
Peter.O,
1
@ Peter.O Nie jestem pewien, jak interpretujesz mój komentarz jako coś innego niż dosłowne, statyczne wyrażenie yes 1 | tr -d $'\n'. Przypuszczam, że możesz użyć powłoki, która nie wykonuje $''leczenia odwrotnym ukośnikiem, lub możesz spróbować znaleźć lokalizację, która się zmienia tr -d $'\n', ale jeszcze jej nie znalazłem.
kojiro
@kojiro: Z yes 1 | tr -d $'\n'przyjemnością wydrukujesz strumień 1znaków i prawie każdą inną jednobajtową wartość, ale nie możesz wydrukować strumienia \nznaków. OP chce być w stanie obsłużyć wszystkie wartości bajtów „między 0x01 a 0xFF”
Peter.O
1
loop() { if [ "$1" = $'\n' ]; then yes "$1"; else yes "$1" | tr -d $'\n' ; fi;
PSkocik,
13

Cóż, jeśli dosłownie chcesz to osiągnąć, możesz użyć haka LD_PRELOAD . Podstawową ideą jest przepisanie funkcji z biblioteki C i użycie jej zamiast normalnej.

Oto prosty przykład, w którym nadpisujemy funkcję read () XOR bufora wyjściowego 0x42.

#define _GNU_SOURCE
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <dlfcn.h> 
#include <unistd.h>

static int dev_zero_fd = -1;

int open64(const char *pathname, int flags)
{
    static int (*true_open64)(const char*, int) = NULL;
    if (true_open64 == NULL) {
        if ((true_open64 = dlsym(RTLD_NEXT, "open64")) == NULL) {
            perror("dlsym");
            return -1;
        }        
    }
    int ret = true_open64(pathname, flags);
    if (strcmp(pathname, "/dev/zero") == 0) {
        dev_zero_fd = ret;
    }
    return ret;
}


ssize_t read(int fd, void *buf, size_t count)
{
    static ssize_t (*true_read)(int, void*, size_t) = NULL;
    if (true_read == NULL) {
        if ((true_read = dlsym(RTLD_NEXT, "read")) == NULL) {
            perror("dlsym");
            return -1;
        }        
    }    

    if (fd == dev_zero_fd) {
        int i;
        ssize_t ret = true_read(fd, buf, count);    
        for (i = 0; i < ret; i++) {
            *((char*)buf + i) ^= 0x42;
        }
        return ret;
    }

    return true_read(fd, buf, count);    
}

Naiwna implementacja spowodowałaby XOR 0x42 na każdym czytanym pliku, co miałoby niepożądane konsekwencje. Aby rozwiązać ten problem, podłączyłem również funkcję open () , dzięki czemu pobierała deskryptor pliku powiązany z / dev / zero. Następnie wykonujemy XOR w naszej funkcji read () if fd == dev_zero_fd.

Stosowanie:

$ gcc hook.c -ldl -shared -o hook.so
$ LD_PRELOAD=$(pwd)/hook.so bash #this spawns a hooked shell
$ cat /dev/zero
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
yoann
źródło
3
Biorąc pod uwagę twoją implementację, możesz mieć symboliczny link z / dev / capbee do / dev / zero, wyszukaj / dev / capbee i zostaw / dev / zero w spokoju. // dev / zero nie będzie taki sam jak / dev / zero.
Robert Jacobs,
1
@RobertJacobs Rzeczywiście. Możemy nawet wygenerować dowiązania symboliczne / dev / 0x01, / dev / 0x02, / dev / 0x03, ... do / dev / zero i przeanalizować nazwę pliku, aby określić maskę bitów do zastosowania.
yoann
11

Pod względem prędkości najszybsze, jakie znalazłem, to:

$ PERLIO=:unix perl -e '$s="\1" x 65536; for(;;){print $s}' | pv -a > /dev/null
[4.02GiB/s]

Dla porownania:

$ tr '\0' '\1' < /dev/zero | pv -a > /dev/null
[ 765MiB/s]
$ busybox tr '\0' '\1' < /dev/zero | pv -a > /dev/null
[ 399MiB/s]

$ yes $'\1' | tr -d '\n' | pv -a > /dev/null
[26.7MiB/s]

$ dash -c 'while:; wykonaj echo -n "\ 1"; gotowe ”| pv -a> / dev / null
[225 kB / s]
$ bash -c 'while:; wykonaj echo -ne "\ 1"; gotowe ”| pv -a> / dev / null
[180 kB / s]

$ < /dev/zero pv -a > /dev/null
[5.56GiB/s]
$ cat /dev/zero | pv -a > /dev/null
[2.82GiB/s]
Stéphane Chazelas
źródło
W moim Debianie perlwydaj 2.13GiB, podczas gdy < /dev/zero8.73GiB. Co może wpłynąć na wydajność?
cuonglm,
@cuonglm, tak, widzę pewne różnice między systemami, ale perljest konsekwentnie szybszy niż inne rozwiązania. Dostaję taką samą przepustowość jak w równoważnym skompilowanym programie C. Test porównawczy dotyczy zarówno aplikacji, jak i harmonogramu systemu. Najbardziej wyróżnia się rozmiar zapisywanych buforów.
Stéphane Chazelas,
@cuonglm Rura również spowalnia. Myślę, że cat /dev/zero| pv -a >/dev/nullda ci również około 2 GiB na sekundę (robi to w moim systemie, podczas gdy < /dev/zero) daje mi około 6GiBps.
PSkocik,
@ StéphaneChazelas Czy mogę zapytać, na jakim systemie jesteś, Stéphane Chazelas? Moje wyniki są dość różne (mogę uzyskać około 2.1GiB z wersji perla). Jestem na Linux ProBook 3.13.0-24-generic #47-Ubuntu SMP Fri May 2 23:30:00 UTC 2014 x86_64 x86_64 x86_64 GNU/LinuxIntel i5 Core w środku.
PSkocik,
1
@PSkocik, Linux 3.16.0-4-amd64 # 1 SMP Debian 3.16.7-ckt9-3 (2015-04-23) x86_64 GNU / Linux, Intel (R) Core (TM) 2 Duo CPU T9600 @ 2.80GHz. Wydaje się, że nowsze jądro robi różnicę (chyba że jest to nowszy perl: v5.20.2)
Stéphane Chazelas
7

Nie ma sensu próbować maskować bitów / xor lub zero bajtów, prawda? Zabranie bajtu i wprowadzenie xorgo do zera nie jest możliwe.

Po prostu utwórz pętlę, która da ci bajty, które chcesz i umieść ją za potokiem lub nazwaną potokiem. Będzie zachowywać się tak samo jak urządzenie postaci (nie marnuje cykli procesora, gdy jest bezczynny):

mkfifo pipe
while : ; do echo -n "a"; done > pipe &

A jeśli chcesz go superoptymalizować, możesz użyć kodu C poniżej:

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) { 
  char c = argc == 1+1 ? argv[1][0] : 'y';

  char buff[BUFSIZ];
  memset(buff, c, BUFSIZ);

  for(;;){ 
    write(1, buff, sizeof(buff)); 
  }
}

skompiluj i uruchom

$ CFLAGS=-O3 make loop
./loop "$the_byte_you_want" > pipe

Test wydajności:

./loop 1 | pv -a >/dev/null 

2,1 GB / s na moim komputerze (nawet nieco szybszym niż cat /dev/zero | pv -a >/dev/null)

PSkocik
źródło
Początkowo próbowałem użyć putchar w C, ale było to powolne.
PSkocik,
Z ciekawości, dlaczego argc == 1+1zamiast agrc == 2?
Przywróć Monikę - notmaynard
@iamnotmaynard Aby przypomnieć sobie, że jest to 1 dla pliku wykonywalnego wiersza poleceń plus 1 argument. :-D
PSkocik
Ach Tak sądziłem, ale chciałem się upewnić, że nie ma żadnego tajnego powodu.
Przywróć Monikę - notmaynard
„Biorąc bajt i ustawiając go na zero, nie można tego robić”. To nie jest prawdą 0 XOR X == X.
jacwah
5

Czytaj zera, tłumacz każde zero na swój wzór!

Odczytujemy z bajtów zero /dev/zeroi używamy trdo zastosowania maski bitowej do każdego z bajtów, tłumacząc każdy bajt zero:

$ </dev/zero tr '\000' '\176' | head -c 10
~~~~~~~~~~$

Oktal 176 to kod ascii ~, więc otrzymujemy 10 ~. (Na $końcu wyniku wskazuje w mojej powłoce, że nie było końca linii - może wyglądać inaczej dla ciebie)

Stwórzmy 0xFFbajty: Hex 0xFFjest ósemkowy 0377. Zera wiodącego nie ma w trwierszu poleceń; Na końcu hexdumpsłuży do odczytu danych wyjściowych.

$ </dev/zero tr '\000' '\377' | head -c 10 | hexdump
0000000 ffff ffff ffff ffff ffff               
000000a

W tym miejscu należy użyć kodów ósemkowych znaków zamiast szesnastkowego. Jest to więc zakres od \000ósemkowego \377(taki sam jak 0xFF).
Użyj ascii -xi, ascii -oaby uzyskać tabelę znaków z szesnastkowymi lub ósemkowymi liczbami indeksu.
(Tylko dla tabeli dziesiętnej i szesnastkowej ascii).

Dosyć szybko

Działa dość szybko, w porównaniu do samego zerowania: cat /dev/zerojest tylko cztery razy szybszy, podczas gdy może doskonale wykorzystywać buforowanie IO, co trnie jest możliwe.

$ </dev/zero tr '\000' '\176' | pv -a >/dev/null
[ 913MB/s]

$ </dev/zero cat | pv -a >/dev/null        
[4.37GB/s]
Volker Siegel
źródło
3

Zależy, co chcesz zrobić z danymi i jak elastycznie chcesz z nich korzystać.

W najgorszym przypadku, jeśli potrzebujesz prędkości, możesz zrobić to samo, co / dev / zero, i po prostu skompilować / dev / one, / dev / two, .. / dev / fourtytwo .. i tak dalej na urządzeniach.

W większości przypadków lepiej jest tworzyć dane bezpośrednio tam, gdzie są potrzebne, więc wewnątrz programu / skryptu jako stałą. Dzięki większej ilości informacji ludzie mogą lepiej ci pomóc.

Gość
źródło
1

Infinte printf loop

Zastąp ponownie żądanym \u00bajtem.

while true ; do printf "\u00" ; done | yourapp

Kod C ++:

#include<cstdio>

int main(){
 char out=Byte;
 while(true)
 fwrite(&out,sizeof(out),1,stdout);
}

Kompiluj: zastąp ponownie żądaną Bytewartością.

g++ -O3 -o bin file.cpp -D Byte=0x01

Posługiwać się

./bin | yourapp

nkomputery
źródło