Skip to content

Instantly share code, notes, and snippets.

@afcidk
Last active May 10, 2025 15:13
Show Gist options
  • Save afcidk/18508eaa63da55a5b9799fa6edf936e2 to your computer and use it in GitHub Desktop.
Save afcidk/18508eaa63da55a5b9799fa6edf936e2 to your computer and use it in GitHub Desktop.
A simple kernel module to show why mutex locks matters.

使用 make check 來查看輸出

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#define FIB_DEV "/dev/test_mutex"
int s = 0;
void child(void *data) {
int fd = open(FIB_DEV, O_RDWR);
int tmp = ++s;
if (fd < 0) {
perror("Failed to open character device");
return ;
}
printf("%d: Writing %s to kernel\n", tmp, (char*)data);
write(fd, (char*)data, strlen((char*)data));
usleep(10);
char buf[100];
read(fd, buf, 100);
printf("%d: Received %s from kernel (Should receive %s)\n", tmp, buf, (char*)data);
}
int main()
{
pthread_t t[10];
char strings[][10] = {"aaa", "bbb", "CCC", "ddd", "eee", "fff", "ggg", "hhh", "iii", "jjj"};
for (int i=0; i<10; ++i) {
pthread_create(&(t[i]), NULL, (void*)child, strings[i]);
}
for (int i=0; i<10; ++i) {
pthread_join(t[i], NULL);
}
return 0;
}
CONFIG_MODULE_SIG = n
TARGET_MODULE := test_mutex
obj-m := $(TARGET_MODULE).o
ccflags-y := -std=gnu99 -Wno-declaration-after-statement
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all: client
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
$(RM) client out *.out test
load:
sudo insmod $(TARGET_MODULE).ko
unload:
sudo rmmod $(TARGET_MODULE) || true >/dev/null
client:
gcc -o test client_mtt.c -lpthread
check: all
$(MAKE) unload
$(MAKE) load
sudo ./test
$(MAKE) unload
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kdev_t.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("Kernel module without mutex lock functionality");
MODULE_VERSION("0.1");
#define DEV_FIBONACCI_NAME "test_mutex"
static dev_t fib_dev = 0;
static struct cdev *fib_cdev;
static struct class *fib_class;
#define MUTEX 1
#define MAX_LENGTH 92
static DEFINE_MUTEX(fib_mutex);
char data[100];
static int open(struct inode *inode, struct file *file)
{
if (!mutex_trylock(&fib_mutex)) {
printk(KERN_ALERT "fibdrv is in use, but will still go on.");
// FIXME: If we want the mutex lock to work well, need to return -EBUSY
// return -EBUSY;
}
return 0;
}
static int release(struct inode *inode, struct file *file)
{
mutex_unlock(&fib_mutex);
return 0;
}
/* calculate the fibonacci number at given offset */
static ssize_t read(struct file *file,
char *buf,
size_t size,
loff_t *offset)
{
copy_to_user(buf, data, strlen(data));
return 0;
}
/* write operation is skipped */
static ssize_t write(struct file *file,
const char *buf,
size_t size,
loff_t *offset)
{
copy_from_user(data, buf, 100);
return 1;
}
static loff_t device_lseek(struct file *file, loff_t offset, int orig)
{
loff_t new_pos = 0;
switch (orig) {
case 0: /* SEEK_SET: */
new_pos = offset;
break;
case 1: /* SEEK_CUR: */
new_pos = file->f_pos + offset;
break;
case 2: /* SEEK_END: */
new_pos = MAX_LENGTH - offset;
break;
}
if (new_pos > MAX_LENGTH)
new_pos = MAX_LENGTH; // max case
if (new_pos < 0)
new_pos = 0; // min case
file->f_pos = new_pos; // This is what we'll use now
return new_pos;
}
const struct file_operations fops = {
.owner = THIS_MODULE,
.read = read,
.write = write,
.open = open,
.release = release,
.llseek = device_lseek,
};
static int __init init_dev(void)
{
int rc = 0;
mutex_init(&fib_mutex);
// Let's register the device
// This will dynamically allocate the major number
rc = alloc_chrdev_region(&fib_dev, 0, 1, DEV_FIBONACCI_NAME);
if (rc < 0) {
printk(KERN_ALERT
"Failed to register the fibonacci char device. rc = %i",
rc);
return rc;
}
fib_cdev = cdev_alloc();
if (fib_cdev == NULL) {
printk(KERN_ALERT "Failed to alloc cdev");
rc = -1;
goto failed_cdev;
}
cdev_init(fib_cdev, &fops);
rc = cdev_add(fib_cdev, fib_dev, 1);
if (rc < 0) {
printk(KERN_ALERT "Failed to add cdev");
rc = -2;
goto failed_cdev;
}
fib_class = class_create(THIS_MODULE, DEV_FIBONACCI_NAME);
if (!fib_class) {
printk(KERN_ALERT "Failed to create device class");
rc = -3;
goto failed_class_create;
}
if (!device_create(fib_class, NULL, fib_dev, NULL, DEV_FIBONACCI_NAME)) {
printk(KERN_ALERT "Failed to create device");
rc = -4;
goto failed_device_create;
}
return rc;
failed_device_create:
class_destroy(fib_class);
failed_class_create:
cdev_del(fib_cdev);
failed_cdev:
unregister_chrdev_region(fib_dev, 1);
return rc;
}
static void __exit exit_dev(void)
{
mutex_destroy(&fib_mutex);
device_destroy(fib_class, fib_dev);
class_destroy(fib_class);
cdev_del(fib_cdev);
unregister_chrdev_region(fib_dev, 1);
}
module_init(init_dev);
module_exit(exit_dev);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment