奔路吧linux内核-简单字符设备驱动

面向对象地分析Linux内核设备驱动(2)——Linux内核设备模型与总线

$ tree 
.
├── GPATH
├── GRTAGS
├── GTAGS
├── HelloWorldModule.c
├── Makefile
├── READ.md
├── test
└── test.c

0 directories, 8 files

读写基于kifo环形缓冲区

#include "linux/ipc.h"
#include <linux/init.h> 
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/kfifo.h>
DEFINE_KFIFO(my_demo_fifo,char,64);
static int debug = 0;
#define DEMO_NAME "my_demo_cdev"
static dev_t my_dev;
static struct cdev *demo_dev;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debugging info");
static int mytest= 1;
module_param(mytest,int,0644);
static int count = 1;
MODULE_PARM_DESC(mytest,"test for info param");
void sync_ykz(void)
{
	printk("%d %s \n",__FUNCTION__,__LINE__);
	return;
}
static int demo_open(struct inode* inode,struct file* file)
{
	int major = MAJOR(inode->i_rdev);
	int minjor = MINOR(inode->i_rdev);
	printk("%s %d major:%d minor:%d\n",__FUNCTION__,__LINE__,major,minjor);
return 0; 
}
//static int demo_release(struct inod*inod,struct file*file)
static int demo_release(struct inode *inod, struct file *file)
{
	printk("%s %d \n",__FUNCTION__,__LINE__ );

	return 0;
}
ssize_t demo_write(struct file*file,const char __user* buf,size_t count,loff_t*ppos)
{
	unsigned int actual_write;
	int ret;
	printk("%s %d \n",__FUNCTION__,__LINE__ );
	ret = kfifo_from_user(&my_demo_fifo,buf,count,&actual_write);
	if(ret) {
		return -EIO;
	}
	return actual_write;
}
static ssize_t demo_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	int ret ;
	unsigned int actual_readed;
	printk("%s %d \n",__FUNCTION__,__LINE__ );
	
	ret = kfifo_to_user(&my_demo_fifo, buf, count, &actual_readed);
	if(ret) 
		return -EIO;

	return actual_readed;
}
EXPORT_SYMBOL_GPL(sync_ykz);
struct file_operations ops = {
	.owner = THIS_MODULE,
	.open = demo_open,
	.release = demo_release,
	.write = demo_write,
	.read = demo_read

};
static int __init my_test_module(void) 
{
	int ret;
	if(debug) {
	printk("my fist kernel module init debug %d\n",debug);
	printk("my fist kernel module init mytest%d\n",mytest);
	}

	ret = alloc_chrdev_region(&my_dev,0,count,DEMO_NAME);
	if(ret) {
		printk("failed to alloc_chrdev_region \n");
		return ret;
	}
	demo_dev = cdev_alloc();
	if(!demo_dev) {
		printk("cdev_alloc failed\n");
		goto unregister_chrdev; 

	}
	cdev_init(demo_dev,&ops);
	cdev_add(demo_dev,my_dev,count);
	printk("succful register dev [%d:%d]\n",MAJOR(my_dev),MINOR(my_dev));
	return 0;

unregister_chrdev:
	unregister_chrdev_region(my_dev,count);
	
	return 0;
}
static void __exit my_test_module_exit(void)
{
	unregister_chrdev_region(my_dev,count);

	printk("good bye\n");
}
void sync_zyk(void)
{
	printk("%s %d\n",__FUNCTION__,__LINE__);
}
EXPORT_SYMBOL_GPL(sync_zyk);
module_init(my_test_module);
module_exit(my_test_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYK");
MODULE_DESCRIPTION("MY TEST KERNEL MODULE\n");
MODULE_ALIAS("MY ZYK TEST");






Makefile

BASEINCLUDE ?= /lib/modules/`uname -r`/build
hello-world-modules-objs := HelloWorldModule.o 
obj-m := hello-world-modules.o
all : 
	$(MAKE) -C $(BASEINCLUDE) M=${PWD} modules;
clean:
	$(MAKE) -C $(BASEINCLUDE) M=$(PWD) clean;
	rm -f *.ko;





编译加载模块:

benshushu:5-module# insmod hello-world-modules.ko debug=1
[ 1840.064727] my fist kernel module init debug 1
[ 1840.069524] my fist kernel module init mytest1
[ 1840.070520] succful register dev [245:0]
benshushu:5-module# cat /proc/devices  查看生成了设备对应的设备号为245
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  5 ttyprintk
  7 vcs
 10 misc
 13 input
 29 fb
128 ptm
136 pts
204 ttyAMA
245 my_demo_cdev
250 bsg
251 watchdog
252 rtc
253 dax
254 gpiochip

Block devices:
  7 loop
254 virtblk
259 blkext

手动在/dev下生成节点

benshushu:5-module# sudo mknod /dev/my_demo_cdev c 249 0    
benshushu:5-module# ls /dev/my_demo_cdev  -al
crw-r--r-- 1 root root 249, 0 May 23 02:05 /dev/my_demo_cdev

编写测试 程序:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define DEMO_NAME "/dev/my_demo_cdev"
int main(void) 
{
	char buffer[64];
	char message[] = "The message is the virutal FIFO device";
	int fd;
	int ret;
	char* read_buffer;
	fd = open(DEMO_NAME,O_RDWR);
	if(fd < 0) {
		printf("open device %s fail\n",DEMO_NAME);
		return -1;
	}
	printf("open file successful \n");
	/*write the message to the virtual fifo device*/
	ret = write(fd,message,sizeof(message));
	if(ret) {
		printf("cannot write device %d ret= %d\n",fd,ret);
	}
	
	read(fd,buffer,64);
	printf("buffer is %s\n",buffer);
	close(fd);

return 0;
}

编译:

arch64-linux-gnu-gcc test.c -o test --static
~/runninglinuxkernel_5.0/kmodules/rlk_lab/rlk_basic/zyk-module/5-module$ file test
test: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=055a3136cc84ef1c3bfc11fa45067ea833158833, for GNU/Linux 3.7.0, not stripped

测试结果

 ./test 
[  148.266980] demo_open 27 major:249 minor:0
open file successful 
[  148.272629] demo_write 41 
cannot write device 3 ret= 39
[  148.277495] demo_read 52 
buffer is The message is the virutal FIFO device
[  148.278330] demo_release 3

内核打印 :

dmesg|tail
[   96.320493] demo_read 52 
[   96.321522] demo_release 33 
[  139.693512] demo_open 27 major:249 minor:0
[  139.700655] demo_write 41 
[  139.709481] demo_read 52 
[  139.709981] demo_release 33 
[  148.266980] demo_open 27 major:249 minor:0
[  148.272629] demo_write 41 
[  148.277495] demo_read 52 
[  148.278330] demo_release 33 


注意:
设备号在驱动中最好动态申请,且在退出时释放,接口如下:
alloc_chrdev_region
unregister_chrdev_region

应用 层生成设备节点有两种方法 :
mknod filename type(c,b) major minor

udev机制-》基于sysfs与 tmpfs来实现的

相关文档在内核源码中也有,需要则搜索一下
Documentation

非阻塞方式打开

#include "linux/ipc.h"
#include <linux/init.h> 
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/kfifo.h>
DEFINE_KFIFO(my_demo_fifo,char,64);
static int debug = 0;
#define DEMO_NAME "my_demo_cdev"
static dev_t my_dev;
static struct cdev *demo_dev;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debugging info");
static int mytest= 1;
module_param(mytest,int,0644);
static int count = 1;
MODULE_PARM_DESC(mytest,"test for info param");
void sync_ykz(void)
{
	printk("%d %s \n",__FUNCTION__,__LINE__);
	return;
}
static int demo_open(struct inode* inode,struct file* file)
{
	int major = MAJOR(inode->i_rdev);
	int minjor = MINOR(inode->i_rdev);
	printk("%s %d major:%d minor:%d\n",__FUNCTION__,__LINE__,major,minjor);
return 0; 
}
//static int demo_release(struct inod*inod,struct file*file)
static int demo_release(struct inode *inod, struct file *file)
{
	int major = MAJOR(inod->i_rdev);
	int minjor= MINOR(inod->i_rdev);
	printk("%s %d major:%d minor:%d\n",__FUNCTION__,__LINE__,major,minjor);

	kfifo_reset(&my_demo_fifo);
	return 0;
}
ssize_t demo_write(struct file*file,const char __user* buf,size_t count,loff_t*ppos)
{
	unsigned int actual_write;
	int ret;
	printk("%s %d buf %s\n",__FUNCTION__,__LINE__ ,buf);
	if(kfifo_is_full(&my_demo_fifo)){ if(file->f_flags & O_NONBLOCK)return -EAGAIN; }

	ret = kfifo_from_user(&my_demo_fifo,buf,count,&actual_write);
	if(ret) {
		return -EIO;
	}
	printk("actual_write %d\n",actual_write);
	return actual_write;
}
static ssize_t demo_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	int ret ;
	unsigned int actual_readed;
	printk("%s %d \n",__FUNCTION__,__LINE__ );
	if(kfifo_is_empty(&my_demo_fifo)) {
		if(file->f_flags & O_NONBLOCK) return -EAGAIN;
	}

	
	ret = kfifo_to_user(&my_demo_fifo, buf, count, &actual_readed);
	if(ret) 
		return -EIO;
	printk("actual_readed%d\n",actual_readed);

	return actual_readed;
}
EXPORT_SYMBOL_GPL(sync_ykz);
struct file_operations ops = {
	.owner = THIS_MODULE,
	.open = demo_open,
	.release = demo_release,
	.write = demo_write,
	.read = demo_read

};
static int __init my_test_module(void) 
{
	int ret;
	if(debug) {
	printk("my fist kernel module init debug %d\n",debug);
	printk("my fist kernel module init mytest%d\n",mytest);
	}

	ret = alloc_chrdev_region(&my_dev,0,count,DEMO_NAME);
	if(ret) {
		printk("failed to alloc_chrdev_region \n");
		return ret;
	}
	demo_dev = cdev_alloc();
	if(!demo_dev) {
		printk("cdev_alloc failed\n");
		goto unregister_chrdev; 

	}
	init_waitqueue_head(&read_queue);
	init_waitqueue_head(&write_queue);
	cdev_init(demo_dev,&ops);
	cdev_add(demo_dev,my_dev,count);
	printk("succful register dev [%d:%d]\n",MAJOR(my_dev),MINOR(my_dev));
	return 0;

unregister_chrdev:
	unregister_chrdev_region(my_dev,count);
	
	return 0;
}
static void __exit my_test_module_exit(void)
{
	unregister_chrdev_region(my_dev,count);

	printk("good bye\n");
}
void sync_zyk(void)
{
	printk("%s %d\n",__FUNCTION__,__LINE__);
}
EXPORT_SYMBOL_GPL(sync_zyk);
module_init(my_test_module);
module_exit(my_test_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYK");
MODULE_DESCRIPTION("MY TEST KERNEL MODULE\n");
MODULE_ALIAS("MY ZYK TEST");




测试:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include<string.h>
#include <unistd.h>
#define DEMO_NAME "/dev/my_demo_cdev"
int main(void) 
{
	char buffer[64];
	char message[] = "The message is the virutal FIFO device,the message is the alloc_chrdev_region DEFINE_KFIFO";
	int fd;
	int ret;
	char* read_buffer;
	fd = open(DEMO_NAME,O_RDWR|O_NONBLOCK);
	if(fd < 0) {
		printf("open device %s fail\n",DEMO_NAME);
		return -1;
	}
	printf("open file successful \n");
	memset(buffer,0,sizeof(buffer));
	ret = read(fd,buffer,64);
	if(ret<0) {
		printf("read buf is ret %d\n",ret);
	}
	printf("buffer is %s\n",buffer);
	/*write the message to the virtual fifo device*/
	printf("write message size is %d\n",sizeof(message));
	ret = write(fd,message,sizeof(message));
	if(ret<0) {
		printf("cannot write device %d ret= %d\n",fd,ret);
	}
	ret = write(fd,message,sizeof(message));
	if(ret<0) {
		printf("cannot write device %d ret= %d\n",fd,ret);
	}
	
	read(fd,buffer,64);
	printf("buffer is %s\n",buffer);
	close(fd);

return 0;
}


如下,第二次写就写不进去了,因为 buf 满了

benshushu:5-module# ./test 
[ 2928.016561] demo_open 27 major:249 minor:0
open file successful 
[ 2928.021395] demo_read 58 
read buf is ret -1
buffer is 
write message size is 91
[ 2928.024010] demo_write 44 buf The message is the virutal FIFO device,the message is the alloc_chrdev_region DEFINE_KFIFO
[ 2928.024419] actual_write 64
[ 2928.024557] demo_write 44 buf The message is the virutal FIFO device,the message is the alloc_chrdev_region DEFINE_KFIFO
cannot write device 3 ret= -1
[ 2928.025094] demo_read 58 
[ 2928.025201] actual_readed64
buffer is The message is the virutal FIFO device,the message is the alloc_����
[ 2928.025729] demo_release 35 major:249 minor:0


阻塞例子:

#include "linux/ipc.h"
#include <linux/init.h> 
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/kfifo.h>
#include <linux/wait.h>
DEFINE_KFIFO(my_demo_fifo,char,64);
wait_queue_head_t read_queue;
wait_queue_head_t write_queue;


static int debug = 0;
#define DEMO_NAME "my_demo_cdev"
static dev_t my_dev;
static struct cdev *demo_dev;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debugging info");
static int mytest= 1;
module_param(mytest,int,0644);
static int count = 1;
MODULE_PARM_DESC(mytest,"test for info param");
void sync_ykz(void)
{
	printk("%d %s \n",__FUNCTION__,__LINE__);
	return;
}
static int demo_open(struct inode* inode,struct file* file)
{
	int major = MAJOR(inode->i_rdev);
	int minjor = MINOR(inode->i_rdev);
	printk("%s %d major:%d minor:%d\n",__FUNCTION__,__LINE__,major,minjor);
return 0; 
}
//static int demo_release(struct inod*inod,struct file*file)
static int demo_release(struct inode *inod, struct file *file)
{
	int major = MAJOR(inod->i_rdev);
	int minjor= MINOR(inod->i_rdev);
	printk("%s %d major:%d minor:%d\n",__FUNCTION__,__LINE__,major,minjor);

	kfifo_reset(&my_demo_fifo);
	return 0;
}
ssize_t demo_write(struct file*file,const char __user* buf,size_t count,loff_t*ppos)
{
	unsigned int actual_write;
	int ret;
	printk("%s %d pid %d\n",__FUNCTION__,__LINE__ ,current->pid);
	if(kfifo_is_full(&my_demo_fifo)){ 
		if(file->f_flags & O_NONBLOCK)return -EAGAIN; 
	ret = wait_event_interruptible(write_queue,!kfifo_is_full(&my_demo_fifo));
	if(ret) 
	 return ret;
	}
	ret = kfifo_from_user(&my_demo_fifo,buf,count,&actual_write);
	if(ret) {
		return -EIO;
	}
	printk("%s %d buf %s\n",__FUNCTION__,__LINE__ ,buf);
	if(!kfifo_is_empty(&my_demo_fifo))
	{
	printk("++%s %d buf %s\n",__FUNCTION__,__LINE__ ,buf);
	wake_up_interruptible(&read_queue);
	printk("--%s %d buf %s\n",__FUNCTION__,__LINE__ ,buf);
	}

	printk("actual_write %d\n",actual_write);

	return actual_write;
}
static ssize_t demo_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	int ret ;
	unsigned int actual_readed;
	printk("%s %d pid %d\n",__FUNCTION__,__LINE__ ,current->pid);
	if(kfifo_is_empty(&my_demo_fifo)) {
		if(file->f_flags & O_NONBLOCK) return -EAGAIN;

	ret = wait_event_interruptible(read_queue,!kfifo_is_empty(&my_demo_fifo));
	if(ret) 
	 return ret;
	}
	ret = kfifo_to_user(&my_demo_fifo, buf, count, &actual_readed);
	if(ret) {
		return -EIO;
	}
	printk("actual_readed%d pid:%d buf:%s\n",actual_readed,current->pid,buf);

	if(!kfifo_is_full(&my_demo_fifo))
	{
	printk("++%s %d buf %s\n",__FUNCTION__,__LINE__ ,buf);
	wake_up_interruptible(&write_queue);
	printk("++%s %d buf %s\n",__FUNCTION__,__LINE__ ,buf);
	}

	return actual_readed;
}
EXPORT_SYMBOL_GPL(sync_ykz);
struct file_operations ops = {
	.owner = THIS_MODULE,
	.open = demo_open,
	.release = demo_release,
	.write = demo_write,
	.read = demo_read

};
static int __init my_test_module(void) 
{
	int ret;
	if(debug) {
	printk("my fist kernel module init debug %d\n",debug);
	printk("my fist kernel module init mytest%d\n",mytest);
	}

	ret = alloc_chrdev_region(&my_dev,0,count,DEMO_NAME);
	if(ret) {
		printk("failed to alloc_chrdev_region \n");
		return ret;
	}
	demo_dev = cdev_alloc();
	if(!demo_dev) {
		printk("cdev_alloc failed\n");
		goto unregister_chrdev; 

	}
	init_waitqueue_head(&read_queue);
	init_waitqueue_head(&write_queue);


	cdev_init(demo_dev,&ops);
	cdev_add(demo_dev,my_dev,count);
	printk("succful register dev [%d:%d]\n",MAJOR(my_dev),MINOR(my_dev));
	return 0;

unregister_chrdev:
	unregister_chrdev_region(my_dev,count);
	
	return 0;
}
static void __exit my_test_module_exit(void)
{
	unregister_chrdev_region(my_dev,count);

	printk("good bye\n");
}
void sync_zyk(void)
{
	printk("%s %d\n",__FUNCTION__,__LINE__);
}
EXPORT_SYMBOL_GPL(sync_zyk);
module_init(my_test_module);
module_exit(my_test_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYK");
MODULE_DESCRIPTION("MY TEST KERNEL MODULE\n");
MODULE_ALIAS("MY ZYK TEST");


用cat /echo命令 测试 :
由于队列是模块中共享,因此读进程与写进程会同步(唤醒读阻塞),如下打印显示两个模块 不是在同一个进程 中( pid不一样)

benshushu:6-mdule# cat /dev/my_demo_cdev &
[1] 1303
benshushu:6-mdule# [  790.286479] demo_open 32 major:249 minor:0
[  790.287364] demo_read 76 pid 1303

benshushu:6-mdule# echo "I am Block" > /dev/my_demo_cdev 
[  809.686533] demo_open 32 major:249 minor:0
[  809.688588] demo_write 49 pid 556
[  809.692679] demo_write 60 buf I am Block
[  809.692679] 
[  809.692679] 
[  809.698690] ++demo_write 63 buf I am Block
[  809.698690] 
[  809.698690] 
[  809.700212] --demo_write 65 buf I am Block
[  809.700212] 
[  809.700212] 
benshushu:6-mdule# [  809.705054] actual_readed11 pid:1303 buf:I am Block
[  809.705054] 
[  809.714284] actual_write 11
[  809.715059] demo_release 40 major:249 minor:0
[  809.727612] ++demo_read 92 buf I am Block
[  809.727612] 
[  809.728089] ++demo_read 94 buf I am Block
[  809.728089] 
I am Block
[  809.729117] demo_read 76 pid 1303

benshushu:6-mdule# 


记得等待队列需要初始化,不然会死机

	init_waitqueue_head(&read_queue);
	init_waitqueue_head(&write_queue);

IO多路复用支持

#include "linux/ipc.h"
#include <linux/slab.h>
#include <linux/init.h> 
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/kfifo.h>
#include <linux/wait.h>
#include <linux/poll.h>
struct my_demo_device {
	char name[64];
	struct device *dev;
	wait_queue_head_t read_queue;
	wait_queue_head_t write_queue;
	struct kfifo my_demo_fifo;

};
struct mydemo_private_data {
	struct my_demo_device *device;
	char name[64];
};

#define DEMO_DEVICE_KFIFO_SIZE 64
#define DEMO_DEVICE_MAX_NUM 8
static struct my_demo_device *my_demo_device[DEMO_DEVICE_MAX_NUM];
static int debug = 0;
#define DEMO_NAME "my_demo_cdev"
static dev_t my_dev;
static struct cdev *demo_dev;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debugging info");
static int mytest= 1;
module_param(mytest,int,0644);
static int count = 1;
MODULE_PARM_DESC(mytest,"test for info param");
void sync_ykz(void)
{
	printk("%d %s \n",__FUNCTION__,__LINE__);
	return;
}
static int demo_open(struct inode* inode,struct file* file)
{
	int major = MAJOR(inode->i_rdev);
	int minjor = MINOR(inode->i_rdev);
	struct mydemo_private_data *data;
	struct my_demo_device *device = my_demo_device[minjor];

	printk("%s %d major:%d minor:%d\n",__FUNCTION__,__LINE__,major,minjor);
	data = kmalloc(sizeof(struct mydemo_private_data),GFP_KERNEL);
	if(!data)return -ENOMEM;
	sprintf(data->name,"private_data_%d",minjor);
	data->device = device;
	file->private_data = data;
	return 0; 
}
//static int demo_release(struct inod*inod,struct file*file)
static int demo_release(struct inode *inod, struct file *file)
{
	int major = MAJOR(inod->i_rdev);
	int minjor= MINOR(inod->i_rdev);
	struct mydemo_private_data *data = file->private_data;
	struct my_demo_device* device = data->device;
	printk("%s %d major:%d minor:%d\n",__FUNCTION__,__LINE__,major,minjor);

	kfifo_reset(&device->my_demo_fifo);
	return 0;
}
ssize_t demo_write(struct file*file,const char __user* buf,size_t count,loff_t*ppos)
{
	unsigned int actual_write;
	struct mydemo_private_data* data = file->private_data;
	struct my_demo_device *device = data->device;
	int ret;
	printk("%s %d pid %d\n",__FUNCTION__,__LINE__ ,current->pid);
	if(kfifo_is_full(&device->my_demo_fifo)){ 
		if(file->f_flags & O_NONBLOCK)return -EAGAIN; 
	ret = wait_event_interruptible(device->write_queue,!kfifo_is_full(&device->my_demo_fifo));
	if(ret) 
	 return ret;
	}
	ret = kfifo_from_user(&device->my_demo_fifo,buf,count,&actual_write);
	if(ret) {
		return -EIO;
	}
	printk("%s %d buf %s\n",__FUNCTION__,__LINE__ ,buf);
	if(!kfifo_is_empty(&device->my_demo_fifo))
	{
	printk("++%s %d buf %s\n",__FUNCTION__,__LINE__ ,buf);
	wake_up_interruptible(&device->read_queue);
	printk("--%s %d buf %s\n",__FUNCTION__,__LINE__ ,buf);
	}

	printk("actual_write %d\n",actual_write);

	return actual_write;
}
static ssize_t demo_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	int ret ;
	unsigned int actual_readed;
	struct mydemo_private_data *data = file->private_data;
	struct my_demo_device *device= data->device;
	printk("%s %d pid %d\n",__FUNCTION__,__LINE__ ,current->pid);
	if(kfifo_is_empty(&device->my_demo_fifo)) {
		if(file->f_flags & O_NONBLOCK) return -EAGAIN;

	ret = wait_event_interruptible(device->read_queue,!kfifo_is_empty(&device->my_demo_fifo));
	if(ret) 
	 return ret;
	}
	ret = kfifo_to_user(&device->my_demo_fifo, buf, count, &actual_readed);
	if(ret) {
		return -EIO;
	}
	printk("actual_readed%d pid:%d buf:%s\n",actual_readed,current->pid,buf);

	if(!kfifo_is_full(&device->my_demo_fifo))
	{
	printk("++%s %d buf %s\n",__FUNCTION__,__LINE__ ,buf);
	wake_up_interruptible(&device->write_queue);
	printk("++%s %d buf %s\n",__FUNCTION__,__LINE__ ,buf);
	}

	return actual_readed;
}
static __poll_t demo_poll(struct file *file, struct poll_table_struct *wait) 
{
	int mask = 0;
	struct mydemo_private_data *data = file->private_data;
	struct my_demo_device *device = data->device;
/*add thread to wait queue*/
	poll_wait(file,&device->read_queue,wait);
	poll_wait(file,&device->write_queue,wait);

	if(!kfifo_is_empty(&device->my_demo_fifo))
		mask |= POLL_IN |POLLRDNORM;
	if(!kfifo_is_full(&device->my_demo_fifo))
		mask |= POLL_OUT|POLLWRNORM;
	return mask;

}
EXPORT_SYMBOL_GPL(sync_ykz);
struct file_operations ops = {
	.owner = THIS_MODULE,
	.open = demo_open,
	.release = demo_release,
	.write = demo_write,
	.read = demo_read,
	.poll = demo_poll 

};
static int __init my_test_module(void) 
{
	int ret;
	int i;
	struct my_demo_device *device;
	if(debug) {
	printk("my fist kernel module init debug %d\n",debug);
	printk("my fist kernel module init mytest%d\n",mytest);
	}

	ret = alloc_chrdev_region(&my_dev,0,DEMO_DEVICE_MAX_NUM,DEMO_NAME);
	if(ret) {
		printk("failed to alloc_chrdev_region \n");
		return ret;
	}
	demo_dev = cdev_alloc();
	if(!demo_dev) {
		printk("cdev_alloc failed\n");
		goto unregister_chrdev; 

	}


	cdev_init(demo_dev,&ops);
	ret = cdev_add(demo_dev,my_dev,DEMO_DEVICE_MAX_NUM);//加入的设备数要对,不然加载创建节点后,测试 会发现节点不存在
	if(ret) {
		printk("cdev fail\n");
		goto cdev_fail;
	}
	printk("succful register dev [%d:%d]\n",MAJOR(my_dev),MINOR(my_dev));
	for(i = 0;i<DEMO_DEVICE_MAX_NUM;i++ ){
	        device = kmalloc(sizeof(struct my_demo_device),GFP_KERNEL);
		if(!device){
			ret = -ENOMEM;
			goto free_device;
		}
		sprintf(device->name,"%s%d",DEMO_NAME,i);
		my_demo_device[i] = device;
		init_waitqueue_head(&device->read_queue);
		init_waitqueue_head(&device->write_queue);
		ret = kfifo_alloc(&device->my_demo_fifo,DEMO_DEVICE_KFIFO_SIZE,GFP_KERNEL);
		if(ret) {
			ret = -ENOMEM;
			goto free_kfifo;
		}
	}
	return 0;

free_kfifo:

        for(i= 0;i<DEMO_DEVICE_MAX_NUM;i++) {
		if(&device->my_demo_fifo)
		kfifo_free(&device->my_demo_fifo);
	}
free_device:
	for(i= 0;i<DEMO_DEVICE_MAX_NUM;i++) {
		if(my_demo_device[i]) {
			kfree(my_demo_device[i]);
		}
	}
cdev_fail:
	cdev_del(demo_dev);
unregister_chrdev:
	unregister_chrdev_region(my_dev,DEMO_DEVICE_MAX_NUM);
	
	return 0;
}
static void __exit my_test_module_exit(void)
{
	unregister_chrdev_region(my_dev,count);

	printk("good bye\n");
}
void sync_zyk(void)
{
	printk("%s %d\n",__FUNCTION__,__LINE__);
}
EXPORT_SYMBOL_GPL(sync_zyk);
module_init(my_test_module);
module_exit(my_test_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYK");
MODULE_DESCRIPTION("MY TEST KERNEL MODULE\n");
MODULE_ALIAS("MY ZYK TEST");

其中:加入的设备数要对,不然加载创建节点后,测试 会发现节点不存在

ret = cdev_add(demo_dev,my_dev,DEMO_DEVICE_MAX_NUM);//加入的设备数要对,不然加载创建节点后,测试 会发现节点不存在

应用 测试 程序 :

#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>

int main(void)
{
     int ret;
     struct pollfd fds[2];
     char buffer0[64];
     char buffer1[64];

     fds[0].fd = open("/dev/my_demo_cdev0",O_RDWR);
     if(fds[0].fd == -1)
     {
	     printf("ret == -1 ,errno %d\n",errno);
	     goto fail;
     }
     fds[0].events = POLLIN;
     fds[0].revents = 0;
     fds[1].fd = open("/dev/my_demo_cdev1",O_RDWR);
     if(fds[1].fd == -1)
     {
	     printf("2 ret == -1 ,errno %d\n",errno);
	     goto fail;
     }

     fds[1].events = POLLIN;
     fds[1].revents = 0;

     while(1) {
	     ret = poll(fds,2,-1);
	     if(ret == -1)goto fail;
	     if(fds[0].revents &POLLIN) {
		ret = read(fds[0].fd,buffer0,64);
		if(ret < 0)goto fail;
		printf("buf0 :%s\n",buffer0);

	     }
	     if(fds[1].revents &POLLIN) {
		ret = read(fds[1].fd,buffer1,64);
		if(ret < 0)goto fail;
		printf("buf1 :%s\n",buffer1);

	     }
     }
	return 0;
fail:
	perror("poll exit");
	exit(EXIT_FAILURE);
}

gcc test_poll.c -o test_poll --static
测试 输出结果

benshushu:7-module# mknod /dev/my_demo_cdev1 c 249 0
benshushu:7-module# mknod /dev/my_demo_cdev0 c 249 1

benshushu:7-module# ./test_poll &
[1] 626
benshushu:7-module# [  549.298201] demo_open 48 major:249 minor:1
[  549.298549] demo_open 48 major:249 minor:0

benshushu:7-module# 
benshushu:7-module# echo "HELLO" > /dev/my_demo_cdev0
[  576.128804] demo_open 48 major:249 minor:1
[  576.131091] demo_write 74 pid 565
[  576.135177] demo_write 85 buf HELLO
[  576.135177] 
[  576.138818] ++demo_write 88 buf HELLO
[  576.138818] 
[  576.140524] --demo_write 90 buf HELLO
[  576.140524] 
[  576.144233] actual_write 6
[  576.146630] demo_release 63 major:249 minor:1
benshushu:7-module# [  576.154643] demo_read 103 pid 626

benshushu:7-module# echo "HELLO" > /dev/my_demo_cdev1
[  580.600051] demo_open 48 major:249 minor:0
[  580.601654] demo_write 74 pid 565
[  580.602272] demo_write 85 buf HELLO
[  580.602272] 
[  580.604190] ++demo_write 88 buf HELLO
[  580.604190] 
[  580.605574] --demo_write 90 buf HELLO
[  580.605574] 
[  580.607149] actual_write 6
[  580.613762] demo_release 63 major:249 minor:0
benshushu:7-module# echo "HELLO" > /dev/my_demo_cdev0
[  584.045329] demo_open 48 major:249 minor:1
[  584.046485] demo_write 74 pid 565
[  584.048489] demo_write 85 buf HELLO
[  584.048489] 
[  584.049940] ++demo_write 88 buf HELLO
[  584.049940] 
[  584.052012] --demo_write 90 buf HELLO
[  584.052012] 
[  584.055868] actual_readed6 pid:626 buf:HELLO
[  584.055868] 
[  584.059763] actual_write 6
[  584.061424] ++demo_read 119 buf HELLO
[  584.061424] 
[  584.062581] demo_release 63 major:249 minor:1
[  584.065017] ++demo_read 121 buf HELLO
[  584.065017] 
benshushu:7-module# buf0 :HELLO


benshushu:7-module# echo "HELLO" > /dev/my_demo_cdev1
[  587.325212] demo_open 48 major:249 minor:0
[  587.326571] demo_write 74 pid 565
[  587.328528] demo_write 85 buf HELLO
[  587.328528] 
[  587.329420] ++demo_write 88 buf HELLO
[  587.329420] 
[  587.332490] --demo_write 90 buf HELLO
[  587.332490] 
[  587.333267] actual_write 6
[  587.333909] demo_release 63 major:249 minor:0
benshushu:7-module# 
benshushu:7-module# echo "HELLO" > /dev/my_demo_cdev0
[  599.130927] demo_open 48 major:249 minor:1
[  599.131297] demo_write 74 pid 565
[  599.131429] demo_write 85 buf HELLO
[  599.131429] 
[  599.131593] ++demo_write 88 buf HELLO
[  599.131593] 
[  599.131878] --demo_write 90 buf HELLO
[  599.131878] 
[  599.132106] actual_write 6
[  599.132234] demo_read 103 pid 626
[  599.132260] demo_release 63 major:249 minor:1
[  599.132399] actual_readed6 pid:626 buf:HELLO
[  599.132399] 
[  599.132797] ++demo_read 119 buf HELLO
[  599.132797] 
[  599.133044] ++demo_read 121 buf HELLO
[  599.133044] 
buf0 :HELLO

benshushu:7-module# 
benshushu:7-module# echo "HELLO" > /dev/my_demo_cdev1
[  607.165020] demo_open 48 major:249 minor:0
[  607.167399] demo_write 74 pid 565
[  607.168070] demo_write 85 buf HELLO
[  607.168070] 
[  607.168826] ++demo_write 88 buf HELLO
[  607.168826] 
[  607.170865] demo_read 103 pid 626
[  607.170911] --demo_write 90 buf HELLO
[  607.170911] 
[  607.171217] actual_write 6
[  607.172271] actual_readed6 pid:626 buf:HELLO
[  607.172271] 
[  607.173507] demo_release 63 major:249 minor:0
[  607.179180] ++demo_read 119 buf HELLO
[  607.179180] 
[  607.182949] ++demo_read 121 buf HELLO
[  607.182949] 
buf1 :HELLO

benshushu:7-module# echo "HELLO" > /dev/my_demo_cdev0
[  611.609496] demo_open 48 major:249 minor:1
[  611.611625] demo_write 74 pid 565
[  611.612266] demo_write 85 buf HELLO
[  611.612266] 
[  611.613061] ++demo_write 88 buf HELLO
[  611.613061] 
[  611.615407] --demo_write 90 buf HELLO
[  611.615407] 
[  611.616608] actual_write 6
[  611.616916] demo_read 103 pid 626
[  611.617531] demo_release 63 major:249 minor:1
benshushu:7-module# [  611.618896] actual_readed6 pid:626 buf:HELLO
[  611.618896] 
[  611.626279] ++demo_read 119 buf HELLO
[  611.626279] 
[  611.627515] ++demo_read 121 buf HELLO
[  611.627515] 
buf0 :HELLO


benshushu:7-module# 


异部通知:

测试 程序编译报错:

gcc test_fasyn.c  -o test_fasyn
test_fasyn.c: In function ‘main’:
test_fasyn.c:36:19: warning: assignment to ‘void (*)(int,  siginfo_t *, void *){aka ‘void (*)(int,  struct <anonymous> *, void *)} from incompatible pointer type ‘void (*)(int,  siginfo_t *){aka ‘void (*)(int,  struct <anonymous> *)} [-Wincompatible-pointer-types]
   36 |  act.sa_sigaction = my_signal_fun;
      |                   ^
test_fasyn.c:47:14: error: ‘F_SETSIG’ undeclared (first use in this function)
   47 |  if(fcntl(fd,F_SETSIG,SIGIO) == -1) {
      |              ^~~~~~~~
test_fasyn.c:47:14: note: each undeclared identifier is reported only once for each function it appears in

解决 方法 :
定义宏

#define _GNU_SOURCE

驱动:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/slab.h>
#include <linux/device.h>
#define MEM_SIZE 256
#define MEM_NAME "my_demo_cdev"
struct mem_dev
{
	struct cdev dev;
	char mem[MEM_SIZE];
	struct fasync_struct *async_queue;
};
static struct mem_dev *mem_dev_p;
static dev_t mem_devno;
static struct class *mem_class;


static ssize_t mem_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
	struct mem_dev *dev_p = filp->private_data;
	if (count > MEM_SIZE)
		count = MEM_SIZE;
	if (copy_to_user(buf, dev_p->mem, count))
	{
		return -EFAULT;
	}

	return count;
}


static ssize_t mem_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
	struct mem_dev *dev_p = filp->private_data;

	if (count > MEM_SIZE)
		count = MEM_SIZE;
	if (copy_from_user(dev_p->mem, buf, count))
	{
		return -EFAULT;
	}

	if (dev_p->async_queue)
	{
		kill_fasync(&dev_p->async_queue, SIGIO, POLL_IN);
	}

	return count;
}



static int mem_fasync(int fd, struct file *filp, int mode)
{
	struct mem_dev *dev_p = filp->private_data;
	
	return fasync_helper(fd, filp, mode, &dev_p->async_queue);
}


static int mem_open(struct inode * inode , struct file * filp)
{
	filp->private_data = mem_dev_p;
	return 0;
}


static int mem_release(struct inode * inode, struct file *filp)
{
	mem_fasync(-1, filp, 0);
	return 0;
}


static const struct file_operations mem_fops = 
{
	.owner = THIS_MODULE,
	.open = mem_open,
	.release = mem_release,
	.read = mem_read,
	.write = mem_write,
	.fasync = mem_fasync,
};


static int __init my_mem_init(void)
{
	int ret;
	ret = alloc_chrdev_region(&mem_devno, 0, 1, MEM_NAME);
	if (ret)
	{
		goto out_1;
	}

	mem_dev_p = kmalloc(sizeof(struct mem_dev), GFP_KERNEL);
	if (NULL == mem_dev_p)
	{
		ret = -ENOMEM;
		goto out_2;
	}
	
	memset(mem_dev_p, 0, sizeof(struct mem_dev));

	cdev_init(&mem_dev_p->dev, &mem_fops);
	mem_dev_p->dev.owner = THIS_MODULE;
	mem_dev_p->dev.ops = &mem_fops;
	ret = cdev_add(&mem_dev_p->dev, mem_devno, 1);
	if (ret)
	{
		goto out_3;
	}

	mem_class = class_create(THIS_MODULE, "my_demo_cdev");
	device_create(mem_class, NULL, mem_devno, NULL, "my_demo_cdev");

	printk("mem_init\n");
	return 0;

out_3: kfree(mem_dev_p);
out_2: unregister_chrdev_region(mem_devno, 1);
out_1: return ret;
}


static void __exit my_mem_exit(void)
{
	device_destroy(mem_class, mem_devno);
	class_destroy(mem_class);

	cdev_del(&mem_dev_p->dev);
	kfree(mem_dev_p);
	unregister_chrdev_region(mem_devno, 1);

	printk("mem_exit\n");
}

MODULE_LICENSE("Dual BSD/GPL");
module_init(my_mem_init);
module_exit(my_mem_exit);

测试 程序 :

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


void catch_sigio(int signu)
{
	printf("catch signo\n");
}


int main(void)
{
  int flags;
	if (SIG_ERR == signal(SIGIO, catch_sigio))
	{
		printf("signal failed\n");
		return -1;
	}

	int fd;
	fd = open("/dev/my_demo_cdev", O_RDWR);
	if (-1 == fd)
	{
		perror("open");
		return -2;
	}
	printf("open success\n");

	
	fcntl(fd, F_SETOWN, getpid());
	flags = fcntl(fd, F_GETFL);
	fcntl(fd, F_SETFL, flags | O_ASYNC);


	while (1)
	{
		NULL;
	}
	return 0;
}

测试结果,收到底层的通知

benshushu:9-module# ./test_async  &
[1] 1675
benshushu:9-module# open success
benshushu:9-module# echo 1 > /dev/my_demo_cdev 
benshushu:9-module# catch signo

驱动中添加 了如下,


	mem_class = class_create(THIS_MODULE, "my_demo_cdev");
	device_create(mem_class, NULL, mem_devno, NULL, "my_demo_cdev");

会自动生成 节点

benshushu:9-module# ls /dev/my_demo_cdev  -al
crw------- 1 root root 249, 0 Jun  6 05:30 /dev/my_demo_cdev
benshushu:9-module# 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值