为什么 cat 调用 read() 两次就足够了?

Dsr*_*ast 2 linux kernel kernel-module chardev

我是 Linux 内核模块的新手。我正在根据网络课程学习字符驱动程序模块。我有一个非常简单的模块来创建/dev/chardevexample,我有一个问题需要了解:

当我这样做时echo "hello4" > /dev/chardevexample,我会write按预期看到执行一次。但是,当我这样做时cat /dev/chardevexample,我看到读取执行了两次

我在我的代码和课程材料中都看到了这一点。第一次返回了所有数据read(),为什么还要cat再次调用呢?

到目前为止我所做的所有事情如下:

  1. insmod chardev.ko加载我的模块
  2. echo "hello4" > /dev/chardevexample。这是写入,我在 dmesg 中看到它只发生一次
  3. cat /dev/chardevexample。这是读取的内容,并且dmesg显示它发生了两次。
  4. 我做到了strace cat /dev/chardevexample,而且我确实看到函数调用被调用两次以进行读取。中间也有写

    read(3, "hello4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 131072) = 4096
    write(1, "hello4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...,    4096hello4) = 4096
    read(3, "", 131072)
    
    Run Code Online (Sandbox Code Playgroud)
  5. dmesg读取后(cat 命令)

    [909836.517402] DEBUG-device_read: To User  hello4 and bytes_to_do 4096 ppos 0 # Read #1 
    [909836.517428] DEBUG-device_read: Data send to app hello4, nbytes=4096        # Read #1   
    [909836.519086] DEBUG-device_read: To User   and bytes_to_do 0 ppos 4096       # Read #2  
    [909836.519093] DEBUG-device_read: Data send to app hello4, nbytes=0           # Read #2
    
    Run Code Online (Sandbox Code Playgroud)
  6. 附加了读取、写入和文件操作的代码片段。任何指导都会有所帮助。我广泛搜索并无法理解。因此就有了这个帖子。

    /*!
     * @brief Write to device from userspace to kernel space
     * @returns     Number of bytes written
     */
    
    static ssize_t device_write(struct file *file,  //!< File pointer
                                    const char *buf,//!< from for copy_from_user. Takes 'buf' from user space and writes to 
                                                    //!< kernel space in 'buffer'. Happens on fwrite or write 
                                    size_t lbuf,    //!< length of buffer
                                    loff_t *ppos)   //!< position to write to
    {
            int nbytes = lbuf - copy_from_user(
                                               buffer + *ppos,      /* to */
                                               buf,                 /* from */
                                               lbuf);               /* how many bytes */
            *ppos += nbytes;
            buffer[strcspn(buffer, "\n")] = 0; // Remove End of line character
            pr_info("Recieved data \"%s\" from apps, nbytes=%d\n", buffer, nbytes);
            return nbytes;
    }
    
    /*!
     * @brief Read from device - from kernel space to user space
     * @returns     Number of bytes read
     */
    static ssize_t device_read(struct file *file,//!< File pointer
                               char *buf,   //!< for copy_to_user. buf is 'to' from buffer
                           size_t lbuf, //!< Length of buffer
                           loff_t *ppos)//!< Position {
        int nbytes;
        int maxbytes;
        int bytes_to_do;
        maxbytes = PAGE_SIZE -  *ppos;
        if(maxbytes >lbuf)
                bytes_to_do = lbuf;
        else
                bytes_to_do = maxbytes;
    
        buffer[strcspn(buffer, "\n")] = 0; // Remove End of line character
        printk("DEBUG-device_read: To User  %s and bytes_to_do %d ppos %lld\n", buffer + *ppos, bytes_to_do, *ppos);
        nbytes = bytes_to_do - copy_to_user(
                                        buf, /* to */
                                        buffer + *ppos, /* from */
                                        bytes_to_do); /* how many bytes*/
        *ppos += nbytes;
        pr_info("DEBUG-device_read: Data send to app %s, nbytes=%d\n", buffer, nbytes);
        return nbytes;} /* Every Device is like a file - this is device file operation */ static struct file_operations device_fops = {
            .owner = THIS_MODULE,
            .write = device_write,
            .open  = device_open,
            .read  = device_read,};
    
    Run Code Online (Sandbox Code Playgroud)

tha*_*guy 6

指示文件结束的 Unix 约定是返回read0 字节。

在本例中,cat请求 131072 字节,但仅收到 4096。这是正常现象,不应解释为已到达文件末尾。例如,当您从键盘读取但用户仅输入少量数据时,就会发生这种情况。

因为cat还没有看到 EOF(即read没有返回 0),所以它继续发出read调用,直到看到 EOF。这意味着,如果有任何数据,您总是会看到至少两次读取调用:一次(或多次)读取数据,最后一次返回 0。

  • GNU cat 要求 128 kiB(131072 字节),因为维护者凭经验确定它在当代硬件上具有最佳性能和最低内存使用量(20 年前为 4096)。要读取给定缓冲区大小的文件,您可以使用“dd bs=1234 if=myfilename” (2认同)