¿Cómo puedo poner una máscara de bits en / dev / zero para poder obtener bytes distintos de cero?

20

¿Cómo puedo poner una máscara de bits /dev/zeropara que pueda tener una fuente no solo para 0x00 sino también para cualquier byte entre 0x01 y 0xFF?

Eduard Florinescu
fuente
8
¿Por qué preguntas? Edite la pregunta para motivarla.
Basile Starynkevitch
1
Puede usar esta respuesta como referencia: stackoverflow.com/questions/12634503/how-to-use-xor-in-bash
Romeo Ninov
Di una respuesta a esta pregunta, pero al leerla nuevamente creo que la entendí mal. ¿Desea traducir cada uno 0x00a un valor específico o a un valor aleatorio en el 0x00-0xFFrango?
kos
1
@kos cada uno a un valor específico como 444444...no aleatorio
Eduard Florinescu

Respuestas:

18

El siguiente bashcódigo está configurado para funcionar con el byte representado en binario . Sin embargo, puede cambiarlo fácilmente para manejar ocatal , decimal o hexadecimal simplemente cambiando el valor de la raíz r de 2 a 8, 10o 16respectivamente, y configurándolo en b=consecuencia.

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

EDITAR : maneja el rango completo de valores de bytes: hexadecimal 00 - FF (cuando escribí 00-7F a continuación, estaba considerando solo caracteres UTF-8 de un solo byte).

Si, por ejemplo, solo desea 4 bytes (caracteres en el rango UTF-8 'ASCII'-hexadecimal solo 00-7F) , puede canalizarlo a la cabeza :... | head -c4

Salida (4 caracteres):

~~~~

Para ver la salida en formato de 8 bits, canalícela xxd(o cualquier otro volcado de byte de 1 y 0 *):
por ejemplo. b=10000000y tuberías a:... | head -c4 | xxd -b

0000000: 10000000 10000000 10000000 10000000                    ....
Peter.O
fuente
1
¿Querías escribir o=$(printf ...)para la segunda línea?
jwodder
1
@jwodder: No, la segunda línea es correcta como se muestra. La opción printf-v hace que la salida establezca directamente la variable nombrada inmediatamente después de ella; en este caso, el nombre de la variable es o(para octal ): tenga en cuenta que la -vopción se aplica a la versión integrada de shell de printf(no a la versión / usr / bin / printf )
Peter.O
2
@jwodder Además, en general, la -vopción se asegura de que la variable se configure exactamente a lo que especificó. $(...)transforma la salida primero. Es por eso o=$(printf '\n')que no tendrá el efecto que podría esperar, mientras que printf -vo '\n'sí. (No importa aquí, ya que la salida aquí está en una forma que no se ve afectada por tal transformación, pero si no -vconocía la opción, entonces podría ser útil saberlo).
hvd
18

No puedes hacer eso fácilmente.

Puede considerar escribir su propio módulo de kernel que proporcione dicho dispositivo. No lo recomiendo

Podría escribir un pequeño programa en C escribiendo una secuencia infinita de los mismos bytes en alguna tubería (o en stdout) o FIFO.

Puede usar tr (1) para leer /dev/zeroy traducir cada byte 0 a otra cosa.

Podría usar quizás sí (1) , al menos si puede permitirse el lujo de tener nuevas líneas (o de lo contrario canalizarlas en tr -d '\n'...)

Basile Starynkevitch
fuente
10
O utilizar yes 1 | tr -d $'\n'para el caso.
kojiro
3
@kojiro: eso fallará si intentas yesuna secuencia de \ncaracteres. Una alternativa que maneja \nes: yes '' | tr '\n' "$c"- donde $cpuede haber cualquier carácter de la gama completa de caracteres ASCII.
Peter.O
1
@ Peter.O No estoy seguro de cómo interpretaste mi comentario como algo que no sea la expresión literal y estática yes 1 | tr -d $'\n'. Supongo que podría usar un shell que no haga el $''tratamiento de barra invertida, o podría tratar de encontrar un lugar que altere tr -d $'\n', pero aún no lo he encontrado.
kojiro
@kojiro: yes 1 | tr -d $'\n'con bastante gusto imprimirá una secuencia de 1caracteres y casi cualquier otro valor de un solo byte, pero no puede imprimir una secuencia de \ncaracteres. El OP quiere poder manejar todos los valores de bytes "entre 0x01 y 0xFF"
Peter.O
1
loop() { if [ "$1" = $'\n' ]; then yes "$1"; else yes "$1" | tr -d $'\n' ; fi;
PSkocik
13

Bueno, si literalmente quieres lograr esto, puedes usar un gancho LD_PRELOAD . La idea básica es reescribir una función de la biblioteca C y usarla en lugar de la normal.

Aquí hay un ejemplo simple donde anulamos la función read () para XOR el buffer de salida 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);    
}

Una implementación ingenua haría XOR 0x42 en cada archivo que leamos, lo que tendría consecuencias indeseables. Para resolver este problema, también conecté la función open () , haciendo que recupere el descriptor de archivo asociado con / dev / zero. Entonces, solo realizamos el XOR en nuestra función read () si 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
yoann
fuente
3
Dada su implementación, podría tener un enlace simbólico de / dev / capbee a / dev / zero, busque / dev / capbee y deje / dev / zero solo. // dev / zero no será lo mismo que / dev / zero.
Robert Jacobs
1
@RobertJacobs De hecho. Incluso podríamos generar enlaces simbólicos / dev / 0x01, / dev / 0x02, / dev / 0x03, ... a / dev / zero y analizar el nombre de archivo para determinar la máscara de bits que se aplicará.
yoann
11

En términos de velocidad, lo más rápido que encontré fue:

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

Para comparacion:

$ 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]

$ guión -c 'mientras:; hacer eco -n "\ 1"; hecho '| pv -a> / dev / null
[225KiB / s]
$ bash -c 'while:; do echo -ne "\ 1"; hecho '| pv -a> / dev / null
[180KiB / s]

$ < /dev/zero pv -a > /dev/null
[5.56GiB/s]
$ cat /dev/zero | pv -a > /dev/null
[2.82GiB/s]
Stéphane Chazelas
fuente
En mi Debian, perlrendimiento 2.13GiB, mientras que < /dev/zerorendimiento 8.73GiB. ¿Qué cosa puede afectar el rendimiento?
Cuonglm
@cuonglm, sí, veo alguna variación entre sistemas, pero perles consistentemente más rápido que las otras soluciones. Obtengo el mismo rendimiento que con el programa C compilado equivalente. El punto de referencia está tanto en la aplicación como en el programador del sistema aquí. Lo que hace más diferente es el tamaño de los buffers que se escriben.
Stéphane Chazelas
@cuonglm La tubería también lo ralentiza. Creo que también cat /dev/zero| pv -a >/dev/nullle dará unos 2 GiB por segundo (lo hace en mi sistema, mientras que < /dev/zero) me da alrededor de 6GiBps.
PSkocik
@ StéphaneChazelas ¿Puedo preguntar en qué sistema estás, Stéphane Chazelas? Los resultados en los míos difieren bastante (puedo obtener aproximadamente 2.1GiB de la versión perl). Estoy en 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 por dentro.
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. El kernel más nuevo parece marcar la diferencia (a menos que sea el perl más nuevo: v5.20.2)
Stéphane Chazelas
7

Es un poco inútil tratar de poner una máscara de bits / x o cero bytes, ¿no? Tomar un byte y xorponerlo en cero es un no-op.

Simplemente cree un bucle que le brinde los bytes que desea y póngalo detrás de una tubería o una tubería con nombre. Se comportará más o menos igual que un dispositivo de caracteres (no desperdiciará los ciclos de la CPU cuando esté inactivo):

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

Y si desea super-optimizarlo, puede usar el siguiente código C:

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

compilar y ejecutar

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

Prueba de rendimiento:

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

2.1GB / s en mi máquina (incluso un poco más rápido que cat /dev/zero | pv -a >/dev/null)

PSkocik
fuente
Originalmente intenté usar putchar en C, pero fue lento.
PSkocik
Por curiosidad, ¿por qué en argc == 1+1lugar de agrc == 2?
Restablece a Monica - notmaynard el
@iamnotmaynard Para recordarme que es 1 para el ejecutable de línea de comando más 1 argumento. :-D
PSkocik
Ah Esa era mi suposición, pero quería asegurarme de que no hubiera alguna razón secreta.
Restablece a Monica - notmaynard el
"Tomar un byte y modificarlo con cero es un no-op". Esto no es cierto: 0 XOR X == X.
jacwah
5

¡Lee ceros, traduce cada cero a tu patrón!

Leemos cero bytes /dev/zeroy usamos trpara aplicar una máscara de bits a cada uno de los bytes traduciendo cada byte cero:

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

Octal 176 es el código ascii de ~, por lo que obtenemos 10 ~. (Al $final de la salida indica en mi shell que no había final de línea, podría verse diferente para usted)

Entonces, creemos 0xFFbytes: Hex 0xFFes octal 0377. El cero inicial se deja fuera de la trlínea de comando; Al final, hexdumpse utiliza para hacer que la salida sea legible.

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

Debe usar los códigos octales de los caracteres aquí, en lugar del hexadecimal. Entonces es el rango de \000octal \377(igual que 0xFF).
Use ascii -xy ascii -opara obtener una tabla de los caracteres con números de índice hexadecimales u octales.
(Para una tabla con decimal y hexadecimal, solo ascii).

Bastante rapido

Funciona bastante rápido, en comparación con solo usar los ceros: cat /dev/zeroes solo cuatro veces más rápido, mientras que puede hacer un uso perfecto del almacenamiento en búfer IO, que trno puede.

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

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

Depende de lo que quiera hacer con los datos y de cuán flexible quiera usarlos.

En el peor de los casos, si necesita velocidad, podría hacer lo mismo que / dev / zero, y simplemente compilar los dispositivos / dev / one, / dev / two, .. / dev / fourtytwo .. y así sucesivamente.

En la mayoría de los casos, debería ser mejor crear los datos directamente donde se necesitan, por lo que dentro de un programa / script como una constante. Con más información, la gente podría ayudarlo mejor.

huésped
fuente
1

Bucle de impresión infinito

Reemplace \u00con el byte que desee.

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

Código C ++:

#include<cstdio>

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

Compilar: reemplace Bytecon el valor que desee.

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

Utilizar

./bin | yourapp

ncomputadoras
fuente