面向对象地分析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#