Comment puis-je mettre un masque de bits sur / dev / zero pour pouvoir obtenir des octets autres que zéro?

20

Comment puis-je mettre un masque de bits /dev/zeropour que je puisse avoir une source non seulement pour 0x00 mais aussi pour tout octet entre 0x01 et 0xFF?

Eduard Florinescu
la source
8
Pourquoi demandez-vous? Veuillez modifier la question pour la motiver.
Basile Starynkevitch
1
Vous pouvez utiliser cette réponse comme référence: stackoverflow.com/questions/12634503/how-to-use-xor-in-bash
Romeo Ninov
J'ai répondu à cette question, mais en la relisant, je pense que je l'ai mal comprise. Voulez-vous traduire chacun 0x00en une valeur spécifique ou en une valeur aléatoire dans la 0x00-0xFFplage?
kos
1
@kos chacun à une valeur spécifique comme 444444...pas aléatoire
Eduard Florinescu

Réponses:

18

Le bashcode suivant est configuré pour fonctionner avec l'octet étant representré en binaire . Cependant, vous pouvez facilement le modifier pour gérer ocatal , décimal ou hexadécimal en changeant simplement la valeur radix r de 2 en 8, 10ou 16respectivement et en définissant en b=conséquence.

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

EDIT - Il gère la plage complète des valeurs d'octets: hex 00 - FF (lorsque j'ai écrit 00-7F ci-dessous, je ne considérais que les caractères UTF-8 à un octet).

Si, par exemple, vous ne voulez que 4 octets (caractères dans la plage hex 00-7F uniquement ASCII 'UTF-8) , vous pouvez le diriger dans head :... | head -c4

Sortie (4 caractères):

~~~~

Pour voir la sortie au format 8 bits, dirigez-la vers xxd(ou tout autre vidage d'octets 1 et 0 *):
par exemple. b=10000000et tuyauterie pour:... | head -c4 | xxd -b

0000000: 10000000 10000000 10000000 10000000                    ....
Peter.O
la source
1
Vouliez-vous écrire o=$(printf ...)pour la deuxième ligne?
jwodder
1
@jwodder: Non, la deuxième ligne est correcte comme indiqué. L' option printf-v fait que la sortie définit directement la variable nommée immédiatement après elle; dans ce cas, le nom de cette variable est o(pour octal ) - notez que l' -voption s'applique à la version intégrée au shell de printf(pas à la version / usr / bin / printf )
Peter.O
2
@jwodder De plus, en général, l' -voption s'assure que la variable est définie exactement sur ce que vous avez spécifié. $(...)transforme la sortie en premier. C'est pourquoi o=$(printf '\n')n'aura pas l'effet que vous attendez, alors que le printf -vo '\n'fait. (Cela n'a pas d'importance ici, car la sortie ici est sous une forme qui n'est pas affectée par une telle transformation, mais si vous n'étiez pas au courant de l' -voption, cela pourrait être utile de le savoir.)
hvd
18

Vous ne pouvez pas facilement faire cela.

Vous pourriez envisager d'écrire votre propre module de noyau fournissant un tel périphérique. Je ne le recommande pas.

Vous pouvez écrire un petit programme C en écrivant un flux infini de mêmes octets sur un tube (ou sur stdout) ou FIFO.

Vous pouvez utiliser tr (1) pour lire /dev/zeroet traduire tous les 0 octets en autre chose.

Vous pourriez peut-être utiliser oui (1) , du moins si vous pouvez vous permettre d'avoir des retours à la ligne (ou bien les canaliser tr -d '\n'...)

Basile Starynkevitch
la source
10
Ou utilisez yes 1 | tr -d $'\n'd'ailleurs.
kojiro
3
@kojiro: cela échouera si vous essayez yesun flux de \ncaractères. Une alternative qui gère \nest: yes '' | tr '\n' "$c"- où $cpeut être n'importe quel caractère de la gamme complète de caractères ASCII.
Peter.O
1
@ Peter.O Je ne sais pas comment vous avez interprété mon commentaire comme autre chose que l'expression statique et littérale yes 1 | tr -d $'\n'. Je suppose que vous pouvez utiliser un shell qui ne fait pas le $''traitement de barre oblique inverse, ou vous pouvez essayer de trouver un paramètre régional qui change tr -d $'\n', mais je ne l'ai pas encore trouvé.
kojiro
@kojiro: Vous yes 1 | tr -d $'\n'imprimerez très volontiers un flux de 1caractères et presque toutes les autres valeurs à un octet, mais il ne peut pas imprimer un flux de \ncaractères. L'OP veut être capable de gérer toutes les valeurs d'octets "entre 0x01 et 0xFF"
Peter.O
1
loop() { if [ "$1" = $'\n' ]; then yes "$1"; else yes "$1" | tr -d $'\n' ; fi;
PSkocik
13

Eh bien, si vous voulez littéralement y parvenir, vous pouvez utiliser un hook LD_PRELOAD . L'idée de base est de réécrire une fonction de la bibliothèque C et de l'utiliser à la place de la normale.

Voici un exemple simple où nous remplaçons la fonction read () pour XOR le tampon de sortie avec 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);    
}

Une implémentation naïve ferait XOR 0x42 sur chaque fichier lu, ce qui aurait des conséquences indésirables. Afin de résoudre ce problème, j'ai également accroché la fonction open () , ce qui lui permet de récupérer le descripteur de fichier associé à / dev / zero. Ensuite, nous n'effectuons le XOR dans notre fonction read () que si fd == dev_zero_fd.

Usage:

$ gcc hook.c -ldl -shared -o hook.so
$ LD_PRELOAD=$(pwd)/hook.so bash #this spawns a hooked shell
$ cat /dev/zero
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
yoann
la source
3
Compte tenu de votre implémentation, vous pouvez avoir un lien symbolique de / dev / capbee vers / dev / zero, rechercher / dev / capbee et laisser / dev / zero seul. // dev / zero ne sera pas identique à / dev / zero.
Robert Jacobs
1
@RobertJacobs En effet. Nous pourrions même générer des liens symboliques / dev / 0x01, / dev / 0x02, / dev / 0x03, ... vers / dev / zero et analyser le nom de fichier pour déterminer le masque de bits à appliquer.
yoann
11

En termes de vitesse, la plus rapide que j'ai trouvée était:

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

En comparaison:

$ 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:; do echo -n "\ 1"; fait »| pv -a> / dev / null
[225 Ko / s]
$ bash -c 'tandis que:; do echo -ne "\ 1"; fait »| pv -a> / dev / null
[180 Ko / s]

$ < /dev/zero pv -a > /dev/null
[5.56GiB/s]
$ cat /dev/zero | pv -a > /dev/null
[2.82GiB/s]
Stéphane Chazelas
la source
Dans mon Debian, perldonnez 2,13 Go, tandis que vous < /dev/zeroobtenez 8,73 Go. Quelle chose peut affecter les performances?
cuonglm
@cuonglm, oui, je vois une certaine variation entre les systèmes, mais perlest toujours plus rapide que les autres solutions. J'obtiens le même débit qu'avec le programme C compilé équivalent. Le benchmark est autant sur l'application que sur l'ordonnanceur du système ici. Ce qui fait la différence, c'est la taille des tampons en cours d'écriture.
Stéphane Chazelas
@cuonglm Le tuyau le ralentit aussi. Je pense que cat /dev/zero| pv -a >/dev/nullcela vous donnera également environ 2 Gio par seconde (il le fait sur mon système, alors < /dev/zero) me donne environ 6 Gbits / s.
PSkocik
@ StéphaneChazelas Puis-je vous demander sur quel système êtes-vous, Stéphane Chazelas? Les résultats sur le mien sont assez différents (je peux obtenir environ 2,1 Go de la version Perl). Je suis sur 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 à l'intérieur.
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. Le nouveau noyau semble faire la différence (à moins qu'il ne s'agisse du dernier perl: v5.20.2)
Stéphane Chazelas
7

C'est un peu inutile d'essayer de bitmask / xor zéro octets, n'est-ce pas? Prendre un octet et le xormettre à zéro est un no-op.

Créez simplement une boucle qui vous donne les octets que vous voulez et placez-la derrière un tuyau ou un tuyau nommé. Il se comportera à peu près de la même manière qu'un périphérique de caractères (ne gaspillera pas les cycles de processeur lorsqu'il est inactif):

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

Et si vous voulez le super-optimiser, vous pouvez utiliser le code C ci-dessous:

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

compiler et exécuter

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

Test de performance:

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

2,1 Go / s sur ma machine (même légèrement plus rapide que cat /dev/zero | pv -a >/dev/null)

PSkocik
la source
J'ai essayé à l'origine d'utiliser putchar en C, mais c'était lent.
PSkocik
Par curiosité, pourquoi argc == 1+1au lieu de agrc == 2?
Reinstate Monica - notmaynard
@iamnotmaynard Pour me rappeler que c'est 1 pour l'exécutable de ligne de commande plus 1 argument. :-D
PSkocik
Ah. C'était ma supposition, mais je voulais m'assurer qu'il n'y avait pas de raison secrète.
Reinstate Monica - notmaynard
"Prendre un octet et le xorer avec zéro est un no-op." Ce n'est pas vrai: 0 XOR X == X.
jacwah
5

Lisez les zéros, traduisez chaque zéro à votre modèle!

Nous lisons zéro octet /dev/zeroet utilisons trpour appliquer un masque de bits à chacun des octets en traduisant chaque octet zéro:

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

Octal 176 est le code ascii de ~, donc nous obtenons 10 ~. (Le $à la fin de la sortie indique dans mon shell qu'il n'y avait pas de fin de ligne - cela pourrait être différent pour vous)

Créons donc des 0xFFoctets: Hex 0xFFest octal 0377. Le zéro de tête est omis pour la trligne de commande; À la fin, hexdumpest utilisé pour rendre la sortie lisible.

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

Vous devez utiliser les codes octaux des caractères ici, au lieu de l'hexadécimal. C'est donc la plage de \000à octal \377(identique à 0xFF).
Utilisez ascii -xet ascii -opour obtenir un tableau des caractères avec des numéros d'index hexadécimaux ou octaux.
(Pour un tableau avec décimal et hexadécimal, juste ascii).

Tres rapide

Il fonctionne assez rapidement, comparé à la simple utilisation des zéros: il cat /dev/zeron'est que quatre fois plus rapide, alors qu'il peut faire un usage parfait de la mise en mémoire tampon d'E / S, ce qui trn'est pas possible.

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

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

Cela dépend de ce que vous voulez faire avec les données et de la flexibilité avec laquelle vous souhaitez les utiliser.

Dans le pire des cas, si vous avez besoin de vitesse, vous pouvez faire la même chose que / dev / zero, et simplement compiler les périphériques / dev / one, / dev / two, .. / dev / fourtytwo .. et ainsi de suite.

Dans la plupart des cas, il devrait être préférable de créer les données directement là où elles sont nécessaires, donc à l'intérieur d'un programme / script en tant que constante. Avec plus d'informations, les gens pourraient mieux vous aider.

client
la source
1

Boucle d'impression infinie

Remplacez \u00par l'octet que vous voulez.

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

Code C ++:

#include<cstdio>

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

Compiler: remplacez Byteavec la valeur que vous voulez.

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

Utilisation

./bin | yourapp

nordinateurs
la source