#define SHMDATASIZE 1024
#ifndef vmf_BUFFER
#define vmf_BUFFER SHMDATASIZE//默认管道缓冲区大小
#endif
int vmf_major=vmf_MAJOR;
int vmf_buffer=vmf_BUFFER;
MODULE_PARM(vmf_major,"i");//MODULE_PARM(var,type)模块参量的类型代码: b=byte h=short i=int l=long s=string
MODULE_PARM(vmf_buffer,"i");
MODULE_AUTHOR("lanhai");
//MODULE_LICENSE("GPL");
typedef struct vmf_Dev
{
wait_queue_head_t rdq,wrq;// rdq表示堵塞读的等待队列,wrq是堵塞写的等待队列
char *base; //所分配缓冲区的起始地址
unsigned int buffersize; //是缓冲区的大小
unsigned int len; //表示管道中已有数据块的长度
unsigned int start; //当前应该读取的缓冲区位置相对于base的偏移量
unsigned int readers,writers;//表示vmf设备当前的读者个数和写者个数
struct semaphore sem; //用于互斥的访问的信号量
struct fasync_struct *async_queue;
}vmf_Dev;
struct vmf_Dev *vmf_devices;
static int vmf_fasync(int fd, struct file *filp, int mode)
{
struct vmf_Dev *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);
}
static int vmf_open(struct inode *inode,struct file *filp)//一个管道对应两个文件(读和写),两个节点。
{
vmf_Dev *dev;
//int num=MINOR(inode->i_rdev);//i_rdev--Device major and minor numbers;minor()取次设备号,用major(dev)取主设备号,用mkdev()将主、次设备号和在一起作为设备号(dev)
//printk("<1>MINOR(inode->i_rdev)=%d/n",num);
if(!filp->private_data)//void *private_data;私有空间;系统在调用驱动程序的open方法前将这个指针置为NULL。驱动程序可以将这个字段用于任意目的,也可以忽略这个字段。驱动程序可以用这个字段指向已分配的数据,但是一定要在内核释放file结构前的release方法中清除它。
{
dev=vmf_devices;//vmf_Dev vmf_devices设备赋值给局部变量dev
filp->private_data=dev;//将其保存到文件私有空间中
}
else
{
dev=filp->private_data;//如果私有空间已经使用,说明管道已经打开直接把此私有空间赋值给dev
}
if (down_interruptible(&dev->sem))//一个管道一个互斥锁,由此到up(&dev->sem)是临界区。返回0表示成功
return -ERESTARTSYS;
if(!dev->base)
{
dev->base=kmalloc(vmf_buffer,GFP_KERNEL);
if(!dev->base)
{
up(&dev->sem);
return -ENOMEM;
}
dev->buffersize=vmf_buffer;
dev->len=dev->start=0;//初始化时已写入数据长度和偏移量为0
}
if(filp->f_mode&FMODE_READ)
dev->readers++;
if(filp->f_mode&FMODE_WRITE)
dev->writers++;
filp->private_data=dev;
MOD_INC_USE_COUNT;
up(&dev->sem);
return 0;
}
static int vmf_release(struct inode *inode,struct file *filp)
{
vmf_Dev *dev=filp->private_data;
down(&dev->sem);
if(filp->f_mode&FMODE_READ)
dev->readers--;
if(filp->f_mode&FMODE_WRITE)
{
dev->writers--;
wake_up_interruptible(&dev->sem.wait);
//Linux进程可能会在事件发生之前一直等待下去。例如,一个进程可能会在运行中等待一些写入硬件设备的信息的到来。其中一种方式是进程可以使用sleep()和wakeup()这两个系统调用,进程先使自己处于睡眠状态,等待事件的到来,
//一旦事件发生,进程即可被唤醒。举个例子来说:interruptible_sleep_on(&dev_wait_queue)函数使进程睡眠并且将此进程的进程号加到进程睡眠列表dev_wait_queue中,一旦设备准备好后,设备发出一个中断,从而导致设备
//驱动程序中相应的例程被调用,这个驱动程序例程处理完一些设备要求的事宜后会发出一个唤醒进程的信号,通常使用wake_up_interruptible(&dev_wait_queue)函数,它可以唤醒dev_wait_queue所示列表中的所有进程。
}
if((dev->readers+dev->writers==0)&&(dev->len==0))
{//没有读者、写者并且缓冲区数据为0,释放缓冲区
kfree(dev->base);
dev->base=NULL;
}
up(&dev->sem);
vmf_fasync( -1,filp,0);
MOD_DEC_USE_COUNT;
return 0;
}
static ssize_t vmf_read(struct file *filp,char *buf,size_t count,loff_t *f_pos)
{
vmf_Dev *dev=filp->private_data;//取当前设备文件保的相关数据
ssize_t read=0;
if(f_pos!=&filp->f_pos)
return -ESPIPE;
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
do_more_read:
while(dev->len==0)
{
if(!dev->writers)
{
up(&dev->sem);
return -EAGAIN;
}
up(&dev->sem);
if(filp->f_flags&O_NONBLOCK)
return -EAGAIN;
printk("<1>%s reading:going to sleep/n",current->comm);
if(wait_event_interruptible(dev->rdq,(dev->len>0)))//wait_event使当前进程在等待队列rdq中等待直至dev->len>0为真
return -ERESTARTSYS;
printk("<1>%s has been waken up/n",current->comm);
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
}
while(count>0&&dev->len)
{
char *pipebuf=dev->base+dev->start;//pipebuf指向当前读取数据
ssize_t chars=dev->buffersize-dev->start;//chars表示一次性度取的最大数据量
if(chars>count) chars=count;
if(chars>dev->len) chars=dev->len;//dev->len表示设备中已有的数据块长度
if(copy_to_user(buf,pipebuf,chars))
{//copy_to_user(to,from,n),将pipebuf指向的内核区数据复制到由buf指向的用户区
up(&dev->sem);
return -EFAULT;
}
read+=chars;
dev->start+=chars;
dev->start%=dev->buffersize;
dev->len-=chars;
count-=chars;
buf+=chars;
}
if (!dev->len)
dev->start=0;
if(count&&dev->writers&&!(filp->f_flags&O_NONBLOCK))
{//count大于0说明设备中没有可读数据,但是还要继续读,所以唤醒写等待队列
up(&dev->sem);
wake_up_interruptible(&dev->wrq);
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
goto do_more_read;
}
up(&dev->sem);
wake_up_interruptible(&dev->wrq);//唤醒由于wait_event_interruptible而进入写等待队列的进程
//printk("<1>%s did read %1i bytes/n",current->comm,(long)read);
return read;//已经读出的数据块大小
}
static ssize_t vmf_write(struct file *filp,const char *buf,size_t count,loff_t *f_pos)
{
vmf_Dev *dev=filp->private_data;
ssize_t written=0;
if(f_pos!=&filp->f_pos||count==0)
return -ESPIPE;
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
do_more_write:
while(dev->len==dev->buffersize)
{
up(&dev->sem);
if(filp->f_flags&O_NONBLOCK)
return -EAGAIN;
printk("%s writting:going to sleep/n",current->comm);
if(wait_event_interruptible(dev->wrq,(dev->len<dev->buffersize)))//:wait_event使当前进程在等待队列wrq中等待直至dev->lenbuffersize为真
return -ERESTARTSYS;
printk("%s has been waken up/n",current->comm);
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
}
while(count>0)
{//写数据
char *pipebuf=dev->base+(dev->len+dev->start)%dev->buffersize;//pipebuf指向当前要写入数据的地址,在未读出的数据块好骟写入
ssize_t chars=dev->buffersize-(dev->len+dev->start);//这两行计算可以一次性写入的最大数据量
if(chars<0) chars+=dev->start;
if(chars!=0)
{//chars==0表示缓冲区满
if (chars>count) chars=count;
if (copy_from_user(pipebuf,buf,chars))
{//copy_from_user(to,from,n)
up(&dev->sem);
return -EFAULT;
}
written+=chars;
dev->len+=chars;
count-=chars;
buf+=chars;
if (dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
}
if (count&&!(filp->f_flags&O_NONBLOCK))
{//count大于0说明写空间已满,但是还要继续写,唤醒读等待进程
up(&dev->sem);
wake_up_interruptible(&dev->rdq);
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
goto do_more_write;
}
up(&dev->sem);
wake_up_interruptible(&dev->rdq);
//printk("%s did write %1i bytes/n",current->comm,(long)written);
return written;
}
unsigned int vmf_poll(struct file *filp,poll_table *wait)//poll(struct file *,poll_table *)让一个进程决定它
//是否能无阻塞地从一个或多个打开的文件中读取数据,
//或者向这些文件中写数据。还可以用来实现在无阻塞
//情况下的不同源输入的多路复用
{
vmf_Dev *dev=filp->private_data;
unsigned int mask=0;
poll_wait(filp,&dev->rdq,wait);//将当前进程进入等待队列排队
poll_wait(filp,&dev->wrq,wait);
if (dev->len>0)
mask|=POLLIN|POLLRDNORM;//管道能无阻塞可读
if (dev->len!=dev->buffersize)
mask|=POLLOUT|POLLWRNORM;//管道能无阻塞可写
return mask;
}
static struct file_operations vmf_fops=
{
read: vmf_read,
write: vmf_write,
poll: vmf_poll,
open: vmf_open,
release: vmf_release,
fasync: vmf_fasync,
};
static int __init vmf_init_module(void)//__init是一个段描叙符号 对于任何有这个修饰符号的数据或者函数,都将放在init代码段或者数据段里面。当内核启动完毕后该段的所有空间将被回收
{
int result;
SET_MODULE_OWNER(&vmf_fops);//设置struct module *owner字断,相当于在vmf_fops中设置"owner:THIS_MODULE,"
result=register_chrdev(vmf_major,"vmf",&vmf_fops);
if (result<0)
{
printk(KERN_WARNING "vmf:can't get major %d/n",vmf_major);
return result;
}
if (vmf_major==0)
vmf_major=result;//自动分配的major号
vmf_devices=kmalloc(sizeof(vmf_Dev),GFP_KERNEL);//划分vmf_Dev管道设备的全局空间,本例中11个
if (!vmf_devices)
{
return -ENOMEM;
}
memset(vmf_devices,0,sizeof(vmf_Dev));
init_waitqueue_head(&vmf_devices->rdq);//读等待队列头
init_waitqueue_head(&vmf_devices->wrq);//写等待队列头
sema_init(&vmf_devices->sem,1);//用1来初始化信号量vmfo_devices.sem
return 0;
}
static void __exit vmf_cleanup_module(void)
{
unregister_chrdev(vmf_major,"vmf");
if (vmf_devices)
{
if (vmf_devices->base)
kfree(vmf_devices->base);//释放由kmalloc分配的区域
kfree(vmf_devices);//最终释放由kmalloc分配的区域
}
}
module_init(vmf_init_module);
module_exit(vmf_cleanup_module);
/////////////////////////////////////////////////////////////////////////////////////////////
MAKEFILE和以前的相同
应用程序如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
void input_handler(int signum)
{
printf("receive a signal from globalfifo,signalnum:%d/n",signum);
}
main()
{
int fd, oflags;
fd = open("/shm_a", O_RDWR, S_IRUSR | S_IWUSR);
if (fd != - 1)
{
//启动信号驱动机制
signal(SIGIO, input_handler); //让input_handler()处理SIGIO信号
fcntl(fd, F_SETOWN, getpid());
oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC);
while(1)
{
sleep(100);
}
}
else
{
printf("device open failure/n");
}
}