C 为什么在Linux字符驱动程序读取调用中大小总是=4096?
我一直在网上浏览Linux字符驱动程序示例,但遇到了一个无法解释的行为C 为什么在Linux字符驱动程序读取调用中大小总是=4096?,c,linux,linux-kernel,linux-device-driver,C,Linux,Linux Kernel,Linux Device Driver,我一直在网上浏览Linux字符驱动程序示例,但遇到了一个无法解释的行为 static ssize_t my_read(struct file *f, char __user *user_buf, size_t cnt, loff_t* off) { printk( KERN_INFO "Read called for %zd bytes\n", cnt ); return cnt; } 该消息始终指示cnt=4096字节,无论用户空间中指定的字节数是多少(例如: 但是,用户空间读取
static ssize_t my_read(struct file *f, char __user *user_buf, size_t cnt, loff_t* off)
{
printk( KERN_INFO "Read called for %zd bytes\n", cnt );
return cnt;
}
该消息始终指示cnt=4096
字节,无论用户空间中指定的字节数是多少(例如:
但是,用户空间读取调用
retval = fread(_rx_buffer, sizeof(char), 5, file_ptr);
printf( "fread returned %d bytes\n", retval );
用户空间的输出是
fread returned 5 bytes.
为什么my_read
中的大小值始终为4096,而fread
中的值指示为5?我知道我遗漏了一些东西,但不确定是什么…试试(在unistd.h
中),它应该输出5个字符。当使用libc(,fwrite(3)
等),您使用的是内部libc缓冲区,通常为页面大小(几乎总是4kib)
我相信第一次调用fread()
5字节时,libc会执行4096字节的内部read()
,下面是fread()
将只返回libc在与您使用的文件
结构相关联的缓冲区中已经存在的字节。直到达到4096。第4097个字节将发出另一个4096字节的读取
,依此类推
当您编写时也会发生这种情况,例如使用printf()
,它只是fprintf()
,第一个参数是stdout()
。libc不会直接调用write(2)
,而是将您的内容放在其内部缓冲区中(也是4096字节)。如果您调用它,它将刷新
fflush(stdout);
您自己或任何时候它在发送的字节中找到字节0x0a(ASCII换行符)
试试看,你会发现:
#包括/*用于printf()*/
#包括/*用于睡眠()*/
内部主(空){
printf(“以下消息将不会显示\n”);
printf(“你好,世界!”);
睡眠(3);
printf(“\n直到现在…\n”);
返回0;
}
但是,这将起作用(不使用libc的缓冲):
#包括/*用于printf()*/
#包括/*用于sleep()、write()和STDOUT\u文件号*/
内部主(空){
printf(“将显示以下消息\n”);
写(STDOUT_FILENO,“hello!”,6);
睡眠(3);
printf(“见?\n”);
返回0;
}
STDOUT\u FILENO
是标准输出(1)的默认文件描述符
每次有换行符时进行刷新对于终端用户立即查看消息是必不可少的,而且对于每行处理也是很有帮助的,这在Unix环境中经常进行
因此,即使libc直接使用read()
和write()
系统调用来填充和刷新其缓冲区(顺便说一句,C标准库的Microsoft实现必须使用Windows stuff,可能还有WriteFile
),这些系统调用绝对不知道libc。这会导致在同时使用这两种方法时出现有趣的行为:
#包括/*用于printf()*/
#包括/*for write()和标准输出文件号*/
内部主(空){
printf(“1.第一条消息(立即刷新)\n”);
printf(“2.第二条消息(无刷新)”;
写(STDOUT_FILENO,“3.第三条消息(立即刷新)”,30);
printf(“\n”);
返回0;
}
哪些产出:
1. first message (flushed now)
3. third message (flushed now)2. second message (without flushing)
(第三个在第二个之前!)
另外,请注意,您可以使用关闭libc的缓冲。例如:
#包括/*用于setvbuf()和printf()*/
#包括/*用于睡眠()*/
内部主(空){
setvbuf(标准输出,空,0);
printf(“将显示以下消息\n”);
printf(“你好!”);
睡眠(3);
printf(“见?\n”);
返回0;
}
我从未尝试过,但我想当fopen()
对字符设备进行加密并禁用此设备的I/O缓冲时,您也可以对文件*
执行同样的操作:
FILE*fh=fopen(“/dev/my char device”,“rb”);
setvbuf(fh,NULL,_IONBF,0);
这是否可以预读到具有页面粒度的中间缓冲区(4096是CPU配置中广泛使用的页面大小),或者硬盘的块大小(512用于较旧的,4096用于较新的多TB驱动器)?你试过调用9345字节时会发生什么吗?看起来是这样的。如果我通过用户空间请求10、20或4096字节,我会在日志文件中看到“读取调用4096字节”。如果我通过用户空间请求4097字节,我会看到两个新条目“读取调用4096字节”请求9345将导致日志文件中的两个条目,“读取调用8192字节”和“读取调用4096字节”.1-但是write
是否保证为printf
刷新libc缓冲区?我一直认为它直接进入内核syscall,但我在规范中找不到任何有关行为的信息。write
不知道libc。我将在回答中添加一个示例来说明这一点。我认为它必须有som这与缓冲有关,但不了解发生这种情况的原因(更具体地说,是如何能够请求特定的字节数)。非常好的解释!谢谢。write(0,“hello!”,6);
写入标准输入,而不是标准输出。STDOUT\u FILENO
为1。
1. first message (flushed now)
3. third message (flushed now)2. second message (without flushing)