02-原子變量應用示例

 本系列文章主要講述內核中的互斥與同步操作,主要包括內核中的鎖機制,信號量和互斥體,講述了基礎概念和常用的API函數接口和代碼示例,詳細目錄如下:
01 - 內核中的互斥與同步概述
02 - 原子變量應用示例
03 - 自旋鎖應用示例
04 - 信號量的應用示例
05 - 互斥量的應用示例


 本實例使用原子變量實現了一個應用程序只能打開一個設備的目的,在init函數中初始化原子變量的值爲1,每次打開設備將原子變量的值減1。
 在open函數中對原子變量的值進行判斷,如果原子變量的值等於0表示只有一個應用程序打開了設備,可以進行其他操作,如果原理變量的值小於0,那麼表示此時有應用程序正在使用設備,返回一個錯誤碼EBUSY,並將原子變量的值+1(目的是當正在打開設備的應用程序關閉時,原子變量的值還能恢復到初始值1)。
 具體的代碼部分如下所示,文章最後將測試結果附上,並對其進行了解釋,詳情請參考示例代碼。

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>

dev_t dev_no;
char devname[] = "demo_chrdev";
char classname[] = "demo_class";
struct cdev demo_cdev;
struct class *cls;
struct device *dev;
atomic_t atomic_v;		// 定義一個原子變量,在init函數中初始化

static int demo_open(struct inode *inode, struct file *filp)
{
	/*
		每次打開設備原子變量自減1,atomic_dec_and_test結果爲0返回true
	*/
	if ( !atomic_dec_and_test(&atomic_v) )
	{
		printk("device busy.\n");
		atomic_inc(&atomic_v);
		goto err0;
	}

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

err0:
	return -EBUSY;	/* Device or resource busy */
}

static int demo_release(struct inode *inode, struct file *filp)
{
	printk("%s -- %d.\n", __FUNCTION__, __LINE__);

	atomic_inc(&atomic_v);	// 關閉設備後,原子變量值+1,其他程序可以訪問

	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__);

	atomic_set(&atomic_v, 1) ;		// 原子變量初始化爲1
	
	ret = alloc_chrdev_region(&dev_no, 0, 0, devname);
	if (ret)
	{
		printk("alloc_chrdev_region failed.\n");
		goto region_err;
	}

	cdev_init(&demo_cdev, &demo_ops);

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

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

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

dev_err:
	class_destroy(cls);
cls_err:
	cdev_del(&demo_cdev);	
add_err:
	unregister_chrdev_region(dev_no, 1);
region_err:
	return ret;
}


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

	device_destroy(cls, dev_no);
	class_destroy(cls);
	cdev_del(&demo_cdev);
	unregister_chrdev_region(dev_no, 1);
}


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

1.2 test.c

 示例的應用層測試代碼,包含了設備的打開和關閉函數,在打開和關閉之間延時5秒鐘,方便測試第二個應用程序打開設備的結果。

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

int main(int argc, const char *argv[])
{
	int fd;

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

	sleep(5);

	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		 		// 加載模塊
[   53.871028] demo_init -- 53.
root@am335x-evm:~# ./app &						// 後臺運行
[1] 903
[   61.966737] demo_open -- 27.					// 第一次成功打開
root@am335x-evm:~# ./app  						// 在上一個運行期間,再運行一次
[   64.006403] device busy.						// 內核打印的信息,連着打開兩次,出現device busy
[   66.970339] open: Device or resource busy	// 應用層打印的信息
root@am335x-evm:~#  demo_release -- 37.			// 5秒之後,第一次打開的設備關閉
root@am335x-evm:~# ./app						// 再次打開,可以成功打開
[   71.853160] demo_open -- 27.
[   76.856765] demo_release -- 37.				// 5秒之後關閉
root@am335x-evm:~# rmmod demo.ko 				// 卸載模塊
[   86.067955] demo_exit -- 104.
發佈了57 篇原創文章 · 獲贊 65 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章