Bagaimana saya bisa menggunakan bit mask pada / dev / zero sehingga saya bisa mendapatkan byte selain nol?


20

Bagaimana saya bisa memakai topeng bit /dev/zerosehingga saya dapat memiliki sumber tidak hanya untuk 0x00 tetapi juga untuk byte antara 0x01 dan 0xFF?


8
Kenapa kamu bertanya? Harap edit pertanyaan untuk memotivasi itu.
Basile Starynkevitch

1
Anda dapat menggunakan jawaban ini sebagai referensi: stackoverflow.com/questions/12634503/how-to-use-xor-in-bash
Romeo Ninov

Saya memberikan jawaban untuk pertanyaan ini, tetapi membacanya lagi saya pikir saya salah paham. Apakah Anda ingin menerjemahkan masing 0x00- masing ke nilai tertentu atau ke nilai acak dalam 0x00-0xFFrentang?
kos

1
@kos masing-masing ke nilai tertentu seperti 444444...bukan yang acak
Eduard Florinescu

Jawaban:


18

bashKode berikut ini diatur untuk bekerja dengan byte yang diwakili dalam biner . Namun Anda dapat dengan mudah mengubahnya untuk menangani ocatal , desimal atau hex dengan hanya mengubah radix r nilai 2 untuk 8, 10atau 16masing-masing dan pengaturan b=yang sesuai.

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

EDIT - Ini menangani berbagai nilai byte: hex 00 - FF (ketika saya menulis 00-7F di bawah ini, saya hanya mempertimbangkan karakter byte tunggal UTF-8).

Jika, misalnya, Anda hanya ingin 4 byte (karakter dalam UTF-8 'ASCII'-only hex 00-7F range) , Anda dapat menyalurkannya ke head :... | head -c4

Output (4 karakter):

~~~~

Untuk melihat output dalam format 8-bit, masukkan ke dalam xxd(atau dump byte 1 dan 0 lainnya *):
mis. b=10000000dan perpipaan ke:... | head -c4 | xxd -b

0000000: 10000000 10000000 10000000 10000000                    ....

1
Apakah Anda bermaksud menulis o=$(printf ...)untuk baris kedua?
jwodder

1
@ jwodder: Tidak, baris kedua benar seperti yang ditunjukkan. The printf pilihan -vmenyebabkan tthe keluaran untuk langsung mengatur variabel bernama segera setelah; dalam hal ini yang nama variabel adalah o(untuk oktal ) - catatan bahwa -vopsi berlaku untuk shell-builtin versi printf(bukan ke / usr / bin / printf versi)
Peter.O

2
@ jwodder Juga, secara umum, -vopsi memastikan variabel diatur ke persis apa yang Anda tentukan. $(...)mengubah output terlebih dahulu. Itulah sebabnya mengapa o=$(printf '\n')tidak akan memiliki efek yang Anda harapkan, sedangkan sebaliknya printf -vo '\n'. (Tidak masalah di sini, karena output di sini adalah dalam bentuk yang tidak terpengaruh oleh transformasi seperti itu, tetapi jika Anda tidak mengetahui -vopsi tersebut, maka ini mungkin berguna untuk diketahui.)
hvd

18

Anda tidak dapat dengan mudah melakukan itu.

Anda mungkin mempertimbangkan untuk menulis modul kernel Anda sendiri yang menyediakan perangkat seperti itu. Saya tidak merekomendasikan itu.

Anda dapat menulis sebuah program C kecil yang menulis aliran tak terbatas dari byte yang sama pada beberapa pipa (atau pada stdout) atau FIFO.

Anda dapat menggunakan tr (1) untuk membaca /dev/zerodan menerjemahkan setiap 0 byte ke sesuatu yang lain.

Anda bisa menggunakan mungkin ya (1) , setidaknya jika Anda mampu memiliki baris baru (atau pipa itu ke tr -d '\n'...)


10
Atau gunakan yes 1 | tr -d $'\n'dalam hal ini.
kojiro

3
@ Kojiro: itu akan gagal jika Anda mencoba yesaliran \nkarakter. Alternatif yang menangani \nadalah: yes '' | tr '\n' "$c"- di mana $cbisa karakter apa saja dari berbagai karakter ASCII.
Peter.O

1
@ Peter.O Saya tidak yakin bagaimana Anda menafsirkan komentar saya berarti apa pun selain ekspresi yang harfiah dan statis yes 1 | tr -d $'\n'. Saya kira Anda bisa menggunakan shell yang tidak melakukan $''perawatan backslash, atau Anda bisa mencoba menemukan lokal yang berubah tr -d $'\n', tapi saya belum menemukannya.
kojiro

@kojiro: Anda yes 1 | tr -d $'\n'akan dengan senang hati mencetak aliran 1karakter dan hampir setiap nilai byte tunggal lainnya, tetapi tidak dapat mencetak aliran \nkarakter. OP ingin dapat menangani semua nilai byte "antara 0x01 dan 0xFF"
Peter.O

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

13

Nah, jika Anda benar - benar ingin mencapai ini, Anda dapat menggunakan pengait LD_PRELOAD . Ide dasarnya adalah menulis ulang fungsi dari pustaka C dan menggunakannya sebagai ganti fungsi yang normal.

Berikut adalah contoh sederhana di mana kita menimpa fungsi read () ke XOR buffer output dengan 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);    
}

Implementasi naif akan XOR 0x42 pada setiap file yang kita baca, yang akan memiliki konsekuensi yang tidak diinginkan. Untuk mengatasi masalah ini, saya juga mengaitkan fungsi open () , membuatnya mengambil deskriptor file yang terkait dengan / dev / zero. Kemudian, kita hanya menjalankan XOR pada fungsi read () kita jika fd == dev_zero_fd.

Pemakaian:

$ gcc hook.c -ldl -shared -o hook.so
$ LD_PRELOAD=$(pwd)/hook.so bash #this spawns a hooked shell
$ cat /dev/zero
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

3
Dengan penerapan Anda, Anda bisa memiliki tautan simbolis dari dari / dev / capbee ke / dev / zero, cari / dev / capbee dan tinggalkan / dev / zero saja. // dev / zero tidak akan sama dengan / dev / zero.
Robert Jacobs

1
@RobertJacobs Memang. Kami bahkan dapat membuat symlink / dev / 0x01, / dev / 0x02, / dev / 0x03, ... ke / dev / nol dan mengurai nama file untuk menentukan bitmask yang akan diterapkan.
yoann

11

Dalam hal kecepatan, yang tercepat saya temukan adalah:

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

Untuk perbandingan:

$ 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"; selesai '| pv -a> / dev / null
[225KiB / s]
$ bash -c 'while:; do echo -ne "\ 1"; selesai '| 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]

Di Debian saya, perlhasilkan 2,13GiB, sementara < /dev/zerohasilkan 8,73GiB. Hal apa yang dapat memengaruhi kinerja?
cuonglm

@cuonglm, ya, saya melihat beberapa variasi antara sistem, tetapi perlsecara konsisten lebih cepat daripada solusi lainnya. Saya mendapatkan hasil yang sama dengan program C yang dikompilasi yang setara. Tolok ukurnya ada pada aplikasi seperti pada penjadwal sistem di sini. Apa yang membuatnya paling berbeda adalah ukuran buffer yang sedang ditulis.
Stéphane Chazelas

@cuonglm Pipa memperlambatnya juga. Saya pikir cat /dev/zero| pv -a >/dev/nullakan memberi Anda sekitar 2 GiB per detik juga (itu pada sistem saya, sementara < /dev/zero) memberi saya sekitar 6GiBps.
PSkocik

@ StéphaneChazelas Boleh saya bertanya sistem apa yang Anda pakai, Stéphane Chazelas? Hasil pada saya sangat berbeda (saya bisa mendapatkan sekitar 2.1GiB dari versi perl). Saya menggunakan 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 di dalamnya.
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. Kernel yang lebih baru tampaknya membuat perbedaan (kecuali perl yang lebih baru: v5.20.2)
Stéphane Chazelas

7

Agak ada gunanya mencoba dan bitmask / xor nol byte, bukan? Mengambil byte dan xormemasukkannya dengan nol adalah larangan.

Cukup buat satu loop yang memberi Anda byte yang Anda inginkan dan letakkan di belakang pipa atau pipa bernama. Ini akan berperilaku sama seperti perangkat karakter (tidak akan membuang siklus CPU saat idle):

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

Dan jika Anda ingin mengoptimalkannya secara super, Anda dapat menggunakan kode C di bawah ini:

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

kompilasi & jalankan

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

Uji kinerja:

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

2.1GB / s pada mesin saya (bahkan sedikit lebih cepat dari cat /dev/zero | pv -a >/dev/null)


Saya awalnya mencoba menggunakan putchar di C, tetapi lambat.
PSkocik

Karena penasaran, mengapa argc == 1+1bukannya agrc == 2?
Pasang kembali Monica - notmaynard

@iamnotmaynard Untuk mengingatkan diri sendiri bahwa ini 1 untuk baris perintah yang dapat dieksekusi ditambah 1 argumen. :-D
PSkocik

Ah. Itu dugaan saya, tetapi ingin memastikan tidak ada alasan rahasia.
Pasang kembali Monica - notmaynard

"Mengambil byte dan xoring dengan nol adalah no-op." Hal ini tidak benar: 0 XOR X == X.
jacwah

5

Baca nol, terjemahkan setiap nol ke pola Anda!

Kami membaca nol byte dari /dev/zero, dan gunakan truntuk menerapkan bit mask ke masing-masing byte dengan menerjemahkan setiap byte nol:

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

Oktal 176 adalah kode ascii ~, jadi kami mendapatkan 10 ~. (Di $akhir output menunjukkan di shell saya bahwa tidak ada garis akhir - itu bisa terlihat berbeda untuk Anda)

Jadi, mari kita buat 0xFFbyte: Hex 0xFFadalah oktal 0377. Nol terdepan ditinggalkan untuk trbaris perintah; Pada akhirnya, hexdumpdigunakan untuk membuat output dapat dibaca.

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

Anda perlu menggunakan kode oktal karakter di sini, bukan heksadesimal. Jadi kisaran dari \000oktal \377(sama seperti 0xFF).
Gunakan ascii -xdan ascii -ountuk mendapatkan tabel karakter dengan angka indeks heksadesimal atau oktal.
(Untuk tabel dengan desimal dan heksadesimal, adil ascii).

Cukup cepat

Ini berjalan cukup cepat, dibandingkan dengan hanya menggunakan nol: cat /dev/zerohanya empat kali lebih cepat, sementara itu dapat memanfaatkan buffering IO dengan sempurna, yang trtidak bisa.

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

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

3

Tergantung apa yang ingin Anda lakukan dengan data dan seberapa fleksibel Anda ingin menggunakannya.

Kasus terburuk jika Anda membutuhkan kecepatan, Anda dapat melakukan hal yang sama dengan / dev / zero, dan hanya mengkompilasi / dev / one, / dev / two, .. / dev / fourtytwo .. dan seterusnya perangkat.

Dalam kebanyakan kasus, lebih baik membuat data secara langsung di tempat yang diperlukan, jadi di dalam program / skrip sebagai konstanta. Dengan lebih banyak informasi, orang dapat lebih membantu Anda.


1

Infinte printf loop

Ganti \u00dengan byte yang Anda inginkan.

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

Kode C ++:

#include<cstdio>

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

Kompilasi: ganti Bytedengan nilai yang Anda inginkan.

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

Menggunakan

./bin | yourapp

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.