linux下poll测试例子

最近在分析poll系统调用的实现过程, 写了一个测试例子, 记录下;

首先写一个驱动, 分配一块内存, 将其模拟为硬件;
创建一个sysfs文件节点, 通过其对这块内存写入, 模拟硬件产生数据;
再写一个应用, 监测这块内存, 当里面有数据时, 自动调用驱动的读函数,

驱动:
//poll_driver_test.c

/* 内核维护一个环形缓冲区, 对其有两个操作, 写和读:
 * 写: 当写入'p', 唤醒等待队列, app开始读;
 * 读: 读取缓冲区的数据;
 */
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/uaccess.h>
#include <linux/poll.h>

#define __here__	printk("--->%s, %s, %d;\n", __FILE__, __func__, __LINE__);


#define N_TTY_BUF_SIZE 32		//注意缓冲区大小必须为2次幂, 这样才能截断;
#define MIN(x, y)	(x) > (y) ? (y) : (x);


static int major = 99; 	/* 主设备号, 用于区分设备类 */
static int minor = 0;  	/* 次设备号, 用于区分哪个设备 */

static dev_t devno;
static struct cdev cdev1;
struct class *rat_class;
static struct device *dev1 = NULL;


static char buf[N_TTY_BUF_SIZE];	//模拟的串口缓冲区;
static char ss[1024];				//模拟的用户空间;
static char *ps;

static int i;

/* read: 	  放入缓冲区的地址, 没有截断;
 * tail: 	  取出缓冲区的地址, 被截断;
 * read_tail: 取出缓冲区的地址, 没有截断;
 */

static int read = 0, tail = 0, read_tail = 0;

static wait_queue_head_t rat_wait;
static struct kobject *kot;

static bool wakeup = false;


//写入数据:
static void put_buffer(unsigned char c, char *bot)
{
	bot[read & (N_TTY_BUF_SIZE - 1)] = c;
	read++;
}

//取出数据:
static int get_buffer(void)
{
	int n = 0;
	tail = read_tail & (N_TTY_BUF_SIZE - 1);
	
	n = MIN(read - read_tail, N_TTY_BUF_SIZE - tail);
	
	if (n) {
		memcpy(ps, &buf[tail], n);
		ps += n;
		read_tail += n;
	}

	return n;
}


static int rat_open(struct inode *inodep, struct file *filep)
{
    __here__;
    return 0;
}

static ssize_t rat_read (struct file *filp, char __user *buf, size_t count, loff_t *lof)
{
	int nua, nub, error;

	memset(ss, 0, 1024);
	ps = ss;
	nua = get_buffer();
	nub = get_buffer();
	copy_to_user(buf, ss, nua + nub);

	return nua + nub;
}

static ssize_t rat_write (struct file *filp, const char __user *buf, size_t count, loff_t *lof)
{
	__here__;
	return 0;
}

static unsigned int rat_poll (struct file *file, struct poll_table_struct *wait)
{
	unsigned int mask = 0;

	poll_wait(file, &rat_wait, wait);
	if (wakeup) {
		mask |= POLLIN | POLLRDNORM;
		wakeup = false;
	}

	return mask;
}


static int rat_fasync (int fa, struct file *filp, int fb)
{
	__here__;
	return 0;
}

static int rat_release (struct inode *inode, struct file *filp)  
{  
	__here__;
    return 0;
}

static struct file_operations rat_ops = {
	.owner			= THIS_MODULE,
	.open 			= rat_open,
	.read			= rat_read,
	.write			= rat_write,
	.poll			= rat_poll,
	.fasync			= rat_fasync,
	.release		= rat_release,
};


static ssize_t wt_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	__here__;
	return 1;
}

static ssize_t wt_store(struct device *dev, struct device_attribute *attr,
		 const char *s1, size_t count)
{
	bool toen = false;

	printk("--->%s, %d, s1: %s, count: %d, toen: %d;\n", 
			__func__, __LINE__, s1, count, toen);

	if (*s1 == '\n')
		return 1;
	
	for (i = 0; i < strlen(s1); i++) {
		put_buffer(s1[i], buf);
		if (s1[i] == 'p')
			toen = true;	
	}

	if (toen) {
		wake_up_interruptible(&rat_wait);
		wakeup = true;
	}

	return count;
}


//生成文件: /sys/wta/wtc;
struct device_attribute dev_attr_wtb = { 				
	.attr = {.name = "wtc", .mode = 0777 },		
	.show	= wt_show,						
	.store	= wt_store,						
};

static void mk_syf_file(void)
{
	kot = kobject_create_and_add("wta", NULL);
	sysfs_create_file(kot, &dev_attr_wtb.attr);
}

static int rat_init(void)
{
    int ret;
    printk(KERN_ALERT "rat_init\n");
	
    /* 第一步: 登记设备区间 */
	if (0 == major){
		ret = alloc_chrdev_region(&devno, 0, 1, "rat");		/* cat /proc/devices能看到 */
		major = MAJOR(devno);
	}
	else{					/* 已有主设备号 */
		devno = MKDEV(major, minor);
		ret = register_chrdev_region(devno, 1, "rat");
		if (ret < 0){
			printk(KERN_ERR "my register_chrdev_region fail.\n");
			return ret;
		}
	}
    printk(KERN_INFO "register_chrdev_region ok.\n");
	
    /* 第二步: 注册字符设备驱动 */
    cdev_init(&cdev1, &rat_ops);					/* 确定f_ops */
    ret = cdev_add(&cdev1, devno, 1);				/* 确定devno */
    if (ret < 0){
        printk(KERN_ERR "Uable to add dev\n");
        return ret;
    }
    printk(KERN_INFO "cdev_add success\n");

    rat_class = class_create(THIS_MODULE, "rat");
    dev1 = device_create(rat_class, NULL, devno, NULL, "my_rat");	/* 生成: /dev/my_rat */
    printk(KERN_INFO "device created success\n");

	/* 定义一个等待队列 */
	init_waitqueue_head(&rat_wait);

	/* 创建sysfs文件节点 */
	mk_syf_file();
	
    return 0;
}

static void rat_exit(void)
{
    cdev_del(&cdev1);
    unregister_chrdev_region(devno, 1);
    printk(KERN_ALERT "rat_exit\n");
}
module_init(rat_init);
module_exit(rat_exit);
MODULE_LICENSE("GPL");

应用:
//poll_app_test.c

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

//#define __here__	printf("--->%s, %s, %d;\n", __FILE__, __func__, __LINE__);
#define __here__	do{}while(0);
static char buf[128];

int main(int argc, char **argv)
{
	int fd;
	char* filename = "/dev/my_rat";
	unsigned char key_val;
	int ret;
	struct pollfd *key_fds = NULL;
   
    fd = open(filename, O_RDWR);
    if (fd < 0) {
        printf("error, can't open %s\n", filename);
        return 0;
    }
    
    if(argc !=1){
		printf("Usage : %s ",argv[0]);
		return 0;
    }
	
	key_fds = (struct pollfd *)malloc(sizeof(*key_fds));
    key_fds->fd = fd;
    key_fds->events = POLLIN;		//poll直接返回需要的条件;
	
	while(1) {
		ret =  poll(key_fds, 1, 6000);
		
		if(!ret){
			printf("time out\n");
		}
		else{
			if(key_fds->revents == POLLIN){
				memset(buf, 0, 128);
				read(fd, buf, 128);
				printf("--->%s, %d, buf: %s\n", __func__, __LINE__, buf);
			}
		}
	}
    
   return 0;
}

终端1:
# echo addp > /sys/wta/wtc
[ 26.339663] —>wt_store, 154, s1: addp
[ 26.339663] , count: 5, toen: 0;

终端2:
# ./app
—>main, 48, buf: addp

需要注意的是,在驱动里,poll_wait()并不会阻塞进程,尽管其名字中有wait, 很容易将其功能理解为和 wait_event() 一样;
wake_up_interruptible()唤醒进程后,会回调字符操作函数集的poll(), 这个过程还不清楚怎么来的;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值