globalfifo.c:
/*************************************************************************
> File Name: globalfifo.c
> Author: zhyang
> Mail: 465151655@qq.com
> Created Time: 2015年04月20日
************************************************************************/
#include<linux/module.h>
#include<linux/types.h>
#include<linux/fs.h>
#include<linux/errno.h>
#include<linux/mm.h>
#include<linux/sched.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include <linux/slab.h>
#include<linux/semaphore.h>
#include<linux/wait.h>
#include<linux/poll.h>
#define GLOBALFIFO_SIZE 0X1000 //memory size 4K
//#define MEM_CLEAR 0X1 //clear memory
#define GLOBALFIFO_MAJOR 111 //预设的主设备号
#define GLOBALFIFO_MAGIC 0Xdf
#define MEM_CLEAR _IO(GLOBALFIFO_MAGIC,0x11)
static int globalfifo_major = GLOBALFIFO_MAJOR;
struct globalfifo_dev
{
struct cdev cdev;
unsigned int current_len; //fifo的有效数据长度
unsigned char mem[GLOBALFIFO_SIZE]; //全局内存
// struct semaphore sem_open;
struct semaphore sem;
wait_queue_head_t r_wait; //读队列头
wait_queue_head_t w_wait; //写队列头
};
struct globalfifo_dev *globalfifo_devp;
//读函数
static ssize_t globalfifo_read(struct file *filp,char __user *buf,size_t count,loff_t *ppos)
{
int ret = 0;
struct globalfifo_dev *dev = filp->private_data;
DECLARE_WAITQUEUE(wait,current); //定义等待队列
//信号量互斥
if(down_interruptible(&dev->sem))
return - ERESTARTSYS;
add_wait_queue(&dev->r_wait,&wait); //进入读等待队列头
//接下来就是调用wait_event,说明等待的条件,当条件满足时调用该进程,从队列头中移除
//以下代码相当于wait_event_interruptible(dev-r_wait,dev->current_len != 0);
//等待FIFO非空,才能从中读取数据
if(dev->current_len ==0)
{
if(filp->f_flags & O_NONBLOCK) //如果状态是不可阻塞的那就返回,没什么意义了
{
ret = - EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE); //将此进程转为可被终端的休眠,腾出cpu给 其他进程执行
up(&dev->sem);
schedule(); //调度其他进程执行
if(signal_pending(current)){
ret = - ERESTARTSYS;
goto out2;
}
down_interruptible(&dev->sem);
}
if(count > dev->current_len)
count = dev->current_len;
//赋值数据从内核空间到用户空间
if(copy_to_user(buf,dev->mem,count)) //拷贝内核空间的dev.mem+p到用户空间的buf
{
ret = - EFAULT; //拷贝成功返回0,失败返回没有拷贝成功的字节数
goto out;
}
else{ //copy_to_user返回的是还需要拷贝的字节数,所以如果拷贝成功返回的是0
//读出的数据将从全局内存中移除
memcpy(dev->mem,dev->mem+count,dev->current_len-count); //数据前移
dev->current_len -= count; //长度减少
printk(KERN_INFO "READ %d bytes,current_len :%d\n",count,dev->current_len);
wake_up_interruptible(&dev->w_wait); //有空间可以写,唤醒写等待队列
ret = count;
}
out:up(&dev->sem);
out2:remove_wait_queue(&dev->r_wait,&wait); //把该进程移出读等待队列头
set_current_state(TASK_RUNNING);
return ret; //返回成功读取的字节数
}
//写函数
static ssize_t globalfifo_write(struct file *filp,const char __user *buf,size_t count,loff_t *ppos)
{
int ret = 0;
struct globalfifo_dev *dev = filp->private_data;
DECLARE_WAITQUEUE(wait,current); //定义等待队列
//信号量互斥
if(down_interruptible(&dev->sem))
return - ERESTARTSYS;
add_wait_queue(&dev->w_wait,&wait);
//等带FIFO非满,非满才能写
//wait_event_interruptible(&dev->w_wait,dev->current_len != GLOBALFIFO_SIZE);
if(dev->current_len == GLOBALFIFO_SIZE)
{
if(filp->f_flags & O_NONBLOCK){
ret = - EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE); //将此进程休眠
up(&dev->sem);
schedule();
if(signal_pending(current)){
ret = - ERESTARTSYS;
goto out2;
}
down_interruptible(&dev->sem);
}
if(count > GLOBALFIFO_SIZE-dev->current_len)
count = GLOBALFIFO_SIZE - dev->current_len;
if(copy_from_user(dev->mem+dev->current_len,buf,count))
{
ret = - EFAULT;
goto out;
}
else{
dev->current_len += count;
printk(KERN_INFO "WRITE %d bytes,current_len :%d\n",count,dev->current_len);
wake_up_interruptible(&dev->r_wait); //唤醒读队列
ret = count;
}
out:up(&dev->sem);
out2: remove_wait_queue(&dev->w_wait,&wait);
set_current_state(TASK_RUNNING);
return ret;
}
//定位函数
static loff_t globalfifo_llseek(struct file *filp,loff_t offset,int orig)
{
loff_t ret;
switch (orig)
{
case 0: //从文件开头开始偏移
if(offset < 0){
ret = - EINVAL;
break;
}
if((unsigned int)offset > GLOBALFIFO_SIZE) //偏移越界
{
ret = - EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1: //从当前位置开始偏移
if((filp->f_pos + offset) > GLOBALFIFO_SIZE) //偏移越界
{
ret = - EINVAL;
break;
}
if((filp->f_pos + offset) < 0){
ret = - EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default: ret = - EINVAL;
}
return ret;
}
static long globalfifo_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
struct globalfifo_dev *dev = filp->private_data;
switch (cmd)
{
case MEM_CLEAR:
//信号量互斥
if(down_interruptible(&dev->sem))
return - ERESTARTSYS;
memset(dev->mem,0,GLOBALFIFO_SIZE);
up(&dev->sem);
printk(KERN_INFO "GLOBALFIFO is set to zero\n");
break;
default: return - EINVAL;
}
return 0;
}
//文件打开函数
int globalfifo_open(struct inode *inode,struct file *filp)
{
// dev = container_of(inode->i_cdev,struct globalfifo_dev,cdev);
//信号量
// struct globalfifo_dev *dev = globalfifo_devp;
// if(down_interruptible(&dev->sem_open))
// return - EBUSY;
filp->private_data = globalfifo_devp;
return 0;
}
int globalfifo_release(struct inode *inode,struct file *filp)
{
// struct globalfifo_dev *dev = filp->private_data;
// up(&dev->sem_open);
return 0;
}
static unsigned int globalfifo_poll(struct file *filp,poll_table *wait)
{
unsigned int mask = 0;
struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针
down_interruptible(&dev->sem);
poll_wait(filp,&dev->r_wait,wait);
poll_wait(filp,&dev->w_wait,wait);
//FIFO非空
if(dev->current_len != 0)
mask |= POLLIN | POLLRDNORM; //标示可读
//FIFO非满
if(dev->current_len != GLOBALFIFO_SIZE)
mask |= POLLOUT | POLLWRNORM;
up(&dev->sem);
return mask;
}
static const struct file_operations globalfifo_fops =
{
.owner = THIS_MODULE,
.llseek = globalfifo_llseek,
.read = globalfifo_read,
.write = globalfifo_write,
.unlocked_ioctl = globalfifo_ioctl,
.open = globalfifo_open,
.release = globalfifo_release,
.poll = globalfifo_poll,
};
//初始化并添加cdev结构体
static void globalfifo_setup_cdev(struct globalfifo_dev *dev,int index)
{
int err,devno = MKDEV(globalfifo_major,0);
cdev_init(&dev->cdev,&globalfifo_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &globalfifo_fops;
// err = cdev_add(&dev->cdev,devno,2); //2表示允许2个次设备,一般这一位为1
err = cdev_add(&dev->cdev,devno,1);
if(err)
printk(KERN_NOTICE "ERROR %d adding globalfifo\n",err);
}
//模块加载函数
static __init int globalfifo_init(void)
{
int result;
dev_t devno = MKDEV(globalfifo_major,0); //把major,minor转换为dev_t
//申请设备
if(globalfifo_major)//静态申请
result = register_chrdev_region(devno,1,"globalfifo"); //申请字符设备驱动区域,1表示申请的连续设备编号的个数,字符串是设备名称
else{//静态申请失败
result = alloc_chrdev_region(&devno,0,1,"globalfifo"); //0表示请求的第一个次设备号
globalfifo_major = MAJOR(devno); //赋给globalmem_major
}
if(result < 0)
return result;
globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev),GFP_KERNEL); //动态申请设备结构体内存
if(!globalfifo_devp)
{
result = - ENOMEM;
goto fail_malloc;
}
memset(globalfifo_devp,0,sizeof(struct globalfifo_dev));
// globalfifo_setup_cdev(&globalfifo_devp[0],0); //cdev的初始化和添加
// globalfifo_setup_cdev(&globalfifo_devp[1],1);
globalfifo_setup_cdev(globalfifo_devp,0);
// sema_init(&globalfifo_devp->sem_open,1);
sema_init(&globalfifo_devp->sem,1);
init_waitqueue_head(&globalfifo_devp->r_wait); //初始化队列头
init_waitqueue_head(&globalfifo_devp->w_wait);
return 0;
fail_malloc:unregister_chrdev_region(devno,1);
return result;
}
//驱动模块卸载函数
static __exit void globalfifo_exit(void)
{
// cdev_del(&globalfifo_devp[0].cdev); //删除cdev结构 dev为struct globalfifo_cdev
// cdev_del(&globalfifo_devp[1].cdev);
cdev_del(&globalfifo_devp->cdev);
kfree(globalfifo_devp); //释放设备结构体内存
unregister_chrdev_region(MKDEV(globalfifo_major,0),1); //注销设备区域
}
MODULE_AUTHOR("ZHYANG");
MODULE_LICENSE("Dual BSD/GPL");
module_param(globalfifo_major,int,S_IRUGO);
module_init(globalfifo_init);
module_exit(globalfifo_exit);
测试程序pollmonitor.c:
/*************************************************************************
> File Name: pollmonitor.c
> Author: zhyang
> Mail: 465151655@qq.com
> Created Time: 2015年04月20日 星期一 17时27分15秒
************************************************************************/
#include<stdio.h>
#include<sys/time.h>
#include<fcntl.h>
#include<sys/types.h>
#include<linux/ioctl.h>
#define GLOBALFIFO_MAGIC 0xdf
#define FIFO_CLEAR _IO(GLOBALFIFO_MAGIC,0x11)
#define BUFFER_LEN 20
int main()
{
int fd,num;
char rd_ch[BUFFER_LEN];
fd_set rfds,wfds;
//以非阻塞方式打开
fd = open("/dev/globalfifo",O_RDONLY | O_NONBLOCK);
if(fd != -1)
{
//FIFO_CLEAR
if(ioctl(fd,FIFO_CLEAR,0) < 0)
printf("ioctl command failed\n");
else printf("globalfifo is set to zero\n");
while(1)
{
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(fd,&rfds);
FD_SET(fd,&wfds);
select(fd+1,&rfds,&wfds,NULL,NULL);
//数据可读出
if(FD_ISSET(fd,&rfds))
printf("pollmonitor can be read\n");
//数据可写入
if(FD_ISSET(fd,&wfds))
printf("pollmonitor can be writed\n");
sleep(10);
}
}
else printf("device open failure\n");
}
步骤:
1.make
2.sudo insmod globalfifo.ko
3.sudo mknod /dev/globalfifo c 111 0
4.gcc -o pollmonitor.c pollmonitor.c
5.后台运行pollmonitor: ./pollmonitor &
6.echo 'adfad' > /dev/globalfifo
本人亲测在内核3.16中运行完美,网上的很多代码都是老代码,基本上是内核2的。
echo 之前
echo之后
ps:内核2和3有一些函数的定义是不一样的或者已经被取消了,对于宋宝华当中的代码,大家依据make之后的错误信息修改下就行了,改完就都能运行了,不管是globalmem,还是之后的global,因为宋宝华的书是根据内核2。6来写的,没办法信息都是比较之后的。