分享
分享赚钱 收藏 举报 版权申诉 / 8

类型实验六:Linux设备驱动中的阻塞与非阻塞IO.doc

  • 上传人:czsj190
  • 文档编号:6187108
  • 上传时间:2019-04-01
  • 格式:DOC
  • 页数:8
  • 大小:76KB
  • 配套讲稿:

    如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。

    特殊限制:

    部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。

    关 键  词:
    实验六:Linux设备驱动中的阻塞与非阻塞IO.doc
    资源描述:

    1、实验六:Linux 设备驱动中的阻塞与非阻塞IO1.阻塞与非阻塞I/O阻塞和非阻塞 I/O 是设备访问的两种不同模式,驱动程序可以灵活地支持用户空间对设备的这两种访问方式。阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。而非阻塞操作的进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。驱动程序通常需要提供这样的能力:当应用程序进行 read()、write()等系统调用时,若设备的资源不能获取,而用户又希望以阻塞的方式访问设备,驱动程序应在设备驱动的

    2、 xxx_read()、xxx_write()等操作中将进程阻塞直到资源可以获取,此后,应用程序的 read()、write()等调用才返回,整个过程仍然进行了正确的设备访问,用户并没有感知到;若用户以非阻塞的方式访问设备文件,则当设备资源不可获取时,设备驱动的xxx_read()、xxx_write()等操作应立即返回,read()、write()等系统调用也随即被返回。阻塞从字面上听起来似乎意味着低效率,实则不然,如果设备驱动不阻塞,则用户想获取设备资源只能不停地查询,这反而会无谓地耗费 CPU 资源。而阻塞访问时,不能获取资源的进程将进入休眠,它将 CPU 资源让给其他进程。因为阻塞的进

    3、程会进入休眠状态,因此,必须确保有一个地方能够唤醒休眠的进程。唤醒进程的地方最大可能发生在中断里面,因为硬件资源获得的同时往往伴随着一个中断。代码 1 和代码 2 分别演示了以阻塞和非阻塞方式读取串口一个字符的代码。实际的串口编程中,若使用非阻塞模式,还可借助信号(sigaction)以异步方式访问串口以提高 CPU 利用率,而这里仅仅是为了说明阻塞与非阻塞的区别。代码1 阻塞地读取串口一个字符char buf;fd = open(“/dev/ttyS1“, O_RDWR);.res = read(fd, /当串口上有输入时才返回if(res = 1)printf(“%cn“, buf);代码

    4、2 非阻塞地读取串口一个字符char buf;fd = open(“/dev/ttyS1“, O_RDWR | O_NONBLOCK);.while(read(fd, /串口上无输入也返回,所以要循环尝试读取串口printf(“%cn“, buf);2.等待队列在 Linux 驱动程序中,可以使用等待队列(wait queue)来实现阻塞进程的唤醒。wait queue 很早就作为一个基本的功能单位出现在 Linux 内核里了,它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制。等待队列可以用来同步对系统资源的访问,第 7 章中所讲述的信号量在内核中也依赖等待

    5、队列来实现。Linux2.6 提供如下关于等待队列的操作。(1)定义“等待队列头” 。wait_queue_head_t my_queue;(2)初始化“等待队列头” 。init_waitqueue_head(而下面的 DECLARE_WAIT_QUEUE_HEAD()宏可以作为定义并初始化等待队列头的“快捷方式” 。DECLARE_WAIT_QUEUE_HEAD(name)(3)等待事件。wait_event(queue, condition)wait_event_interruptible(queue, condition)wait_event_timeout(queue, conditi

    6、on, timeout)wait_event_interruptible_timeout(queue, condition, timeout)第一个参数 queue 作为等待队列头的等待队列被唤醒,而且第二个参数 condition 必须满足,否则阻塞。wait_event()和 wait_event_interruptible()的区别在于后者可以被信号打断,而前者不能。加上_timeout 后的宏意味着阻塞等待的超时时间,以 jiffy 为单位,在第三个参数的 timeout 到达时,不论 condition 是否满足,均返回。wait()当 condition 满足时,wait_even

    7、t()会立即返回,否则,阻塞等待 condition 满足。(4)唤醒队列。void wake_up(wait_queue_head_t *queue);void wake_up_interruptible(wait_queue_head_t *queue);上述操作会唤醒以 queue 作为等待队列头的所有等待队列中所有属于该等待队列头的等待队列对应的进程。函数 wake_up()必须与 wait_event()或者 wait_event_timeout()成对地使用,而函数 wake_up_interruptible()则必须与 wait_event_interruptible()或者 w

    8、ait_event_interruptible_timeout()成对地使用。函数 wake_up()可以唤醒处于 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE 的进程,而函数 wake_up_interruptible()只能唤醒处于 TASK_INTERRUPTIBLE 的进程。3.支持阻塞操作的globalfifo设备驱动现在我们给虚拟设备 globalmem 增加这样的约束:把 globalmem 中的全局内存变成一个 FIFO,只有当FIFO 中有数据的时候(即有进程把数据写到这个 FIFO 而且没有被读进程读空) ,读进程才能把数据读出,而且读

    9、取后的数据会从 globalmem 的全局内存中被拿掉;只有当 FIFO 非满时(即还有一些空间未被写,或写满后被读进程从这个 FIFO 中读出了数据) ,写进程才能往这个 FIFO 中写入数据。现在,将 globalmem 重命名为 globalfifo,在 globalfifo 中,读 FIFO 将唤醒写 FIFO,而写 FIFO 也将唤醒读 FIFO。首先,需要修改设备结构体,在其中增加两个等待队列头,分别对应于读和写,并增加了current_len 成员用于表征目前 FIFO 中有效数据的长度。如代码 3 所示。代码3 globalfifo设备结构体struct globalfifo_

    10、devstruct cdev cdev; /*cdev 结构体*/unsigned int current_len; /*fifo 有效数据长度*/unsigned char memGLOBALFIFO_SIZE; /*全局内存*/wait_queue_head_t r_wait; /*阻塞读用的等待队列头*/wait_queue_head_t w_wait; /*阻塞写用的等待队列头*/;这个等待队列需在设备驱动模块加载函数中调用 init_waitqueue_head()被初始化,新的设备驱动模块加载函数如代码 4 所示。代码4 globalfifo设备初始化函数int globalfif

    11、o_init(void)int ret;dev_t devno = MKDEV(globalfifo_major, 0);/* 申请设备号*/if(globalfifo_major)ret = register_chrdev_region(devno, 1, “globalfifo“);else /* 动态申请设备号 */ret = alloc_chrdev_region(globalfifo_major = MAJOR(devno);if(ret sem); /*初始化信号量*/init_waitqueue_head( /*初始化读等待队列头*/init_waitqueue_head( /*

    12、初始化写等待队列头*/return 0;fail_malloc: unregister_chrdev_region(devno, 1);return ret;设备驱动读写操作需要被修改,在读函数中需增加等待 globalfifo_devp-w_wait 被唤醒的语句,而在写操作中唤醒 globalfifo_devp-r_wait,如代码 5 所示。代码5 增加等待队列后的globalfifo读写函数static ssize_t globalfifo_read(struct file *filp, char _user *buf, size_t count,loff_t *ppos)int re

    13、t;struct globalfifo_dev *dev = filp-private_data; /获得设备结构体指针wait_event_interruptible(dev-r_wait, dev-current_len != 0);if(count dev-current_len)count = dev-current_len;if(copy_to_user(buf, dev-mem, count)ret = - EFAULT;elsememcpy(dev-mem, dev-mem + count, dev-current_len - count); /fifo 数据前移dev-curr

    14、ent_len -= count; /有效数据长度减少printk(KERN_INFO “read %d bytes(s),current_len:%dn“, count, dev-current_len);wake_up_interruptible( /唤醒写等待队列ret = count;return ret;static ssize_t globalfifo_write(struct file *filp, const char _user *buf,size_t count, loff_t *ppos)struct globalfifo_dev *dev = filp-private_

    15、data; /获得设备结构体指针int ret;wait_event_interruptible(dev-w_wait, dev-current_len != GLOBALFIFO_SIZE);if(count GLOBALFIFO_SIZE - dev-current_len)count = GLOBALFIFO_SIZE - dev-current_len;if(copy_from_user(dev-mem + dev-current_len, buf, count)ret = - EFAULT;elsedev-current_len += count;printk(KERN_INFO “

    16、written %d bytes(s),current_len:%dn“, count, dev-current_len);wake_up_interruptible( /唤醒读等待队列ret = count;return ret;globalfifo.c 如代码 6 所示。代码6 globalfifo.c#include #include #include #include #include #include #include #include #include #include #include #define GLOBALFIFO_SIZE 20 /*全局 fifo 最大 20 字节*/

    17、#define GLOBALFIFO_MAJOR 150 /*预设的 globalfifo 的主设备号*/static int globalfifo_major = GLOBALFIFO_MAJOR;struct globalfifo_dev struct cdev cdev; /*cdev 结构体*/ unsigned int current_len; /*fifo 有效数据长度*/unsigned char memGLOBALFIFO_SIZE; /*全局内存*/ wait_queue_head_t r_wait; /*阻塞读用的等待队列头*/ wait_queue_head_t w_wa

    18、it; /*阻塞写用的等待队列头*/ ;struct globalfifo_dev *globalfifo_devp; /*设备结构体指针*/int globalfifo_open(struct inode *inode, struct file *filp)filp-private_data = globalfifo_devp;return 0;int globalfifo_release(struct inode *inode, struct file *filp)return 0;static ssize_t globalfifo_read(struct file *filp, char

    19、 _user *buf, size_t count,loff_t *ppos)int ret;struct globalfifo_dev *dev = filp-private_data;wait_event_interruptible(dev-r_wait, dev-current_len != 0);if(count dev-current_len)count = dev-current_len;if(copy_to_user(buf, dev-mem, count)ret = - EFAULT;elsememcpy(dev-mem, dev-mem + count, dev-curren

    20、t_len - count); /fifo 数据前移dev-current_len -= count; /有效数据长度减少printk(KERN_INFO “read %d bytes(s),current_len:%dn“, count, dev-current_len);wake_up_interruptible( /唤醒写等待队列ret = count;return ret;static ssize_t globalfifo_write(struct file *filp, const char _user *buf,size_t count, loff_t *ppos)struct g

    21、lobalfifo_dev *dev = filp-private_data; /获得设备结构体指针int ret;wait_event_interruptible(dev-w_wait, dev-current_len != GLOBALFIFO_SIZE);if(count GLOBALFIFO_SIZE - dev-current_len)count = GLOBALFIFO_SIZE - dev-current_len;if(copy_from_user(dev-mem + dev-current_len, buf, count)ret = - EFAULT;elsedev-curre

    22、nt_len += count;printk(KERN_INFO “written %d bytes(s),current_len:%dn“, count, dev-current_len);wake_up_interruptible( /唤醒读等待队列ret = count;return ret;static const struct file_operations globalfifo_fops =.owner = THIS_MODULE,.read = globalfifo_read,.write = globalfifo_write,.open = globalfifo_open,.r

    23、elease = globalfifo_release,;static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)int err, devno = MKDEV(globalfifo_major, index);cdev_init(dev-cdev.owner = THIS_MODULE;dev-cdev.ops = err = cdev_add(if(err)printk(KERN_NOTICE “Error %d adding LED%d“, err, index);int globalfifo_init

    24、(void)int ret;dev_t devno = MKDEV(globalfifo_major, 0);if (globalfifo_major)ret = register_chrdev_region(devno, 1, “globalfifo“);else ret = alloc_chrdev_region(globalfifo_major = MAJOR(devno);if(ret r_wait); /*初始化读等待队列头*/init_waitqueue_head( /*初始化写等待队列头*/return 0;fail_malloc: unregister_chrdev_regio

    25、n(devno, 1);return ret;void globalfifo_exit(void)cdev_del( /*注销 cdev*/kfree(globalfifo_devp); /*释放设备结构体内存*/unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*释放设备号*/MODULE_LICENSE(“Dual BSD/GPL“);module_init(globalfifo_init);module_exit(globalfifo_exit);makefile 文件如下 :ifneq ($(KERNELRELEASE)

    26、,)obj-m := globalfifo.o elseKERNELDIR = /usr/src/linux-headers-2.6.31-14-generic/PWD := $(shell pwd)default:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean:rm -rf *.o * core .depend .*.cmd *.ko *.mod.c endif4.在用户空间验证globalfifo的读写编写两个用户态的程序来测试,第一个用于读/dev/globalfifo,要求缓冲区为空时不能读;另一个用于写/dev/globalfifo,要求

    27、缓冲区满时不能写。读文件的程序为:#include #include #include #include #include void main()int fd;char str10;fd = open(“/dev/globalfifo“, O_RDWR, S_IRUSR | S_IWUSR);if(fd != - 1)read(fd, str, 10); /程序将阻塞在此语句,除非有针对 globalfifo 的输入printf(“The globalfifo is %sn“, str);elseprintf(“device open failuren“);写文件的程序为:#include #

    28、include #include #include #include void main()int fd;char str100;fd = open(“/dev/globalfifo“, O_RDWR, S_IRUSR | S_IWUSR);if(fd != - 1)printf(“Please input the globalfifo:n“);scanf(“%s“, str);write(fd, str, strlen(str);elseprintf(“device open failuren“);打开两个终端,分别运行上述两个应用程序,发现当在第二个终端中没有输入数据时,第一个终端没有输出(阻塞) 。

    展开阅读全文
    提示  道客多多所有资源均是用户自行上传分享,仅供网友学习交流,未经上传用户书面授权,请勿作他用。
    关于本文
    本文标题:实验六:Linux设备驱动中的阻塞与非阻塞IO.doc
    链接地址:https://www.docduoduo.com/p-6187108.html
    关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

    道客多多用户QQ群:832276834  微博官方号:道客多多官方   知乎号:道客多多

    Copyright© 2025 道客多多 docduoduo.com 网站版权所有世界地图

    经营许可证编号:粤ICP备2021046453号    营业执照商标

    1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png 9.png 10.png



    收起
    展开