completed, not tested

This commit is contained in:
2025-04-23 20:11:13 +08:00
parent daa4621991
commit 00541876d3
5 changed files with 276 additions and 61 deletions

61
.vscode/settings.json vendored
View File

@@ -1,61 +0,0 @@
{
"files.associations": {
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"random": "cpp",
"ratio": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"initializer_list": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"typeinfo": "cpp",
"syncstream": "cpp",
"csignal": "cpp",
"sstream": "cpp",
"fstream": "cpp"
},
"C_Cpp.default.compilerPath": "/usr/bin/clang++"
}

24
lab6/.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,24 @@
{
"configurations": [
{
"name": "Linux Kernel Module",
"includePath": [
"/usr/lib/modules/6.14.3-arch1-1/build/include",
"/usr/lib/modules/6.14.3-arch1-1/build/arch/x86/include",
"/usr/lib/modules/6.14.3-arch1-1/build/include/uapi",
"/usr/lib/modules/6.14.3-arch1-1/build/include/generated/uapi",
"/usr/lib/modules/6.14.3-arch1-1/build/include/generated",
"/usr/lib/modules/6.14.3-arch1-1/build/arch/x86/include/generated"
],
"defines": [
"__KERNEL__",
"MODULE"
],
"compilerPath": "/usr/bin/g++",
"cStandard": "c11",
"cppStandard": "c++20",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}

9
lab6/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,9 @@
{
"files.associations": {
"cdev.h": "c",
"module.h": "c",
"types.h": "c",
"init.h": "c"
},
"C_Cpp.default.compilerPath": "/usr/bin/clang++"
}

13
lab6/Makefile Normal file
View File

@@ -0,0 +1,13 @@
obj-m += mypipe.o
OUT_DIR := build
.PHONY kern_mod writer reader clean
all: kern_mod writer reader
kern_mod: mypipe.c
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
rm -rf $(OUT_DIR)

230
lab6/mypipe.c Normal file
View File

@@ -0,0 +1,230 @@
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
// Ref:
// https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html
// https://olegkutkov.me/2018/03/14/simple-linux-character-device-driver/
#define MYPIPE_BUFFER_SIZE 4096
#define MYPIPE_DEVICE_NAME "wendy"
static dev_t dev_major = 0;
enum MyPipeMinors {
MYPIPE_MINOR_IN = 0,
MYPIPE_MINOR_OUT,
MYPIPE_MINOR_COUNT,
};
struct mypipe_data {
struct cdev cdev;
u8 buffer[MYPIPE_BUFFER_SIZE];
size_t write_pos;
size_t read_pos;
size_t data_len;
};
// This could be an array if we want multiple cdev
static struct mypipe_data pdata;
static struct class *mypipedev_class = NULL;
// Protects the buffer
static DEFINE_MUTEX(mypipe_lock);
// These are two wait_queue_head_t for storing tasks that are waiting for
// writing/ reading
static DECLARE_WAIT_QUEUE_HEAD(read_queue);
static DECLARE_WAIT_QUEUE_HEAD(write_queue);
static int mypipe_open(struct inode *inode, struct file *file) {
printk("Mypipe opened.");
return 0;
}
static int mypipe_release(struct inode *inode, struct file *file) {
printk("Mypipe released.");
return 0;
}
static int mypipe_read(struct file *filep, char __user *buf, size_t size,
loff_t *offset) {
ssize_t ret = 0;
if (iminor(file_iniode(filep)) != MYPIPE_MINOR_OUT) {
return -EINVAL;
}
// Acquire the lock. If not acquired and interrupted by signal, let kernel
// to try restart the syscall
if (mutex_lock_interruptible(&mypipe_lock)) {
return -ERESTARTSYS;
}
// We currently have no data, but we can wait for possible new to write in
while (pdata.data_len == 0) {
mutex_unlock(&mypipe_lock);
if (filep->f_flags & O_NONBLOCK) {
return -EAGAIN;
}
if (wait_event_interruptible(read_queue, pdata.data_len > 0)) {
return -ERESTARTSYS;
}
// Woken, relock for checking the condition
if (mutex_lock_interruptible(&mypipe_lock)) {
return -ERESTARTSYS;
}
}
// data_len > 0
size_t to_read = min(size, pdata.data_len);
size_t until_end = min(to_read, MYPIPE_BUFFER_SIZE - pdata.read_pos);
if (copy_to_user(buf, pdata.buffer + pdata.read_pos, until_end)) {
ret = -EFAULT;
goto out;
}
if (to_read > until_end) {
if (copy_to_user(buf + until_end, pdata.buffer, to_read - until_end)) {
ret = -EFAULT;
goto out;
}
}
pdata.read_pos = (pdata.read_pos + to_read) % MYPIPE_BUFFER_SIZE;
pdata.data_len -= to_read;
ret = to_read;
wake_up_interruptible(&write_queue);
out:
mutex_unlock(&mypipe_lock);
return ret;
}
static int mypipe_write(struct file *filep, char __user *buf, size_t size,
loff_t *offset) {
// This is very similar to read(), just write instead of read
ssize_t ret = 0;
if (iminor(file_indo(filep)) != MYPIPE_MINOR_IN) {
return -EINVAL;
}
if (mutex_lock_interruptible(&mypipe_lock)) {
return -ERESTARTSYS;
}
while (pdata.data_len == MYPIPE_BUFFER_SIZE) {
mutex_unlock(&mypipe_lock);
if (filep->f_flags & O_NONBLOCK) {
return -EAGAIN;
}
if (wait_event_interruptible(write_queue,
pdata.data_len < MYPIPE_BUFFER_SIZE)) {
return -ERESTARTSYS;
}
if (mutex_lock_interruptible(&mypipe_lock)) {
return -ERESTARTSYS;
}
}
size_t space_left = MYPIPE_BUFFER_SIZE - pdata.data_len;
size_t to_write = min(size, space_left);
size_t until_end = min(to_write, MYPIPE_BUFFER_SIZE - pdata.write_pos);
if (copy_from_user(pdata.buffer + pdata.write_pos, buf, until_end)) {
ret = -EFAULT;
goto out;
}
if (to_write > until_end) {
if (copy_from_user(pdata.buffer, buf + until_end,
to_write - until_end)) {
ret = -EFAULT;
goto out;
}
}
pdata.write_pos = (pdata.write_pos + to_write) % MYPIPE_BUFFER_SIZE;
pdata.data_len += to_write;
ret = to_write;
wake_up_interruptible(&read_queue);
out:
mutex_unlock(&mypipe_lock);
return ret;
}
const struct file_operations mypipe_fops = {.owner = THIS_MODULE,
.open = mypipe_open,
.read = mypipe_read,
.write = mypipe_write,
.release = mypipe_release};
static int __init mypipe_init(void) {
int ret;
dev_t dev;
// Acquire a region of major, minor for us to use. The name appears in
// /proc/devices (and potentially /sys ?).
ret = alloc_chrdev_region(&dev, 0, MYPIPE_MINOR_COUNT, MYPIPE_DEVICE_NAME);
if (ret) {
return ret;
}
dev_major = MAJOR(dev);
// Create the class in sysfs. The name appears in /sys/class/<name>
mypipedev_class = class_create(MYPIPE_DEVICE_NAME);
if (IS_ERR(mypipedev_class)) {
ret = PTR_ERR(mypipedev_class);
goto del_cdev;
}
// Create a cdev in our pdata
cdev_init(&pdata.cdev, &mypipe_fops);
pdata.cdev.owner = THIS_MODULE;
// Register this newly created cdev to kernel. We only have one dev, so we
// can MKDEV(dev_major, 0).
// Also, we want to devices that one handles write and one read, so the last
// arg is not 1, but 2 (In this case, MYPIPE_MINOR_COUNT)
ret = cdev_add(&pdata.cdev, MKDEV(dev_major, 0), MYPIPE_MINOR_COUNT);
if (ret)
goto unregister;
// Create device node /dev/mypipe_in and /dev/mypipe_out
device_create(mypipedev_class, NULL, MKDEV(dev_major, MYPIPE_MINOR_IN),
NULL, "%s_in", MYPIPE_DEVICE_NAME);
device_create(mypipedev_class, NULL, MKDEV(dev_major, MYPIPE_MINOR_OUT),
NULL, "%s_out", MYPIPE_DEVICE_NAME);
printk(KERN_INFO "Wendy: module loaded\n");
return 0;
del_cdev:
cdev_del(&pdata.cdev);
unregister:
unregister_chrdev_region(MKDEV(dev_major, 0), MYPIPE_MINOR_COUNT);
return ret;
}
static void __exit mypipe_destroy(void) {
// This is basically the reverse operation of mypipe_init
device_destroy(mypipedev_class, MKDEV(dev_major, MYPIPE_MINOR_IN));
device_destroy(mypipedev_class, MKDEV(dev_major, MYPIPE_MINOR_OUT));
class_unregister(mypipedev_class);
class_destory(mypipedev_class);
cdev_del(&pdata.cdev);
unregister_chrdev_region(MKDEV(dev_major, 0), MYPIPE_MINOR_COUNT);
printk(KERN_INFO "Wendy: module unloaded");
}
module_init(mypipe_init);
module_exit(mypipe_destroy);
MODULE_LICENSE("GPL");