跳转至

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代表释放

struct gpioled_dev *dev = filp->private_data;

/* 关闭驱动文件的时候释放原子变量 */
atomic_inc(&dev->lock);

自旋锁

实验只是在上锁的时候 处理了一个变量,所以这里记录常规操作

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”的子节点

pinctrl_key: keygrp {
    fsl,pins = <
        MX6UL_PAD_UART1_CTS_B__GPIO1_IO18 0xF080 /* KEY0 */
    >;
};

在根节点“/”下创建 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

make dtbs

可以进入到/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;
}