Linux 互斥体实验-基于正点原子IMX6ULL开发板

1 互斥体简介

在 FreeRTOS 和 UCOS 中也有互斥体,将信号量的值设置为 1 就可以使用信号量进行互斥访问了,虽然可以通过信号量实现互斥,但是 Linux 提供了一个比信号量更专业的机制来进行互斥,它就是互斥体—mutex。互斥访问表示一次只有一个线程可以访问共享资源,不能递归申请互斥体。在我们编写 Linux 驱动的时候遇到需要互斥访问的地方建议使用 mutex。Linux 内核使用 mutex 结构体表示互斥体,定义如下:

struct mutex {
	/* 1: unlocked, 0: locked, negative: locked, possible waiters */
	atomic_t		count;
	spinlock_t		wait_lock;
	struct list_head	wait_list;
};

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

2 互斥体 API 函数

有关互斥体的 API 函数如下所示

DEFINE_MUTEX(name) //定义并初始化一个 mutex 变量。
void mutex_init(mutex *lock) //初始化 mutex。
void mutex_lock(struct mutex *lock)//获取 mutex,也就是给 mutex 上锁。如果获取不到就进休眠。
void mutex_unlock(struct mutex *lock) //释放 mutex,也就给 mutex 解锁。
int mutex_trylock(struct mutex *lock)//尝试获取 mutex,如果成功就返回 1,如果失败就返回 0。
int mutex_is_locked(struct mutex *lock)//判断 mutex 是否被获取,如果是的话就返回1,否则返回 0。
int mutex_lock_interruptible(struct mutex *lock)//使用此函数获取信号量失败进入休眠以后可以被信号打断。

互斥体的使用如下所示:

struct mutex lock; /* 定义一个互斥体 */
mutex_init(&lock); /* 初始化互斥体 */

mutex_lock(&lock); /* 上锁 */
 /* 临界区 */
mutex_unlock(&lock); /* 解锁 */

3互斥体实验

3.1实验程序编写

1、 mutex.c驱动文件文件内容如下

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>

#define GPIOLED_CNT 1
#define GPIOLED_NAME "gpioled"
#define LEDOFF 0
#define LEDON 1

/*gpioled 设备结构体*/
struct gpioled_dev
{
    dev_t devid;
    int major;
    int minor;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    int led_gpio;

    struct mutex lock; /* 互斥体 */
};

struct gpioled_dev gpioled; /*LED*/

static int led_open(struct inode *inode, struct file *filp)
{

    filp->private_data = &gpioled;

    /* 获取互斥体,可以被信号打断 */
    if (mutex_lock_interruptible(&gpioled.lock))
    {
        return -ERESTARTSYS;
    }

#if 0
 mutex_lock(&gpioled.lock); /* 不能被信号打断 */
#endif

    return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{

    struct gpioled_dev *dev = filp->private_data;

    /* 释放互斥体 */
    mutex_unlock(&dev->lock);

    return 0;
}

static ssize_t led_write(struct file *filp, const char __user *buf,
                         size_t count, loff_t *ppos)
{
    int ret;
    unsigned char databuf[1];
    struct gpioled_dev *dev = filp->private_data;

    ret = copy_from_user(databuf, buf, count);
    if (ret < 0)
    {
        return -EINVAL;
    }

    if (databuf[0] == LEDON)
    {
        gpio_set_value(dev->led_gpio, 0);
    }
    else if (databuf[0] == LEDOFF)
    {
        gpio_set_value(dev->led_gpio, 1);
    }
    return 0;
}

/*操作集*/
static const struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .write = led_write,
    .open = led_open,
    .release = led_release,
};

/*驱动入口函数*/
static int __init led_init(void)
{
    int ret = 0;

    /* 初始化互斥体 */
    mutex_init(&gpioled.lock);

    /*1,注册字符设备驱动*/
    gpioled.major = 0;
    if (gpioled.major)
    { /*给定主设备号*/
        gpioled.devid = MKDEV(gpioled.major, 0);
        register_chrdev_region(gpioled.devid, GPIOLED_CNT, "GPIOLED_NAME");
    }
    else
    {
        alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, "GPIOLED_NAME");
        gpioled.major = MAJOR(gpioled.devid);
        gpioled.minor = MINOR(gpioled.devid);
    }
    printk("gpioled major =%d, minor =%d \r\n", gpioled.major, gpioled.minor);

    /*2,初始化cdev*/
    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev, &led_fops);

    /*3,添加cdev*/
    cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);

    /*4,创建类*/
    gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
    if (IS_ERR(gpioled.class))
    {
        return PTR_ERR(gpioled.class);
    }
    /*5,创建设备*/
    gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
    if (IS_ERR(gpioled.device))
    {
        return PTR_ERR(gpioled.device);
    }

    /*1,获取设备节点*/
    gpioled.nd = of_find_node_by_path("/gpioled");
    if (gpioled.nd == NULL)
    {
        ret = -EINVAL;
        goto fail_findnode;
    }

    /*2,获取LED所对应的GPIO*/
    gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpios", 0);
    if (gpioled.led_gpio < 0)
    {
        printk("can't find led gpio\r\n");
        ret = -EINVAL;
        goto fail_findnode;
    }
    printk("led gpio num =%d\r\n", gpioled.led_gpio);

    /*3,申请IO*/
    ret = gpio_request(gpioled.led_gpio, "led-gpio");
    if (ret)
    {
        printk("Failed to request the led gpio\r\n");
        ret = -EINVAL;
        goto fail_findnode;
    }

    /*4,使用IO,设置为输出*/
    ret = gpio_direction_output(gpioled.led_gpio, 1);
    if (ret)
    {
        goto fail_setoutput;
    }

    /*5,输出低电平,点亮led灯*/
    gpio_set_value(gpioled.led_gpio, 0);

    return 0;

fail_setoutput:
    gpio_free(gpioled.led_gpio);
fail_findnode:

    return ret;
}

/*驱动出口函数*/
static void __exit led_exit(void)
{
    /*关灯*/
    gpio_set_value(gpioled.led_gpio, 1);

    /*注销字符设备驱动*/
    cdev_del(&gpioled.cdev);
    unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
    device_destroy(gpioled.class, gpioled.devid);
    class_destroy(gpioled.class);

    /*释放IO*/
    gpio_free(gpioled.led_gpio);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("supersmart");

第一步:在驱动入口函数中调用 mutex_init 初始化 mutex。
第二步:在 open 函数中调用 mutex_lock_interruptible获取mutex。
第三步:在 release 函数中调用 mutex_unlock 函数释放 mutex,这样其他应用程序就可以获取 mutex 了。

2、mutexAPP.c 测试APP内容如下

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define LEDOFF 0
#define LEDON 1
int main(int argc, char *argv[])
{
    int fd, retvalue;
    char *filename;
    unsigned char databuf[1];
    unsigned char cnt;

    if (argc != 3)
    {
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    fd = open(filename, O_RDWR);
    if (fd < 0)
    {
        printf("file %s open failed!\r\n", filename);
        return -1;
    }

    databuf[0] = atoi(argv[2]); /*将字符转化为数字*/
    retvalue = write(fd, databuf, sizeof(databuf));
    if (retvalue < 0)
    {
        printf("LED Control Failed ! \r\n");
        close(fd);
        return -1;
    }
    
    /*模拟应用占用驱动25s*/
    while(1)
    {
        sleep(5);
        cnt++;
        printf("App runing times:%d\r\n",cnt);
        if(cnt>=5) break;
    }
    printf("App runing finish!\r\n");
    close(fd);
    return 0;
}

3.2运行测试

1、编译驱动程序(略)
2、编译测试 APP(略)
3、运行测试

加载驱动
在这里插入图片描述
测试驱动
在这里插入图片描述
两个命令都是运行在后台,第一条命令先获取到mutex,因此可以操作 LED 灯,将LED 灯打开,并且占有 25S。第二条命令因为获取mutex失败而进入休眠状态,等待第一条命令运行完毕并释放互斥体以后才拥有 LED 灯使用权,将 LED 灯关闭,运行结果如上图所示

卸载驱动
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值