2

I am new to Linux kernel module. I am learning char driver module based on a web course. I have a very simple module that creates a /dev/chardevexample, and I have a question for my understanding:

When I do echo "hello4" > /dev/chardevexample, I see the write execute exactly once as expected. However, when I do cat /dev/chardevexample, I see the read executed two times.

I see this both in my code and in the course material. All the data was returned in the first read(), so why does cat call it again?

All the things I did so far are as follows:

  1. insmod chardev.ko to load my module
  2. echo "hello4" > /dev/chardevexample. This is the write and I see it happening exactly once in dmesg
  3. cat /dev/chardevexample. This is the read, and dmesg shows it happening twice.
  4. I did strace cat /dev/chardevexample, and I indeed see the function call being called twice for read. There is a write in between as well

    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)
    
  5. dmesg after read (cat command)

    [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
    
  6. Code snippet for read, write and file_operations is attached. Any guidance would help. I searched extensively and couldn't understand. Hence the post.

    /*!
     * @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,};
    
that other guy
  • 116,971
  • 11
  • 170
  • 194
Dsrivast
  • 39
  • 1
  • 10

1 Answers1

6

The Unix convention for indicating end-of-file is to have read return 0 bytes.

In this case, cat asks for 131072 bytes and only receives 4096. This is normal and not to be interpreted as having reached the end of the file. For example, it happens when you read from the keyboard but the user only inputs a small amount of data.

Because cat has not yet seen EOF (i.e. read did not return 0), it continues to issue read calls until it does. This means that if there's any data, you will always see a minimum of two read calls: one (or more) for the data, and one final one that returns 0.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • Thanks for the response. This makes sense now. Dumb question, why is cat asking for 131072 Bytes and can I change it? – Dsrivast Feb 03 '20 at 19:50
  • 2
    GNU cat asks for 128 kiB (131072 bytes) because the maintainer empirically determined this to have the best performance with the lowest memory usage on contemporary hardware (20 years ago it was 4096). To read a file with a given buffer size, you can instead use `dd bs=1234 if=myfilename` – that other guy Feb 03 '20 at 20:00
  • Thanks again. for some reason dd bs=1234 was still reading more than text content. Based on other answers, dd count=8 iflag=count_bytes if=/dev/chardevexample and iI see only one read. Is this correct? – Dsrivast Feb 04 '20 at 01:48
  • If you want to do a single read of 8 bytes, use `dd bs=8 count=1 if=/dev/chardevexample`. Obviously these are all just workarounds for the bugs in your module. – that other guy Feb 04 '20 at 01:58
  • Thanks. yes this works and appears as the better usage than what I was using. – Dsrivast Feb 04 '20 at 18:23
  • 1
    @Dsrivast The question had several issues: 1. "understanding question on read" is not a helpful title and does not help people know if this is the problem they have, 2. It did not mention what you expected or why, 3. The steps to reproduce are very hard to replicate: for example, there's only partial source code and no build command. 4. If you had tried `strace cat` on any other file you'd see it's unrelated to your module, so it could have been a simple "Why does `echo hello | strace -e read cat` shows two read calls when the first one returned all the data?" – that other guy Feb 07 '20 at 00:54