Come posso mettere una maschera di bit su / dev / zero in modo da poter ottenere byte diversi da zero?


20

Come posso mettere una maschera di bit in /dev/zeromodo da poter avere una sorgente non solo per 0x00 ma anche per qualsiasi byte compreso tra 0x01 e 0xFF?


8
Perché lo chiedi? Modifica la domanda per motivarla.
Basile Starynkevitch il

1
Puoi usare questa risposta come riferimento: stackoverflow.com/questions/12634503/how-to-use-xor-in-bash
Romeo Ninov,

Ho dato una risposta a questa domanda, ma leggendola di nuovo penso di aver capito male. Vuoi tradurre ciascuno 0x00in un valore specifico o in un valore casuale 0x00-0xFFnell'intervallo?
kos l'

1
@kos ciascuno per un valore specifico come 444444...non uno casuale
Eduard Florinescu l'

Risposte:


18

Il seguente bashcodice è impostato per funzionare con il byte rappresentato in binario . Tuttavia, puoi facilmente cambiarlo per gestire l' ocatale , il decimale o l' esagono semplicemente cambiando il valore radix r di 2 in 8, 10o 16rispettivamente e impostando di b=conseguenza.

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

EDIT - Gestisce l' intera gamma di valori byte: hex 00 - FF (quando ho scritto 00-7F sotto, stavo considerando solo caratteri UTF-8 a byte singolo).

Se, ad esempio, vuoi solo 4 byte (caratteri nell'intervallo 00-7F esadecimale solo ASC1-UTF-8) , puoi inserirlo nella testa :... | head -c4

Uscita (4 caratteri):

~~~~

Per vedere l'output in formato 8 bit, esegui il pipe xxd(o qualsiasi altro dump di byte 1 e 0 *):
ad es. b=10000000e tubazioni per:... | head -c4 | xxd -b

0000000: 10000000 10000000 10000000 10000000                    ....

1
Intendevi scrivere o=$(printf ...)per la seconda riga?
jwodder,

1
@jwodder: No, la seconda riga è corretta come mostrato. L' opzione printf-v fa sì che l'output imposti direttamente la variabile chiamata immediatamente dopo di essa; in questo caso il nome di quella variabile è o(per ottale ) - nota che l' -vopzione si applica alla versione shell-builtin di printf(non alla versione / usr / bin / printf )
Peter.O

2
@jwodder Inoltre, in generale, l' -vopzione assicura che la variabile venga impostata esattamente su ciò che hai specificato. $(...)trasforma prima l'output. Ecco perché o=$(printf '\n')non avrà l'effetto che ci si potrebbe aspettare, mentre lo printf -vo '\n'fa. (Non importa qui, poiché l'output qui è in una forma che non è influenzata da una tale trasformazione, ma se non si fosse a conoscenza -vdell'opzione, questo potrebbe essere utile sapere.)
hvd

18

Non puoi farlo facilmente.

Si potrebbe prendere in considerazione la possibilità di scrivere il proprio modulo del kernel fornendo tale dispositivo. Non lo consiglio.

Potresti scrivere un piccolo programma C scrivendo un flusso infinito di stessi byte su una pipe (o su stdout) o FIFO.

Puoi usare tr (1) per leggere /dev/zeroe tradurre ogni 0 byte in qualcos'altro.

Potresti usare forse yes (1) , almeno se puoi permetterti di avere newline (oppure inoltralo in tr -d '\n'...)


10
O utilizzare yes 1 | tr -d $'\n'per quella materia.
Kojiro,

3
@kojiro: fallirà se provi a yesun flusso di \ncaratteri. Un'alternativa che gestisce \nè: yes '' | tr '\n' "$c"- dove $cpuò essere qualsiasi carattere dell'intera gamma di caratteri ASCII.
Peter

1
@ Peter.O Non sono sicuro di come hai interpretato il mio commento per significare qualcosa di diverso dall'espressione letterale e statica yes 1 | tr -d $'\n'. Suppongo che potresti usare una shell che non fa il $''trattamento con la barra rovesciata, oppure potresti provare a trovare un locale che si altera tr -d $'\n', ma non l'ho ancora trovato.
Kojiro,

@kojiro: Stampa in yes 1 | tr -d $'\n'modo abbastanza felice un flusso di 1caratteri e quasi ogni altro valore a byte singolo, ma non è possibile stampare un flusso di \ncaratteri. L'OP vuole essere in grado di gestire tutti i valori di byte "tra 0x01 e 0xFF"
Peter.O

1
loop() { if [ "$1" = $'\n' ]; then yes "$1"; else yes "$1" | tr -d $'\n' ; fi;
PSkocik,

13

Bene, se vuoi letteralmente raggiungere questo obiettivo, puoi usare un hook LD_PRELOAD . L'idea di base è di riscrivere una funzione dalla libreria C e usarla al posto di quella normale.

Ecco un semplice esempio in cui sovrascriviamo la funzione read () per XOR il buffer di output con 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);    
}

Un'implementazione ingenua avrebbe XOR 0x42 su ogni file che leggiamo, il che avrebbe conseguenze indesiderate. Per risolvere questo problema, ho anche agganciato la funzione open () , facendogli recuperare il descrittore di file associato a / dev / zero. Quindi eseguiamo XOR solo sulla nostra funzione read () se fd == dev_zero_fd.

Uso:

$ gcc hook.c -ldl -shared -o hook.so
$ LD_PRELOAD=$(pwd)/hook.so bash #this spawns a hooked shell
$ cat /dev/zero
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

3
Data l'implementazione, potresti avere un collegamento simbolico da / dev / capbee a / dev / zero, cercare / dev / capbee e lasciare / dev / zero da solo. // dev / zero non sarà uguale a / dev / zero.
Robert Jacobs,

1
@RobertJacobs In effetti. Potremmo persino generare collegamenti simbolici / dev / 0x01, / dev / 0x02, / dev / 0x03, ... a / dev / zero e analizzare il nome file per determinare la maschera di bit da applicare.
giovedì

11

In termini di velocità, il più veloce che ho trovato è stato:

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

Per confronto:

$ 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 'mentre:; do echo -n "\ 1"; fatto "| pv -a> / dev / null
[225 KiB / s]
$ bash -c 'mentre:; do echo -ne "\ 1"; fatto "| pv -a> / dev / null
[180 KiB / s]

$ < /dev/zero pv -a > /dev/null
[5.56GiB/s]
$ cat /dev/zero | pv -a > /dev/null
[2.82GiB/s]

Nel mio Debian, perlcedere 2.13GiB, mentre < /dev/zerocedere 8.73GiB. Quale cosa può influire sulle prestazioni?
cuonglm,

@cuonglm, sì, vedo alcune variazioni tra i sistemi, ma perlè costantemente più veloce delle altre soluzioni. Ottengo lo stesso throughput dell'equivalente programma C compilato. Il benchmark è tanto sull'applicazione quanto sullo scheduler del sistema qui. Ciò che rende il più diverso è la dimensione dei buffer in fase di scrittura.
Stéphane Chazelas,

@cuonglm Anche la pipa rallenta. Penso che cat /dev/zero| pv -a >/dev/nullti darà anche circa 2 GiB al secondo (lo fa sul mio sistema, mentre < /dev/zero) mi dà circa 6GiBps.
PSkocik,

@ StéphaneChazelas Posso chiederti su quale sistema sei, Stéphane Chazelas? I miei risultati sono abbastanza diversi (posso ottenere circa 2.1GiB dalla versione perl). Sono su 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 all'interno.
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. Il nuovo kernel sembra fare la differenza (a meno che non sia il perl più recente: v5.20.2)
Stéphane Chazelas,

7

È un po 'inutile provare e maschera di bit / xo zero byte, non è vero? Prendere un byte e xorinserirlo con zero è un no-op.

Basta creare un loop che ti dia i byte desiderati e metterlo dietro una pipe o una pipe denominata. Si comporterà praticamente come un dispositivo a caratteri (non sprecherà i cicli della CPU quando è inattivo):

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

E se desideri ottimizzarlo in modo ottimale, puoi utilizzare il codice C di seguito:

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

compilare ed eseguire

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

Test della prestazione:

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

2.1GB / s sulla mia macchina (anche leggermente più veloce di cat /dev/zero | pv -a >/dev/null)


Inizialmente ho provato a usare putchar in C, ma è stato lento.
PSkocik,

Per curiosità, perché argc == 1+1invece di agrc == 2?
Ripristina Monica - notmaynard l'

@iamnotmaynard Per ricordare a me stesso che è 1 per l'eseguibile della riga di comando più 1 argomento. :-D
PSkocik

Ah. Questa era la mia ipotesi, ma volevo assicurarmi che non ci fossero ragioni segrete.
Ripristina Monica - notmaynard l'

"Prendere un byte e azzerarlo con zero è un no-op." Questo non è vero: 0 XOR X == X.
jacwah,

5

Leggi gli zeri, traduci ogni zero nel tuo schema!

Leggiamo zero byte da /dev/zeroe utilizziamo trper applicare una maschera di bit a ciascuno dei byte traducendo ogni zero byte:

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

L'ottobre 176 è il codice ascii di ~, quindi ne otteniamo 10 ~. (La $fine dell'output indica nella mia shell che non c'era fine della linea - potrebbe avere un aspetto diverso per te)

Quindi, creiamo 0xFFbyte: esadecimale 0xFFè ottale 0377. Lo zero iniziale viene lasciato fuori per la trriga di comando; Alla fine, hexdumpviene utilizzato per rendere leggibile l'output.

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

È necessario utilizzare i codici ottali dei caratteri qui, anziché esadecimali. Quindi è la gamma da \000a ottale \377(uguale a 0xFF).
Utilizzare ascii -xe ascii -oper ottenere una tabella dei caratteri con numeri di indice esadecimali o ottali.
(Per una tabella con decimale ed esadecimale, solo ascii).

Abbastanza veloce

Funziona abbastanza velocemente, rispetto al solo utilizzo degli zeri: cat /dev/zeroè solo quattro volte più veloce, mentre può fare un uso perfetto del buffering IO, che trnon può.

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

$ </dev/zero cat | pv -a >/dev/null        
[4.37GB/s]

3

Dipende da cosa vuoi fare con i dati e da quanto flessibile vuoi usarli.

Nel peggiore dei casi, se hai bisogno di velocità, potresti fare lo stesso di / dev / zero, e semplicemente compilare / dev / one, / dev / two, .. / dev / fourtytwo .. e così via.

Nella maggior parte dei casi dovrebbe essere meglio creare i dati direttamente dove sono necessari, quindi all'interno di un programma / script come costante. Con più informazioni le persone potrebbero aiutarti meglio.


1

Loop di stampa infinito

Sostituire \u00con il byte desiderato.

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

Codice C ++:

#include<cstdio>

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

Compila: riposiziona Bytecon il valore desiderato.

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

Uso

./bin | yourapp

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.