05-互斥量的应用示例

 本系列文章主要讲述内核中的互斥与同步操作,主要包括内核中的锁机制,信号量和互斥体,讲述了基础概念和常用的API函数接口和代码示例,详细目录如下:
01 - 内核中的互斥与同步概述
02 - 原子变量应用示例
03 - 自旋锁应用示例
04 - 信号量的应用示例
05 - 互斥量的应用示例

 信号量除了不能用于中断的上下文,还有一个缺点就是不是很智能。在获取信号量的代码中只要信号量的值为0,进程就马上休眠了。但是更一般的情况是,在不用等待很长的时间后信号量马上就可以获得,那么信号量的操作就要经历使进程先休眠再被唤醒的一个漫长过程。可以在信号量不能获取的时候稍微耐心等待一小段时间,如果在这段时间能够获取信号量,那么获取信号量的操作就可以立即返回,否则再将进程休眠也不迟。为了实现这种比较智能化的信号量,内核提供了另外一种专门用于互斥的高效率信号量,也就是互斥量,也叫互斥体,类型为 struct mutex,
 互斥访问表示一次只有一个线程可以访问共享资源,不能递归申请互斥体。在我们编写 Linux 驱动的时候遇到需要互斥访问的地方建议使用 mutex。互斥体和二值信号量类似,只不过互斥体是专门用于互斥访问的。

 具体的代码部分如下所示,文章最后将测试结果附上,并对其进行了解释,详情请参考示例代码。

1 示例代码

1.1 demo.c

 驱动部分的代码,主要包含了互斥体的实现和判断,init、open和release函数的实现,具体内容在代码中进行注释。

#include <linux/module.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <asm/atomic.h>
#include <linux/slab.h>				// kzalloc和kfree的头文件

typedef struct 
{
	dev_t dev_no;
	char devname[20];
	char classname[20];
	struct cdev demo_cdev;
	struct class *cls;
	struct device *dev;
	struct mutex my_mutex;			// 定义一个互斥体,在init函数中进行初始化
}demo_struct;
demo_struct *my_demo_dev = NULL;	// 定义一个设备结构体指针,指向NULL

static int demo_open(struct inode *inode, struct file *filp)
{
	/* 首先判断设备是否可用 */
	if( mutex_lock_interruptible(&my_demo_dev->my_mutex) )		// 访问共享资源之前获取互斥体,成功获取返回0
	{
		return -ERESTARTSYS;						// 不能获取返回错误码
	}
	
	printk("%s -- %d.\n", __FUNCTION__, __LINE__);
	
	return 0;

}

static int demo_release(struct inode *inode, struct file *filp)
{
	/* 释放互斥量 */
	mutex_unlock(&my_demo_dev->my_mutex);

	printk("%s -- %d.\n", __FUNCTION__, __LINE__);
	
	return 0;
}

struct file_operations demo_ops = {
	.open = demo_open,
	.release=  demo_release,
};

static int __init demo_init(void)
{
	int ret;

	printk("%s -- %d.\n", __FUNCTION__, __LINE__);

	/* 开辟空间 */
	my_demo_dev =  kzalloc(sizeof(demo_struct), GFP_KERNEL);
	if ( IS_ERR(my_demo_dev) )
	{
		printk("kzalloc failed.\n");
		ret = PTR_ERR(my_demo_dev);
		goto kzalloc_err;
	}
	strcpy(my_demo_dev->devname, "demo_chrdev");	// 给设备名字赋值
	strcpy(my_demo_dev->classname, "demo_class");	// 给设备类的名字赋值

	mutex_init(&my_demo_dev->my_mutex);				// 初始化互斥体
	
	ret = alloc_chrdev_region(&my_demo_dev->dev_no, 0, 0, my_demo_dev->devname);
	if (ret)
	{
		printk("alloc_chrdev_region failed.\n");
		goto region_err;
	}

	cdev_init(&my_demo_dev->demo_cdev, &demo_ops);

	ret = cdev_add(&my_demo_dev->demo_cdev, my_demo_dev->dev_no, 1);
	if (ret < 0)
	{
		printk("cdev_add failed.\n");
		goto add_err;
	}

	my_demo_dev->cls = class_create(THIS_MODULE, my_demo_dev->classname);		/* 在目录/sys/class/.. */
	if ( IS_ERR(my_demo_dev->cls) )
	{
		ret = PTR_ERR(my_demo_dev->cls);
		printk("class_create failed.\n");
		goto cls_err;
	}

	my_demo_dev->dev = device_create(my_demo_dev->cls, NULL, my_demo_dev->dev_no, NULL, "chrdev%d", 0);	/* 在目录/dev/.. */
	if ( IS_ERR(my_demo_dev->dev) )
	{
		ret = PTR_ERR(my_demo_dev->dev);
		printk("device_create failed.\n");
		goto dev_err;
	}
	
	return 0;

dev_err:
	class_destroy(my_demo_dev->cls);	
cls_err:
	cdev_del(&my_demo_dev->demo_cdev);
add_err:
	unregister_chrdev_region(my_demo_dev->dev_no, 1);
region_err:
	kfree(my_demo_dev);		// 释放空间,避免内存泄漏
kzalloc_err:
	return ret;
}


static void __exit demo_exit(void)
{
	printk("%s -- %d.\n", __FUNCTION__, __LINE__);

	device_destroy(my_demo_dev->cls, my_demo_dev->dev_no);
	class_destroy(my_demo_dev->cls);
	cdev_del(&my_demo_dev->demo_cdev);
	unregister_chrdev_region(my_demo_dev->dev_no, 1);
	kfree(my_demo_dev);		// 释放空间,避免内存泄漏
}

module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");

1.2 test.c

 示例的应用层测试代码,包含了设备的打开和关闭函数,在打开和关闭之间延时5秒钟,方便测试第二个应用程序打开设备的结果。同时保留了传参功能,以便更好的区分是不同的应用程序所打印的内容。

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

int main(int argc, const char *argv[])
{
	int fd;
	int i, count = 1;

	if(argc < 2)
	{
		printf("please input param: \n");
		return -1;
	}

	fd = open("/dev/chrdev0", O_RDWR, 0666);
	if (fd < 0)
	{
		perror("open");
		return -1;
	}

	for (i=0; i<5; i++)
	{
		printf("process %d : count = %d\n",atoi(argv[1]), count);
		count++;
		sleep(1);
	}

	close(fd);

	return 0;
}

1.3 Makefile

KERNELDIR ?= /home/linux/ti-processor-sdk-linux-am335x-evm-04.00.00.04/board-support/linux-4.9.28/
PWD := $(shell pwd)

all:
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) modules
	arm-linux-gnueabihf-gcc test.c -o app
install:
	sudo cp *.ko  app /tftpboot
clean:
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) clean
	rm app

obj-m += demo.o

1.4 测试如果

 从测试结果并对比上个示例可以看出,互斥体和二值信号量类似,只不过互斥体是专门用于互斥访问的。

root@am335x-evm:~# insmod demo.ko 		// 加载模块
[	47.409156] demo_init -- 53.		
root@am335x-evm:~# ./app 1 &			// 后台运行应用程序1
[1] 921
[	72.317511] demo_open -- 28.			
process 1 : count = 1					// 应用层开始打印程序1的信息
root@am335x-evm:~# ./app 2 &			// 运行应用程序2
[2] 922
process 1 : count = 2					// 信号量没有释放,一直打印程序1的信息
process 1 : count = 3
process 1 : count = 4
process 1 : count = 5
[	77.329346] demo_release -- 39.		// 程序1执行完释放信号量
[	77.332688] demo_open -- 28.			// 程序2获得信号量开始执行
process 2 : count = 1					// 应用层开始打印程序2的信息
process 2 : count = 2
process 2 : count = 3
process 2 : count = 4
process 2 : count = 5
[	82.337123] demo_release -- 39.		// 程序2执行完释放信号量
[1]-  Done					  ./app 1
[2]+  Done					  ./app 2
root@am335x-evm:~# rmmod demo.ko 
[  108.937996] demo_exit -- 117.
发布了57 篇原创文章 · 获赞 64 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_36310253/article/details/102912473
今日推荐