Linux高级字符设备驱动

雨点打透心脏的1/2处 2022-03-31 05:47 423阅读 0赞

转载:http://www.linuxidc.com/Linux/2012-05/60469p4.htm

1、什么是Poll方法,功能是什么?

120517165197221.png

2、Select系统调用(功能)
Select系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用进程。
int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set *exceptfds, const struct timeval *timeout)
Select系统调用(参数)
1)Maxfd:
文件描述符的范围,比待检测的最大文件描述符大1
2)Readfds:
被读监控的文件描述符集
3)Writefds:
被写监控的文件描述符集
4)Exceptfds:
被异常监控的文件描述符集;
5)Timeout:

定时器,Timeout取不同的值,该调用有不同的表现:

1>Timeout值为0,不管是否有文件满足要求,都立刻返回,无文件满足要求返回0,有文件满足要求返回一个正值。
2>Timeout为NULL,select将阻塞进程,直到某个文件满足要求
3>Timeout 值 为 正 整 数 , 就 是 等 待 的 最 长 时 间 , 即select在timeout时间内阻塞进程。
3、Select系统调用(返回值)
Select调用返回时,返回值有如下情况:
1)正常情况下返回满足要求的文件描述符个数;
2)经过了timeout等待后仍无文件满足要求,返回值为0;
3)如果select被某个信号中断,它将返回-1并设置errno为EINTR。
4)如果出错,返回-1并设置相应的errno。
4、Select系统调用(使用方法)
1)将要监控的文件添加到文件描述符集
2)调用Select开始监控
3)判断文件是否发生变化
系统提供了4个宏对描述符集进行操作:
#include
void FD_SET(int fd, fd_set *fdset)
void FD_CLR(int fd, fd_set *fdset)
void FD_ZERO(fd_set *fdset)
void FD_ISSET(int fd, fd_set *fdset)
宏FD_SET将文件描述符fd添加到文件描述符集fdset中;
宏FD_CLR从文件描述符集fdset中清除文件描述符fd;
宏FD_ZERO清空文件描述符集fdset;
在调用select后使用FD_ISSET来检测文件描述符集fdset中的文件fd发生了变化。
FD_ZERO(&fds); //清空集合
FD_SET(fd1,&fds); //设置描述符
FD_SET(fd2,&fds); //设置描述符
maxfdp=fd1+1; //描述符最大值加1,假设fd1>fd2
switch(select(maxfdp,&fds,NULL,NULL,&timeout))
case -1: exit(-1);break; //select错误,退出程序
case 0:break;
default:
if(FD_ISSET(fd1,&fds)) //测试fd1是否可读

5、poll方法

应用程序常常使用select系统调用,它可能会阻塞进程。这个调用由驱动的 poll 方法实现,原型为:unsigned int (*poll)(struct file *filp,poll_table *wait)

Poll设备方法负责完成:
1)使用poll_wait将等待队列添加到poll_table中。
2)返回描述设备是否可读或可写的掩码。
位掩码
1>POLLIN 设备可读
2>POLLRDNORM数据可读
3>POLLOUT\设备可写
4>POLLWRNORM数据可写
设备可读通常返回(POLLIN|POLLRDNORM )
设备可写通常返回(POLLOUT|POLLWRNORM )
6、范例

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

struct scull_pipe *dev =filp->private_data;
unsigned int mask =0;
/* 把等待队列添加到poll_table */
poll_wait(filp,&dev->inq,wait);
/*返回掩码*/
if (有数据可读)
mask = POLLIN |POLLRDNORM;/*设备可读*/
return mask;

}

7、工作原理
Poll方法只是做一个登记,真正的阻塞发生在select.c 中的 do_select函数。

8、实例分析

1)poll型设备驱动memdev.h源码

  1. #ifndef _MEMDEV_H_
  2. #define _MEMDEV_H_
  3. #ifndef MEMDEV_MAJOR
  4. #define MEMDEV_MAJOR 0 /*预设的mem的主设备号*/
  5. #endif
  6. #ifndef MEMDEV_NR_DEVS
  7. #define MEMDEV_NR_DEVS 2 /*设备数*/
  8. #endif
  9. #ifndef MEMDEV_SIZE
  10. #define MEMDEV_SIZE 4096
  11. #endif
  12. /*mem设备描述结构体*/
  13. struct mem_dev
  14. {
  15. char *data;
  16. unsigned long size;
  17. wait_queue_head_t inq;
  18. };
  19. #endif /* _MEMDEV_H_ */

2)Poll型设备驱动memdev.c源码

  1. #include <linux/module.h>
  2. #include <linux/types.h>
  3. #include <linux/fs.h>
  4. #include <linux/errno.h>
  5. #include <linux/mm.h>
  6. #include <linux/sched.h>
  7. #include <linux/init.h>
  8. #include <linux/cdev.h>
  9. #include <asm/io.h>
  10. #include <asm/system.h>
  11. #include <asm/uaccess.h>
  12. #include <linux/poll.h>
  13. #include "memdev.h"
  14. static mem_major = MEMDEV_MAJOR;
  15. bool have_data = false; /*表明设备有足够数据可供读*/
  16. module_param(mem_major, int, S_IRUGO);
  17. struct mem_dev *mem_devp; /*设备结构体指针*/
  18. struct cdev cdev;
  19. /*文件打开函数*/
  20. int mem_open(struct inode *inode, struct file *filp)
  21. {
  22. struct mem_dev *dev;
  23. /*获取次设备号*/
  24. int num = MINOR(inode->i_rdev);
  25. if (num >= MEMDEV_NR_DEVS)
  26. return -ENODEV;
  27. dev = &mem_devp[num];
  28. /*将设备描述结构指针赋值给文件私有数据指针*/
  29. filp->private_data = dev;
  30. return 0;
  31. }
  32. /*文件释放函数*/
  33. int mem_release(struct inode *inode, struct file *filp)
  34. {
  35. return 0;
  36. }
  37. /*读函数*/
  38. static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
  39. {
  40. unsigned long p = *ppos;
  41. unsigned int count = size;
  42. int ret = 0;
  43. struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/
  44. /*判断读位置是否有效*/
  45. if (p >= MEMDEV_SIZE)
  46. return 0;
  47. if (count > MEMDEV_SIZE - p)
  48. count = MEMDEV_SIZE - p;
  49. while (!have_data) /* 没有数据可读,考虑为什么不用if,而用while */
  50. {
  51. if (filp->f_flags & O_NONBLOCK)
  52. return -EAGAIN;
  53. wait_event_interruptible(dev->inq,have_data);
  54. }
  55. /*读数据到用户空间*/
  56. if (copy_to_user(buf, (void*)(dev->data + p), count))
  57. {
  58. ret = - EFAULT;
  59. }
  60. else
  61. {
  62. *ppos += count;
  63. ret = count;
  64. printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);
  65. }
  66. have_data = false; /* 表明不再有数据可读 */
  67. /* 唤醒写进程 */
  68. return ret;
  69. }
  70. /*写函数*/
  71. static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
  72. {
  73. unsigned long p = *ppos;
  74. unsigned int count = size;
  75. int ret = 0;
  76. struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/
  77. /*分析和获取有效的写长度*/
  78. if (p >= MEMDEV_SIZE)
  79. return 0;
  80. if (count > MEMDEV_SIZE - p)
  81. count = MEMDEV_SIZE - p;
  82. /*从用户空间写入数据*/
  83. if (copy_from_user(dev->data + p, buf, count))
  84. ret = - EFAULT;
  85. else
  86. {
  87. *ppos += count;
  88. ret = count;
  89. printk(KERN_INFO "written %d bytes(s) from %d\n", count, p);
  90. }
  91. have_data = true; /* 有新的数据可读 */
  92. /* 唤醒读进程 */
  93. wake_up(&(dev->inq));
  94. return ret;
  95. }
  96. /* seek文件定位函数 */
  97. static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)
  98. {
  99. loff_t newpos;
  100. switch(whence) {
  101. case 0: /* SEEK_SET */
  102. newpos = offset;
  103. break;
  104. case 1: /* SEEK_CUR */
  105. newpos = filp->f_pos + offset;
  106. break;
  107. case 2: /* SEEK_END */
  108. newpos = MEMDEV_SIZE -1 + offset;
  109. break;
  110. default: /* can't happen */
  111. return -EINVAL;
  112. }
  113. if ((newpos<0) || (newpos>MEMDEV_SIZE))
  114. return -EINVAL;
  115. filp->f_pos = newpos;
  116. return newpos;
  117. }
  118. unsigned int mem_poll(struct file *filp, poll_table *wait)
  119. {
  120. struct mem_dev *dev = filp->private_data;
  121. unsigned int mask = 0;
  122. /*将等待队列添加到poll_table表中 */
  123. poll_wait(filp, &dev->inq, wait);
  124. if (have_data)
  125. mask |= POLLIN | POLLRDNORM; /* readable */
  126. return mask;
  127. }
  128. /*文件操作结构体*/
  129. static const struct file_operations mem_fops =
  130. {
  131. .owner = THIS_MODULE,
  132. .llseek = mem_llseek,
  133. .read = mem_read,
  134. .write = mem_write,
  135. .open = mem_open,
  136. .release = mem_release,
  137. .poll = mem_poll,
  138. };
  139. /*设备驱动模块加载函数*/
  140. static int memdev_init(void)
  141. {
  142. int result;
  143. int i;
  144. dev_t devno = MKDEV(mem_major, 0);
  145. /* 静态申请设备号*/
  146. if (mem_major)
  147. result = register_chrdev_region(devno, 2, "memdev");
  148. else /* 动态分配设备号 */
  149. {
  150. result = alloc_chrdev_region(&devno, 0, 2, "memdev");
  151. mem_major = MAJOR(devno);
  152. }
  153. if (result < 0)
  154. return result;
  155. /*初始化cdev结构*/
  156. cdev_init(&cdev, &mem_fops);
  157. cdev.owner = THIS_MODULE;
  158. cdev.ops = &mem_fops;
  159. /* 注册字符设备 */
  160. cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);
  161. /* 为设备描述结构分配内存*/
  162. mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);
  163. if (!mem_devp) /*申请失败*/
  164. {
  165. result = - ENOMEM;
  166. goto fail_malloc;
  167. }
  168. memset(mem_devp, 0, sizeof(struct mem_dev));
  169. /*为设备分配内存*/
  170. for (i=0; i < MEMDEV_NR_DEVS; i++)
  171. {
  172. mem_devp[i].size = MEMDEV_SIZE;
  173. mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
  174. memset(mem_devp[i].data, 0, MEMDEV_SIZE);
  175. /*初始化等待队列*/
  176. init_waitqueue_head(&(mem_devp[i].inq));
  177. //init_waitqueue_head(&(mem_devp[i].outq));
  178. }
  179. return 0;
  180. fail_malloc:
  181. unregister_chrdev_region(devno, 1);
  182. return result;
  183. }
  184. /*模块卸载函数*/
  185. static void memdev_exit(void)
  186. {
  187. cdev_del(&cdev); /*注销设备*/
  188. kfree(mem_devp); /*释放设备结构体内存*/
  189. unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/
  190. }
  191. MODULE_AUTHOR("David Xie");
  192. MODULE_LICENSE("GPL");
  193. module_init(memdev_init);
  194. module_exit(memdev_exit);
  195. 3)测试程序app-read.c源码
  196. #include <stdio.h>
  197. #include <stdlib.h>
  198. #include <unistd.h>
  199. #include <sys/ioctl.h>
  200. #include <sys/types.h>
  201. #include <sys/stat.h>
  202. #include <fcntl.h>
  203. #include <sys/select.h>
  204. #include <sys/time.h>
  205. #include <errno.h>
  206. int main()
  207. {
  208. int fd;
  209. fd_set rds; //声明描述符集合
  210. int ret;
  211. char Buf[128];
  212. /*初始化Buf*/
  213. strcpy(Buf,"memdev is char dev!");
  214. printf("BUF: %s\n",Buf);
  215. /*打开设备文件*/
  216. fd = open("/dev/memdev0",O_RDWR);
  217. FD_ZERO(&rds); //清空描述符集合
  218. FD_SET(fd, &rds); //设置描述符集合
  219. /*清除Buf*/
  220. strcpy(Buf,"Buf is NULL!");
  221. printf("Read BUF1: %s\n",Buf);
  222. ret = select(fd + 1, &rds, NULL, NULL, NULL);//调用select()监控函数
  223. if (ret < 0)
  224. {
  225. printf("select error!\n");
  226. exit(1);
  227. }
  228. if (FD_ISSET(fd, &rds)) //测试fd1是否可读
  229. read(fd, Buf, sizeof(Buf));
  230. /*检测结果*/
  231. printf("Read BUF2: %s\n",Buf);
  232. close(fd);
  233. return 0;
  234. }

发表评论

表情:
评论列表 (有 0 条评论,423人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Linux字符设备驱动模型(一)

    从事Linux开发也有几年时间了,期间也写了一些比较简单的驱动,但一直没有做系统的整理,今天终于想静下心来做一些整理,首先就从最基本的字符设备驱动开始。先贴出一个简单的字符设备

    相关 Linux字符设备驱动基础(二)

    Linux字符设备驱动基础(二) 5 设备号相关操作 设备号由主设备号和次设备号组成。主设备号用来标识与设备文件相连的驱动程序,用以反映设备类型;次设备号是用于驱动