日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux高级字符设备之Poll操作

發布時間:2024/9/21 linux 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux高级字符设备之Poll操作 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

 在用戶程序中,select()和poll()也是與設備阻塞與非阻塞訪問息息相關的,使用非阻塞I/O的應用程序通常會使用select和poll系統調用查詢是否可對設備進行無阻塞的訪問。select系統調用最終會引發設備驅動中的poll函數被執行。

一、select()系統調用:
用于多路監控,當沒有一個文件滿足要求時,select將阻塞調用進程。
1.select()原型:

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,const struct timeval *timeout); /* *@maxfd : 需要檢查的文件描述符個數,數值應該比是三組fd_set中最大數更大(即一般取所有文件描述符的最大值加1),而不是實際文件描述符的總數。 *@readfds: 用來檢查可讀性的一組文件描述符。 *@writesfds: 用來檢查可寫性的一組文件描述符。 *@exceptsfds:用來檢查意外狀態的文件描述符。(注:錯誤并不是意外狀態) *@timeout:NULL指針代表無限等待,否則是指向timeval結構的指針,代表最長等待時間。(如果其中tv_sec和tv_usec都等于0, 則文件描述符的狀態不被影響,但函數并不掛起)

返回值:
(1)正常情況下返回滿足要求的文件描述符個數;
(2)經過了timeout等待后仍無文件滿足要求,返回0;
(3)如果select被某個信號中斷,將返回-1并設置errno為EINTR;
(4)若出錯,返回-1并設置相應的errno;

2.select的使用方法:
(1)將要監控的文件添加到文件描述符集;
(2)調用select開始監控;
(3)判斷文件是否發生變化;

?

3.系統提供四個宏對描述符集進行操作:

void FD_SET(int fd, fd_set *fdset); //將文件描述符fd添加到文件描述符集fdset中; void FD_CLR(int fd, fd_set *fdset); //從文件描述符集fdset中清除文件描述符fd; void FD_ISSET(int fd, fd_set *fdset); //在調用select后使用FD_ISSET來檢測文件描述符集中的文件fd發生了變化 void FD_ZERO(fd_set *fdset);//清空文件描述符集

?

二、Poll方法:

1.poll函數原型:

unsigned int(*poll)(struct file *filp, struct poll_table *wait); //第一個參數為file結構體指針,第二個參數為輪詢表指針。

這個函數應該進行以下兩項工作:

(1)對可能引起設備文件狀態變化的等待隊列調用poll_wait()函數,將對應等待隊列添加到poll_table中;?
(2)返回表示是否能對設備進行無阻塞可讀或可寫訪問的掩碼;
  位掩碼:POLLRDNORM, POLLIN,POLLOUT,POLLWRNORM
  設備可讀,通常返回:(POLLIN | POLLRDNORM)
  設備可寫,通常返回:(POLLOUT | POLLWRNORM)


三、調用過程:

Linux下select調用的過程:

1、用戶層應用程序調用select(),底層調用poll())
2、核心層調用sys_select() ------> do_select()
  最終調用文件描述符fd對應的struct file類型變量的struct file_operations *f_op的poll函數。
  poll指向的函數返回當前可否讀寫的信息。
  1)如果當前可讀寫,返回讀寫信息。
  2)如果當前不可讀寫,則阻塞進程,并等待驅動程序喚醒,重新調用poll函數,或超時返回。

3、驅動需要實現poll函數。
當驅動發現有數據可以讀寫時,通知核心層,核心層重新調用poll指向的函數查詢信息。

poll_wait(filp,&wait_q,wait) // 此處將當前進程加入到等待隊列中,但并不阻塞

  在中斷中使用wake_up_interruptible(&wait_q)喚醒等待隊列。

?

四、實例分析:

1.memdev.h

#ifndef _MEMDEV_H_ #define _MEMDEV_H_#ifndef MEMDEV_MAJOR #define MEMDEV_MAJOR 0 /*預設的mem的主設備號*/ #endif#ifndef MEMDEV_NR_DEVS #define MEMDEV_NR_DEVS 2 /*設備數*/ #endif#ifndef MEMDEV_SIZE #define MEMDEV_SIZE 4096 #endif /*mem設備描述結構體*/ struct mem_dev { char *data; unsigned long size; wait_queue_head_t inq; };#endif /* _MEMDEV_H_ */

?

2.memdev.c

#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 <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h>#include <linux/poll.h> #include "memdev.h"static mem_major = MEMDEV_MAJOR; bool have_data = false; /*表明設備有足夠數據可供讀*/module_param(mem_major, int, S_IRUGO);struct mem_dev *mem_devp; /*設備結構體指針*/struct cdev cdev; /*文件打開函數*/ int mem_open(struct inode *inode, struct file *filp) {struct mem_dev *dev;/*獲取次設備號*/int num = MINOR(inode->i_rdev);if (num >= MEMDEV_NR_DEVS) return -ENODEV;dev = &mem_devp[num];/*將設備描述結構指針賦值給文件私有數據指針*/filp->private_data = dev;return 0; }/*文件釋放函數*/ int mem_release(struct inode *inode, struct file *filp) {return 0; }/*讀函數*/ static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) {unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct mem_dev *dev = filp->private_data; /*獲得設備結構體指針*//*判斷讀位置是否有效*/if (p >= MEMDEV_SIZE)return 0;if (count > MEMDEV_SIZE - p)count = MEMDEV_SIZE - p;while (!have_data) /* 沒有數據可讀,考慮為什么不用if,而用while */{if (filp->f_flags & O_NONBLOCK)return -EAGAIN;wait_event_interruptible(dev->inq,have_data);}/*讀數據到用戶空間*/if (copy_to_user(buf, (void*)(dev->data + p), count)){ret = - EFAULT;}else{*ppos += count;ret = count;printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);}have_data = false; /* 表明不再有數據可讀 *//* 喚醒寫進程 */return ret; }/*寫函數*/ static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) {unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct mem_dev *dev = filp->private_data; /*獲得設備結構體指針*//*分析和獲取有效的寫長度*/if (p >= MEMDEV_SIZE)return 0;if (count > MEMDEV_SIZE - p)count = MEMDEV_SIZE - p;/*從用戶空間寫入數據*/if (copy_from_user(dev->data + p, buf, count))ret = - EFAULT;else{*ppos += count;ret = count;printk(KERN_INFO "written %d bytes(s) from %d\n", count, p);}have_data = true; /* 有新的數據可讀 *//* 喚醒讀進程 */wake_up(&(dev->inq));return ret; }/* seek文件定位函數 */ static loff_t mem_llseek(struct file *filp, loff_t offset, int whence) { loff_t newpos;switch(whence) {case 0: /* SEEK_SET */newpos = offset;break;case 1: /* SEEK_CUR */newpos = filp->f_pos + offset;break;case 2: /* SEEK_END */newpos = MEMDEV_SIZE -1 + offset;break;default: /* can't happen */return -EINVAL;}if ((newpos<0) || (newpos>MEMDEV_SIZE))return -EINVAL;filp->f_pos = newpos;return newpos;} unsigned int mem_poll(struct file *filp, poll_table *wait) {struct mem_dev *dev = filp->private_data; unsigned int mask = 0;/*將等待隊列添加到poll_table */poll_wait(filp, &dev->inq, wait);if (have_data) mask |= POLLIN | POLLRDNORM; /* readable */return mask; }/*文件操作結構體*/ static const struct file_operations mem_fops = {.owner = THIS_MODULE,.llseek = mem_llseek,.read = mem_read,.write = mem_write,.open = mem_open,.release = mem_release,.poll = mem_poll, };/*設備驅動模塊加載函數*/ static int memdev_init(void) {int result;int i;dev_t devno = MKDEV(mem_major, 0);/* 靜態申請設備號*/if (mem_major)result = register_chrdev_region(devno, 2, "memdev");else /* 動態分配設備號 */{result = alloc_chrdev_region(&devno, 0, 2, "memdev");mem_major = MAJOR(devno);} if (result < 0)return result;/*初始化cdev結構*/cdev_init(&cdev, &mem_fops);cdev.owner = THIS_MODULE;cdev.ops = &mem_fops;/* 注冊字符設備 */cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);/* 為設備描述結構分配內存*/mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);if (!mem_devp) /*申請失敗*/{result = - ENOMEM;goto fail_malloc;}memset(mem_devp, 0, sizeof(struct mem_dev));/*為設備分配內存*/for (i=0; i < MEMDEV_NR_DEVS; i++) {mem_devp[i].size = MEMDEV_SIZE;mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);memset(mem_devp[i].data, 0, MEMDEV_SIZE);/*初始化等待隊列*/init_waitqueue_head(&(mem_devp[i].inq));//init_waitqueue_head(&(mem_devp[i].outq)); }return 0;fail_malloc: unregister_chrdev_region(devno, 1);return result; }/*模塊卸載函數*/ static void memdev_exit(void) {cdev_del(&cdev); /*注銷設備*/kfree(mem_devp); /*釋放設備結構體內存*/unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*釋放設備號*/ }MODULE_AUTHOR("David Xie"); MODULE_LICENSE("GPL");module_init(memdev_init); module_exit(memdev_exit);


3.app-write.c

#include <stdio.h>int main() {FILE *fp = NULL;char Buf[128];/*打開設備文件*/fp = fopen("/dev/memdev0","r+");if (fp == NULL){printf("Open Dev memdev Error!\n");return -1;}/*寫入設備*/strcpy(Buf,"memdev is char dev!");printf("Write BUF: %s\n",Buf);fwrite(Buf, sizeof(Buf), 1, fp);sleep(5);fclose(fp);return 0; }

?

4.app-read.c

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/select.h> #include <sys/time.h> #include <errno.h>int main() {int fd;fd_set rds;int ret;char Buf[128];/*初始化Buf*/strcpy(Buf,"memdev is char dev!");printf("BUF: %s\n",Buf);/*打開設備文件*/fd = open("/dev/memdev0",O_RDWR);FD_ZERO(&rds);FD_SET(fd, &rds);/*清除Buf*/strcpy(Buf,"Buf is NULL!");printf("Read BUF1: %s\n",Buf);ret = select(fd + 1, &rds, NULL, NULL, NULL);if (ret < 0) {printf("select error!\n");exit(1);}if (FD_ISSET(fd, &rds)) read(fd, Buf, sizeof(Buf)); /*檢測結果*/printf("Read BUF2: %s\n",Buf);close(fd);return 0; }

總結

以上是生活随笔為你收集整理的Linux高级字符设备之Poll操作的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。