Day 6 - User Space โ Kernel Driver Interface¶
๐ Date¶
2026-03-XX
๐ฏ Today's Goal¶
- Understand how user space interacts with kernel drivers
- Learn character device driver basics
- Implement open / read / write / release
- Understand file abstraction in Linux
๐ง What I Learned¶
1. Everything is a file¶
In Linux, devices are exposed as files:
User space interacts with drivers using:
2. Call Flow¶
User Space:
open()
read()
write()
close()
โ syscall
Kernel (VFS):
sys_open()
sys_read()
sys_write()
sys_close()
โ
Driver:
file_operations
.open
.read
.write
.release
3. open vs release¶
- open() โ driver open()
- close() โ driver release()
Important:
- release is NOT close
- release is called when last reference is dropped
4. File instance and offset¶
Each open() creates a new file instance:
- has its own offset
- has its own lifecycle
EOF is per-open, not global
๐ง Implementation Summary¶
file_operations¶
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = my_open,
.read = my_read,
.write = my_write,
.release = my_release,
};
Read with EOF handling¶
if (*offset >= data_size)
return 0;
bytes_read = min(len, data_size - *offset);
copy_to_user(...);
*offset += bytes_read;
Write¶
๐ Observations¶
Why cat always reads data¶
Each execution of cat:
Next execution:
EOF behavior¶
- EOF only applies within one open
- new open resets offset
๐ Concurrency¶
Problem¶
This has race condition
Solution¶
mutex_lock(&lock);
if (device_opened) {
ret = -EBUSY;
goto out;
}
device_opened = 1;
out:
mutex_unlock(&lock);
๐ง Key Takeaways¶
- Driver is a stateful object, not just functions
- Must follow file semantics
- Multi-process is default in Linux
- Offset and EOF must be handled correctly
โ ๏ธ Pitfalls¶
- EOF is not global
- release is not equal to close
- global variables must be protected
- kernel arguments usually do not need NULL check
๐ Next¶
- ioctl
- blocking read
- poll/select
or
- device tree
- platform driver
๐งช How to Test the Driver¶
This section describes the full workflow from loading the module to unloading it.
1. Build the module¶
2. Insert the module¶
Check kernel log:
Expected output:
3. Create device node¶
Replace <major> with the value from dmesg:
4. Write to device¶
Check kernel log:
5. Read from device¶
Expected output:
6. Verify EOF behavior¶
Run:
Observation:
- Each
cattriggers a new open - Offset resets to 0
- Data is readable again
7. Test partial read¶
Expected:
8. Test single-open protection¶
Expected:
- Second open should fail with
Device or resource busy
9. Remove device node (optional)¶
10. Remove the module¶
Verify:
Expected:
๐ Full Workflow Summary¶
make
sudo insmod mydev.ko
dmesg | tail
sudo mknod /dev/mydev c <major> 0
sudo chmod 666 /dev/mydev
echo "hello world" > /dev/mydev
cat /dev/mydev
sudo rmmod mydev
โ ๏ธ Notes¶
- Device node must match correct major number
- If insmod fails, check dmesg
- Always remove module before rebuilding
๐ฏ Summary¶
Linux driver = file interface + stateful behavior