歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 驅動支持select函數

驅動支持select函數

日期:2017/3/1 9:47:49   编辑:Linux編程

I/O多路轉接至今還不是 POSIX的組成部分。SVR4和 4.3 + BSD都提供select函數以執行I/O多路轉接。poll函數只由SVR4 提供。SVR4 實際上用poll實現select。I/O多路轉接的基本思想是:先構造一張有關描述符的表,然後調用一個函數,它要到這些描述符中的一個已准備好進行 I/O時才返回。在返回時,它告訴進程哪一個描述符已准備好可以進行 I/O。

I/O多路轉接在服務器端用的比較多,可以同時處理多個連接的接入,但是也有缺陷,貌似只能接受1024個接入,因此現在又了epoll,當然這不是討論的重點了。

select的函數原型為:

int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);



其中 readfds、writefds、exceptfds 分別是被 select()監視的讀、寫和異常處理的文件描述符集合,numfds 的值是需要檢查的號碼最高的文件描述符加 1。timeout 參數是一個指向 struct timeval類型的指針,它可以使select()在等待timeout時間後若沒有文件描述符准備好則返回。

struct timeval 數據結構為:

struct timeval

{

int tv_sec; /* seconds */

int tv_usec; /* microseconds */

};

除此之外,我們還將使用下列 API:

FD_ZERO(fd_set *set)――清除一個文件描述符集;

FD_SET(int fd,fd_set *set)――將一個文件描述符加入文件描述符集中;

FD_CLR(int fd,fd_set *set)――將一個文件描述符從文件描述符集中清除;

FD_ISSET(int fd,fd_set *set)――判斷文件描述符是否被置位。



select 用於查詢設備的狀態,以便用戶程序獲知是否能對設備進行非阻塞的訪問,需要設備驅動程序中的poll 函數支持。 驅動程序中 poll 函數中最主要用到的一個 API 是 poll_wait,其原型如下:

void poll_wait(struct file *filp, wait_queue_heat_t *queue, poll_table * wait);

poll_wait 函數所做的工作是把當前進程添加到 wait 參數指定的等待列表(poll_table)中。

需要說明的是,poll_wait 函數並不阻塞,程序中 poll_wait(filp, &outq, wait)這句話的意思並不是說一直等待 outq 信號量可獲得,真正的阻塞動作是上層的 select/poll 函數中完成的。select/poll 會在一個循環中對每個需要監聽的設備調用它們自己的 poll 支持函數以使得當前進程被加入各個設備的等待列表。若當前沒有任何被監聽的設備就緒,則內核進行調度(調用 schedule)讓出 cpu 進入阻塞狀態,schedule 返回時將再次循環檢測是否有操作可以進行,如此反復;否則,若有任意一個設備就緒,select/poll 都立即返回。

針對前面的文章的程序,我們在驅動中加入對poll的支持,程序改進為:

/* global_poll.c */

#include <linux/module.h>

#include <linux/init.h>

#include <linux/fs.h>

#include <asm/uaccess.h>

#include <linux/wait.h>

#include <linux/semaphore.h>

#include <linux/device.h>

#include <linux/cdev.h>

#include <linux/sched.h>

#include <linux/poll.h>



MODULE_LICENSE("GPL");



#define init_MUTEX(LOCKNAME) sema_init(LOCKNAME,1)





#define DEVICE_NAME "CDEV_ZHU"

static struct class *cdev_class;



struct cdev dev_c;

dev_t dev;



static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);

static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);

static unsigned int globalvar_poll(struct file *filp, poll_table *wait);



struct file_operations globalvar_fops =

{

read: globalvar_read,

write: globalvar_write,

poll: globalvar_poll,

};



static int global_var = 0;

static struct semaphore sem;

static wait_queue_head_t outq;

static int flag = 0;



static int __init globalvar_init(void)

{

int ret,err;

ret = alloc_chrdev_region(&dev,0,1,DEVICE_NAME) ;

if (ret)

{

printk("globalvar register failure");

}

else

{

cdev_init(&dev_c,&globalvar_fops);



err = cdev_add(&dev_c,dev,1);



if(err)

{

printk(KERN_NOTICE "error %d adding FC_dev\n",err);

unregister_chrdev_region(dev, 1);

return err;

}

else

{

printk("device register success! \n");

}



cdev_class = class_create(THIS_MODULE,DEVICE_NAME);

if(IS_ERR(cdev_class))

{

printk("ERR:cannot create a cdev_class\n");

unregister_chrdev_region(dev, 1);

return -1;

}

device_create(cdev_class, NULL, dev, 0, DEVICE_NAME);



init_MUTEX(&sem);

init_waitqueue_head(&outq);

}

return ret;

}





static void __exit globalvar_exit(void)

{

device_destroy(cdev_class,dev);

class_destroy(cdev_class);

unregister_chrdev_region(dev,1);

printk("device ");

}





static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)

{



if (wait_event_interruptible(outq, flag != 0))

{

return - ERESTARTSYS;

}



if (down_interruptible(&sem))

{

return - ERESTARTSYS;

}



flag = 0;

if (copy_to_user(buf, &global_var, sizeof(int)))

{

up(&sem);

return - EFAULT;

}



up(&sem);



return sizeof(int);

}



static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)

{

if (down_interruptible(&sem))

{

return - ERESTARTSYS;

}

if (copy_from_user(&global_var, buf, sizeof(int)))

{

up(&sem);

return - EFAULT;

}

up(&sem);

flag = 1;



wake_up_interruptible(&outq);



return sizeof(int);

}



static unsigned int globalvar_poll(struct file *filp, poll_table *wait)

{

unsigned int mask = 0;



poll_wait(filp, &outq, wait);



if(flag != 0)

{

mask |= POLLIN | POLLRDNORM;

}



return mask;

}

module_init(globalvar_init);

module_exit(globalvar_exit);

Copyright © Linux教程網 All Rights Reserved