如何在/ dev / zero上放置位掩码,以便可以获取非零的字节?


20

如何设置位掩码,/dev/zero以便不仅可以获取0x00的源,还可以获取0x01和0xFF之间的任何字节的源?


8
你为什么要问?请编辑问题以激发它。
Basile Starynkevitch 2015年


我给了这个问题一个答案,但是再读一遍,我想我误会了它。您要将每个0x00值转换为特定值还是0x00-0xFF范围内的随机值?
kos 2015年

1
@kos每个参数都具有特定值,444444...而不是随机值
Eduard Florinescu 2015年

Answers:


18

以下bash代码设置为以二进制表示的字节起作用。但是你可以很容易地改变它处理ocatal十进制十六进制通过简单地改变基数 r 的价值281016分别设置b=相应。

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

编辑 -它确实处理所有范围的字节值:十六进制 00 - FF(当我在下面编写00-7F时,我只考虑了单字节UTF-8字符)。

例如,如果您只需要4个字节UTF-8'ASCII'十六进制00-7F范围内的字符),则可以将其传送head中... | head -c4

输出(4个字符):

~~~~

看在8位格式的输出,管道将它导入xxd(或任何其他的1和0的字节转储*):
例如。b=10000000并输送至:... | head -c4 | xxd -b

0000000: 10000000 10000000 10000000 10000000                    ....

1
您是要写o=$(printf ...)第二行吗?
jwodder

1
@jwodder:不,第二行是正确的,如图所示。该printf的选项-v导致里边反输出直接设置后立即命名变量; 在这种情况下,变量的名称为o八进制)-请注意,该-v选项适用于shell内置版本printf(不适用于/ usr / bin / printf版本)
Peter.O 2015年

2
@jwodder此外,通常,该-v选项还可以确保将变量设置为恰好指定的值。$(...)首先转换输出。这就是为什么o=$(printf '\n')不会产生您期望的效果,而却printf -vo '\n'如此的原因。(此处无关紧要,因为此处的输出格式不受这种转换的影响,但是如果您不知道该-v选项,那么可能会很有用。)
hvd

18

您不能轻易做到这一点。

您可能会考虑编写自己的提供此类设备的内核模块。我不建议那样。

您可以编写一个微型C程序,在某个管道(或stdout)或FIFO 上写入相同字节的无限流。

你可以用 tr(1)读取/dev/zero每个0字节并将其转换为其他内容。

您可以使用yes(1),至少在您负担得起换行符的情况下(或将其换成tr -d '\n'...)


10
yes 1 | tr -d $'\n'为此使用。
kojiro 2015年

3
@kojiro:如果您尝试输入字符yes流,那将失败\n。处理的替代方法\n是:yes '' | tr '\n' "$c"–哪里$c可以是所有ASCII字符范围的任何char。
Peter.O 2015年

1
@ Peter.O我不确定您如何将我的评论解释为除文字静态表达式之外的其他含义yes 1 | tr -d $'\n'。我想您可以使用一个不进行$''反斜杠处理的外壳,或者您可以尝试找到一个可以改变的语言环境tr -d $'\n',但是我还没有找到它。
kojiro 2015年

@kojiro:您yes 1 | tr -d $'\n'将很愉快地打印1字符流和几乎所有其他单字节值,但是它不能打印\n字符流。OP希望能够处理“ 0x01和0xFF之间”的
Peter.O 2015年

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

13

好吧,如果您确实想实现此目标,则可以使用 LD_PRELOAD挂钩。基本思想是从C库重写一个函数,然后使用它代替常规函数。

这是一个简单的示例,其中我们重写read()函数以将输出缓冲区与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关联的文件描述符。然后,如果仅对read()函数执行XOR运算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,并解析文件名以确定要应用的位掩码。
yoann 2015年

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]

$破折号-c'while:; 做echo -n“ \ 1”; 完成” | pv -a> / dev / null
[225KiB / s]
$ bash -c'while :; 做echo -ne“ \ 1”; 完成” | 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]

在我的Debian中,perl产量为2.13GiB,而< /dev/zero产量为8.73GiB。哪些因素会影响性能?
cuonglm 2015年

@cuonglm,是的,我发现系统之间存在一些差异,但perl始终比其他解决方案快。我得到与等效的编译C程序相同的吞吐量。基准测试在应用程序上与在系统的调度程序上一样多。最不同的是要写入的缓冲区的大小。
斯特凡Chazelas

@cuonglm管道也会减慢速度。我想cat /dev/zero| pv -a >/dev/null每秒也会为您提供大约2 GiBs(它在我的系统上确实达到了,而< /dev/zero)也使我大约达到6GiBps。
PSkocik 2015年

@StéphaneChazelas请问您使用的是什么系统,StéphaneChazelas?我的结果差别很大(我可以从perl版本中获得2.1GiB的信息)。我在使用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 2015年

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。较新的内核似乎有所作为(除非它是新的Perl:v5.20.2)
斯特凡Chazelas

7

尝试将位掩码/异或为零字节是没有意义的,不是吗?取一个字节并将xor其清零是没有操作的。

只需创建一个循环,为您提供所需的字节,然后将其放在管道或命名管道的后面即可。它的行为与字符设备几乎相同(空闲时不会浪费CPU周期):

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.1GB / s(甚至比还要快cat /dev/zero | pv -a >/dev/null


我最初尝试在C中使用putchar,但是速度很慢。
PSkocik 2015年

出于好奇,为什么要argc == 1+1代替agrc == 2
恢复莫妮卡-notmaynard

@iamnotmaynard提醒自己,命令行可执行文件的值为1,另加1个参数。:-D
PSkocik 2015年

啊。那是我的猜测,但想确保没有任何秘密原因。
恢复莫妮卡-notmaynard

“获取字节并将其与零进行异或运算是无操作的。” 这是不正确的:0 XOR X == X
jacwah 2015年

5

读取零,将每个零转换为您的模式!

我们从中读取零字节/dev/zero,并tr通过转换每个零字节来对每个字节应用位掩码:

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

八进制176是的ASCII码~,因此我们得到10 ~。($输出末尾的表示我的外壳中没有行尾-对您来说它可能看起来有所不同)

因此,让我们创建0xFF字节:十六进制0xFF为八进制0377tr命令行的前导零被忽略了;最后,hexdump用于使输出可读。

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

您需要在此处使用字符的八进制代码,而不是十六进制。因此,范围是从\000到八进制\377(与相同0xFF)。
使用ascii -xascii -o获取具有十六进制或八进制索引号的字符表。
(对于具有十进制和十六进制的表,仅为ascii)。

蛮快

与仅使用零相比,它运行得相当快:cat /dev/zero仅是四倍,而它却可以完美利用IO缓冲,而这tr是不可能的。

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

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

3

取决于您要对数据进行何种操作以及使用数据的灵活性。

最糟糕的情况是,如果需要速度,则可以执行与/ dev / zero相同的操作,只需编译/dev/one、/dev/two、../dev/fourtytwo ..等设备。

在大多数情况下,最好直接在需要的地方创建数据,因此在程序/脚本中将其作为常量。有了更多信息,人们可以更好地帮助您。


1

无限printf循环

\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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.