Как я могу поставить битовую маску на / dev / zero, чтобы я мог получить байты, отличные от нуля?

20

Как я могу надеть битовую маску, /dev/zeroчтобы иметь источник не только для 0x00, но и для любого байта между 0x01 и 0xFF?

Эдуард Флоринеску
источник
8
Почему ты справшиваешь? Пожалуйста, отредактируйте вопрос, чтобы мотивировать его.
Василий Старынкевич
1
Вы можете использовать этот ответ в качестве ссылки: stackoverflow.com/questions/12634503/how-to-use-xor-in-bash
Ромео Нинов
Я дал ответ на этот вопрос, но читая его снова, я думаю, что я не понял его. Вы хотите перевести каждое из них 0x00к определенному значению или к случайному значению в 0x00-0xFFдиапазоне?
Кос
1
@kos каждый к определенному значению, как 444444...не случайному
Эдуард Флоринеску

Ответы:

18

Следующий bashкод настроен для работы с байтом, представленным в двоичном виде . Однако вы можете легко изменить его для обработки ocatal , десятичного или шестигранника , просто изменяя базисное r значение 2 для 8, 10или , 16соответственно , и установки b=соответственно.

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

РЕДАКТИРОВАТЬ - Он обрабатывает полный диапазон значений байтов: hex 00 - FF (когда я писал ниже 00-7F, я рассматривал только однобайтовые символы UTF-8).

Если, например, вы хотите только 4 байта (символы в диапазоне UTF-8 «ASCII' только шестигранный 00-7F) , вы можете конвейеру в голову :... | head -c4

Выход (4 символа):

~~~~

Чтобы увидеть вывод в 8-битном формате, направьте его в xxd(или любой другой байтовый дамп 1 и 0 *):
например. b=10000000и трубопровод к:... | head -c4 | xxd -b

0000000: 10000000 10000000 10000000 10000000                    ....
Peter.O
источник
1
Вы хотели написать o=$(printf ...)для второй строки?
jwodder
1
@jwodder: Нет, вторая строка верна, как показано. Printf опция -vвызывает Лобовой выход непосредственно установить переменную сразу после него; в этом случае имя этой переменной o(для восьмеричного ) - обратите внимание, что этот -vпараметр применяется к встроенной в оболочку версии printf(не к версии / usr / bin / printf )
Peter.O
2
@jwodder Кроме того, в общем, -vопция гарантирует, что переменная будет установлена точно в соответствии с тем , что вы указали. $(...)сначала преобразует вывод. Вот почему o=$(printf '\n')не будет иметь эффекта, которого вы можете ожидать, в то время как printf -vo '\n'да. (Это не имеет значения, поскольку выходные данные представлены в форме, не подверженной такому преобразованию, но если вы не знали об этой -vопции, это может быть полезно знать.)
hvd
18

Вы не можете легко сделать это.

Вы можете написать свой собственный модуль ядра с таким устройством. Я не рекомендую это.

Вы можете написать крошечную C-программу, записывающую бесконечный поток одних и тех же байтов в какой-либо канал (или в stdout) или FIFO.

Вы можете использовать tr (1) для чтения /dev/zeroи перевода каждого 0 байта в другое.

Вы могли бы использовать, возможно, да (1) , по крайней мере, если вы можете позволить себе переводы строки (или еще трубку в tr -d '\n'...)

Василий Старынкевич
источник
10
Или используйте yes 1 | tr -d $'\n'в этом отношении.
Кодзиро
3
@kojiro: это не удастся, если вы попытаетесь yesпоток \nсимволов. Альтернатива, которая обрабатывает \n: yes '' | tr '\n' "$c"- где $cможет быть любой символ из полного диапазона символов ASCII.
Peter.O
1
@ Peter.O Я не уверен, как вы интерпретировали мой комментарий, чтобы означать что-то кроме буквального, статического выражения yes 1 | tr -d $'\n'. Я полагаю, вы могли бы использовать оболочку, которая не выполняет $''обработку обратной косой черты, или вы можете попытаться найти локаль, которая изменяет tr -d $'\n', но я пока не нашел ее.
Кодзиро
@kojiro: Вы с yes 1 | tr -d $'\n'радостью напечатаете поток 1символов и почти все другие однобайтовые значения, но он не сможет напечатать поток \nсимволов. ОП хочет иметь возможность обрабатывать все байтовые значения «между 0x01 и 0xFF»
Peter.O
1
loop() { if [ "$1" = $'\n' ]; then yes "$1"; else yes "$1" | tr -d $'\n' ; fi;
PSkocik
13

Что ж, если вы в буквальном смысле хотите этого добиться, вы можете использовать ловушку LD_PRELOAD . Основная идея заключается в том, чтобы переписать функцию из библиотеки C и использовать ее вместо обычной.

Вот простой пример, где мы переопределяем функцию read () для XOR выходного буфера с 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);    
}

Наивная реализация будет XOR 0x42 для каждого файла, который мы читаем, что будет иметь нежелательные последствия. Чтобы решить эту проблему, я также подключил функцию open () , заставив ее извлечь файловый дескриптор, связанный с / dev / zero. Тогда мы только выполняем XOR в нашей функции read (), если fd == dev_zero_fd.

Использование:

$ gcc hook.c -ldl -shared -o hook.so
$ LD_PRELOAD=$(pwd)/hook.so bash #this spawns a hooked shell
$ cat /dev/zero
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
Йоанн
источник
3
Учитывая вашу реализацию, вы можете получить символическую ссылку от / dev / capbee до / dev / zero, выполнить поиск / dev / capbee и оставить / dev / zero в одиночку. // dev / zero не будет таким же, как / dev / zero.
Роберт Джейкобс
1
@RobertJacobs Действительно. Мы могли бы даже сгенерировать символические ссылки / dev / 0x01, / dev / 0x02, / dev / 0x03, ... в / dev / zero и проанализировать имя файла, чтобы определить применяемую битовую маску.
Йоан
11

С точки зрения скорости, самым быстрым, что я нашел, было:

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

Для сравнения:

$ 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"; сделано '| pv -a> / dev / null
[225 КБ / с]
$ bash -c 'while:; do echo -ne "\ 1"; сделано '| pv -a> / dev / null
[180 КБ / с]

$ < /dev/zero pv -a > /dev/null
[5.56GiB/s]
$ cat /dev/zero | pv -a > /dev/null
[2.82GiB/s]
Стефан Шазелас
источник
В моем Debian вы perlполучите 2.13 ГБ, а < /dev/zero8.73 ГБ. Что может повлиять на производительность?
cuonglm
@cuonglm, да, я вижу некоторые различия между системами, но perlвсегда быстрее, чем другие решения. Я получаю ту же пропускную способность, что и с эквивалентной скомпилированной C-программой. Этот тест для приложения так же важен, как и для системного планировщика. Что отличает больше всего, так это размер записываемых буферов.
Стефан Шазелас
@cuonglm Труба тоже тормозит. Я думаю, cat /dev/zero| pv -a >/dev/nullчто даст вам около 2 ГиБ в секунду (это происходит в моей системе, в то время как < /dev/zero) дает мне около 6 ГБ / с.
PSkocik
@ StéphaneChazelas Могу я спросить, в какой ты системе, Стефан Шазелас? Результаты по моему весьма различны (я могу получить около 2,1 ГБ из Perl-версии). Я на 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 внутри.
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® Core ™ 2 Duo T9600 @ 2.80 ГГц. Более новое ядро, кажется, имеет значение (если это не более новый perl: v5.20.2)
Стефан Шазелас
7

Бессмысленно пытаться использовать битовую маску / xor нулевых байтов, не так ли? Взять байт и xorзаписать его с нуля - нет.

Просто создайте цикл, который даст вам нужные байты, и поместите его за канал или именованный канал. Он будет вести себя почти так же, как символьное устройство (не будет тратить циклы ЦП во время простоя):

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

И если вы хотите супероптимизировать его, вы можете использовать код 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)); 
  }
}

скомпилировать и запустить

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

Тест производительности:

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

2,1 ГБ / с на моей машине (даже немного быстрее, чем cat /dev/zero | pv -a >/dev/null)

PSkocik
источник
Первоначально я пытался использовать putchar в C, но это было медленно.
PSkocik
Из любопытства, почему argc == 1+1вместо agrc == 2?
Восстановить Монику - Нотмайнард
@iamnotmaynard Чтобы напомнить себе, что это 1 для исполняемого файла командной строки плюс 1 аргумент. :-D
PSkocik
Ах. Это было мое предположение, но я хотел убедиться, что не было какой-то секретной причины.
Восстановить Монику - Нотмайнард
«Взятие байта и ксероксация его с нуля - неоправданно». Это не правда 0 XOR X == X.
Джеква
5

Читайте нули, переводите каждый ноль в ваш шаблон!

Мы читаем нулевые байты из /dev/zeroи используем, trчтобы применить битовую маску к каждому байту, переводя каждый нулевой байт:

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

Octal 176 - это код ascii ~, поэтому мы получаем 10 ~. (В $конце вывода указывает, что в моей оболочке не было конца строки - он может выглядеть по-другому для вас)

Итак, давайте создадим 0xFFбайты: Hex 0xFFявляется восьмеричным 0377. Ведущий ноль опущен для trкомандной строки; В конце hexdumpиспользуется, чтобы сделать вывод читабельным.

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

Вам нужно использовать восьмеричные коды символов здесь, а не шестнадцатеричные. Так что это диапазон от \000восьмеричного \377(так же, как 0xFF).
Используйте ascii -xи, ascii -oчтобы получить таблицу символов с шестнадцатеричным или восьмеричным индексными числами.
(Для таблицы с десятичной и шестнадцатеричной, просто ascii).

Довольно быстро

Он работает довольно быстро, по сравнению только с использованием нулей: cat /dev/zeroон всего в четыре раза быстрее, в то время как он может идеально использовать буферизацию ввода-вывода, что trневозможно.

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

$ </dev/zero cat | pv -a >/dev/null        
[4.37GB/s]
Volker Siegel
источник
3

Зависит от того, что вы хотите сделать с данными и насколько гибко вы хотите их использовать.

В худшем случае, если вам нужна скорость, вы можете сделать то же самое, что и / dev / zero, и просто скомпилировать / dev / one, / dev / two, .. / dev / fourtytwo .. и другие устройства.

В большинстве случаев лучше создавать данные непосредственно там, где это необходимо, то есть внутри программы / скрипта как константы. С большим количеством информации люди могли бы лучше помочь вам.

гость
источник
1

Infinte printf loop

Заменить \u00на байт, который вы хотите.

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

Код C ++:

#include<cstdio>

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

Компилировать: заменить Byteна значение, которое вы хотите.

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

использование

./bin | yourapp

ncomputers
источник