Como colocar uma máscara de bit em / dev / zero para obter bytes diferentes de zero?


20

Como colocar uma máscara de bits /dev/zeropara poder ter uma fonte não apenas para 0x00, mas também para qualquer byte entre 0x01 e 0xFF?


8
Por que você pergunta? Edite a pergunta para motivá-la.
— Basile Starynkevitch

11
Você pode usar esta resposta como referência: stackoverflow.com/questions/12634503/how-to-use-xor-in-bash
— Romeo Ninov

Dei uma resposta a essa pergunta, mas, ao ler novamente, acho que não a compreendi. Deseja converter cada 0x00um deles em um valor específico ou em um valor aleatório no 0x00-0xFFintervalo?
— kos

11
@kos cada um para um valor específico, como 444444...não um acaso
— Eduard Florinescu

Respostas:


18

O bashcódigo a seguir está definido para trabalhar com o byte sendo representado em binário . No entanto, você pode alterá-lo facilmente para lidar com ocatal , decimal ou hexadecimal , simplesmente alterando o valor da raiz r de 2 para 8, 10ou 16respectivamente, e configurando de b=acordo.

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

EDIT - Ele lida com toda a gama de valores de bytes: hex 00 - FF (quando escrevi 00-7F abaixo, estava considerando apenas caracteres UTF-8 de byte único).

Se, por exemplo, você quiser apenas 4 bytes (caracteres no intervalo hex 00-7F somente UTF-8 'ASCII') , poderá inseri- lo na cabeça :... | head -c4

Saída (4 caracteres):

~~~~

Para ver a saída no formato de 8 bits, coloque-a em xxd(ou em qualquer outro despejo de bytes 1 e 0 *):
por exemplo. b=10000000e tubulação para:... | head -c4 | xxd -b

0000000: 10000000 10000000 10000000 10000000                    ....

11
Você quis escrever o=$(printf ...)para a segunda linha?
— jwodder

11
@jwodder: Não, a segunda linha está correta, como mostrado. A opção printf-v faz com que a saída defina diretamente a variável nomeada imediatamente após; neste caso, que o nome de variável é o(para octal ) - note que a -vopção se aplica ao shell-builtin versão printf(não o / usr / bin / printf versão)
— Peter.O

2
@jwodder Além disso, em geral, a -vopção garante que a variável seja definida exatamente como você especificou. $(...)transforma a saída primeiro. É por isso o=$(printf '\n')que não terá o efeito que você poderia esperar, ao passo que printf -vo '\n'sim. (Não importa aqui, já que a saída aqui é de uma forma que não é afetado por essa transformação, mas se você não estavam cientes da -vopção, então isso pode ser útil para saber.)
— HVD

18

Você não pode fazer isso facilmente.

Você pode escrever seu próprio módulo do kernel, fornecendo esse dispositivo. Eu não recomendo isso.

Você pode escrever um pequeno programa C escrevendo um fluxo infinito dos mesmos bytes em algum pipe (ou on stdout) ou FIFO.

Você pode usar tr (1) para ler /dev/zeroe traduzir cada 0 byte para outra coisa.

Você pode usar talvez sim (1) , pelo menos se puder pagar por novas linhas (ou então introduzi-lo em tr -d '\n'...)


10
Ou use yes 1 | tr -d $'\n'para esse assunto.
— kojiro

3
@kojiro: isso falhará se você tentar yesum fluxo de \ncaracteres. Uma alternativa que lida com \né: yes '' | tr '\n' "$c"- onde $cpode haver qualquer caractere de toda a gama de caracteres ASCII.
— Peter.O

11
@ Peter.O Não sei bem como você interpretou meu comentário como algo que não seja a expressão estática literal yes 1 | tr -d $'\n'. Suponho que você possa usar um shell que não faça o $''tratamento de barra invertida ou tentar encontrar um local que seja alterado tr -d $'\n', mas ainda não o encontrei.
— Kojiro # 1155

@kojiro: Você yes 1 | tr -d $'\n'imprimirá com alegria um fluxo de 1caracteres e quase todos os outros valores de um byte, mas não pode imprimir um fluxo de \ncaracteres. O OP quer ser capaz de lidar com todos os valores de byte "entre 0x01 e 0xFF"
— Peter.O

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

13

Bem, se você literalmente deseja conseguir isso, pode usar um gancho LD_PRELOAD . A idéia básica é reescrever uma função da biblioteca C e usá-la em vez da normal.

Aqui está um exemplo simples em que substituímos a função read () para XOR o buffer de saída com 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);    
}

Uma implementação ingênua XOR 0x42 em todos os arquivos que lemos, o que teria conseqüências indesejáveis. Para resolver esse problema, eu também instalei a função open () , fazendo com que ela buscasse o descritor de arquivo associado a / dev / zero. Então, apenas executamos o XOR em nossa função 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
Dada a sua implementação, você pode ter um link simbólico de / dev / capbee para / dev / zero, procure por / dev / capbee e deixe / dev / zero sozinho. // dev / zero não será igual a / dev / zero.
— Robert Jacobs

11
@RobertJacobs Indeed. Poderíamos até gerar links simbólicos / dev / 0x01, / dev / 0x02, / dev / 0x03, ... para / dev / zero e analisar o nome do arquivo para determinar a máscara de bit a ser aplicada.
— yoann

11

Em termos de velocidade, o mais rápido que encontrei foi:

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

Para comparação:

$ 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:; faça eco -n "\ 1"; feito '| pv -a> / dev / null
[225KiB / s]
$ bash -c 'while:; faça eco -ne "\ 1"; feito '| 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]

No meu Debian, perlrendimento 2.13GiB, enquanto < /dev/zerorendimento 8.73GiB. O que pode afetar o desempenho?
— cuonglm

@ cuonglm, sim, vejo algumas variações entre os sistemas, mas perlé consistentemente mais rápido que as outras soluções. Eu obtenho a mesma taxa de transferência que com o programa C compilado equivalente. A referência é tanto no aplicativo quanto no agendador do sistema aqui. O que torna o mais diferente é o tamanho dos buffers que estão sendo gravados.
— Stéphane Chazelas

@cuonglm O tubo também diminui a velocidade. Eu acho que cat /dev/zero| pv -a >/dev/nullvou lhe dar cerca de 2 GiBs por segundo (o que acontece no meu sistema, enquanto < /dev/zero) me dá cerca de 6GiBps.
— PSKocik

@ StéphaneChazelas Posso perguntar em que sistema você está, Stéphane Chazelas? Os resultados no meu diferem bastante (posso obter cerca de 2.1GiB da versão perl). Estou no 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

11
@PSkocik, Linux 3.16.0-4-amd64 # 1 SMP Debian 3.16.7-ckt9-3 (2015-04-23) x86_64 GNU / Linux, CPU Intel (R) Core (TM) 2 Duo T9600 a 2.80GHz. Um novo kernel parece fazer a diferença (a menos que os perl mais recentes: v5.20.2)
— Stéphane Chazelas

7

É meio inútil tentar usar bitmask / x ou zero bytes, não é? Pegar um byte e xorzerá-lo é um no-op.

Basta criar um loop com os bytes desejados e colocá-lo atrás de um pipe ou pipe nomeado. Ele se comportará da mesma forma que um dispositivo de caractere (não desperdiçará ciclos de CPU quando ocioso):

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

E se você quiser super otimizá-lo, pode usar o código C abaixo:

#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 e executar

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

Teste de performance:

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

2,1 GB / s na minha máquina (até um pouco mais rápido que cat /dev/zero | pv -a >/dev/null)


Originalmente, tentei usar o putchar em C, mas era lento.
— PSKocik

Por curiosidade, por que em argc == 1+1vez deagrc == 2 ?
— Reintegrar Monica iamnotmaynard

@iamnotmaynard Para lembrar a mim mesmo que é 1 para o executável da linha de comando mais 1 argumento. :-D
— PSkocik

Ah Esse foi o meu palpite, mas queria ter certeza de que não havia nenhuma razão secreta.
— Reintegrar Monica iamnotmaynard

"Pegar um byte e copiá-lo com zero é um no-op." Isso não é verdade:0 XOR X == X .
— 21415 Jacwah

5

Leia zeros, traduza cada zero para o seu padrão!

Lemos zero bytes de /dev/zeroe usamos trpara aplicar uma máscara de bit a cada um dos bytes, traduzindo cada byte de zero:

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

Octal 176 é o código ascii de ~, então obtemos 10 ~. (O $final da saída indica no meu shell que não havia final de linha - pode parecer diferente para você)

Então, vamos criar 0xFFbytes: Hex 0xFFé octal 0377. O zero à esquerda é deixado de fora para a trlinha de comando; No final, hexdumpé usado para tornar a saída legível.

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

Você precisa usar os códigos octais dos caracteres aqui, em vez do hexadecimal. Portanto, é o intervalo de\000 até octal \377(o mesmo que 0xFF).
Use ascii -xe ascii -opara obter uma tabela dos caracteres com números de índice hexadecimal ou octal.
(Para uma tabela com decimal e hexadecimal, apenas ascii).

Bem rápido

Ele roda bastante rápido, comparado ao uso de zeros: cat /dev/zeroé apenas quatro vezes mais rápido, enquanto pode fazer perfeito uso do buffer IO, quetr não pode.

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

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

3

Depende do que você deseja fazer com os dados e de quão flexível você deseja usá-los.

Na pior das hipóteses, se você precisar de velocidade, poderá fazer o mesmo que o / dev / zero e apenas compilar o / dev / one, / dev / two, .. / dev / fourtytwo .. e assim por diante.

Na maioria dos casos, deve ser melhor criar os dados diretamente onde são necessários, portanto, dentro de um programa / script como uma constante. Com mais informações, as pessoas poderiam ajudá-lo melhor.


1

Infinte printf loop

Substitua \u00com o byte desejado.

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: substitua Bytepelo valor desejado.

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

Usar

./bin | yourapp

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.