21.Linux的并发和竞争
原子整型操作¶
这个操作判断使得只有一个应用调用LED模块
在注册模块的时候
/* gpioled设备结构体 */
struct gpioled_dev{
...
atomic_t lock; /* 原子变量 */
};
/* 初始化原子变量 */
atomic_set(&gpioled.lock, 1); /* 原子变量初始值为1 */
然后在打开文件的时候,判断原子变量的值来检查LED有没有被别的应用使用,如果没有,就减减当前应用使用
if (!atomic_dec_and_test(&gpioled.lock)) //从 gpioled.lock 减 1,如果结果为 0 就返回真,否则返回假
{
atomic_inc(&gpioled.lock); /* 小于0的话就加1,使其原子变量等于0 */
return -EBUSY; /* LED被使用,返回忙 */
}
在关闭释放设备的时候把原子变量加1代表释放
自旋锁¶
实验只是在上锁的时候 处理了一个变量,所以这里记录常规操作
spinlock_t lock; /* 自旋锁 */
unsigned long flags;
/* 初始化自旋锁 */
spin_lock_init(&gpioled.lock);
//线程
spin_lock_irqsave(&lock, flags); /* 上锁 */
if (gpioled.dev_stats) { /* 如果设备被使用了 */
spin_unlock_irqrestore(&lock, flags);/* 解锁 */
return -EBUSY;
}
gpioled.dev_stats++; /* 如果设备没有打开,那么就标记已经打开了 */
spin_unlock_irqrestore(&lock, flags);/* 解锁 */
/* 中断服务函数 */
void irq() {
spin_lock(&lock)/* 获取锁*/
/* 临界区 */
spin_unlock(&lock)/* 释放锁 */
}
信号量¶
struct semaphore sem; /* 信号量 */
/* 初始化信号量 */
sema_init(&sem, 1);
/* 获取信号量 */
if (down_interruptible(&sem)) { /* 获取信号量,进入休眠状态的进程可以被信号打断 */
return -ERESTARTSYS;
}
#if 0
down(&sem); /* 不能被信号打断 */
#endif
up(&sem); /* 释放信号量,信号量值加1 */
互斥体¶
struct mutex lock; /* 互斥体 */
/* 初始化互斥体 */
mutex_init(&lock);
/* 获取互斥体,可以被信号打断 */
if (mutex_lock_interruptible(&lock)) {
return -ERESTARTSYS;
}
#if 0
mutex_lock(&lock); /* 不能被信号打断 */
#endif
/* 释放互斥锁 */
mutex_unlock(&lock);
按键输入示例¶
dts中添加pinctrl 节点,在 iomuxc 节点的 imx6ul-evk 子节点下创建一个名为“pinctrl_key”的子节点
在根节点“/”下创建 KEY 节点,节点名为“key”,节点内容如下
key {
#address-cells = <1>;
#size-cells = <1>;
compatible = "atkalpha-key";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_key>;
key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>;/* KEY0 */
status = "okay";
};
编译dts,然后用新的dts文件启动Linux
可以进入到/proc/device-tree/base下查看是否有alphaled这个节点信息。
可以进入到/sys/firmware/devicetree/base/alphaled下查看是否有alphaled这个节点的相关信息,可以查看一下 compatible、status 等属性值是否和我们设置的一致。
按键驱动相关
定义按键值的原子类型,和初始化
/* key设备结构体 */
struct key_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct device_node *nd; /* 设备节点 */
int key_gpio; /* key所使用的GPIO编号 */
atomic_t keyvalue; /* 按键值 */
};
struct key_dev keydev; /* key设备 */
atomic_set(&keydev.keyvalue, 0);
初始化按键,不知道为什么,这里将初始化按键放在了文件打开处,以前的教程都是放在注册处的。
static int keyio_init(void)
{
keydev.nd = of_find_node_by_path("/key");
if (keydev.nd== NULL) {
return -EINVAL;
}
keydev.key_gpio = of_get_named_gpio(keydev.nd ,"key-gpio", 0);
if (keydev.key_gpio < 0) {
printk("can't get key0\r\n");
return -EINVAL;
}
printk("key_gpio=%d\r\n", keydev.key_gpio);
/* 初始化key所使用的IO */
gpio_request(keydev.key_gpio, "key0"); /* 请求IO */
gpio_direction_input(keydev.key_gpio); /* 设置为输入 */
return 0;
}
读取按键值的时候
static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int ret = 0;
int value;
struct key_dev *dev = filp->private_data;
if (gpio_get_value(dev->key_gpio) == 0) { /* key0按下 */
while(!gpio_get_value(dev->key_gpio)); /* 等待按键释放 */
atomic_set(&dev->keyvalue, KEY0VALUE);
} else {
atomic_set(&dev->keyvalue, INVAKEY); /* 无效的按键值 */
}
value = atomic_read(&dev->keyvalue);
ret = copy_to_user(buf, &value, sizeof(value));
return ret;
}