应用程序调用select,select系统调用的原型:
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
fd_set数据结构来表示要监听的设备,里面存放的是设备的文件描述符
nfds:当前进程最大的文件描述符+1
readfds:监听设备的读操作
writefds:监听设备的写操作
execptfds:监听设备的异常情况
timeout:超时处理,如果指定为NULL,select如果以上三种情况都没有发生,进行永远的休眠,如果指定超时时间,超时到期立即返回
功能:监听设备,前提是要将设备的文件描述符放置在fd_set文件描述符
集合中,如果三种情况都不满足,当前进程调用select进行休眠操作,
如果设备可用或者超时,select函数立即返回,执行后续代码
关于文件描述符结合fd_set的操作宏:
void FD_CLR(int fd, fd_set *set); //将集合中的某一个设备清除
int FD_ISSET(int fd, fd_set *set); //判断设备是否可用
void FD_SET(int fd, fd_set *set); //添加要监听的设备
void FD_ZERO(fd_set *set); //初始化fd_set
对应底层驱动的实现:
当应用程序调用select或者poll时,都会调用到底层驱动的同一个poll接口,
底层驱动的poll接口在struct file_operations中:
unsigned int (*poll) (struct file *, struct poll_table_struct *);
这个底层poll的实现及其简单,都会调用poll_wait函数,然后判断
数据的可用性或者异常,根据此信息返回0或者非0值,这样就完成了
底层poll的实现。
通过分析大量内核其他的驱动代码,得到关于poll接口使用的特点:
1.首先驱动程序都会定义一个等待队列头
2.然后调用poll_wait(文件指针,等待队列头,设备列表),调用
这个函数的效果是让当前进程添加到这个队列头中,注意只是添加,
不会立即休眠
3.最后判断设备的状态,根据状态返回0或者非0
如果要深入了解poll和select的使用,需要了解内核的系统调用流程:
fs/select.c
app:select/poll->sys_select/sys_poll->drv_poll:
sys_poll:
do_sys_poll
do_poll
int timed_out = 0, count = 0;
for (;;) {
for(遍历所监听的设备)
if (do_pollfd(pfd, pt)) {
//根据fd找到对应的file,然后判断file->f_op(它指向的是
驱动的file_operations,)中是否有poll接口,如果有,
那么调用它,并且返回poll接口的返回值,说明变量count
的值有底层驱动poll的返回值决定
count++;
pt = NULL;
}
//如果遍历设备,执行各个设备驱动的poll接口,都返回0
,接下来就会判断当前进程是否接收到信号,如果接收到
信号count为非0值
if (!count) {
count = wait->error;
if (signal_pending(current))
count = -EINTR;
}
//跳出死循环的条件是:count非0或者time_out非0(超时)
if (count || timed_out)
break;
//如果设备即不可用,有没有接收到信号,那么当前进程
进入休眠,如果指定了超时时间,超时时间到期,
立即返回0,timed_out=1,死循环再执行一次
if (!poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack))
timed_out = 1;
}
} //如果调出死循环,就会立即返回到用户空间,也表示select
或者poll系统调用函数的返回
总结:
1.驱动的poll接口完成一下两个工作
1.调用poll_wait将当前进程添加到等待队列头中,注意每个驱动程序
都有对应的唯一的队列头,但里面添加的指定的进程只有应用程序对应的主进程
2.判断设备的状态,返回0或者非0,0表示设备不可用或者正常,菲0表示设备可用或者异常
2.系统调用过程完成:
1.调用底层驱动poll接口来判断数据的可用性或者异常情况
2.判断当前进程是否接收到信号,如果接收到信号,理解返回到用户空间
3.如果设备不可用或者没有接收到信号,进入真正的休眠
4.如果休眠,要返回用户空间的条件:
1.数据可用,底层驱动来判断数据可用,比如中断,在中断处理函数中唤醒休眠的进程
2.接收到信号
3.超时返回
案例:给第二版本的按键驱动程序添加poll的实现
案例:在测试程序中将此段代码放在while(1)前面,看看效果
FD_ZERO(&rdfd);
FD_SET(fd, &rdfd); //监听按键
//FD_SET(fd2, &rdfd); 监听串口
tv.tv_sec = 5; //超时时间5秒
tv.tv_usec = 0;
______________________________________________________________________________________
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//定义驱动硬件私有结构体
struct button_hw_priv {
char *name;
unsigned long gpio;
int irq;
int code;
};
//定义平台私有结构
struct button_platdata {
struct button_hw_priv *buttons;
int nbuttons;
};
//定义驱动私有结构体
struct button_sf_priv {
struct cdev btn_cdev;
int major;
struct class *cls;
};
//定义按键状态结构
struct button_event{
int code; //键值
int state; //状态
};
static struct button_event btn_event;
static wait_queue_head_t btn_wq;
static int is_press;
//初始化按键信息
static struct button_hw_priv btn_info[] = {
[0] = {
.name = "KEY_UP",
.gpio = S5PV210_GPH0(0),
.irq = IRQ_EINT(0),
.code = KEY_UP
},
[1] = {
.name = "KEY_DOWN",
.gpio = S5PV210_GPH0(1),
.irq = IRQ_EINT(1),
.code = KEY_DOWN
},
};
//初始化平台私有的硬件信息
static struct button_platdata button_data = {
.buttons = btn_info,
.nbuttons = ARRAY_SIZE(btn_info)
};
static void button_release(struct device *dev)
{
}
//分配初始化platform_device
static struct platform_device button_dev = {
.name = "mybutton",
.id = -1,
.dev = {
.release = button_release,
.platform_data = &button_data
}
};
static int button_open(struct inode *inode,struct file *file)
{
struct button_sf_priv *pbtn = container_of(inode->i_cdev, struct button_sf_priv,btn_cdev);
file->private_data = pbtn;
return 0;
}
static unsigned int button_poll(struct file *file,struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &btn_wq, wait); //不会立即休眠
if(is_press) {
mask |= POLLIN | POLLRDNORM;//设备可用,数据可用
}
return mask;
}
static ssize_t button_read(struct file *file,char __user *buf,size_t count,loff_t *ppos)
{
wait_event_interruptible(btn_wq, is_press != 0);
is_press = 0;
copy_to_user(buf, &btn_event, sizeof(btn_event));
return count;
}
static struct file_operations button_fops = {
.owner = THIS_MODULE,
.open = button_open,
.poll = button_poll,
.read = button_read
};
static irqreturn_t button_isr(int irq, void *dev_id)
{
struct button_hw_priv *pbtn_hw =
(struct button_hw_priv *)dev_id;
btn_event.state = !gpio_get_value(pbtn_hw->gpio);
btn_event.code = pbtn_hw->code;
wake_up_interruptible(&btn_wq);
is_press = 1;
return IRQ_HANDLED;
}
static int button_probe(struct platform_device *pdev)
{
int i;
dev_t dev_id;
struct button_platdata *pbtndata =pdev->dev.platform_data;
struct button_sf_priv *pbtn = kzalloc(sizeof(struct button_sf_priv), GFP_KERNEL);
if (pbtn->major) {
dev_id = MKDEV(pbtn->major, 0);
register_chrdev_region(dev_id, 1, "button");
} else {
alloc_chrdev_region(&dev_id, 0, 1, "button");
pbtn->major = MAJOR(dev_id);
}
cdev_init(&pbtn->btn_cdev, &button_fops);
cdev_add(&pbtn->btn_cdev, dev_id, 1);
pbtn->cls = class_create(THIS_MODULE, "button");
device_create(pbtn->cls, NULL, dev_id, NULL, "button");
for(i = 0; i < pbtndata->nbuttons; i++) {
struct button_hw_priv *pbtn_hw = NULL;
pbtn_hw = &pbtndata->buttons[i];
gpio_request(pbtn_hw->gpio, pbtn_hw->name);
request_irq(pbtn_hw->irq, button_isr,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,pbtn_hw->name, pbtn_hw);
}
init_waitqueue_head(&btn_wq);
dev_set_drvdata(&pdev->dev, pbtn);
return 0;
}
static int button_remove(struct platform_device *pdev)
{
int i;
struct button_platdata *pbtndata = pdev->dev.platform_data;
struct button_sf_priv *pbtn = dev_get_drvdata(&pdev->dev);
dev_t dev_id = MKDEV(pbtn->major, 0);
for(i = 0; i < pbtndata->nbuttons; i++) {
struct button_hw_priv *pbtn_hw = &pbtndata->buttons[i];
gpio_free(pbtn_hw->gpio);
free_irq(pbtn_hw->irq, pbtn_hw);
}
device_destroy(pbtn->cls, dev_id);
class_destroy(pbtn->cls);
cdev_del(&pbtn->btn_cdev);
unregister_chrdev_region(dev_id, 1);
kfree(pbtn);
return 0;
}
//分配初始化platform_driver
static struct platform_driver button_drv = {
.driver = {
.name = "mybutton",
.owner = THIS_MODULE
},
.probe = button_probe,
.remove = button_remove
};
static int button_init(void)
{
//注册platform_device
platform_device_register(&button_dev);
//注册platform_driver
platform_driver_register(&button_drv);
return 0;
}
static void button_exit(void)
{
platform_device_unregister(&button_dev);
platform_driver_unregister(&button_drv);
}
module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL v2");
______________________________________________________________________________________
测试APP
______________________________________________________________________________________
#include
#include
#include
#include
#include
#include
#include
#include
struct button_event {
int code;
int state;
};
int main(int argc, char *argv[])
{
struct button_event btn_event;
int fd;
fd_set rdfd; //读设备集合
struct timeval tv;
int ret = 0;
fd = open("/dev/button", O_RDWR);
if (fd < 0) {
printf("open button failed.
");
return -1;
}
while (1) {
FD_ZERO(&rdfd);
FD_SET(fd, &rdfd); //监听按键
//FD_SET(fd2, &rdfd); 监听串口
tv.tv_sec = 5; //超时时间5秒
tv.tv_usec = 0;
//启动监听
ret = select(fd+1, &rdfd, NULL, NULL, &tv);
if (ret < 0)
printf("select error!
");
else if (ret == 0)
printf("timeout!
");
else {
if (FD_ISSET(fd, &rdfd)) {
//判断数据是否来源按键
read(fd, &btn_event,
sizeof(btn_event));
printf("code = %d, state = %d
",
btn_event.code,
btn_event.state);
}
}
}
close(fd);
return 0;
}