Linux内核中的并发与竞态

Linux内核中的并发与竞态

1.什么是竞态

当多个进程同时访问同一个驱动的临界资源的时间,竞态就产生了。

临界资源:一次仅允许一个进程使用的共享资源。各进程采用互斥的方式,实现共享的资源

临界区:每个进程中访问临界资源的那段代码称为临界区,每次只允许一个进程进入,进入之后不允许其他进程进入。使用临界区时,一般不允许其代码运行时间过长,只要运行在临界区的线程还没离开,其它所有进入此临界区的线程都会被挂起而进入等待状态,在一定程度上影响程序的运行性能。

2.产生竞态的根本原因

  1. 对于单核CPU来说,如果支持内核抢占,就会产生竞态。

  2. 对于多核CPU来说,核与核之间本身就会产生竞态

  3. 中断和进程之间也会产生竞态。

    ​ 判断题:在ARM架构下,中断和中断也会产生竞态

    ​ 答:false ARM架构的CPU不支持中断的嵌套

3.内核中解决竞态的方法

  • 中断屏蔽

  • 自旋锁

  • 信号量

  • 互斥体

  • 原子操作

    3.1中断屏蔽

    中断屏蔽是将中断临时关闭,当关闭中断后抢占,中断都不会产生了。

    但是中断屏蔽的时间要尽可能的短,长时间的屏蔽很可能会导致用户数据的丢失或者内核的奔溃。

    中断屏蔽只支持单核CPU。

3.1.1中断屏蔽的API

     local_irq_disable();//关闭中断
     //临界资源
     local_irq_enable();//开启中断

3.2自旋锁

 自旋锁是针对多核设计的。当一个进程获取到自旋锁之后,另一个进程也想获取这把锁,此时后一个进程就处于自旋状态,也叫做盲等锁。

 - 自旋锁是需要消耗CPU资源的

 - 自旋锁保护的临界区要尽可能小,在临界区中只能做简短的,不耗时的,也不能有休眠的操作。

 - 在自旋锁保护的临界区也不能调用copy_to_user/copy_from_user等函数,可能会被打断导致用户数据的丢失。

 - 自旋锁可能会导致死锁(在同一进程内多次获取同一把未解锁的锁)

 - 自旋锁可以工作在中断上下文

 - 自旋锁在上锁的时间会关闭抢占

3.2.1 自旋锁的API

	   1.定义自旋锁
           spinlock_t lock;
       2.初始化自旋锁
           void spin_lock_init(spinlock_t *lock)
       3.上锁
           void spin_lock(spinlock_t *lock)//上锁的时间中断可以打断锁
           void spin_lock_irq(spinklock_t *lock)//上锁的时间中断打断不了锁
       4.解锁
        void spin_unlock((spinlock_t *lock))
       	void spin_unlock_irq((spinlock_t *lock))

3.2.2自旋锁代码实现

 #include <linux/cdev.h>
       #include <linux/device.h>
       #include <linux/fs.h>
       #include <linux/init.h>
       #include <linux/module.h>
       #include <linux/slab.h>
       #include <linux/uaccess.h>
       #define CNAME "myled"
       struct cdev* cdev;
       struct class* cls;
       struct device* dev;
       unsigned int major = 0;
       unsigned int minor = 0;
       char kbuf[128] = { 0 };
       const int count = 3;
       //1.定义自旋锁
       spinlock_t lock;
       int flags = 0;
       int mycdev_open(struct inode* inode, struct file* file)
       {
           // 3.上锁
           spin_lock(&lock);
           if (flags != 0) {
               spin_unlock(&lock);
               return -EBUSY;
           }
           flags = 1;
           spin_unlock(&lock);
           printk("%s %s %d\n", __FILE__, __func__, __LINE__);
           return 0;
       }
       
       ssize_t mycdev_read(struct file* file, char __user* ubuf, size_t size, loff_t* offs)
       {
           int ret;
           printk("%s %s %d\n", __FILE__, __func__, __LINE__);
           if (size > sizeof(kbuf)) {
               size = sizeof(kbuf);
           }
           ret = copy_to_user(ubuf, kbuf, size);
           if (ret) {
               printk("copy data to user error\n");
               return -EIO;
           }
           return size;
       }
       
       ssize_t mycdev_write(struct file* file,
           const char __user* ubuf, size_t size, loff_t* offs)
       {
           int ret;
           printk("%s %s %d\n", __FILE__, __func__, __LINE__);
           if (size > sizeof(kbuf)) {
               size = sizeof(kbuf);
           }
           ret = copy_from_user(kbuf, ubuf, size);
           if (ret) {
               printk("copy data from user error\n");
               return -EIO;
           }
           return size;
       }
       
       int mycdev_close(struct inode* inode, struct file* file)
       {
           spin_lock(&lock);
           flags = 0;
           spin_unlock(&lock);
           printk("%s %s %d\n", __FILE__, __func__, __LINE__);
           return 0;
       }
       const struct file_operations fops = {
           .open = mycdev_open,
           .read = mycdev_read,
           .write = mycdev_write,
           .release = mycdev_close,
       };
       static int __init mycdev_init(void)
       {
           int ret, i;
           dev_t devno;
           // 1.分配对象
           cdev = cdev_alloc();
           if (NULL == cdev) {
               printk("cdev alloc memory error\n");
               ret = -ENOMEM;
               goto ERR1;
           }
           // 2.初始化对象
           cdev_init(cdev, &fops);
           // 3.申请设备号
           if (major > 0) {
               //静态指定
               ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
               if (ret) {
                   printk("static request device number error\n");
                   goto ERR2;
               }
           } else {
               //动态申请
               ret = alloc_chrdev_region(&devno, minor, count, CNAME);
               if (ret) {
                   printk("dynamic request device number error\n");
                   goto ERR2;
               }
               major = MAJOR(devno);
               minor = MINOR(devno);
           }
           // 4.注册
           ret = cdev_add(cdev, MKDEV(major, minor), count);
           if (ret) {
               printk("register char device driver error\n");
               goto ERR3;
           }
           // 5.自动创建设备节点
           cls = class_create(THIS_MODULE, CNAME);
           if (IS_ERR(cls)) {
               printk("class create error\n");
               ret = PTR_ERR(cls);
               goto ERR4;
           }
           for (i = minor; i < count; i++) {
               dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
               if (IS_ERR(dev)) {
                   printk("device create error\n");
                   ret = PTR_ERR(dev);
                   goto ERR5;
               }
           }
           //2.初始化自旋锁
           spin_lock_init(&lock);
           return 0;
       
       ERR5:
           for (--i; i >= minor; i--) {
               device_destroy(cls, MKDEV(major, i));
           }
           class_destroy(cls);
       ERR4:
           cdev_del(cdev);
       ERR3:
           unregister_chrdev_region(MKDEV(major, minor), count);
       ERR2:
           kfree(cdev);
       ERR1:
           return ret;
       }
       static void __exit mycdev_exit(void)
       {
           int i = 0;
           for (i = minor; i < count; i++) {
               device_destroy(cls, MKDEV(major, i));
           }
           class_destroy(cls);
           unregister_chrdev_region(MKDEV(major, minor), count);
           kfree(cdev);
       }
       
       module_init(mycdev_init);
       module_exit(mycdev_exit);
       MODULE_LICENSE("GPL");
       #include <head.h>
       
       char buf[128] = { 0 };
       int main(int argc, const char* argv[])
       {
           int fd;
       
           if ((fd = open("/dev/mycdev0", O_RDWR)) == -1)
               PRINT_ERR("open error");
       
           write(fd, buf, sizeof(buf));
           sleep(7);
           read(fd, buf, sizeof(buf));
       
           close(fd);
           return 0;
       }

3.3信号量

   当一个进程获取到信号量的时间,另外一个进程也想获取这个信号量,此时后一个进程处于休眠状态。
   
   - 当信号量获取不到资源的时间,不消耗CPU资源
   
   - 信号量不会产生死锁
   
   - 信号量在上锁的时间不会关闭抢占
   
   - 信号量保护的临界区比较大,可以有延时,耗时甚至休眠的操作
   
   - 信号量可以工作在进程上下文

3.3.1信号量的API

     1.定义信号量
         struct sempore sem;
     2.初始化信号量
         void sema_init(struct semphore *sem ,int val)
         //当val的值设置为1的时间才有互斥的效果,val为0时间是同步的效果
     3.上锁
         void down(struct semphore *sem);
     	void down_trylock(struct semphore *sem);//尝试获取锁,成功返回0,失败返回1
     4.解锁
         void up(struct semphore *sem);

3.3.2信号量代码实现

 #include <linux/cdev.h>
 #include <linux/device.h>
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 #define CNAME "myled"
 struct cdev* cdev;
 struct class* cls;
 struct device* dev;
 unsigned int major = 0;
 unsigned int minor = 0;
 char kbuf[128] = { 0 };
 const int count = 3;
 // 1.定义信号量
 struct semaphore sem;
 int flags = 0;
 int mycdev_open(struct inode* inode, struct file* file)
 {
     // 3.上锁
     if (down_trylock(&sem)) {
         printk("get lock error\n");
         return -EBUSY;
     }
     printk("%s %s %d\n", __FILE__, __func__, __LINE__);
     return 0;
 }
 
 ssize_t mycdev_read(struct file* file, char __user* ubuf, size_t size, loff_t* offs)
 {
     int ret;
     printk("%s %s %d\n", __FILE__, __func__, __LINE__);
     if (size > sizeof(kbuf)) {
         size = sizeof(kbuf);
     }
     ret = copy_to_user(ubuf, kbuf, size);
     if (ret) {
         printk("copy data to user error\n");
         return -EIO;
     }
     return size;
 }
 
 ssize_t mycdev_write(struct file* file,
     const char __user* ubuf, size_t size, loff_t* offs)
 {
     int ret;
     printk("%s %s %d\n", __FILE__, __func__, __LINE__);
     if (size > sizeof(kbuf)) {
         size = sizeof(kbuf);
     }
     ret = copy_from_user(kbuf, ubuf, size);
     if (ret) {
         printk("copy data from user error\n");
         return -EIO;
     }
     return size;
 }
 
 int mycdev_close(struct inode* inode, struct file* file)
 {
     printk("%s %s %d\n", __FILE__, __func__, __LINE__);
     // 4.解锁
     up(&sem);
     return 0;
 }
 const struct file_operations fops = {
     .open = mycdev_open,
     .read = mycdev_read,
     .write = mycdev_write,
     .release = mycdev_close,
 };
 static int __init mycdev_init(void)
 {
     int ret, i;
     dev_t devno;
     // 1.分配对象
     cdev = cdev_alloc();
     if (NULL == cdev) {
         printk("cdev alloc memory error\n");
         ret = -ENOMEM;
         goto ERR1;
     }
     // 2.初始化对象
     cdev_init(cdev, &fops);
     // 3.申请设备号
     if (major > 0) {
         //静态指定
         ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
         if (ret) {
             printk("static request device number error\n");
             goto ERR2;
         }
     } else {
         //动态申请
         ret = alloc_chrdev_region(&devno, minor, count, CNAME);
         if (ret) {
             printk("dynamic request device number error\n");
             goto ERR2;
         }
         major = MAJOR(devno);
         minor = MINOR(devno);
     }
     // 4.注册
     ret = cdev_add(cdev, MKDEV(major, minor), count);
     if (ret) {
         printk("register char device driver error\n");
         goto ERR3;
     }
     // 5.自动创建设备节点
     cls = class_create(THIS_MODULE, CNAME);
     if (IS_ERR(cls)) {
         printk("class create error\n");
         ret = PTR_ERR(cls);
         goto ERR4;
     }
     for (i = minor; i < count; i++) {
         dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
         if (IS_ERR(dev)) {
             printk("device create error\n");
             ret = PTR_ERR(dev);
             goto ERR5;
         }
     }
     // 2.初始化信号量
     sema_init(&sem, 1);
     return 0;
 
 ERR5:
     for (--i; i >= minor; i--) {
         device_destroy(cls, MKDEV(major, i));
     }
     class_destroy(cls);
 ERR4:
     cdev_del(cdev);
 ERR3:
     unregister_chrdev_region(MKDEV(major, minor), count);
 ERR2:
     kfree(cdev);
 ERR1:
     return ret;
 }
 static void __exit mycdev_exit(void)
 {
     int i = 0;
     for (i = minor; i < count; i++) {
         device_destroy(cls, MKDEV(major, i));
     }
     class_destroy(cls);
     unregister_chrdev_region(MKDEV(major, minor), count);
     kfree(cdev);
 }
 
 module_init(mycdev_init);
 module_exit(mycdev_exit);
 MODULE_LICENSE("GPL");
 #include <head.h>
 
 char buf[128] = { 0 };
 int main(int argc, const char* argv[])
 {
     int fd;
 
     if ((fd = open("/dev/mycdev0", O_RDWR)) == -1)
         PRINT_ERR("open error");
 
     write(fd, buf, sizeof(buf));
     sleep(7);
     read(fd, buf, sizeof(buf));
 
     close(fd);
     return 0;
 }

3.4 互斥体

当一个进程获取到互斥体的时间,另外一个进程也想获取这个互斥体,后一个进程处于休眠状态

  • 当互斥体获取不到资源的时间,不消耗CPU资源
  • 互斥体不会产生死锁
  • 互斥体在上锁的时间不会关闭抢占
  • 互斥体的临界资源比较大,可以有延时,耗时,甚至休眠的操作
  • 互斥体可以工作在进程上下文
  • 互斥体在上锁前会稍微等一会,在不确定临界区大小的时间预先选择互斥体

3.4.1互斥体的API

   1.定义互斥体
       struct mutex mutex;
   2.初始化互斥体
       mutex_init(&mutex);
   3.上锁
       void mutex_lock(struct mutex *lock);
   	int mutex_trylock(struct mutex*lock);//尝试获取锁,成功返回1,失败返回0
   4.解锁
       void mutex_unlock(struct mutex*lock)

3.4.2互斥体代码实现

#include <linux/cdev.h>
   #include <linux/device.h>
   #include <linux/fs.h>
   #include <linux/init.h>
   #include <linux/module.h>
   #include <linux/slab.h>
   #include <linux/uaccess.h>
   #define CNAME "myled"
   struct cdev* cdev;
   struct class* cls;
   struct device* dev;
   unsigned int major = 0;
   unsigned int minor = 0;
   char kbuf[128] = { 0 };
   const int count = 3;
   // 1.定义互斥体
   struct mutex mutex;
   
   int mycdev_open(struct inode* inode, struct file* file)
   {
       // 3.尝试上锁,成功返回1,失败返回0
       if (mutex_trylock(&mutex)) {
           printk("get lock error\n");
           return -EBUSY; //如果锁的资源不可用,立即返回
       }
       //如果锁的资源可用,就往下执行
       printk("%s %s %d\n", __FILE__, __func__, __LINE__);
       return 0;
   }
   
   ssize_t mycdev_read(struct file* file, char __user* ubuf, size_t size, loff_t* offs)
   {
       int ret;
       printk("%s %s %d\n", __FILE__, __func__, __LINE__);
       if (size > sizeof(kbuf)) {
           size = sizeof(kbuf);
       }
       ret = copy_to_user(ubuf, kbuf, size);
       if (ret) {
           printk("copy data to user error\n");
           return -EIO;
       }
       return size;
   }
   
   ssize_t mycdev_write(struct file* file,
       const char __user* ubuf, size_t size, loff_t* offs)
   {
       int ret;
       printk("%s %s %d\n", __FILE__, __func__, __LINE__);
       if (size > sizeof(kbuf)) {
           size = sizeof(kbuf);
       }
       ret = copy_from_user(kbuf, ubuf, size);
       if (ret) {
           printk("copy data from user error\n");
           return -EIO;
       }
       return size;
   }
   
   int mycdev_close(struct inode* inode, struct file* file)
   {
       printk("%s %s %d\n", __FILE__, __func__, __LINE__);
       // 4.解锁
       mutex_unlock(&mutex);
       return 0;
   }
   const struct file_operations fops = {
       .open = mycdev_open,
       .read = mycdev_read,
       .write = mycdev_write,
       .release = mycdev_close,
   };
   static int __init mycdev_init(void)
   {
       int ret, i;
       dev_t devno;
       // 1.分配对象
       cdev = cdev_alloc();
       if (NULL == cdev) {
           printk("cdev alloc memory error\n");
           ret = -ENOMEM;
           goto ERR1;
       }
       // 2.初始化对象
       cdev_init(cdev, &fops);
       // 3.申请设备号
       if (major > 0) {
           //静态指定
           ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
           if (ret) {
               printk("static request device number error\n");
               goto ERR2;
           }
       } else {
           //动态申请
           ret = alloc_chrdev_region(&devno, minor, count, CNAME);
           if (ret) {
               printk("dynamic request device number error\n");
               goto ERR2;
           }
           major = MAJOR(devno);
           minor = MINOR(devno);
       }
       // 4.注册
       ret = cdev_add(cdev, MKDEV(major, minor), count);
       if (ret) {
           printk("register char device driver error\n");
           goto ERR3;
       }
       // 5.自动创建设备节点
       cls = class_create(THIS_MODULE, CNAME);
       if (IS_ERR(cls)) {
           printk("class create error\n");
           ret = PTR_ERR(cls);
           goto ERR4;
       }
       for (i = minor; i < count; i++) {
           dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
           if (IS_ERR(dev)) {
               printk("device create error\n");
               ret = PTR_ERR(dev);
               goto ERR5;
           }
       }
       // 2.初始化互斥体
       mutex_init(&mutex);
       return 0;
   
   ERR5:
       for (--i; i >= minor; i--) {
           device_destroy(cls, MKDEV(major, i));
       }
       class_destroy(cls);
   ERR4:
       cdev_del(cdev);
   ERR3:
       unregister_chrdev_region(MKDEV(major, minor), count);
   ERR2:
       kfree(cdev);
   ERR1:
       return ret;
   }
   static void __exit mycdev_exit(void)
   {
       int i = 0;
       for (i = minor; i < count; i++) {
           device_destroy(cls, MKDEV(major, i));
       }
       class_destroy(cls);
       unregister_chrdev_region(MKDEV(major, minor), count);
       kfree(cdev);
   }
   
   module_init(mycdev_init);
   module_exit(mycdev_exit);
   MODULE_LICENSE("GPL");
   #include <head.h>
   
   char buf[128] = "12345";
   int main(int argc, const char* argv[])
   {
       int fd;
   
       if ((fd = open("/dev/mycdev0", O_RDWR)) == -1)
           PRINT_ERR("open error");
   
       write(fd, buf, sizeof(buf));
       sleep(7);
       read(fd, buf, sizeof(buf));
       printf("buf=%s\n", buf);
   
       close(fd);
       return 0;
   }

3.5原子操作

本身强调的原子性,对变量的修改是一个不可分割的整体。内部实现时间保证了只有一个CPU在执行,对这个变量的修改是通过内联汇编完成的

 typedef struct {
     	int counter;
   } atomic_t;

3.5.1 原子操作的API

   1.定义原子变量并初始化
       atomic_t atm = ATOMIC_INIT(1);
   	atomic_t atm = ATOMIC_INIT(-1);
   2.上锁
       int atomic_dec_and_test(atomic_t *v)
       //让原子变量的值减去1,然后和0比较,如果结果为0代表获取锁成功了,这个函数返回真,否者返回假
       int atomic_inc_and_test(atomic_t *v)//让原子变量的值加1,然后和0比较,如果结果为0代表获取锁成功了,这个函数返回真,否者返回假
   3.解锁
       atomic_inc(atomic_t *v)  //加1 必须保证原子变量为初始化时的值
       atomic_dec(atomic_t *v)  //减1 必须保证原子变量为初始化时的值

3.5.2原子操作代码实现

   #include <linux/cdev.h>
   #include <linux/device.h>
   #include <linux/fs.h>
   #include <linux/init.h>
   #include <linux/module.h>
   #include <linux/slab.h>
   #include <linux/uaccess.h>
   #define CNAME "myled"
   struct cdev* cdev;
   struct class* cls;
   struct device* dev;
   unsigned int major = 0;
   unsigned int minor = 0;
   char kbuf[128] = { 0 };
   const int count = 3;
   // 1.定义原子变量并初始化
   atomic_t atomic = ATOMIC_INIT(1);
   
   int mycdev_open(struct inode* inode, struct file* file)
   {
       // 让原子变量减去1,和0比较,结果为0就代表获取锁成功了,函数返回真
       if (!atomic_dec_and_test(&atomic)) {
           // 进来代表获取锁失败 此时原子变量减1为0 必须恢复为初始化的值,也就是执行解锁操作
           atomic_inc(&atomic);
           printk("get lock error\n");
           return -EBUSY; //如果锁的资源不可用,立即返回
       }
       //如果锁的资源可用,就往下执行
       printk("%s %s %d\n", __FILE__, __func__, __LINE__);
       return 0;
   }
   
   ssize_t mycdev_read(struct file* file, char __user* ubuf, size_t size, loff_t* offs)
   {
       int ret;
       printk("%s %s %d\n", __FILE__, __func__, __LINE__);
       if (size > sizeof(kbuf)) {
           size = sizeof(kbuf);
       }
       ret = copy_to_user(ubuf, kbuf, size);
       if (ret) {
           printk("copy data to user error\n");
           return -EIO;
       }
       return size;
   }
   
   ssize_t mycdev_write(struct file* file,
       const char __user* ubuf, size_t size, loff_t* offs)
   {
       int ret;
       printk("%s %s %d\n", __FILE__, __func__, __LINE__);
       if (size > sizeof(kbuf)) {
           size = sizeof(kbuf);
       }
       ret = copy_from_user(kbuf, ubuf, size);
       if (ret) {
           printk("copy data from user error\n");
           return -EIO;
       }
       return size;
   }
   
   int mycdev_close(struct inode* inode, struct file* file)
   {
       printk("%s %s %d\n", __FILE__, __func__, __LINE__);
       //此时原子变量为0 必须恢复为初始化的值 也就是解锁的操作
       atomic_inc(&atomic);
       return 0;
   }
   const struct file_operations fops = {
       .open = mycdev_open,
       .read = mycdev_read,
       .write = mycdev_write,
       .release = mycdev_close,
   };
   static int __init mycdev_init(void)
   {
       int ret, i;
       dev_t devno;
       // 1.分配对象
       cdev = cdev_alloc();
       if (NULL == cdev) {
           printk("cdev alloc memory error\n");
           ret = -ENOMEM;
           goto ERR1;
       }
       // 2.初始化对象
       cdev_init(cdev, &fops);
       // 3.申请设备号
       if (major > 0) {
           //静态指定
           ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
           if (ret) {
               printk("static request device number error\n");
               goto ERR2;
           }
       } else {
           //动态申请
           ret = alloc_chrdev_region(&devno, minor, count, CNAME);
           if (ret) {
               printk("dynamic request device number error\n");
               goto ERR2;
           }
           major = MAJOR(devno);
           minor = MINOR(devno);
       }
       // 4.注册
       ret = cdev_add(cdev, MKDEV(major, minor), count);
       if (ret) {
           printk("register char device driver error\n");
           goto ERR3;
       }
       // 5.自动创建设备节点
       cls = class_create(THIS_MODULE, CNAME);
       if (IS_ERR(cls)) {
           printk("class create error\n");
           ret = PTR_ERR(cls);
           goto ERR4;
       }
       for (i = minor; i < count; i++) {
           dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
           if (IS_ERR(dev)) {
               printk("device create error\n");
               ret = PTR_ERR(dev);
               goto ERR5;
           }
       }
   
       return 0;
   
   ERR5:
       for (--i; i >= minor; i--) {
           device_destroy(cls, MKDEV(major, i));
       }
       class_destroy(cls);
   ERR4:
       cdev_del(cdev);
   ERR3:
       unregister_chrdev_region(MKDEV(major, minor), count);
   ERR2:
       kfree(cdev);
   ERR1:
       return ret;
   }
   static void __exit mycdev_exit(void)
   {
       int i = 0;
       for (i = minor; i < count; i++) {
           device_destroy(cls, MKDEV(major, i));
       }
       class_destroy(cls);
       unregister_chrdev_region(MKDEV(major, minor), count);
       kfree(cdev);
   }
   
   module_init(mycdev_init);
   module_exit(mycdev_exit);
   MODULE_LICENSE("GPL");
   

   #include <head.h>
   
   char buf[128] = "12345";
   int main(int argc, const char* argv[])
   {
       int fd;
   
       if ((fd = open("/dev/mycdev0", O_RDWR)) == -1)
           PRINT_ERR("open error");
   
       write(fd, buf, sizeof(buf));
       sleep(7);
       read(fd, buf, sizeof(buf));
       printf("buf=%s\n", buf);
   
       close(fd);
       return 0;
   }
   
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值