全部博文(298)
分类: LINUX
2011-03-24 12:50:23
(6)poll方法应用及其自动创建设备-模拟FIFO
注:所以文章红色字体代表需要特别注意和有问题还未解决的地方,蓝色字体表示需要注意的地方
1.本文所介绍的程序平台
开发板:arm9-mini2440
虚拟机为:Red Hat Enterprise Linux 5
开发板上系统内核版本:linux-2.6.32.2
Poll方法
什么是Poll方法,功能是什么?
系统调用(用户空间)
Open
Close
Read
Write
Ioctl
lseek
Select
驱动(内核空间)
Open
Release
Read
Write
Ioctl
llseek
Poll
Select系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用 进程。
int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set
*exceptfds, const struct timeval *timeout)
Maxfd:
文件描述符的范围,比待检测的最大文件描述符大1
Readfds:
被读监控的文件描述符集
Writefds:
被写监控的文件描述符集
Exceptfds:
被异常监控的文件描述符集;
Timeout:
定时器
Timeout取不同的值,该调用有不同的表现:
Timeout值为0,不管是否有文件满足要求,都立刻返
回,无文件满足要求返回0,有文件满足要求返回一个正值。
Timeout为NULL,select将阻塞 进程,直到某个文件
满足要求
Timeout值为正整数,就是等待的最长时间,即 select在timeout时间内阻塞 进程。
Select调用返回时,返回值有如下情况:
1.正常情况下返回满足要求的文件描述符个 数;
2.经过了timeout等待后仍无文件满足要求,返回值为0;
3.如果select被某个信号中断,它将返回-1并设置errno为EINTR。
4.如果出错,返回-1并设置相应的errno。
Select系统调用(使用方法)
1. 将要监控的文件添加到文件描述符集
2. 调用Select开始监控
3. 判断文件是否发生变化
系统提供了4个宏对描述符集进行操作:
#include
void FD_SET(int fd, fd_set *fdset)
void FD_CLR(int fd, fd_set *fdset)
void FD_ZERO(fd_set *fdset)
void FD_ISSET(int fd, fd_set *fdset)
宏FD_SET将文件描述符fd添加到文件描述符集fdset中;
宏FD_CLR从文件描述符集fdset中清除文件描述符fd;
宏FD_ZERO清空文件描述符集fdset;
在调用select后使用FD_ISSET来检测文件描述符集fdset中的文件fd发生了变化。
FD_ZERO(&fds); //清空集合
FD_SET(fd1,&fds); //设置描述符
FD_SET(fd2,&fds); //设置描述符
maxfdp=fd1+1; //描述符最大值加1,假设fd1>fd2
switch(select(maxfdp,&fds,NULL,NULL,&timeout))
case -1: exit(-1);break; //select错误,退出程序
case 0:break;
default:
if(FD_ISSET(fd1,&fds)) //测试fd1是否可读
Poll方法
应用程序常常使用select系统调用,它可能会阻塞进程。这个调用由驱动的 poll方法实现,原型为:
unsigned int (*poll)(struct file *filp,poll_table *wait)
Poll设备方法负责完成:
1. 使用poll_wait将等待队列添加到poll_table中。
2. 返回描述设备是否可读或可写的掩码。
位掩码
POLLIN
设备可读
POLLRDNORM
数据可读
POLLOUT
设备可写
POLLWRNORM
数据可写
设备可读通常返回(POLLIN|POLLRDNORM )
设备可写通常返回(POLLOUT|POLLWRNORM )
范例
static unsigned int mem_poll(struct file *filp,poll_table *wait)
{
struct scull_pipe *dev =filp->private_data;
unsigned int mask =0;
/* 把进程添加到等待队列 */
poll_wait(filp,&dev->inq,wait);
/*返回掩码*/
if (有数据可读)
mask = POLLIN |POLLRDNORM;/*设备可读*/
return mask;
}
2.程序清单
本次实验程序为《LDD3》的代码,本人作了改动和较为详细的注释,如有错误请指出。
poll_fs.h
#ifndef _GONG_H_
#define _GONG_H_
#include
#undef PDEBUG /* undef it, just in case */
#ifdef GONG_DEBUG
# ifdef __KERNEL__
/* This one if debugging is on, and kernel space */
# define PDEBUG(fmt, args...) printk( KERN_DEBUG "gong_pipe: " fmt, ## args)
# else
/* This one for user space */
# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
# endif
#else
# define PDEBUG(fmt, args...) /* not debugging: nothing */
#endif
#undef PDEBUGG
#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */
#ifndef GONG_MAJOR
#define GONG_MAJOR 0 /*预设的mem的主设备号*/
#endif
#ifndef GONG_NR_DEVS
#define GONG_NR_DEVS 3/*设备数*/
#endif
#ifndef GONG_SIZE
#define GONG_SIZE 1024
#endif
#define BUFSIZE 1024
/*定义幻数*/
/* Use 'k' as magic number */
#define GONG_IOC_MAGIC 'k'
/* Please use a different 8-bit number in your code */
#define GONG_P_IOCTSIZE _IO(GONG_IOC_MAGIC, 0)
#define GONG_P_IOCQSIZE _IO(GONG_IOC_MAGIC, 1)
/* ... more to come */
#define GONG_IOC_MAXNR 1
#endif
poll_fs.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "poll_fs.h"
int gong_poll_major = GONG_MAJOR;
int gong_poll_minor = 0;
int gong_poll_nr = GONG_NR_DEVS;/* number of pipe devices */
int gong_poll_buffer = BUFSIZE;/* buffer size */
/*module_param 字段是一个权限值; 你应当使用
这个值控制谁可以存取这些模块参数在 sysfs 中的表示.如果 perm 被设为 0,
就根本没有 sysfs 项. 否则, 它出现在 /sys/module下面, 带有给定的权限.
使用 S_IRUGO 作为参数可以被所有人读取, 但是不能改变; S_IRUGO|S_IWUSR
允许 root 来改变参数. 注意, 如果一个参数被 sysfs 修改, 你的模块看到的参数值也改变了,
但是你的模块没有任何其他的通知. 你应当不要使模块参数可写,
除非你准备好检测这个改变并且因而作出反应.*/
module_param(gong_poll_major, int, S_IRUGO);
module_param(gong_poll_minor, int, S_IRUGO);
module_param(gong_poll_nr, int, 0);
module_param(gong_poll_buffer, int, 0);
struct gong_pipe {
/*等待队列可实现进程的阻塞,等待队列可看作保存进程的容器*/
wait_queue_head_t inq, outq; /* read and write queues */
char *buffer, *end; /* begin of buf, end of buf */
int buffersize; /* used in pointer arithmetic */
char *rp, *wp; /* where to read, where to write */
int nreaders, nwriters; /* number of openings for r/w */
struct fasync_struct *async_queue; /* asynchronous readers */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
//struct class *dev_class;
//class结构指针,用来保存即将要创建的设备结构名,用于自动在/dev下创建设备文件,再为每个设备调用device_create创建对应的设备
//struct device *device1;
//两个即将在、/dev 下创建的设备名
//自动创建设备文件
static struct class * myclass;
static struct device * mydev;
/* parameters */
dev_t gong_p_devno; /* Our first device number */
static struct gong_pipe *gong_p_devices;//pointer!!!!!
static int gong_p_fasync(int fd, struct file *filp, int mode);
static int spacefree(struct gong_pipe *dev);
static int gong_p_open(struct inode *inode, struct file *filp)
{
struct gong_pipe *dev;
/*第一种获取方法*/
//dev = container_of(inode->i_cdev, struct gong_pipe, cdev);
//filp->private_data = dev;
/*第二种获取方法*/
/*获取次设备号*/
int num = MINOR(inode->i_rdev);
/*大于最大的次设备号就错误了 因为注册的时候要指定次设备的数目*/
if(num >= gong_poll_nr)
return -ENODEV;
dev = &gong_p_devices[num];//
/*将设备描述结构指针赋值给文件私有数据指针*/
filp->private_data = dev;//!!
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
if (!dev->buffer) {//未初始化的buffer一定为NULL吗???
/* allocate the buffer */
dev->buffer = kmalloc(gong_poll_buffer, GFP_KERNEL);
if (!dev->buffer) {
up(&dev->sem);
return -ENOMEM;
}
}
dev->buffersize = gong_poll_buffer;
dev->end = dev->buffer + dev->buffersize;//确定缓冲区结束地址
dev->rp = dev->wp = dev->buffer; /* rd and wr from the beginning !!!!*/
/* use f_mode,not f_flags: it's cleaner (fs/open.c tells why) */
if (filp->f_mode & FMODE_READ)
dev->nreaders++;
if (filp->f_mode & FMODE_WRITE)
dev->nwriters++;
up(&dev->sem);
return nonseekable_open(inode, filp);
}
static int gong_p_release(struct inode *inode, struct file *filp)//关闭文件描述符的时候
{
struct gong_pipe *dev = filp->private_data;
/* remove this filp from the asynchronously notified filp's */
/*当文件被关闭时必须调用fasync 方法,来从活动的异步读取进程列表中删除该文件。
尽管这个调用仅当 filp->f_flags 被设置为 FASYNC 时才需要,但不管什么情况,
调用这个函数不会有问题,并且是普遍的实现方法*/
gong_p_fasync(-1, filp, 0);
down(&dev->sem);
if (filp->f_mode & FMODE_READ)
dev->nreaders--;
if (filp->f_mode & FMODE_WRITE)
dev->nwriters--;
if (dev->nreaders + dev->nwriters == 0) {
kfree(dev->buffer);
dev->buffer = NULL; /* the other fields are not checked on open */
}
up(&dev->sem);
return 0;
}
static ssize_t gong_p_read (struct file *filp, char __user *buf, size_t count,
loff_t *f_pos)
{
struct gong_pipe *dev = filp->private_data;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
//考虑到多个Read操作同时等待的情况,这里不能用if代理while???
while (dev->rp == dev->wp) { /* nothing to read */
up(&dev->sem); /* release the lock */
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
/*printk(KERN_INFO "The process is \"%s\" (pid %i)\n", current->comm, current->pid);
存于 current->comm
的命令名称是由当前进程执行的程序文件的基本名称( 截短到 15 个字符, 如果需要 ).
*/
PDEBUG("\"%s\" reading: going to sleep\n", current->comm);
if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp)))
return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
/* otherwise loop, but first reacquire the lock */
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
}
/* ok, data is there, return something !!!*/
if (dev->wp > dev->rp)
count = min(count, (size_t)(dev->wp - dev->rp));//
else /* the write pointer has wrapped, return data up to dev->end */
/*the write pointer has wrapped 指的是数据写满 后wp指针会自动设定为开始的位置*/
count = min(count, (size_t)(dev->end - dev->rp));
printk("<5> count is %d\n", count);
///copy_to_user成功返回0 copy过去不会自动添加'\0'???
//好像要自动添加注意把测试程序的缓冲区memset 这样原有的数据不会造成影响
if (copy_to_user(buf, dev->rp, count)) {
up (&dev->sem);
return -EFAULT;
}
dev->rp += count;
if (dev->rp == dev->end)/*如果rp读完了 指针设定为开始的位置*/
dev->rp = dev->buffer; /* wrapped */
up (&dev->sem);
/* finally, awake any writers and return */
wake_up_interruptible(&dev->outq);
PDEBUG("\"%s\" did read %li bytes\n",current->comm, (long)count);/////
return count;
}
/* How much space is free? */
/*循环队列 计算空余空间*/
static int spacefree(struct gong_pipe *dev)
{
if (dev->rp == dev->wp)
return dev->buffersize;
//由于gong_poll_buffer = buffersize 所以不用buffersize-1
return ((dev->rp + dev->buffersize - dev->wp) % dev->buffersize);
}
/* Wait for space for writing; caller must hold device semaphore. On
* error the semaphore will be released before returning. */
static int gong_getwritespace(struct gong_pipe *dev, struct file *filp)
{
/*手工休眠
(1)创建和初始化一个等待队列
(2)添加等待队列入口到队列,并设置进程状态
(3)在检查确认仍然需要休眠之后调用 schedule
(4)schedule 返回,就到了清理时间:
void finish_wait(wait_queue_head_t *queue, wait_queue_t *wait);
*/
/*认真地看简单休眠中的 wait_event(queue, condition) 和 wait_event_interruptible(queue, condition)
底层源码会发现,其实他们只是手工休眠中的函数的组合。所以怕麻烦的话还是用wait_event比较好。*/
while (spacefree(dev) == 0) { /* full */
//创建和初始化一个等待队列。常由宏定义完成:
/*常用的做法是放一个 DEFINE_WAIT 在循环的顶部,来实现休眠。*/
DEFINE_WAIT(wait);
up(&dev->sem);
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
PDEBUG("\"%s\" writing: going to sleep\n",current->comm);
/*void prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state);
queue 和 wait 分别地是等待队列头和进程入口。state 是进程的新状态:
TASK_INTERRUPTIBLE(可中断休眠,推荐)或TASK_UNINTERRUPTIBLE(不可中断休眠,不推荐)。*/
prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE);
if (spacefree(dev) == 0)
schedule();
finish_wait(&dev->outq, &wait);
/*检测进程p是否有待处理的信号(p->thread_info->flags中TIF_SIGPENDING位是否置位)*/
if (signal_pending(current))
return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
}
return 0;
}
static ssize_t gong_p_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos)
{
struct gong_pipe *dev = filp->private_data;
int result;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
/* Make sure there's space to write */
result = gong_getwritespace(dev, filp);
if (result)
return result; /* gong_getwritespace called up(&dev->sem) */
/* ok, space is there, accept something */
count = min(count, (size_t)spacefree(dev));
if (dev->wp >= dev->rp)
count = min(count, (size_t)(dev->end - dev->wp)); /* to end-of-buf */
else /* the write pointer has wrapped, fill up to rp-1 */
count = min(count, (size_t)(dev->rp - dev->wp - 1));
PDEBUG("Going to accept %li bytes to %p from %p\n", (long)count, dev->wp, buf);
if (copy_from_user(dev->wp, buf, count)) {
up (&dev->sem);
return -EFAULT;
}
dev->wp += count;
if (dev->wp == dev->end)
dev->wp = dev->buffer; /* wrapped */
up(&dev->sem);
/* finally, awake any reader */
wake_up_interruptible(&dev->inq); /* blocked in read() and select() */
/*当数据到达时,kill_fasync 被用来通知相关的进程,它的参数是被传递的信号(常常是 SIGIO)和 band(几乎都是 POLL_IN)。*/
/* and signal asynchronous readers, explained late in chapter 5 */
if (dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
PDEBUG("\"%s\" did write %li bytes\n",current->comm, (long)count);
return count;
}
static unsigned int gong_p_poll(struct file *filp, poll_table *wait)
{
struct gong_pipe *dev = filp->private_data;
unsigned int mask = 0;
/*
* The buffer is circular; it is considered full
* if "wp" is right behind "rp" and empty if the
* two are equal.
*/
down(&dev->sem);
/*poll_wait函数所做的工作是把当前进程的等待队列添加到wait参数指定的等待列表(poll_table)中
poll_wait函数并不阻塞,程序中poll_wait(filp, &outq, wait)这句话的意思并不是说一直
等待outq信号量可获得,真正的阻塞动作是上层的select/poll函数中完成的
对可能引起设备文件状态变化的等待队列调用poll_wait()函数,将对应的等待队列头添加到poll_table;
*/
poll_wait(filp, &dev->inq, wait);//
poll_wait(filp, &dev->outq, wait);
if (dev->rp != dev->wp)
mask |= POLLIN | POLLRDNORM; /* readable */
if (spacefree(dev))
mask |= POLLOUT | POLLWRNORM; /* writable */
up(&dev->sem);
return mask;
}
/* remove this filp from the asynchronously notified filp's
scull_p_fasync(-1, filp, 0);*/
static int gong_p_fasync(int fd, struct file *filp, int mode)
{
struct gong_pipe *dev = filp->private_data;
/*调用fasync_helper 来从相关的进程列表中添加或去除文件。除了最后一个参数,
其他所有参数都时被提供给 fasync 方法的相同参数并被直接传递。
当数据到达时,kill_fasync 被用来通知相关的进程,它的参数是被传递的信号(常常是 SIGIO)
和 band(几乎都是 POLL_IN)。*/
/*
fasync_helper is invoked to add or remove entries from the list of interested processes.
我的理解是:当A、B、C三个进程同时使用FASYNC标志打开同一个设备时候,该函数会被调用三次,
并且都被加入到设备的fasync_struct数据结构中。当发送通知消息时候,三个进程会同时收到。
*/
return fasync_helper(fd, filp, mode, &dev->async_queue);///
}
int gong_p_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int err = 0;
struct gong_pipe *dev = filp->private_data;
/*
* extract the type and number bitfields, and don't decode
* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
*/
if (_IOC_TYPE(cmd) != GONG_IOC_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd) > GONG_IOC_MAXNR) return -ENOTTY;
/*
* the direction is a bitmask, and VERIFY_WRITE catches R/W
* transfers. `Type' is user-oriented, while
* access_ok is kernel-oriented, so the concept of "read" and
* "write" is reversed
*/
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (err) return -EFAULT;
switch(cmd) {
/*
* The following two change the buffer size for gongpipe.
* The gongpipe device uses this same ioctl method, just to
* write less code. Actually, it's the same driver, isn't it?
*/
case GONG_P_IOCTSIZE:
//?????????????????????????????????????????????
/*gong_poll_buffer = arg;//这里这样设置完全无用 在打开的时候就会设定缓冲区大小!!!下面为我修改的代码 先释放空间在重新分配*/
kfree(dev->buffer);
gong_poll_buffer = arg;
dev->buffer = kmalloc(gong_poll_buffer, GFP_KERNEL);
dev->buffersize = gong_poll_buffer;
dev->end = dev->buffer + dev->buffersize;//确定缓冲区结束地址
dev->rp = dev->wp = dev->buffer; /* rd and wr from the beginning !!!!*/
break;
case GONG_P_IOCQSIZE:
return gong_poll_buffer;
default: /* redundant, as cmd was checked against MAXNR */
return -ENOTTY;
}
return 0;
}
struct file_operations gong_pipe_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = gong_p_read,
.write = gong_p_write,
.poll = gong_p_poll,
.ioctl = gong_p_ioctl,
.open = gong_p_open,
.release = gong_p_release,
.fasync = gong_p_fasync,
};
/*
* Set up a cdev entry.
*/
static void gong_p_setup_cdev(struct gong_pipe *dev, int index)
{
//int err, devno = gong_p_devno + index;//
int err;
//测试 经过测试每次加index的值就是合成的设备号的值
dev_t devno = MKDEV(gong_poll_major, index);
printk("<5> gong_p_devno: %ld\n", (long)gong_p_devno);
printk("<5> index: %ld\n", (long)index);
printk("<5> devno: %uld\n", (unsigned long)devno);
devno = gong_p_devno + index;
printk("<5> gong_p_devno: %ld\n", (long)gong_p_devno);
printk("<5> index: %ld\n", (long)index);
printk("<5> devno: %uld\n", (unsigned long)devno);
cdev_init(&dev->cdev, &gong_pipe_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add (&dev->cdev, devno, 1);//??为啥是1啊??应该是gong_poll_nr? 这里注册难道不是一起注册的啊
//根据我的猜测这里每次循环只注册了一个所以为1
if (err)
printk(KERN_NOTICE "Error %d adding gongpipe%d", err, index);
}
int gong_p_init(void)
{
int result, i;
myclass = class_create(THIS_MODULE, "my_device_driver");
/*
* Get a range of minor numbers to work with, asking for a dynamic
* major unless directed otherwise at load time.
*/
if (gong_poll_major) {
gong_p_devno = MKDEV(gong_poll_major, gong_poll_minor);
result = register_chrdev_region(gong_p_devno, gong_poll_nr, "gong_pipe");
} else {
result = alloc_chrdev_region(&gong_p_devno, 0, gong_poll_nr,
"gong_pipe");
gong_poll_major = MAJOR(gong_p_devno);
//根据我的猜测动态分配的设备号 次设备号从0开始
printk("<5> %ld\n", (long)gong_p_devno);
gong_p_devno = MKDEV(gong_poll_major, 0);
printk("<5> %ld\n", (long)gong_p_devno);
//测试动态分配的设备号的起始位置经常测试次设备号从0开始
}
if (result < 0) {
printk(KERN_WARNING "pipe: can't get major %d\n", gong_poll_major);
return result;
}
/*设备注册:
1.分配cdev
动态分配:
struct cdev *cdev_alloc(void)
2.初始化cdev
void cdev_init(struct cdev *cdev, const struct file_operation *fops)
3.添加cdev
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
*/
/*初始化cdev结构*/
//cdev_init(&cdev, &gong_pipe_fops);
/*注册字符设备*/
//cdev_add(&cdev, MKDEV(gong_poll_major, 0), gong_poll_nr);
//注意这里分配了gong_poll_nr个数目的gong_p_devices结构的空间
//要先分配 这样才可以对其他的分配
//这里要先分配了才能注册设备
gong_p_devices = kmalloc(gong_poll_nr * sizeof(struct gong_pipe), GFP_KERNEL);
if (gong_p_devices == NULL) {
unregister_chrdev_region(gong_p_devno, gong_poll_nr);
return 0;
}
memset(gong_p_devices, 0, gong_poll_nr * sizeof(struct gong_pipe));
for (i = 0; i < gong_poll_nr; i++) {
init_waitqueue_head(&(gong_p_devices[i].inq));
init_waitqueue_head(&(gong_p_devices[i].outq));
init_MUTEX(&gong_p_devices[i].sem);
gong_p_setup_cdev(gong_p_devices + i, i);
}
#ifdef GONG_DEBUG
create_proc_read_entry("gongpipe", 0, NULL, gong_read_p_mem, NULL);//??
#endif
//自动创建设备文件
//mydev = device_create(myclass, NULL, MKDEV(gong_poll_major, 0), "scullpipe0");
//2.6.26以上
mydev = device_create(myclass, NULL, MKDEV(gong_poll_major, 0), NULL,"scullpipe0");
mydev = device_create(myclass, NULL, MKDEV(gong_poll_major, 1), NULL,"scullpipe1");
return 0;
}
/*
* This is called by cleanup_module or on failure.
* It is required to never fail, even if nothing was initialized first
*/
void gong_p_cleanup(void)
{
int i;
#ifdef GONG_DEBUG
remove_proc_entry("gong_pipe", NULL);//??/
#endif
/*
//卸载模块时候也要同事把/dev/下的设备名卸载掉
device_unregister(mydev); //卸载掉dev中对应的设备文件
class_destroy(myclass); //卸载设备队对应的class
*/
//?????????????device_destroy device_unregister的区别
if (!gong_p_devices)
return; /* nothing else to release */
device_destroy(myclass, gong_p_devno);
//cdev_del(&cdev);
for (i = 0; i < gong_poll_nr; i++) {
cdev_del(&gong_p_devices[i].cdev);
kfree(gong_p_devices[i].buffer);//
}
kfree(gong_p_devices);
unregister_chrdev_region(gong_p_devno, gong_poll_nr);
gong_p_devices = NULL; /* pedantic */
class_destroy(myclass);
}
module_init(gong_p_init);
module_exit(gong_p_cleanup);
MODULE_LICENSE("Dual BSD/GPL");
注意:先前驱动程序有错 但是自动创建的设备能看见,不能打开,显示的是:
“no such device or address” ,驱动程序错误在初始化的时候添加设备的时候错误注意的
测试代码:
scull.h
#include
/*
* Ioctl definitions
*/
/* Use 'k' as magic number */
#define GONG_IOC_MAGIC 'k'
/* Please use a different 8-bit number in your code */
#define GONG_P_IOCTSIZE _IO(GONG_IOC_MAGIC, 0)
#define GONG_P_IOCQSIZE _IO(GONG_IOC_MAGIC, 1)
pipe_test.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "scull.h"
int main()
{
char buffer1[20]={0};
int pipetest0, pipetest1;
int code=21, i=0;
struct pollfd poll_list[2];
int retval;
if ((pipetest0 = open("/dev/scullpipe0",O_RDONLY)) < 0) {
printf("open scullpipe0 error! \n");
exit(1);
}
printf("open scullpipe0 ! \n");
if ((pipetest1 = open("/dev/scullpipe1",O_RDONLY)) < 0) {
printf("open scullpipe1 error! \n");
exit(1);
}
printf("open scullpipe1 ! \n");
if ( ioctl(pipetest0 , GONG_P_IOCTSIZE , code ) < 0) {
printf("pipetest0 ioctl GONG_P_IOCTSIZE error! \n");
exit(1);
}
printf(" GONG_P_IOCTSIZE : scull_p_buffer0=%d !\n" ,ioctl( pipetest0 , GONG_P_IOCQSIZE , NULL ) );
if ( ioctl(pipetest1 , GONG_P_IOCTSIZE , code ) < 0) {
printf("pipetest1 ioctl GONG_P_IOCTSIZE error! \n");
exit(1);
}
printf(" GONG_P_IOCTSIZE : scull_p_buffer1=%d !\n" ,ioctl( pipetest1 , GONG_P_IOCQSIZE , NULL ) );
close(pipetest0);
printf("close pipetest0 ! \n");
close(pipetest1);
printf("close pipetest1 ! \n");
if ((pipetest0 = open("/dev/scullpipe0",O_RDONLY)) < 0) {
printf("reopen scullpipe0 error! \n");
exit(1);
}
printf("reopen scullpipe0 ! \n");
if ((pipetest1 = open("/dev/scullpipe1",O_RDONLY)) < 0) {
printf("reopen scullpipe1 error! \n");
exit(1);
}
printf("reopen scullpipe1 ! \n");
poll_list[0].fd = pipetest0;
poll_list[1].fd = pipetest1;
poll_list[0].events = POLLIN|POLLRDNORM;
poll_list[1].events = POLLIN|POLLRDNORM;
while(1)
{
retval = poll(poll_list,(unsigned long)2,-1);
/* retval 总是大于0或为-1,因为我们在阻塞中工作 */
if(retval < 0)
{
fprintf(stderr,"poll错误: %s\n",strerror(errno));
return -1;
}
if(poll_list[0].revents&(POLLIN|POLLRDNORM))
{
for(i=0;i<20;i+=5)
buffer1[i] = 80;
if ((code=read(pipetest0 , buffer1 , 20)) != 20) printf("read from pipetest0 error! code=%d\n",code);
else printf("read from pipetest0 ok! code=%d \n",code);
for(i=0;i<20;i+=5)
printf("[%d]=%d [%d]=%d [%d]=%d [%d]=%d [%d]=%d\n",i,buffer1[i],i+1,buffer1[i+1],i+2,buffer1[i+2],i+3,buffer1[i+3],i+4,buffer1[i+4]);
}
if(poll_list[1].revents&(POLLIN|POLLRDNORM))
{
if ((code=read(pipetest1 , buffer1 , 20)) != 20) printf("read from pipetest1 error! code=%d \n",code);
else printf("read from pipetest1 ok! code=%d \n",code);
for(i=0;i<20;i+=5)
printf("[%d]=%c [%d]=%c [%d]=%c [%d]=%c [%d]=%c\n",i,buffer1[i],i+1,buffer1[i+1],i+2,buffer1[i+2],i+3,buffer1[i+3],i+4,buffer1[i+4]);
}
}
close(pipetest0 );
printf("close pipetest0 ! \n");
close(pipetest1 );
printf("close pipetest1 ! \n");
printf("\n");
exit(0);
}
Arm平台pipe管道测试:
[root@FriendlyARM /udisk]# insmod poll_fs.ko
265289728
265289728
gong_p_devno: 265289728
index: 0
devno: 265289728ld//测试设备号怎么递增
gong_p_devno: 265289728
index: 0
devno: 265289728ld
gong_p_devno: 265289728
index: 1
devno: 265289729ld
gong_p_devno: 265289728
index: 1
devno: 265289729ld
gong_p_devno: 265289728
index: 2
devno: 265289730ld
gong_p_devno: 265289728
index: 2
devno: 265289730ld
[root@FriendlyARM /udisk]#
[root@FriendlyARM /udisk]# ls -l /dev/scullpipe1 自动创建成功
crw-rw---- 1 root root 253, 1 Mar 22 09:03 /dev/scullpipe1
[root@FriendlyARM /udisk]# ./pipe_test1 &
[root@FriendlyARM /udisk]# open scullpipe0 !
open scullpipe1 !
GONG_P_IOCTSIZE : scull_p_buffer0=21 !
GONG_P_IOCTSIZE : scull_p_buffer1=21 !
close pipetest0 !
close pipetest1 !
reopen scullpipe0 !
reopen scullpipe1 !
[root@FriendlyARM /udisk]# echo 2232132 > /dev/scullpipe0
count is 8
read from pipetest0 error! code=8
[0]=50 [1]=50 [2]=51 [3]=50 [4]=49
[5]=51 [6]=50 [7]=10 [8]=0 [9]=0
[10]=80 [11]=0 [12]=0 [13]=0 [14]=0
[15]=80 [16]=0 [17]=0 [18]=0 [19]=0
//经过测试 会自动添加’\0’ 在缓冲区的末尾
[root@FriendlyARM /udisk]# echo 2232132 > /dev/scullpipe1
[root@FriendlyARM /udisk]# count is 8
read from pipetest1 error! code=8
[0]=2 [1]=2 [2]=3 [3]=2 [4]=1
[5]=3 [6]=2 [7]=
[8]= [9]=
[10]= [11]= [12]= [13]= [14]=
[15]= [16]= [17]= [18]= [19]=
read_write_test.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "scull.h"
int main()
{
int fd;
char string[80];
char in;
int data;
if ((fd = open("/dev/scullpipe0",O_RDWR)) < 0) {
printf("open scullpipe0 error! \n");
exit(1);
}
while(1)
{
in = getchar();
printf("in is %c \n", in);
switch(in)
{
case '1':
printf("input your string:");
in = getchar();
gets(string);
printf("string is %s \n", string);
if(write(fd, string, strlen(string)) < 0)
{
printf("write error \n");
exit(1);
}
break;
case '2':
printf("input your read number:");
scanf("%d", &data);
in = getchar();
printf("data is %d\n", data);
memset(string, -1, sizeof(string));///!!!!!!测试追加'\0'
if(read(fd, string, data) < 0)
{
printf("read error \n");
exit(1);
}
else
{
//string[data] = '\0';
printf("string is %s \n", string);
}
break;
case '3':
printf("input your buffer size:");
scanf("%d", &data);
in = getchar();
if ( ioctl(fd , GONG_P_IOCTSIZE , data ) < 0) {
printf("pipetest0 ioctl GONG_P_IOCTSIZE error! \n");
exit(1);
}
printf(" GONG_P_IOCTSIZE : scull_p_buffer0=%d !\n" ,ioctl( fd , GONG_P_IOCQSIZE , NULL ) );
break;
}
}
return 0;
}
Arm平台读写测试:
[root@FriendlyARM /udisk]# ./read_write_test
1
in is 1
input your string:gongzhi
string is gongzhi
2
in is 2
input your read number:5
data is 5
count is 5
string is
[0] = 103
[1] = 111
[2] = 110
[3] = 103
[4] = 122
[5] = 80
[6] = 80
[7] = 80
[8] = 80
[9] = 80
[10] = 80
[11] = 80
[12] = 80
[13] = 80
[14] = 80
[15] = 80
[16] = 80
[17] = 80
[18] = 80
[19] = 80
//经过这里测试追加’\0’ 得出不追加 与前面矛盾??
1
in is 1
input your string:gongzhi
string is gongzhi
2
in is 2
input your read number:4
data is 4
count is 4
string is
[0] = 104
[1] = 105
[2] = 103
[3] = 111
[4] = 80
[5] = 80
[6] = 80
[7] = 80
[8] = 80
[9] = 80
[10] = 80
[11] = 80
[12] = 80
[13] = 80
[14] = 80
[15] = 80
[16] = 80
[17] = 80
[18] = 80
[19] = 80
//先输入“gongzhi“读出5个字符后管道里剩“hi”,再输入“gongzhi“,从中读取四个 读出的是”higo“,说明读出之前里面的数据为”higongzhi“ 符合先进先出的fifo规则
//重新设置fifo大小为5个字节 并清空了数据
3
in is 3
input your buffer size:5
GONG_P_IOCTSIZE : scull_p_buffer0=5 !
2
in is 2
input your read number:5
data is 5
//管道里没有数据 阻塞
asynctest.c
#include
#include
#include
#include
#include
#include
int gotdata=0;
void sighandler(int signo)
{
if (signo==SIGIO)
gotdata++;
return;
}
char buffer[21];
int main(int argc, char **argv)
{
int pipetest0;
int count;
struct sigaction action;
if ((pipetest0 = open("/dev/scullpipe0",O_RDONLY)) < 0) {
printf("open scullpipe0 error! \n");
exit(1);
}
memset(&action, 0, sizeof(action));
action.sa_handler = sighandler;
action.sa_flags = 0;
sigaction(SIGIO, &action, NULL);
/*异步通知
通过使用异步通知,应用程序可以在数据可用时收到一个信号,而无需不停地轮询。
启用步骤:
(1)它们指定一个进程作为文件的拥有者:使用 fcntl 系统调用发出 F_SETOWN 命令,这个拥有者进程的 ID 被保存在 filp->f_owner。目的:让内核知道信号到达时该通知哪个进程。
(2)使用 fcntl 系统调用,通过 F_SETFL 命令设置 FASYNC 标志。
*/
fcntl(pipetest0, F_SETOWN, getpid());//F_SETOWN设置异步IO所所有权
/*fcntl(pipetest0, F_GETFL) 获得其文件状态标志 然后与FASYNC或运算,最后设置*/
fcntl(pipetest0, F_SETFL, fcntl(pipetest0, F_GETFL) | FASYNC);//F_SETFL设置文件状态标志
while(1) {
/* this only returns if a signal arrives */
sleep(86400); /* one day */
if (!gotdata)
continue;
count=read(pipetest0, buffer, 21);
/* buggy: if avail data is more than 4kbytes... */
write(1,buffer,count);
gotdata=0;
break;
}
close(pipetest0 );
printf("close pipetest0 ! \n");
printf("exit !\n");
exit(0);
}
Arm平台异步测试:
[root@FriendlyARM /udisk]# ./asynctest &
[root@FriendlyARM /udisk]# echo 23231 > /dev/scullpipe0
count : 6
result : 0
count : 4
count : 4
count : 4
count is 4
count : 2
result : 0
count : 2
count : 1
count : 1
count : 1
result : 0
count : 1
count : 1
count : 1
2323close pipetest0 !
exit !
[1] + Done ./asynctest
[root@FriendlyARM /udisk]#