本章实验由第六章“设备树led驱动”代码修改而来,设备树不需要改动。
文章目录- 一、原子操作
- 1.驱动程序
- 2.测试
- 二、自旋锁实验
- 1.驱动程序
- 2.测试
- 三、信号量
- 1.驱动程序
- 2.测试
- 四、互斥体实验
- 1.驱动程序
- 2.测试
- 五、总结
一、原子操作 1.驱动程序
实现一次只能操作一个led,当这个驱动正在操作这个led时,其他驱动不可访问这个led。
思路:在设备结构体定义一个原子整形变量,并在入口函数初始化为1,在使用open打开驱动的时候,先检查原子变量的值是否为1,是的话减一并设置私有数据,驱动正常运行。如果不是1,代表驱动在被占用,返回APP文件,打开驱动失败。在关闭设备函数release里要释放原子变量,加一。
以下程序修改自gpioled.c,撷取部分。
struct gpioled_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd;
int led_gpio;
atomic_t num;
};
struct gpioled_dev gpioled;
static int led_open(struct inode *inode, struct file *filp)
{
if(atomic_dec_and_test(&gpioled.num)){
filp->private_data = &gpioled;
return 0;
}
else{
atomic_inc(&gpioled.num);
return -1;
}
}
static int led_release(struct inode *inode, struct file *filp)
{
struct gpioled_dev *dev = filp->private_data;
atomic_inc(&dev->num);
return 0;
}
static int __init led_init(void)
{
atomic_set(&gpioled.num, 1);
return 0;
}
2.测试
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename;
unsigned char databuf[1];
filename = argv[1];
fd = open(filename, O_RDWR);
databuf[0] = atoi(argv[2]);
retvalue = write(fd, databuf, sizeof(databuf));
while(1){
sleep(5);//延迟5s
cnt++;
printf("App running times:%drn", cnt);
if(cnt >= 5) break; //5次 25s
}
retvalue = close(fd);
return 0;
}
在驱动程序卡在while时,还没close,原子变量还是0,再次执行APP,驱动无法打开。
执行ledApp时,输入&,可在后台运行程序,输入框仍可以用。
./ledApp /dev/led 1&二、自旋锁实验 1.驱动程序
自旋锁保护的临界区尽可能短,因此在open函数加锁,release函数开锁不可取。思路:申请一个自旋锁标志位为1,在open函数判断flag是否为0,若是则返回负值,打开失败。若是1,则加锁,flag自减1,解锁。再设置私有数据。release函数加锁,flag自加1,解锁。
struct gpioled_dev{
spinlock_t spinlock;
int flag;
};
struct gpioled_dev gpioled;
static int led_open(struct inode *inode, struct file *filp)
{
unsigned long flag;
spin_lock_irqsave(&gpioled.spinlock, flag);
if(gpioled.flag == 0) {
spin_unlock_irqsave(&gpioled.spinlock, flag);
return -1;
}
gpioled.flag --;
spin_unlock_irqsave(&gpioled.spinlock, flag);
filp->private_data = &gpioled;
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
spin_lock_irqsave(&gpioled.lock, flag);
gpioled.flag = 1;
spin_unlock_irqsave(&gpioled.spinlock, flag);
return 0;
}
static int __init led_init(void)
{
int ret = 0;
unsigned long flag;
spin_lock_init(&gpioled.lock);
spin_lock_irqsave(&gpioled.lock, flag);
gpioled.flag = 1;
spin_unlock_irqsave(&gpioled.spinlock, flag);
return 0;
}
2.测试
和前一节一样。
三、信号量 1.驱动程序信号量导致休眠,因此信号量保护的临界区没有时间限制,可以在open函数申请信号量,release函数释放信号量。
#include2.测试struct gpioled_dev{ struct semaphore sema; }; struct gpioled_dev gpioled; static int led_open(struct inode *inode, struct file *filp) { if(down_trylock(&gpioled.sema)) return -1; filp->private_data = &gpioled; return 0; } static int led_release(struct inode *inode, struct file *filp) { struct gpioled_dev *dev = filp->private_data up(&dev->sema); return 0; } static int __init led_init(void) { int ret = 0; sema_init(&gpioled.sema, 1); return 0; }
和上节一样。
四、互斥体实验 1.驱动程序上一节信号量实验,把信号量设置为1,相当于互斥体,对于互斥建议使用专门的互斥体。
struct gpioled_dev{
struct mutex mutex;
};
struct gpioled_dev gpioled;
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpioled;
if(mutex_lock_interruptible(&gpioled.mutex))
return -1;
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
struct gpioled_dev *dev = filp->private_data
mutex_unlock(&dev.mutex);
return 0;
}
static int __init led_init(void)
{
int ret = 0;
mutex_init(&gpioled.mutex);
return 0;
}
2.测试
和前一节一样。
五、总结原子操作只能对整形使用,因此自旋锁和信号量应用较广泛。
自旋锁会导致死循环,锁定期间不允许阻塞,因此要求临界区小。信号量允许临界期大。互斥体是信号量为1的特殊情况。
读写自旋锁和读写信号量是条件放宽的自旋锁和信号量,它们允许多个执行单元对共享资源的并发读,但不能并发写。



