当前位置:   article > 正文

Linux 并发与竞争_linux 单核 多线程 并发 问题

linux 单核 多线程 并发 问题

目录

1.并发与竞争

1.1并发与竞争简介

2.原子操作

​编辑 3.自旋锁

4.信号量

 5. 互斥体

6.原子操作实验

 7.自旋锁

8.信号量

9.互斥体

1.并发与竞争

1.1并发与竞争简介

        并发就是多个“用户”同时访问同一个共享资源。Linux 系统是个多任务操作系统,会存在多个任务同时访问同一片内存区域,这些任务可能会相互覆盖这段内存中的数据,造成内存数据混乱。针对这个问题必须要做处理,严重的话可能会导致系统崩溃。现在的 Linux 系统并发产生的原因很复杂,总结一下有下面几个主要原因:

        (1)多线程并发访问,Linux是多任务系统。

        (2)抢占式并发访问,就是说调度程序可以在任意时刻抢占正在运行的线程,从而运行其他的线程。

        (3)中断程序并发访问。

        (4)SMP核间并发访问,多核CPU存在核间并发访问。

        并发访问带来的问题就是竞争。所谓的临界区就是共享数据段,对于临界区必须保证一次只有一个线程访问,也就是要保证临界区是原子访问的。如果多个进程同时操作临界区就表示存在竞争,我们在编写驱动的时候一定要注意避免并发和防止竞争访问。

2.原子操作

        原子操作就是指不能再进一步分割的操作,一般原子操作用于变量或者位操作。Linux 内核定义了叫做 atomic_t 的结构体来完成整形数据的原子操作,在使用中用原子变量来代替整形变量:

  1. typedef struct {
  2. int counter;
  3. } atomic_t;

如果要使用原子操作 API 函数,首先要先定义一个 atomic_t 的变量,如下所示:

atomic_t a; //定义 a

也可以在定义原子变量的时候给原子变量赋初值,如下所示:

  1. atomic_t b = ATOMIC_INIT(0); //定义原子变量 b 并赋初值为 0
  2. #define ATOMIC_INIT(i) { (i) }

        原子变量有了,接下来就是对原子变量进行操作,比如读、写、增加、减少等等, Linux 内
核提供了大量的原子操作 API 函数:

        以上API函数都是操作原子变量中的counter来实现目的的。        

 原子位操作 API 函数:

 3.自旋锁

        原子操作只能对整形变量或者位进行保护,但是,在实际的使用环境中怎么可能只有整形
变量或位这么简单的临界区
。举个最简单的例子,设备结构体变量就不是整型变量,我们对于
结构体中成员变量的操作也要保证原子性,在线程 A 对结构体变量使用期间,应该禁止其他的
线程来访问此结构体变量,这些工作原子操作都不能胜任,需要本节要讲的锁机制,在 Linux
内核中就是自旋锁。

        当一个线程要访问某个共享资源的时候首先要先获取相应的锁, 锁只能被一个线程持有,只要此线程不释放持有的锁,那么其他的线程就不能获取此锁。对于自旋锁而言,如果自旋锁正在被线程 A 持有,线程 B 想要获取自旋锁,那么线程 B 就会处于忙循环-旋转-等待状态,线程 B 不会进入休眠状态或者说去做其他的处理,而是会一直傻傻的在那里“转圈圈”的等待锁可用。

        自旋锁的“自旋”也就是“原地打转”的意思,“原地打转”的目的是为了等待自旋锁可以用,可以访问共享资源。把自旋锁比作一个变量 a,变量 a=1 的时候表示共享资源可用,当 a=0的时候表示共享资源不可用。现在线程 A 要访问共享资源,发现 a=0(自旋锁被其他线程持有),那么线程 A 就会不断的查询 a 的值,直到 a=1。从这里我们可以看到自旋锁的一个缺点:那就等待自旋锁的线程会一直处于自旋状态,这样会浪费处理器时间,降低系统性能,所以自旋锁的持有时间不能太长。所以自旋锁适用于短时期的轻量级加锁,如果遇到需要长时间持有锁的场景那就需要换其他的方法了,这个我们后面会讲解。Linux 内核使用结构体 spinlock_t 表示自旋锁:

  1. typedef struct spinlock {
  2. union {
  3. struct raw_spinlock rlock;
  4. #ifdef CONFIG_DEBUG_LOCK_ALLOC
  5. # define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
  6. struct {
  7. u8 __padding[LOCK_PADSIZE];
  8. struct lockdep_map dep_map;
  9. };
  10. #endif
  11. };
  12. } spinlock_t;

        在使用自旋锁之前,肯定要先定义一个自旋锁变量,定义方法如下所示:

spinlock_t lock; //定义自旋锁

自旋锁的API函数: 

        上述API函数适用于SMP或支持抢占的单CPU下线程之间的并发访问,也就是用于线程与线程之间,被自旋锁保护的临界区一定不能调用任何能够引起睡眠和阻塞的API 函数,否则的话会可能会导致死锁现象的发生。自旋锁会自动禁止抢占,也就说当线程 A得到锁以后会暂时禁止内核抢占。如果线程 A 在持有锁期间进入了休眠状态,那么线程 A 会自动放弃 CPU 使用权。线程 B 开始运行,线程 B 也想要获取锁,但是此时锁被 A 线程持有,而且内核抢占还被禁止了!线程 B 无法被调度出去,那么线程 A 就无法运行,锁也就无法释放,好了,死锁发生了!如果此时中断也要插一脚,中断也想访问共享资源,那该怎么办呢?首先可以肯定的是,中断里面可以使用自旋锁,但是在中断里面使用自旋锁的时候,在获取锁之前一定要先禁止本地中断(也就是本 CPU 中断,对于多核 SOC来说会有多个 CPU 核),否则可能导致锁死现象的发生:

 一般在线程中使用 spin_lock_irqsave/spin_unlock_irqrestore,在中断中使用 spin_lock/spin_unlock,示例代码如下所示:

         下半部(BH)也会竞争共享资源,有些资料也会将下半部叫做底半部

 其他类型的锁:

(1)读写自旋锁

  1. typedef struct {
  2. arch_rwlock_t raw_lock;
  3. #ifdef CONFIG_GENERIC_LOCKBREAK
  4. unsigned int break_lock;
  5. #endif
  6. #ifdef CONFIG_DEBUG_SPINLOCK
  7. unsigned int magic, owner_cpu;
  8. void *owner;
  9. #endif
  10. #ifdef CONFIG_DEBUG_LOCK_ALLOC
  11. struct lockdep_map dep_map;
  12. #endif
  13. } rwlock_t;

(2)顺序锁

  1. typedef struct {
  2. struct seqcount seqcount;
  3. spinlock_t lock;
  4. } seqlock_t;

        自旋锁使用注意事项:

(1)因为在等待自旋锁的时候处于“自旋”状态,因此锁的持有时间不能太长,一定要短,否则的话会降低系统性能。如果临界区比较大,运行时间比较长的话要选择其他的并发处理方式,比如稍后要讲的信号量和互斥体。
(2)自旋锁保护的临界区内不能调用任何可能导致线程休眠的 API 函数,否则的话可能导致死锁。
(3)不能递归申请自旋锁,因为一旦通过递归的方式申请一个你正在持有的锁,那么你就必须“自旋”,等待锁被释放,然而你正处于“自旋”状态,根本没法释放锁。结果就是自己把自己锁死了!
(4)在编写驱动程序的时候我们必须考虑到驱动的可移植性,因此不管你用的是单核的还是多核的 SOC,都将其当做多核 SOC 来编写驱动程序。

4.信号量

        Linux 内核也提供了信号量机制,信号量常常用于控制对共享资源的访问。相比于自旋锁,信号量可以使线程进入休眠状态,比如 A 与 B、 C 合租了一套房子,这个房子只有一个厕所,一次只能一个人使用。某一天早上 A 去上厕所了,过了一会 B 也想用厕所,因为 A 在厕所里面,所以 B 只能等到 A 用来了才能进去。 B 要么就一直在厕所门口等着,等 A 出来,这个时候就相当于自旋锁。 B 也可以告诉 A,让 A 出来以后通知他一下,然后 B 继续回房间睡觉,这个时候相当于信号量。可以看出,使用信号量会提高处理器的使用效率,毕竟不用一直傻乎乎的在那里“自旋”等待。但是,信号量的开销要比自旋锁大,因为信号量使进程进入休眠状态以后会进行进程上下文切换,切换就会有开销。信号量特点:

(1)因为信号量可以使等待资源线程进入休眠状态,因此适用于那些占用资源比较久的场合
(2)因此信号量不能用于中断中,因为信号量会引起休眠,中断不能休眠。
(3)如果共享资源的持有时间比较短,那就不适合使用信号量了,因为频繁的休眠、切换线程引起的开销要远大于信号量带来的那点优势。

        Linux 内核使用 semaphore 结构体表示信号量,结构体内容如下所示:

  1. struct semaphore {
  2. raw_spinlock_t lock;
  3. unsigned int count;
  4. struct list_head wait_list;
  5. };

要想使用信号量就得先定义,然后初始化信号量。有关信号量的 API 函数如下:

  1. #define DEFINE_SEMAPHORE(name) \
  2. struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
  3. static inline void sema_init(struct semaphore *sem, int val)
  4. {
  5. static struct lock_class_key __key;
  6. *sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
  7. lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);
  8. }
  9. void down(struct semaphore *sem)
  10. {
  11. unsigned long flags;
  12. raw_spin_lock_irqsave(&sem->lock, flags);
  13. if (likely(sem->count > 0))
  14. sem->count--;
  15. else
  16. __down(sem);
  17. raw_spin_unlock_irqrestore(&sem->lock, flags);
  18. }
  19. int down_trylock(struct semaphore *sem)
  20. {
  21. unsigned long flags;
  22. int count;
  23. raw_spin_lock_irqsave(&sem->lock, flags);
  24. count = sem->count - 1;
  25. if (likely(count >= 0))
  26. sem->count = count;
  27. raw_spin_unlock_irqrestore(&sem->lock, flags);
  28. return (count < 0);
  29. }
  30. int down_interruptible(struct semaphore *sem)
  31. {
  32. unsigned long flags;
  33. int result = 0;
  34. raw_spin_lock_irqsave(&sem->lock, flags);
  35. if (likely(sem->count > 0))
  36. sem->count--;
  37. else
  38. result = __down_interruptible(sem);
  39. raw_spin_unlock_irqrestore(&sem->lock, flags);
  40. return result;
  41. }
  42. void up(struct semaphore *sem)
  43. {
  44. unsigned long flags;
  45. raw_spin_lock_irqsave(&sem->lock, flags);
  46. if (likely(list_empty(&sem->wait_list)))
  47. sem->count++;
  48. else
  49. __up(sem);
  50. raw_spin_unlock_irqrestore(&sem->lock, flags);
  51. }
  52. EXPORT_SYMBOL(up);

其中__down和__down_interruptible的区别:

  1. static noinline void __sched __down(struct semaphore *sem)
  2. {
  3. __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
  4. }
  5. static noinline int __sched __down_interruptible(struct semaphore *sem)
  6. {
  7. return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
  8. }

信号量的使用如下所示:

 5. 互斥体

         将信号量的值设置为 1 就可以使用信号量进行互斥访问了,虽然可以通过信号量实现互斥,但是 Linux 提供了一个比信号量更专业的机制来进行互斥,它就是互斥体—mutex。互斥访问表示一次只有一个线程可以访问共享资源,不能递归申请互斥体。Linux 内核使用 mutex 结构体表示互斥体,定义如下:

  1. struct mutex {
  2. /* 1: unlocked, 0: locked, negative: locked, possible waiters */
  3. atomic_t count;
  4. spinlock_t wait_lock;
  5. struct list_head wait_list;
  6. #if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER)
  7. struct task_struct *owner;
  8. #endif
  9. #ifdef CONFIG_MUTEX_SPIN_ON_OWNER
  10. struct optimistic_spin_queue osq; /* Spinner MCS lock */
  11. #endif
  12. #ifdef CONFIG_DEBUG_MUTEXES
  13. void *magic;
  14. #endif
  15. #ifdef CONFIG_DEBUG_LOCK_ALLOC
  16. struct lockdep_map dep_map;
  17. #endif
  18. };

在使用 mutex 的时候要注意如下几点:
①、 mutex 可以导致休眠,因此不能在中断中使用 mutex,中断中只能使用自旋锁。
②、和信号量一样, mutex 保护的临界区可以调用引起阻塞的 API 函数
③、因为一次只有一个线程可以持有 mutex,因此,必须由 mutex 的持有者释放 mutex。并
且 mutex 不能递归上锁和解锁。

有关互斥体的 API 函数如下:

  1. #define DEFINE_MUTEX(mutexname) \
  2. struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
  3. void
  4. __mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)
  5. {
  6. atomic_set(&lock->count, 1);
  7. spin_lock_init(&lock->wait_lock);
  8. INIT_LIST_HEAD(&lock->wait_list);
  9. mutex_clear_owner(lock);
  10. #ifdef CONFIG_MUTEX_SPIN_ON_OWNER
  11. osq_lock_init(&lock->osq);
  12. #endif
  13. debug_mutex_init(lock, name, key);
  14. }
  15. void __sched mutex_lock(struct mutex *lock)
  16. {
  17. might_sleep();
  18. /*
  19. * The locking fastpath is the 1->0 transition from
  20. * 'unlocked' into 'locked' state.
  21. */
  22. __mutex_fastpath_lock(&lock->count, __mutex_lock_slowpath);
  23. mutex_set_owner(lock);
  24. }
  25. void __sched mutex_unlock(struct mutex *lock)
  26. {
  27. /*
  28. * The unlocking fastpath is the 0->1 transition from 'locked'
  29. * into 'unlocked' state:
  30. */
  31. #ifndef CONFIG_DEBUG_MUTEXES
  32. /*
  33. * When debugging is enabled we must not clear the owner before time,
  34. * the slow path will always be taken, and that clears the owner field
  35. * after verifying that it was indeed current.
  36. */
  37. mutex_clear_owner(lock);
  38. #endif
  39. __mutex_fastpath_unlock(&lock->count, __mutex_unlock_slowpath);
  40. }
  41. int __sched mutex_trylock(struct mutex *lock)
  42. {
  43. int ret;
  44. ret = __mutex_fastpath_trylock(&lock->count, __mutex_trylock_slowpath);
  45. if (ret)
  46. mutex_set_owner(lock);
  47. return ret;
  48. }
  49. static inline int mutex_is_locked(struct mutex *lock)
  50. {
  51. return atomic_read(&lock->count) != 1;
  52. }
  53. int __sched mutex_lock_interruptible(struct mutex *lock)
  54. {
  55. int ret;
  56. might_sleep();
  57. ret = __mutex_fastpath_lock_retval(&lock->count);
  58. if (likely(!ret)) {
  59. mutex_set_owner(lock);
  60. return 0;
  61. } else
  62. return __mutex_lock_interruptible_slowpath(lock);
  63. }

互斥体的使用如下所示:

6.原子操作实验

        本节实验重点就是使用atomic 来实现一次只能允许一个应用访问 LED。

  1. #include <linux/types.h>
  2. #include <linux/kernel.h>
  3. #include <linux/delay.h>
  4. #include <linux/ide.h>
  5. #include <linux/init.h>
  6. #include <linux/module.h>
  7. #include <linux/errno.h>
  8. #include <linux/gpio.h>
  9. #include <linux/cdev.h>
  10. #include <linux/device.h>
  11. #include <linux/of.h>
  12. #include <linux/of_address.h>
  13. #include <linux/of_gpio.h>
  14. #include <asm/mach/map.h>
  15. #include <asm/uaccess.h>
  16. #include <asm/io.h>
  17. #define GPIOLED_CNT 1 /* 设备号个数 */
  18. #define GPIOLED_NAME "gpioled" /* 名字 */
  19. #define LEDOFF 0 /* 关灯 */
  20. #define LEDON 1 /* 开灯 */
  21. /* gpioled设备结构体 */
  22. struct gpioled_dev{
  23. dev_t devid; /* 设备号 */
  24. struct cdev cdev; /* cdev */
  25. struct class *class; /* 类 */
  26. struct device *device; /* 设备 */
  27. int major; /* 主设备号 */
  28. int minor; /* 次设备号 */
  29. struct device_node *nd; /* 设备节点 */
  30. int led_gpio; /* led所使用的GPIO编号 */
  31. atomic_t lock; /* 原子变量 */
  32. };
  33. struct gpioled_dev gpioled; /* led设备 */
  34. /*
  35. * @description : 打开设备
  36. * @param - inode : 传递给驱动的inode
  37. * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
  38. * 一般在open的时候将private_data指向设备结构体。
  39. * @return : 0 成功;其他 失败
  40. */
  41. static int led_open(struct inode *inode, struct file *filp)
  42. {
  43. /* 通过判断原子变量的值来检查LED有没有被别的应用使用 */
  44. if (!atomic_dec_and_test(&gpioled.lock)) {
  45. atomic_inc(&gpioled.lock); /* 小于0的话就加1,使其原子变量等于0 */
  46. return -EBUSY; /* LED被使用,返回忙 */
  47. }
  48. filp->private_data = &gpioled; /* 设置私有数据 */
  49. return 0;
  50. }
  51. /*
  52. * @description : 从设备读取数据
  53. * @param - filp : 要打开的设备文件(文件描述符)
  54. * @param - buf : 返回给用户空间的数据缓冲区
  55. * @param - cnt : 要读取的数据长度
  56. * @param - offt : 相对于文件首地址的偏移
  57. * @return : 读取的字节数,如果为负值,表示读取失败
  58. */
  59. static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
  60. {
  61. return 0;
  62. }
  63. /*
  64. * @description : 向设备写数据
  65. * @param - filp : 设备文件,表示打开的文件描述符
  66. * @param - buf : 要写给设备写入的数据
  67. * @param - cnt : 要写入的数据长度
  68. * @param - offt : 相对于文件首地址的偏移
  69. * @return : 写入的字节数,如果为负值,表示写入失败
  70. */
  71. static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
  72. {
  73. int retvalue;
  74. unsigned char databuf[1];
  75. unsigned char ledstat;
  76. struct gpioled_dev *dev = filp->private_data;
  77. retvalue = copy_from_user(databuf, buf, cnt);
  78. if(retvalue < 0) {
  79. printk("kernel write failed!\r\n");
  80. return -EFAULT;
  81. }
  82. ledstat = databuf[0]; /* 获取状态值 */
  83. if(ledstat == LEDON) {
  84. gpio_set_value(dev->led_gpio, 0); /* 打开LED灯 */
  85. } else if(ledstat == LEDOFF) {
  86. gpio_set_value(dev->led_gpio, 1); /* 关闭LED灯 */
  87. }
  88. return 0;
  89. }
  90. /*
  91. * @description : 关闭/释放设备
  92. * @param - filp : 要关闭的设备文件(文件描述符)
  93. * @return : 0 成功;其他 失败
  94. */
  95. static int led_release(struct inode *inode, struct file *filp)
  96. {
  97. struct gpioled_dev *dev = filp->private_data;
  98. /* 关闭驱动文件的时候释放原子变量 */
  99. atomic_inc(&dev->lock);
  100. return 0;
  101. }
  102. /* 设备操作函数 */
  103. static struct file_operations gpioled_fops = {
  104. .owner = THIS_MODULE,
  105. .open = led_open,
  106. .read = led_read,
  107. .write = led_write,
  108. .release = led_release,
  109. };
  110. /*
  111. * @description : 驱动入口函数
  112. * @param : 无
  113. * @return : 无
  114. */
  115. static int __init led_init(void)
  116. {
  117. int ret = 0;
  118. /* 初始化原子变量 */
  119. atomic_set(&gpioled.lock, 1); /* 原子变量初始值为1 */
  120. /* 设置LED所使用的GPIO */
  121. /* 1、获取设备节点:gpioled */
  122. gpioled.nd = of_find_node_by_path("/gpioled");
  123. if(gpioled.nd == NULL) {
  124. printk("gpioled node not find!\r\n");
  125. return -EINVAL;
  126. } else {
  127. printk("gpioled node find!\r\n");
  128. }
  129. /* 2、 获取设备树中的gpio属性,得到LED所使用的LED编号 */
  130. gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
  131. if(gpioled.led_gpio < 0) {
  132. printk("can't get led-gpio");
  133. return -EINVAL;
  134. }
  135. printk("led-gpio num = %d\r\n", gpioled.led_gpio);
  136. /* 3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */
  137. ret = gpio_direction_output(gpioled.led_gpio, 1);
  138. if(ret < 0) {
  139. printk("can't set gpio!\r\n");
  140. }
  141. /* 注册字符设备驱动 */
  142. /* 1、创建设备号 */
  143. if (gpioled.major) { /* 定义了设备号 */
  144. gpioled.devid = MKDEV(gpioled.major, 0);
  145. register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
  146. } else { /* 没有定义设备号 */
  147. alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申请设备号 */
  148. gpioled.major = MAJOR(gpioled.devid); /* 获取分配号的主设备号 */
  149. gpioled.minor = MINOR(gpioled.devid); /* 获取分配号的次设备号 */
  150. }
  151. printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor);
  152. /* 2、初始化cdev */
  153. gpioled.cdev.owner = THIS_MODULE;
  154. cdev_init(&gpioled.cdev, &gpioled_fops);
  155. /* 3、添加一个cdev */
  156. cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
  157. /* 4、创建类 */
  158. gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
  159. if (IS_ERR(gpioled.class)) {
  160. return PTR_ERR(gpioled.class);
  161. }
  162. /* 5、创建设备 */
  163. gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
  164. if (IS_ERR(gpioled.device)) {
  165. return PTR_ERR(gpioled.device);
  166. }
  167. return 0;
  168. }
  169. /*
  170. * @description : 驱动出口函数
  171. * @param : 无
  172. * @return : 无
  173. */
  174. static void __exit led_exit(void)
  175. {
  176. /* 注销字符设备驱动 */
  177. cdev_del(&gpioled.cdev);/* 删除cdev */
  178. unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注销设备号 */
  179. device_destroy(gpioled.class, gpioled.devid);
  180. class_destroy(gpioled.class);
  181. }
  182. module_init(led_init);
  183. module_exit(led_exit);
  184. MODULE_LICENSE("GPL");
  185. MODULE_AUTHOR("wrw");

 7.自旋锁

  1. #include <linux/types.h>
  2. #include <linux/kernel.h>
  3. #include <linux/delay.h>
  4. #include <linux/ide.h>
  5. #include <linux/init.h>
  6. #include <linux/module.h>
  7. #include <linux/errno.h>
  8. #include <linux/gpio.h>
  9. #include <linux/cdev.h>
  10. #include <linux/device.h>
  11. #include <linux/of.h>
  12. #include <linux/of_address.h>
  13. #include <linux/of_gpio.h>
  14. #include <asm/mach/map.h>
  15. #include <asm/uaccess.h>
  16. #include <asm/io.h>
  17. #define GPIOLED_CNT 1 /* 设备号个数 */
  18. #define GPIOLED_NAME "gpioled" /* 名字 */
  19. #define LEDOFF 0 /* 关灯 */
  20. #define LEDON 1 /* 开灯 */
  21. /* gpioled设备结构体 */
  22. struct gpioled_dev{
  23. dev_t devid; /* 设备号 */
  24. struct cdev cdev; /* cdev */
  25. struct class *class; /* 类 */
  26. struct device *device; /* 设备 */
  27. int major; /* 主设备号 */
  28. int minor; /* 次设备号 */
  29. struct device_node *nd; /* 设备节点 */
  30. int led_gpio; /* led所使用的GPIO编号 */
  31. int dev_stats; /* 设备使用状态,0,设备未使用;>0,设备已经被使用 */
  32. spinlock_t lock; /* 自旋锁 */
  33. };
  34. struct gpioled_dev gpioled; /* led设备 */
  35. /*
  36. * @description : 打开设备
  37. * @param - inode : 传递给驱动的inode
  38. * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
  39. * 一般在open的时候将private_data指向设备结构体。
  40. * @return : 0 成功;其他 失败
  41. */
  42. static int led_open(struct inode *inode, struct file *filp)
  43. {
  44. unsigned long flags;
  45. filp->private_data = &gpioled; /* 设置私有数据 */
  46. spin_lock_irqsave(&gpioled.lock, flags); /* 上锁 */
  47. if (gpioled.dev_stats) { /* 如果设备被使用了 */
  48. spin_unlock_irqrestore(&gpioled.lock, flags);/* 解锁 */
  49. return -EBUSY;
  50. }
  51. gpioled.dev_stats++; /* 如果设备没有打开,那么就标记已经打开了 */
  52. spin_unlock_irqrestore(&gpioled.lock, flags);/* 解锁 */
  53. return 0;
  54. }
  55. /*
  56. * @description : 从设备读取数据
  57. * @param - filp : 要打开的设备文件(文件描述符)
  58. * @param - buf : 返回给用户空间的数据缓冲区
  59. * @param - cnt : 要读取的数据长度
  60. * @param - offt : 相对于文件首地址的偏移
  61. * @return : 读取的字节数,如果为负值,表示读取失败
  62. */
  63. static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
  64. {
  65. return 0;
  66. }
  67. /*
  68. * @description : 向设备写数据
  69. * @param - filp : 设备文件,表示打开的文件描述符
  70. * @param - buf : 要写给设备写入的数据
  71. * @param - cnt : 要写入的数据长度
  72. * @param - offt : 相对于文件首地址的偏移
  73. * @return : 写入的字节数,如果为负值,表示写入失败
  74. */
  75. static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
  76. {
  77. int retvalue;
  78. unsigned char databuf[1];
  79. unsigned char ledstat;
  80. struct gpioled_dev *dev = filp->private_data;
  81. retvalue = copy_from_user(databuf, buf, cnt);
  82. if(retvalue < 0) {
  83. printk("kernel write failed!\r\n");
  84. return -EFAULT;
  85. }
  86. ledstat = databuf[0]; /* 获取状态值 */
  87. if(ledstat == LEDON) {
  88. gpio_set_value(dev->led_gpio, 0); /* 打开LED灯 */
  89. } else if(ledstat == LEDOFF) {
  90. gpio_set_value(dev->led_gpio, 1); /* 关闭LED灯 */
  91. }
  92. return 0;
  93. }
  94. /*
  95. * @description : 关闭/释放设备
  96. * @param - filp : 要关闭的设备文件(文件描述符)
  97. * @return : 0 成功;其他 失败
  98. */
  99. static int led_release(struct inode *inode, struct file *filp)
  100. {
  101. unsigned long flags;
  102. struct gpioled_dev *dev = filp->private_data;
  103. /* 关闭驱动文件的时候将dev_stats减1 */
  104. spin_lock_irqsave(&dev->lock, flags); /* 上锁 */
  105. if (dev->dev_stats) {
  106. dev->dev_stats--;
  107. }
  108. spin_unlock_irqrestore(&dev->lock, flags);/* 解锁 */
  109. return 0;
  110. }
  111. /* 设备操作函数 */
  112. static struct file_operations gpioled_fops = {
  113. .owner = THIS_MODULE,
  114. .open = led_open,
  115. .read = led_read,
  116. .write = led_write,
  117. .release = led_release,
  118. };
  119. /*
  120. * @description : 驱动入口函数
  121. * @param : 无
  122. * @return : 无
  123. */
  124. static int __init led_init(void)
  125. {
  126. int ret = 0;
  127. /* 初始化自旋锁 */
  128. spin_lock_init(&gpioled.lock);
  129. /* 设置LED所使用的GPIO */
  130. /* 1、获取设备节点:gpioled */
  131. gpioled.nd = of_find_node_by_path("/gpioled");
  132. if(gpioled.nd == NULL) {
  133. printk("gpioled node not find!\r\n");
  134. return -EINVAL;
  135. } else {
  136. printk("gpioled node find!\r\n");
  137. }
  138. /* 2、 获取设备树中的gpio属性,得到LED所使用的LED编号 */
  139. gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
  140. if(gpioled.led_gpio < 0) {
  141. printk("can't get led-gpio");
  142. return -EINVAL;
  143. }
  144. printk("led-gpio num = %d\r\n", gpioled.led_gpio);
  145. /* 3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */
  146. ret = gpio_direction_output(gpioled.led_gpio, 1);
  147. if(ret < 0) {
  148. printk("can't set gpio!\r\n");
  149. }
  150. /* 注册字符设备驱动 */
  151. /* 1、创建设备号 */
  152. if (gpioled.major) { /* 定义了设备号 */
  153. gpioled.devid = MKDEV(gpioled.major, 0);
  154. register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
  155. } else { /* 没有定义设备号 */
  156. alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申请设备号 */
  157. gpioled.major = MAJOR(gpioled.devid); /* 获取分配号的主设备号 */
  158. gpioled.minor = MINOR(gpioled.devid); /* 获取分配号的次设备号 */
  159. }
  160. printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor);
  161. /* 2、初始化cdev */
  162. gpioled.cdev.owner = THIS_MODULE;
  163. cdev_init(&gpioled.cdev, &gpioled_fops);
  164. /* 3、添加一个cdev */
  165. cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
  166. /* 4、创建类 */
  167. gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
  168. if (IS_ERR(gpioled.class)) {
  169. return PTR_ERR(gpioled.class);
  170. }
  171. /* 5、创建设备 */
  172. gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
  173. if (IS_ERR(gpioled.device)) {
  174. return PTR_ERR(gpioled.device);
  175. }
  176. return 0;
  177. }
  178. /*
  179. * @description : 驱动出口函数
  180. * @param : 无
  181. * @return : 无
  182. */
  183. static void __exit led_exit(void)
  184. {
  185. /* 注销字符设备驱动 */
  186. cdev_del(&gpioled.cdev);/* 删除cdev */
  187. unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注销设备号 */
  188. device_destroy(gpioled.class, gpioled.devid);
  189. class_destroy(gpioled.class);
  190. }
  191. module_init(led_init);
  192. module_exit(led_exit);
  193. MODULE_LICENSE("GPL");
  194. MODULE_AUTHOR("WRW");

8.信号量

  1. #include <linux/types.h>
  2. #include <linux/kernel.h>
  3. #include <linux/delay.h>
  4. #include <linux/ide.h>
  5. #include <linux/init.h>
  6. #include <linux/module.h>
  7. #include <linux/errno.h>
  8. #include <linux/gpio.h>
  9. #include <linux/cdev.h>
  10. #include <linux/device.h>
  11. #include <linux/of.h>
  12. #include <linux/of_address.h>
  13. #include <linux/of_gpio.h>
  14. #include <linux/semaphore.h>
  15. #include <asm/mach/map.h>
  16. #include <asm/uaccess.h>
  17. #include <asm/io.h>
  18. #define GPIOLED_CNT 1 /* 设备号个数 */
  19. #define GPIOLED_NAME "gpioled" /* 名字 */
  20. #define LEDOFF 0 /* 关灯 */
  21. #define LEDON 1 /* 开灯 */
  22. /* gpioled设备结构体 */
  23. struct gpioled_dev{
  24. dev_t devid; /* 设备号 */
  25. struct cdev cdev; /* cdev */
  26. struct class *class; /* 类 */
  27. struct device *device; /* 设备 */
  28. int major; /* 主设备号 */
  29. int minor; /* 次设备号 */
  30. struct device_node *nd; /* 设备节点 */
  31. int led_gpio; /* led所使用的GPIO编号 */
  32. struct semaphore sem; /* 信号量 */
  33. };
  34. struct gpioled_dev gpioled; /* led设备 */
  35. /*
  36. * @description : 打开设备
  37. * @param - inode : 传递给驱动的inode
  38. * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
  39. * 一般在open的时候将private_data指向设备结构体。
  40. * @return : 0 成功;其他 失败
  41. */
  42. static int led_open(struct inode *inode, struct file *filp)
  43. {
  44. filp->private_data = &gpioled; /* 设置私有数据 */
  45. /* 获取信号量 */
  46. if (down_interruptible(&gpioled.sem)) { /* 获取信号量,进入休眠状态的进程可以被信号打断 */
  47. return -ERESTARTSYS;
  48. }
  49. #if 0
  50. down(&gpioled.sem); /* 不能被信号打断 */
  51. #endif
  52. return 0;
  53. }
  54. /*
  55. * @description : 从设备读取数据
  56. * @param - filp : 要打开的设备文件(文件描述符)
  57. * @param - buf : 返回给用户空间的数据缓冲区
  58. * @param - cnt : 要读取的数据长度
  59. * @param - offt : 相对于文件首地址的偏移
  60. * @return : 读取的字节数,如果为负值,表示读取失败
  61. */
  62. static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
  63. {
  64. return 0;
  65. }
  66. /*
  67. * @description : 向设备写数据
  68. * @param - filp : 设备文件,表示打开的文件描述符
  69. * @param - buf : 要写给设备写入的数据
  70. * @param - cnt : 要写入的数据长度
  71. * @param - offt : 相对于文件首地址的偏移
  72. * @return : 写入的字节数,如果为负值,表示写入失败
  73. */
  74. static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
  75. {
  76. int retvalue;
  77. unsigned char databuf[1];
  78. unsigned char ledstat;
  79. struct gpioled_dev *dev = filp->private_data;
  80. retvalue = copy_from_user(databuf, buf, cnt);
  81. if(retvalue < 0) {
  82. printk("kernel write failed!\r\n");
  83. return -EFAULT;
  84. }
  85. ledstat = databuf[0]; /* 获取状态值 */
  86. if(ledstat == LEDON) {
  87. gpio_set_value(dev->led_gpio, 0); /* 打开LED灯 */
  88. } else if(ledstat == LEDOFF) {
  89. gpio_set_value(dev->led_gpio, 1); /* 关闭LED灯 */
  90. }
  91. return 0;
  92. }
  93. /*
  94. * @description : 关闭/释放设备
  95. * @param - filp : 要关闭的设备文件(文件描述符)
  96. * @return : 0 成功;其他 失败
  97. */
  98. static int led_release(struct inode *inode, struct file *filp)
  99. {
  100. struct gpioled_dev *dev = filp->private_data;
  101. up(&dev->sem); /* 释放信号量,信号量值加1 */
  102. return 0;
  103. }
  104. /* 设备操作函数 */
  105. static struct file_operations gpioled_fops = {
  106. .owner = THIS_MODULE,
  107. .open = led_open,
  108. .read = led_read,
  109. .write = led_write,
  110. .release = led_release,
  111. };
  112. /*
  113. * @description : 驱动入口函数
  114. * @param : 无
  115. * @return : 无
  116. */
  117. static int __init led_init(void)
  118. {
  119. int ret = 0;
  120. /* 初始化信号量 */
  121. sema_init(&gpioled.sem, 1);
  122. /* 设置LED所使用的GPIO */
  123. /* 1、获取设备节点:gpioled */
  124. gpioled.nd = of_find_node_by_path("/gpioled");
  125. if(gpioled.nd == NULL) {
  126. printk("gpioled node not find!\r\n");
  127. return -EINVAL;
  128. } else {
  129. printk("gpioled node find!\r\n");
  130. }
  131. /* 2、 获取设备树中的gpio属性,得到LED所使用的LED编号 */
  132. gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
  133. if(gpioled.led_gpio < 0) {
  134. printk("can't get led-gpio");
  135. return -EINVAL;
  136. }
  137. printk("led-gpio num = %d\r\n", gpioled.led_gpio);
  138. /* 3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */
  139. ret = gpio_direction_output(gpioled.led_gpio, 1);
  140. if(ret < 0) {
  141. printk("can't set gpio!\r\n");
  142. }
  143. /* 注册字符设备驱动 */
  144. /* 1、创建设备号 */
  145. if (gpioled.major) { /* 定义了设备号 */
  146. gpioled.devid = MKDEV(gpioled.major, 0);
  147. register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
  148. } else { /* 没有定义设备号 */
  149. alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申请设备号 */
  150. gpioled.major = MAJOR(gpioled.devid); /* 获取分配号的主设备号 */
  151. gpioled.minor = MINOR(gpioled.devid); /* 获取分配号的次设备号 */
  152. }
  153. printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor);
  154. /* 2、初始化cdev */
  155. gpioled.cdev.owner = THIS_MODULE;
  156. cdev_init(&gpioled.cdev, &gpioled_fops);
  157. /* 3、添加一个cdev */
  158. cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
  159. /* 4、创建类 */
  160. gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
  161. if (IS_ERR(gpioled.class)) {
  162. return PTR_ERR(gpioled.class);
  163. }
  164. /* 5、创建设备 */
  165. gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
  166. if (IS_ERR(gpioled.device)) {
  167. return PTR_ERR(gpioled.device);
  168. }
  169. return 0;
  170. }
  171. /*
  172. * @description : 驱动出口函数
  173. * @param : 无
  174. * @return : 无
  175. */
  176. static void __exit led_exit(void)
  177. {
  178. /* 注销字符设备驱动 */
  179. cdev_del(&gpioled.cdev);/* 删除cdev */
  180. unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注销设备号 */
  181. device_destroy(gpioled.class, gpioled.devid);
  182. class_destroy(gpioled.class);
  183. }
  184. module_init(led_init);
  185. module_exit(led_exit);
  186. MODULE_LICENSE("GPL");
  187. MODULE_AUTHOR("wrw");

9.互斥体

  1. #include <linux/types.h>
  2. #include <linux/kernel.h>
  3. #include <linux/delay.h>
  4. #include <linux/ide.h>
  5. #include <linux/init.h>
  6. #include <linux/module.h>
  7. #include <linux/errno.h>
  8. #include <linux/gpio.h>
  9. #include <linux/cdev.h>
  10. #include <linux/device.h>
  11. #include <linux/of.h>
  12. #include <linux/of_address.h>
  13. #include <linux/of_gpio.h>
  14. #include <linux/semaphore.h>
  15. #include <asm/mach/map.h>
  16. #include <asm/uaccess.h>
  17. #include <asm/io.h>
  18. #define GPIOLED_CNT 1 /* 设备号个数 */
  19. #define GPIOLED_NAME "gpioled" /* 名字 */
  20. #define LEDOFF 0 /* 关灯 */
  21. #define LEDON 1 /* 开灯 */
  22. /* gpioled设备结构体 */
  23. struct gpioled_dev{
  24. dev_t devid; /* 设备号 */
  25. struct cdev cdev; /* cdev */
  26. struct class *class; /* 类 */
  27. struct device *device; /* 设备 */
  28. int major; /* 主设备号 */
  29. int minor; /* 次设备号 */
  30. struct device_node *nd; /* 设备节点 */
  31. int led_gpio; /* led所使用的GPIO编号 */
  32. struct mutex lock; /* 互斥体 */
  33. };
  34. struct gpioled_dev gpioled; /* led设备 */
  35. /*
  36. * @description : 打开设备
  37. * @param - inode : 传递给驱动的inode
  38. * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
  39. * 一般在open的时候将private_data指向设备结构体。
  40. * @return : 0 成功;其他 失败
  41. */
  42. static int led_open(struct inode *inode, struct file *filp)
  43. {
  44. filp->private_data = &gpioled; /* 设置私有数据 */
  45. /* 获取互斥体,可以被信号打断 */
  46. if (mutex_lock_interruptible(&gpioled.lock)) {
  47. return -ERESTARTSYS;
  48. }
  49. #if 0
  50. mutex_lock(&gpioled.lock); /* 不能被信号打断 */
  51. #endif
  52. return 0;
  53. }
  54. /*
  55. * @description : 从设备读取数据
  56. * @param - filp : 要打开的设备文件(文件描述符)
  57. * @param - buf : 返回给用户空间的数据缓冲区
  58. * @param - cnt : 要读取的数据长度
  59. * @param - offt : 相对于文件首地址的偏移
  60. * @return : 读取的字节数,如果为负值,表示读取失败
  61. */
  62. static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
  63. {
  64. return 0;
  65. }
  66. /*
  67. * @description : 向设备写数据
  68. * @param - filp : 设备文件,表示打开的文件描述符
  69. * @param - buf : 要写给设备写入的数据
  70. * @param - cnt : 要写入的数据长度
  71. * @param - offt : 相对于文件首地址的偏移
  72. * @return : 写入的字节数,如果为负值,表示写入失败
  73. */
  74. static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
  75. {
  76. int retvalue;
  77. unsigned char databuf[1];
  78. unsigned char ledstat;
  79. struct gpioled_dev *dev = filp->private_data;
  80. retvalue = copy_from_user(databuf, buf, cnt);
  81. if(retvalue < 0) {
  82. printk("kernel write failed!\r\n");
  83. return -EFAULT;
  84. }
  85. ledstat = databuf[0]; /* 获取状态值 */
  86. if(ledstat == LEDON) {
  87. gpio_set_value(dev->led_gpio, 0); /* 打开LED灯 */
  88. } else if(ledstat == LEDOFF) {
  89. gpio_set_value(dev->led_gpio, 1); /* 关闭LED灯 */
  90. }
  91. return 0;
  92. }
  93. /*
  94. * @description : 关闭/释放设备
  95. * @param - filp : 要关闭的设备文件(文件描述符)
  96. * @return : 0 成功;其他 失败
  97. */
  98. static int led_release(struct inode *inode, struct file *filp)
  99. {
  100. struct gpioled_dev *dev = filp->private_data;
  101. /* 释放互斥锁 */
  102. mutex_unlock(&dev->lock);
  103. return 0;
  104. }
  105. /* 设备操作函数 */
  106. static struct file_operations gpioled_fops = {
  107. .owner = THIS_MODULE,
  108. .open = led_open,
  109. .read = led_read,
  110. .write = led_write,
  111. .release = led_release,
  112. };
  113. /*
  114. * @description : 驱动入口函数
  115. * @param : 无
  116. * @return : 无
  117. */
  118. static int __init led_init(void)
  119. {
  120. int ret = 0;
  121. /* 初始化互斥体 */
  122. mutex_init(&gpioled.lock);
  123. /* 设置LED所使用的GPIO */
  124. /* 1、获取设备节点:gpioled */
  125. gpioled.nd = of_find_node_by_path("/gpioled");
  126. if(gpioled.nd == NULL) {
  127. printk("gpioled node not find!\r\n");
  128. return -EINVAL;
  129. } else {
  130. printk("gpioled node find!\r\n");
  131. }
  132. /* 2、 获取设备树中的gpio属性,得到LED所使用的LED编号 */
  133. gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
  134. if(gpioled.led_gpio < 0) {
  135. printk("can't get led-gpio");
  136. return -EINVAL;
  137. }
  138. printk("led-gpio num = %d\r\n", gpioled.led_gpio);
  139. /* 3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */
  140. ret = gpio_direction_output(gpioled.led_gpio, 1);
  141. if(ret < 0) {
  142. printk("can't set gpio!\r\n");
  143. }
  144. /* 注册字符设备驱动 */
  145. /* 1、创建设备号 */
  146. if (gpioled.major) { /* 定义了设备号 */
  147. gpioled.devid = MKDEV(gpioled.major, 0);
  148. register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
  149. } else { /* 没有定义设备号 */
  150. alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申请设备号 */
  151. gpioled.major = MAJOR(gpioled.devid); /* 获取分配号的主设备号 */
  152. gpioled.minor = MINOR(gpioled.devid); /* 获取分配号的次设备号 */
  153. }
  154. printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor);
  155. /* 2、初始化cdev */
  156. gpioled.cdev.owner = THIS_MODULE;
  157. cdev_init(&gpioled.cdev, &gpioled_fops);
  158. /* 3、添加一个cdev */
  159. cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
  160. /* 4、创建类 */
  161. gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
  162. if (IS_ERR(gpioled.class)) {
  163. return PTR_ERR(gpioled.class);
  164. }
  165. /* 5、创建设备 */
  166. gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
  167. if (IS_ERR(gpioled.device)) {
  168. return PTR_ERR(gpioled.device);
  169. }
  170. return 0;
  171. }
  172. /*
  173. * @description : 驱动出口函数
  174. * @param : 无
  175. * @return : 无
  176. */
  177. static void __exit led_exit(void)
  178. {
  179. /* 注销字符设备驱动 */
  180. cdev_del(&gpioled.cdev);/* 删除cdev */
  181. unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注销设备号 */
  182. device_destroy(gpioled.class, gpioled.devid);
  183. class_destroy(gpioled.class);
  184. }
  185. module_init(led_init);
  186. module_exit(led_exit);
  187. MODULE_LICENSE("GPL");
  188. MODULE_AUTHOR("wrw");

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号