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?

Eduard Florinescu
fonte
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                    ....
Peter.O
fonte
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'...)

Basile Starynkevitch
fonte
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
yoann
fonte
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]
Stéphane Chazelas
fonte
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)

PSkocik
fonte
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]
Volker Siegel
fonte
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.

hóspede
fonte
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

ncomputadores
fonte