以T10案例代码为例,咱们在内核中申请了一片缓冲区,假设此刻有2个及以上进程同时来访问这个内核驱动,并且两者同时执行到了下述代码区,那么此刻内核将会拷贝2个用户空间的数据到一个缓冲区,假设进程A恰好拷贝了一半数据到内核buffer后进程B也拷贝部分数据给内核buffer,那么将会致使内核中的buffer内数据变得不可控。node
ret = copy_from_user(kbuf, buf, write_bytes);
上述过程其实就是属于一种竞态(race condition),竞态会致使对共享内存的非控制访问,最终可能致使内存泄漏甚至系统崩溃等灾难性的结果。linux
/kernel/arch/arm/include/asm/atomic.h
下/* 功能:设置原子的值 */ static inline void atomic_set(atomic_t *v, int i); /* 定义原子变量并初始化为0 */ atomic_t value = ATOMIC_INIT(0); /* 获取原子变量的值 */ #define atomic_read(v) ((v)->counter) /* 原子变量加减 */ #define atomic_add(i, v) (void) atomic_add_return(i, v) #define atomic_inc(v) (void) atomic_add_return(1, v) #define atomic_sub(i, v) (void) atomic_sub_return(i, v) #define atomic_dec(v) (void) atomic_sub_return(1, v)
atomic_dec_and_test
函数会返回0,仍表示驱动被占用,那么驱动将会返回一个忙的信号#include <linux/init.h> #include <linux/module.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <linux/miscdevice.h> #include <asm/atomic.h> #include "cdev_test_v4.h" #define BUF_SIZR 1024 #define MAJOR_NUM 168 #define MINOR_NUM 0 struct demo_cdev { char *buffer; //my private memory int value; struct miscdevice *mdev; }; /* step1: malloc memory for char device */ struct demo_cdev *demo_dev; static atomic_t demo_available = ATOMIC_INIT(1); static ssize_t demo_read(struct file *file, char __user *buf, size_t size, loff_t *pos) { int ret = -1; int read_bytes; struct demo_cdev *demo = file->private_data; char *kbuf = demo->buffer + *pos; printk(KERN_INFO "Enter: %s\n", __func__); /* determine whether user has finished reading data */ if (*pos >= BUF_SIZR) { return 0; } if (size > (BUF_SIZR - *pos)) { read_bytes = BUF_SIZR - *pos; } else { read_bytes = size; } ret = copy_to_user(buf, kbuf, read_bytes); if (ret != 0) { return -EFAULT; } *pos += read_bytes; return read_bytes; } static ssize_t demo_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) { int ret; int write_bytes; struct demo_cdev *demo = file->private_data; char *kbuf = demo->buffer + *pos; printk(KERN_INFO "Enter: %s\n", __func__); if (*pos >= BUF_SIZR) { return 0; } if (size > (BUF_SIZR - *pos)) { write_bytes = BUF_SIZR - *pos; } else { write_bytes = size; } ret = copy_from_user(kbuf, buf, write_bytes); if (ret != 0) { return -EFAULT; } *pos += write_bytes; return write_bytes; } static int demo_open(struct inode *node, struct file *file) { printk(KERN_INFO "Enter: %s\n", __func__); if (!atomic_dec_and_test(&demo_available)) { printk(KERN_ERR "file already open\n"); /* 别忘了将信号量加回来 */ atomic_inc(&demo_available); return -EBUSY; } file->private_data = (void *)demo_dev; return 0; } static int demo_release(struct inode *node, struct file *file) { printk(KERN_INFO "Enter: %s\n", __func__); atomic_inc(&demo_available); return 0; } static long demo_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0; struct demo_cdev *demo = file->private_data; switch(cmd) { case CDEV_TEST_V4_CLEAN: memset(demo->buffer, 0x00, BUF_SIZR); printk(KERN_INFO "cmd: clean\n"); break; case CDEV_TEST_V4_GETVAL: put_user(demo->value, (int *)arg); printk(KERN_INFO "cmd: getval\n"); break; case CDEV_TEST_V4_SETVAL: demo->value = (int)arg; printk(KERN_INFO "cmd: setval\n"); break; default: break; } return (long)ret; } static struct file_operations demo_operation= { .open = demo_open, .release = demo_release, .read = demo_read, .write = demo_write, .unlocked_ioctl = demo_ioctl, }; static struct miscdevice misc_struct = { .minor = MISC_DYNAMIC_MINOR, .name = "misc_dev", .fops = &demo_operation, }; static int __init demo_init(void) { int ret = -1; printk(KERN_INFO "Enter: %s\n", __func__); demo_dev = (struct demo_cdev *)kmalloc(sizeof(struct demo_cdev), GFP_KERNEL); if (!demo_dev) { printk(KERN_ERR "failed to malloc demo_dev\n"); ret = -ENOMEM; goto ERROR_MALLOC_DEMODEV; } demo_dev->value = 1; demo_dev->buffer = (char *)kmalloc(BUF_SIZR, GFP_KERNEL); if (!demo_dev->buffer) { printk(KERN_ERR "malloc %d bytes failed\n", BUF_SIZR); ret = -ENOMEM; goto ERROR_MALLOC_BUFFER; } memset(demo_dev->buffer, 0x00, BUF_SIZR); demo_dev->mdev = &misc_struct; ret = misc_register(demo_dev->mdev); if (ret < 0) { printk(KERN_ERR "failed to register misc\n"); goto ERROR_MISC; } printk(KERN_INFO "demo char device init done\n"); return 0; ERROR_MISC: kfree(demo_dev->buffer); demo_dev->buffer = NULL; ERROR_MALLOC_BUFFER: kfree(demo_dev); demo_dev = NULL; ERROR_MALLOC_DEMODEV: return ret; } static void __exit demo_exit(void) { printk(KERN_INFO "Enter: %s\n", __func__); misc_deregister(demo_dev->mdev); kfree(demo_dev->buffer); demo_dev->buffer = NULL; kfree(demo_dev); demo_dev = NULL; printk(KERN_INFO "demo char device exit done\n"); } module_init(demo_init); module_exit(demo_exit); MODULE_AUTHOR("Qi Han"); MODULE_LICENSE("GPL");
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include "cdev_test_v4.h" #define DEVICE_PATH "/dev/misc_dev" int main(void) { int fd; int ret; printf("opening...\n"); fd = open(DEVICE_PATH, O_RDWR); if (fd < 0) { printf("failed to open\n"); return -1; } printf("sleeping...\n"); sleep(7); printf("closing\n"); close(fd); return 0; }
hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ sudo insmod atomic.ko [sudo] hq 的密码: hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ sudo chmod 666 /dev/misc_dev hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ ./test opening... sleeping... closing hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ ./test & [1] 7064 hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ opening... sleeping... hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ ./test opening... failed to open hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ ./test opening... failed to open hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ ./test opening... failed to open hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ ./test opening... failed to open hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ closing [1]+ 已完成 ./test hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$
自旋锁(spin_lock)是一种典型的对临界资源进行互斥访问的手段,顾名思义,为了得到一个自旋锁,在某个CPU上运行的代码要先执行一个原子操做,该操做测试并设置某个内存变量,在操做完成以前其余执行单元不能访问到这个内存变量。api
若是测试结果代表锁已经空闲,则该程序将会得到这个自旋锁并继续执行;若是测试代表锁被占用,程序将会在一个小的循环内重复这个“测试并设置”的操做,即“自旋”的动做,即原地打转,当自旋锁持有者经过重置该变量释放这个自旋锁以后,某个等待的“测试并设置”操做向其调用者报告锁已经被释放。bash
/* 定义自旋锁 */ spinlock_t lock; /* 动态初始化自旋锁 */ spin_lock_init(&lock); /* 得到自旋锁,若是可以马上得到锁就立刻返回,不然它将自旋在那里直到锁被释放 */ spin_lock(lock); /* 尝试得到自旋锁,若是可以马上得到锁就返回真,不然马上返回假,即退出原地打转的状态 */ spin_trylock(lock); /* 释放自旋锁,与lock和trylock配合使用 */ spin_unlock(lock);
spinlock_t lock; spin_lock_init(lock); spin_lock(lock); ... //临界区 spin_unlock(lock);
#include <linux/init.h> #include <linux/module.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <linux/miscdevice.h> #include <asm/atomic.h> #include <linux/spinlock.h> #include "cdev_test_v4.h" #define BUF_SIZR 1024 #define MAJOR_NUM 168 #define MINOR_NUM 0 struct demo_cdev { char *buffer; //my private memory int value; struct miscdevice *mdev; spinlock_t lock; int open_count; //要维护的变量值 }; /* step1: malloc memory for char device */ struct demo_cdev *demo_dev; static ssize_t demo_read(struct file *file, char __user *buf, size_t size, loff_t *pos) { int ret = -1; int read_bytes; struct demo_cdev *demo = file->private_data; char *kbuf = demo->buffer + *pos; printk(KERN_INFO "Enter: %s\n", __func__); /* determine whether user has finished reading data */ if (*pos >= BUF_SIZR) { return 0; } if (size > (BUF_SIZR - *pos)) { read_bytes = BUF_SIZR - *pos; } else { read_bytes = size; } ret = copy_to_user(buf, kbuf, read_bytes); if (ret != 0) { return -EFAULT; } *pos += read_bytes; return read_bytes; } static ssize_t demo_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) { int ret; int write_bytes; struct demo_cdev *demo = file->private_data; char *kbuf = demo->buffer + *pos; printk(KERN_INFO "Enter: %s\n", __func__); if (*pos >= BUF_SIZR) { return 0; } if (size > (BUF_SIZR - *pos)) { write_bytes = BUF_SIZR - *pos; } else { write_bytes = size; } ret = copy_from_user(kbuf, buf, write_bytes); if (ret != 0) { return -EFAULT; } *pos += write_bytes; return write_bytes; } static int demo_open(struct inode *node, struct file *file) { printk(KERN_INFO "Enter: %s\n", __func__); spin_lock(&demo_dev->lock); if (demo_dev->open_count) { printk(KERN_ERR "already open\n"); /* 退出的时候须要解锁,否则会死锁 */ spin_unlock(&demo_dev->lock); return -EBUSY; } demo_dev->open_count ++; spin_unlock(&demo_dev->lock); printk(KERN_INFO "open\n"); file->private_data = (void *)demo_dev; return 0; } static int demo_release(struct inode *node, struct file *file) { struct demo_cdev *demo = (struct demo_cdev *)file->private_data; printk(KERN_INFO "Enter: %s\n", __func__); spin_lock(&demo->lock); demo->open_count --; spin_unlock(&demo->lock); return 0; } static long demo_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0; struct demo_cdev *demo = file->private_data; switch(cmd) { case CDEV_TEST_V4_CLEAN: memset(demo->buffer, 0x00, BUF_SIZR); printk(KERN_INFO "cmd: clean\n"); break; case CDEV_TEST_V4_GETVAL: put_user(demo->value, (int *)arg); printk(KERN_INFO "cmd: getval\n"); break; case CDEV_TEST_V4_SETVAL: demo->value = (int)arg; printk(KERN_INFO "cmd: setval\n"); break; default: break; } return (long)ret; } static struct file_operations demo_operation= { .open = demo_open, .release = demo_release, .read = demo_read, .write = demo_write, .unlocked_ioctl = demo_ioctl, }; static struct miscdevice misc_struct = { .minor = MISC_DYNAMIC_MINOR, .name = "misc_dev", .fops = &demo_operation, }; static int __init demo_init(void) { int ret = -1; printk(KERN_INFO "Enter: %s\n", __func__); demo_dev = (struct demo_cdev *)kmalloc(sizeof(struct demo_cdev), GFP_KERNEL); if (!demo_dev) { printk(KERN_ERR "failed to malloc demo_dev\n"); ret = -ENOMEM; goto ERROR_MALLOC_DEMODEV; } demo_dev->value = 1; demo_dev->buffer = (char *)kmalloc(BUF_SIZR, GFP_KERNEL); if (!demo_dev->buffer) { printk(KERN_ERR "malloc %d bytes failed\n", BUF_SIZR); ret = -ENOMEM; goto ERROR_MALLOC_BUFFER; } memset(demo_dev->buffer, 0x00, BUF_SIZR); spin_lock_init(&demo_dev->lock); demo_dev->mdev = &misc_struct; demo_dev->open_count = 0; ret = misc_register(demo_dev->mdev); if (ret < 0) { printk(KERN_ERR "failed to register misc\n"); goto ERROR_MISC; } printk(KERN_INFO "demo char device init done\n"); return 0; ERROR_MISC: kfree(demo_dev->buffer); demo_dev->buffer = NULL; ERROR_MALLOC_BUFFER: kfree(demo_dev); demo_dev = NULL; ERROR_MALLOC_DEMODEV: return ret; } static void __exit demo_exit(void) { printk(KERN_INFO "Enter: %s\n", __func__); misc_deregister(demo_dev->mdev); kfree(demo_dev->buffer); demo_dev->buffer = NULL; kfree(demo_dev); demo_dev = NULL; printk(KERN_INFO "demo char device exit done\n"); } module_init(demo_init); module_exit(demo_exit); MODULE_AUTHOR("Qi Han"); MODULE_LICENSE("GPL");
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include "cdev_test_v4.h" #define DEVICE_PATH "/dev/misc_dev" int main(void) { int fd; int ret; printf("opening...\n"); fd = open(DEVICE_PATH, O_RDWR); if (fd < 0) { printf("failed to open\n"); return -1; } printf("sleeping...\n"); sleep(7); printf("closing\n"); close(fd); return 0; }
hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ sudo insmod spin_lock.ko hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ sudo chmod 666 /dev/misc_dev hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ ./test opening... sleeping... closing hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ ./test & [1] 8647 hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ opening... sleeping... hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ cat /dev/misc_dev cat: /dev/misc_dev: 设备或资源忙 hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ closing [1]+ 已完成 ./test hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ dmesg [18503.722177] open [18505.514911] Enter: demo_open [18505.514912] already open [18513.726177] Enter: demo_release
copy_from_user()
、copy_to_user()
、kmalloc
、msleep
等函数,可能致使系统崩溃。由于当进程调度到其余设备的时候可能不会执行解锁操做,进而形成死锁信号量(semaphore)是用于保护临界区的一种经常使用方法,它的用法与自旋锁相似,可是,与自旋锁不同的是,当获取不到信号量时,进程不会原地打转,而是进入休眠状态。架构
/* 定义信号量 */ struct semaphore sem; /* 初始化信号量,该函数初始化信号量并设置信号量sem的值为val,尽管信号量能够被初始化为大于1的值进而成为计数信号量,可是一般不建议这么作??? */ void sema_init(struct semaphore *sem, int val); /* 初始化一个互斥的信号量并把sem的值设为1 */ #define init_MUTEX(sem) sema_init(sem, 1) DELCEAR_MUTEX(name) /* 初始化一个信号量并把sem的值设为0 */ #define init_MUTEX_LOCKED(sem) sema_init(sem, 0) DELCEAR_MUTEX_LOCKED(name) /* 获取信号量,该函数用于获取信号量sem,他会致使睡眠,所以不能再中断上下文中使用,由于中断要求快进快出,假设在中断里面执行down函数,那么进程将会在中断里面休眠,进而使得CPU卡死在中断里面,没法跳出(中断优先级高,不可被打断) */ void down(struct semaphore *sem); /* 获取信号量,由于down进入睡眠状态的进程不能被信号打断,可是由于down_interrruptible而进入睡眠状态的进程能被信号打断,信号也会致使该函数返回,返回非0。使用该函数的时候须要对返回值进行检查,若是为非0,一般马上返回-ERESTARTSYS */ void down_interruptible(struct semaphore *sem); /* 尝试获取信号量sem,若是能马上得到就返回0,不然返回非0,不会致使调用者睡眠,能够在中断上下文中使用 */ int down_trylock(struct semaphore *sem); /* 释放信号量,唤醒调用者 */ void up(struct semaphore *sem);
#include <linux/init.h> #include <linux/module.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <linux/miscdevice.h> #include <linux/semaphore.h> #include "cdev_test_v4.h" #define BUF_SIZR 1024 #define MAJOR_NUM 168 #define MINOR_NUM 0 struct demo_cdev { char *buffer; //my private memory int value; struct miscdevice *mdev; struct semaphore lock; }; /* step1: malloc memory for char device */ struct demo_cdev *demo_dev; static ssize_t demo_read(struct file *file, char __user *buf, size_t size, loff_t *pos) { int ret = -1; int read_bytes; struct demo_cdev *demo = file->private_data; char *kbuf = demo->buffer + *pos; printk(KERN_INFO "Enter: %s\n", __func__); /* determine whether user has finished reading data */ if (*pos >= BUF_SIZR) { return 0; } if (size > (BUF_SIZR - *pos)) { read_bytes = BUF_SIZR - *pos; } else { read_bytes = size; } ret = copy_to_user(buf, kbuf, read_bytes); if (ret != 0) { return -EFAULT; } *pos += read_bytes; return read_bytes; } static ssize_t demo_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) { int ret; int write_bytes; struct demo_cdev *demo = file->private_data; char *kbuf = demo->buffer + *pos; printk(KERN_INFO "Enter: %s\n", __func__); if (*pos >= BUF_SIZR) { return 0; } if (size > (BUF_SIZR - *pos)) { write_bytes = BUF_SIZR - *pos; } else { write_bytes = size; } ret = copy_from_user(kbuf, buf, write_bytes); if (ret != 0) { return -EFAULT; } *pos += write_bytes; return write_bytes; } static int demo_open(struct inode *node, struct file *file) { printk(KERN_INFO "Enter: %s\n", __func__); if (down_trylock(&demo_dev->lock)) { printk(KERN_INFO "already open\n"); return -EBUSY; } file->private_data = (void *)demo_dev; return 0; } static int demo_release(struct inode *node, struct file *file) { struct demo_cdev *demo = (struct demo_cdev*)(file->private_data); printk(KERN_INFO "Enter: %s\n", __func__); up(&demo->lock); return 0; } static long demo_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0; struct demo_cdev *demo = file->private_data; switch(cmd) { case CDEV_TEST_V4_CLEAN: memset(demo->buffer, 0x00, BUF_SIZR); printk(KERN_INFO "cmd: clean\n"); break; case CDEV_TEST_V4_GETVAL: put_user(demo->value, (int *)arg); printk(KERN_INFO "cmd: getval\n"); break; case CDEV_TEST_V4_SETVAL: demo->value = (int)arg; printk(KERN_INFO "cmd: setval\n"); break; default: break; } return (long)ret; } static struct file_operations demo_operation= { .open = demo_open, .release = demo_release, .read = demo_read, .write = demo_write, .unlocked_ioctl = demo_ioctl, }; static struct miscdevice misc_struct = { .minor = MISC_DYNAMIC_MINOR, .name = "misc_dev", .fops = &demo_operation, }; static int __init demo_init(void) { int ret = -1; printk(KERN_INFO "Enter: %s\n", __func__); demo_dev = (struct demo_cdev *)kmalloc(sizeof(struct demo_cdev), GFP_KERNEL); if (!demo_dev) { printk(KERN_ERR "failed to malloc demo_dev\n"); ret = -ENOMEM; goto ERROR_MALLOC_DEMODEV; } demo_dev->value = 1; demo_dev->buffer = (char *)kmalloc(BUF_SIZR, GFP_KERNEL); if (!demo_dev->buffer) { printk(KERN_ERR "malloc %d bytes failed\n", BUF_SIZR); ret = -ENOMEM; goto ERROR_MALLOC_BUFFER; } memset(demo_dev->buffer, 0x00, BUF_SIZR); /* init for semaphore lock */ sema_init(&demo_dev->lock, 1); demo_dev->mdev = &misc_struct; ret = misc_register(demo_dev->mdev); if (ret < 0) { printk(KERN_ERR "failed to register misc\n"); goto ERROR_MISC; } printk(KERN_INFO "demo char device init done\n"); return 0; ERROR_MISC: kfree(demo_dev->buffer); demo_dev->buffer = NULL; ERROR_MALLOC_BUFFER: kfree(demo_dev); demo_dev = NULL; ERROR_MALLOC_DEMODEV: return ret; } static void __exit demo_exit(void) { printk(KERN_INFO "Enter: %s\n", __func__); misc_deregister(demo_dev->mdev); kfree(demo_dev->buffer); demo_dev->buffer = NULL; kfree(demo_dev); demo_dev = NULL; printk(KERN_INFO "demo char device exit done\n"); } module_init(demo_init); module_exit(demo_exit); MODULE_AUTHOR("Qi Han"); MODULE_LICENSE("GPL");
hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ sudo insmod semaphore.ko hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ sudo chm chmem chmod hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ sudo chmod 666 /dev/misc_dev hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ cat /dev/misc_dev hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ ./test & [1] 9715 hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ opening... sleeping... hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ cat /dev/misc_dev cat: /dev/misc_dev: 设备或资源忙 hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ cat /dev/misc_dev cat: /dev/misc_dev: 设备或资源忙 hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ closing [1]+ 已完成 ./test hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ cat /dev/misc_dev hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$
- 两者都是解决互斥问题的基本手段,面对特定状况咱们须要加以抉择
- 信号量是进程级的,用于多个进程之间资源的互斥,若是竞争失败,将会发生进程上下文切换(发生休眠),由于进程上下文切换开销较大,所以只有当前进程用资源时间较长的时候,选用信号量才是较好的选择
- 而当咱们所保护的临界资源访问时间比较短的时候,使用自旋锁比较方便,它不会引发进程睡眠而致使上下文切换
若是咱们将信号量初始化为0,那么它可用于同步。同步即须要执行单元拥有某特定的执行顺序,须要保证执行的前后顺序。此处以以下代码段为例子,在执行单元A中首先将互斥量初始化为0,故在执行down
函数的时候执行单元A会休眠,直到执行单元B执行up
函数释放信号量以后,执行单元A才会得到信号量,进而继续执行代码区域b。也就是说执行单元A的执行须要执行单元B的唤醒,进而实现了同步。固然linux也为咱们提供了一个完成量来实现同步。并发
执行单元A 执行单元B struct semphore sem; init_MUTEX_LOCKED(&sem);/*初始化信号量为0*/ 代码区域c 代码区域a down(&sem); <------激活---------- up(&sem); 代码区域b
/* step1:定义完成量 */ struct completion my_completion; /* step2:初始化完成量 */ init_completion(&my_completion); DECLARE_COMPLETION(my_completion); /* step3:等待完成量 */ void wait_for_completion(struct completion *c); /* step4:唤醒完成量 */ void complete(struct completion *c); //唤醒一个等待的执行单元 void complete_all(struct completion *c); //唤醒全部等待的执行单元
#include <linux/init.h> #include <linux/module.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <linux/miscdevice.h> #include <linux/completion.h> #include "cdev_test_v4.h" #define BUF_SIZR 1024 #define MAJOR_NUM 168 #define MINOR_NUM 0 struct demo_cdev { char *buffer; //my private memory int value; struct miscdevice *mdev; struct completion com; }; /* step1: malloc memory for char device */ struct demo_cdev *demo_dev; static ssize_t demo_read(struct file *file, char __user *buf, size_t size, loff_t *pos) { int ret = -1; int read_bytes; struct demo_cdev *demo = file->private_data; char *kbuf = demo->buffer + *pos; printk(KERN_INFO "Enter: %s\n", __func__); printk(KERN_INFO "Waitting for completion...\n"); wait_for_completion(&demo->com); printk(KERN_INFO "Waitting done\n"); /* determine whether user has finished reading data */ if (*pos >= BUF_SIZR) { return 0; } if (size > (BUF_SIZR - *pos)) { read_bytes = BUF_SIZR - *pos; } else { read_bytes = size; } ret = copy_to_user(buf, kbuf, read_bytes); if (ret != 0) { return -EFAULT; } *pos += read_bytes; return read_bytes; } static ssize_t demo_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) { int ret; int write_bytes; struct demo_cdev *demo = file->private_data; char *kbuf = demo->buffer + *pos; printk(KERN_INFO "Enter: %s\n", __func__); if (*pos >= BUF_SIZR) { return 0; } if (size > (BUF_SIZR - *pos)) { write_bytes = BUF_SIZR - *pos; } else { write_bytes = size; } ret = copy_from_user(kbuf, buf, write_bytes); if (ret != 0) { return -EFAULT; } *pos += write_bytes; complete(&demo->com); return write_bytes; } static int demo_open(struct inode *node, struct file *file) { printk(KERN_INFO "Enter: %s\n", __func__); file->private_data = (void *)demo_dev; return 0; } static int demo_release(struct inode *node, struct file *file) { printk(KERN_INFO "Enter: %s\n", __func__); return 0; } static long demo_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0; struct demo_cdev *demo = file->private_data; switch(cmd) { case CDEV_TEST_V4_CLEAN: memset(demo->buffer, 0x00, BUF_SIZR); printk(KERN_INFO "cmd: clean\n"); break; case CDEV_TEST_V4_GETVAL: put_user(demo->value, (int *)arg); printk(KERN_INFO "cmd: getval\n"); break; case CDEV_TEST_V4_SETVAL: demo->value = (int)arg; printk(KERN_INFO "cmd: setval\n"); break; default: break; } return (long)ret; } static struct file_operations demo_operation= { .open = demo_open, .release = demo_release, .read = demo_read, .write = demo_write, .unlocked_ioctl = demo_ioctl, }; static struct miscdevice misc_struct = { .minor = MISC_DYNAMIC_MINOR, .name = "misc_dev", .fops = &demo_operation, }; static int __init demo_init(void) { int ret = -1; printk(KERN_INFO "Enter: %s\n", __func__); demo_dev = (struct demo_cdev *)kmalloc(sizeof(struct demo_cdev), GFP_KERNEL); if (!demo_dev) { printk(KERN_ERR "failed to malloc demo_dev\n"); ret = -ENOMEM; goto ERROR_MALLOC_DEMODEV; } demo_dev->value = 1; demo_dev->buffer = (char *)kmalloc(BUF_SIZR, GFP_KERNEL); if (!demo_dev->buffer) { printk(KERN_ERR "malloc %d bytes failed\n", BUF_SIZR); ret = -ENOMEM; goto ERROR_MALLOC_BUFFER; } memset(demo_dev->buffer, 0x00, BUF_SIZR); init_completion(&demo_dev->com); demo_dev->mdev = &misc_struct; ret = misc_register(demo_dev->mdev); if (ret < 0) { printk(KERN_ERR "failed to register misc\n"); goto ERROR_MISC; } printk(KERN_INFO "demo char device init done\n"); return 0; ERROR_MISC: kfree(demo_dev->buffer); demo_dev->buffer = NULL; ERROR_MALLOC_BUFFER: kfree(demo_dev); demo_dev = NULL; ERROR_MALLOC_DEMODEV: return ret; } static void __exit demo_exit(void) { printk(KERN_INFO "Enter: %s\n", __func__); misc_deregister(demo_dev->mdev); kfree(demo_dev->buffer); demo_dev->buffer = NULL; kfree(demo_dev); demo_dev = NULL; printk(KERN_INFO "demo char device exit done\n"); } module_init(demo_init); module_exit(demo_exit); MODULE_AUTHOR("Qi Han"); MODULE_LICENSE("GPL");
cat
读取数据的时候进程会阻塞,直到咱们向内核写入数据后读的线程才会退出阻塞状态。hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ sudo insmod completion.ko [sudo] hq 的密码: hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ sudo chmod 666 /dev/misc_dev hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ cat /dev/misc_dev & [1] 5602 hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ echo "hanqi" -> /dev/misc_dev hanqi - hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ dmesg [ 4463.854757] Enter: demo_init [ 4463.854864] demo char device init done [ 4490.500256] Enter: demo_open [ 4490.500268] Enter: demo_read [ 4490.500269] Waitting for completion... [ 4546.197831] Enter: demo_open [ 4546.197841] Enter: demo_write [ 4546.197859] Enter: demo_release [ 4546.197907] Waitting done [ 4546.197989] Enter: demo_release
虽然咱们能够经过信号量完成互斥的操做,可是在linux中也为咱们提供了一套标准的mutex互斥锁机制函数
/* 定义互斥锁 */ struct mutex my_mutex; /* 初始化互斥锁 */ mutex_init(&my_mutex); /* 获取互斥体(上锁) */ void inline __sched mutex_lock(struct mutex *lock); int __sched mutex_lock_interruptible(struct mutex *lock); int __sched mutex_trylock(struct mutex *lock); /* 释放互斥锁 */ void __sched mutex_unlock(struct mutex *lock); /* 1:__sched宏展开后以下,将带有__sched的函数放到,sched.text段,即若是不想让函数在waiting channel中显示出来,就应该加上__sched */ /* Attach to any functions which should be ignored in wchan output */ #define __sched __attribute__((__section__(".sched.text")))
cat "/proc/<pid>/wchan"
,进而.sched.text
段的代码是会被wchan所忽略的,schedule这个函数是不会出如今wchan的结果中cat "/proc/<pid>/wchan"
查看进程死在了哪一个函数中,显然结果是read
函数中demo_readhq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ cat /dev/misc_dev demo_readhq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ ps aux hq 5664 0.0 0.0 16860 592 pts/0 D+ 12:27 0:00 cat /dev/misc_dev hq 5665 2.0 0.0 20140 3348 pts/1 R+ 12:27 0:00 ps aux hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$ cat "/proc/5664/wchan" demo_read hq@hq-virtual-machine:~/桌面/linux-5.8/drivers/study_driver_hq/cdev_test$
#include <linux/init.h> #include <linux/module.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <linux/miscdevice.h> #include <linux/mutex.h> #include "cdev_test_v4.h" #define BUF_SIZR 1024 #define MAJOR_NUM 168 #define MINOR_NUM 0 struct demo_cdev { char *buffer; //my private memory int value; struct miscdevice *mdev; struct mutex lock; }; /* step1: malloc memory for char device */ struct demo_cdev *demo_dev; static ssize_t demo_read(struct file *file, char __user *buf, size_t size, loff_t *pos) { int ret = -1; int read_bytes; struct demo_cdev *demo = file->private_data; char *kbuf = demo->buffer + *pos; printk(KERN_INFO "Enter: %s\n", __func__); /* lock */ mutex_lock(&demo->lock); /* determine whether user has finished reading data */ if (*pos >= BUF_SIZR) { return 0; } if (size > (BUF_SIZR - *pos)) { read_bytes = BUF_SIZR - *pos; } else { read_bytes = size; } ret = copy_to_user(buf, kbuf, read_bytes); if (ret != 0) { mutex_unlock(&demo->lock); return -EFAULT; } *pos += read_bytes; /* unlock */ mutex_unlock(&demo->lock); return read_bytes; } static ssize_t demo_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) { int ret; int write_bytes; struct demo_cdev *demo = file->private_data; char *kbuf = demo->buffer + *pos; printk(KERN_INFO "Enter: %s\n", __func__); /* lock */ mutex_lock(&demo->lock); if (*pos >= BUF_SIZR) { return 0; } if (size > (BUF_SIZR - *pos)) { write_bytes = BUF_SIZR - *pos; } else { write_bytes = size; } ret = copy_from_user(kbuf, buf, write_bytes); if (ret != 0) { mutex_unlock(&demo->lock); return -EFAULT; } *pos += write_bytes; /* unlock */ mutex_unlock(&demo->lock); return write_bytes; } static int demo_open(struct inode *node, struct file *file) { printk(KERN_INFO "Enter: %s\n", __func__); file->private_data = (void *)demo_dev; return 0; } static int demo_release(struct inode *node, struct file *file) { printk(KERN_INFO "Enter: %s\n", __func__); return 0; } static long demo_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0; struct demo_cdev *demo = file->private_data; switch(cmd) { case CDEV_TEST_V4_CLEAN: memset(demo->buffer, 0x00, BUF_SIZR); printk(KERN_INFO "cmd: clean\n"); break; case CDEV_TEST_V4_GETVAL: put_user(demo->value, (int *)arg); printk(KERN_INFO "cmd: getval\n"); break; case CDEV_TEST_V4_SETVAL: demo->value = (int)arg; printk(KERN_INFO "cmd: setval\n"); break; default: break; } return (long)ret; } static struct file_operations demo_operation= { .open = demo_open, .release = demo_release, .read = demo_read, .write = demo_write, .unlocked_ioctl = demo_ioctl, }; static struct miscdevice misc_struct = { .minor = MISC_DYNAMIC_MINOR, .name = "misc_dev", .fops = &demo_operation, }; static int __init demo_init(void) { int ret = -1; printk(KERN_INFO "Enter: %s\n", __func__); demo_dev = (struct demo_cdev *)kmalloc(sizeof(struct demo_cdev), GFP_KERNEL); if (!demo_dev) { printk(KERN_ERR "failed to malloc demo_dev\n"); ret = -ENOMEM; goto ERROR_MALLOC_DEMODEV; } demo_dev->value = 1; demo_dev->buffer = (char *)kmalloc(BUF_SIZR, GFP_KERNEL); if (!demo_dev->buffer) { printk(KERN_ERR "malloc %d bytes failed\n", BUF_SIZR); ret = -ENOMEM; goto ERROR_MALLOC_BUFFER; } memset(demo_dev->buffer, 0x00, BUF_SIZR); mutex_init(&demo_dev->lock); demo_dev->mdev = &misc_struct; ret = misc_register(demo_dev->mdev); if (ret < 0) { printk(KERN_ERR "failed to register misc\n"); goto ERROR_MISC; } printk(KERN_INFO "demo char device init done\n"); return 0; ERROR_MISC: kfree(demo_dev->buffer); demo_dev->buffer = NULL; ERROR_MALLOC_BUFFER: kfree(demo_dev); demo_dev = NULL; ERROR_MALLOC_DEMODEV: return ret; } static void __exit demo_exit(void) { printk(KERN_INFO "Enter: %s\n", __func__); misc_deregister(demo_dev->mdev); kfree(demo_dev->buffer); demo_dev->buffer = NULL; kfree(demo_dev); demo_dev = NULL; printk(KERN_INFO "demo char device exit done\n"); } module_init(demo_init); module_exit(demo_exit); MODULE_AUTHOR("Qi Han"); MODULE_LICENSE("GPL");