Chinaunix首页 | 论坛 | 博客
  • 博客访问: 437999
  • 博文数量: 133
  • 博客积分: 3259
  • 博客等级: 中校
  • 技术积分: 1255
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-14 13:49
文章存档

2012年(6)

2011年(112)

2010年(16)

分类: LINUX

2011-06-02 14:35:26

1: ※ “全局性”是指,假如打开设备多次,所有打开它的文件描述符共享其中的数据。“持久性”是指,假如设备关闭后再次打开,数据不丢失。 2:   3: ※ 真实的驱动程序利用中断与它们的设备同步 4:   5: 主设备号和次设备号 6:   7: ※ 主设备号标识设备对应的驱动程序;次设备号由内核使用,用于正确确定设备文件所指的设备。我们可以通过次设备号获得一个指向内核设备的直接指针,也可将次设备号当作设备本地数组的索引,不管用哪种方式,除了知道次设备号用来指向驱动程序所实现的设备之外,内核本身基本上不关心关于次设备号的任何其他消息。 8:   9: ※ 设备编号的内部表达 10:   11: n 在内核中,dev_t类型(在linux/types.h中定义)用来保存设备编号——包括主设备号和次设备号。 12:   13: n MAJOR; MINOR MKDEV 14:   15: ※ 分配和释放设备编号 16:   17: n linux/fs.h: 18:   19: int register_chrdev_region; 20:   21: int alloc_chrdev_region; 22:   23: void unregister_chrdev_region; 24:   25: 驱动程序需要将设备编号和内部函数连接起来,这些内部函数用来实现设备的操作。 26:   27: ※ 动态分配主设备号 28:   29: n 某些主设备号已经静态地分配给了大部分公用设备。在内核源码树的Documentation/device.txt文件中可以找到这些设备的列表。 30:   31: n 一旦驱动程序被广泛使用,随机选定的主设备号可能造成冲突和麻烦 32:   33: n 强烈推荐你不要随便选择一个一个当前不用的设备号做为主设备号,而使用动态分配机制获取你的主设备号。 34:   35: n 动态分配的缺点是,由于分配给你的主设备号不能保证总是一样的,无法事先创建设备节点。然而这不是什么问题,这是因为一旦分配了设备号,你就可以从/proc/devices读到。为了加载一个设备驱动程序,对insmod的调用被替换为一个简单的脚本,它通过/proc/devices获得新分配的主设备号,并创建节点 36:   37: #!/bin/sh 38:   39: module="scull" 40:   41: device="scull" 42:   43: mode="664" 44:   45: # invoke insmod with all arguments we got 46:   47: # and use a pathname, as newer modutils don‘t look in . by default 48:   49: /sbin/insmod ./$module.ko $ exit 1 50:   51: # remove stale nodes 52:   53: rm -f /dev/$[0-3] 54:   55: major=$ 56:   57: mknod /dev/$0 c $major 0 58:   59: mknod /dev/$1 c $major 1 60:   61: mknod /dev/$2 c $major 2 62:   63: mknod /dev/$3 c $major 3 64:   65: # give appropriate group/permissions, and change the group. 66:   67: # Not all distributions have staff, some have "wheel" instead. 68:   69: group="staff" 70:   71: grep -q ‘^staff:‘ /etc/group group="wheel" 72:   73: chgrp $group /dev/$[0-3] 74:   75: chmod $mode /dev/$[0-3] 76:   77: n 分配主设备号的最佳方式:默认采用动态分配,同时保留在加载甚至是编译时指定主设备号的余地。 78:   79: n Here‘s the code we use in scull ‘s source to get a major number: 80:   81: if else 82:   83: if 84:   85: ※ 一些重要的数据结构 86:   87: n文件操作file_operations 88:   89: ulinux/fs.h中定义file_operations。 90:   91: u__user表明指针是一个用户空间地址,因此不能被直接引用。 92:   93: ussize_t ; 初始化设备上的异步写入操作。 94:   95: uunsigned int ; 96: 是poll、epoll和select这三个系统调用的后端实现。可用来查询某个或多个文件描述符 97: 上的读取或写入是否会被阻塞。 98:   99: u int ; 100: 用于请求将设备内存映射到进程地址空间。 101:   102: uint ; 103: 用户调用它来刷新待处理的数据。 104:   105: uint ; 106: 用来通知设备其FASYNC标志发生了变化。 107:   108: file结构 109:   110: ustruct file是一个内核结构,不会出现在用户程序中 111:   112: inode结构 113:   114: u内核用inode结构在内部表示文件,因此它和file结构不同,后者表示打开的文件描述符。 115: 对单个文件,可能会有许多个表示打开的文件描述符的file结构,但它们都指向单个inode 116: 结构。 117:   118: udev_t i_rdev:对表示设备文件的inode结构,该字段包含了真正的设备编号。 119:   120: ustruct cdev i_cdev;struct cdev表示字符设备的内核的内部结构。 121: 当inode指向一个字符设备文件时,该字段包含了指向struct cdev结构的指针 122:   123: uunsigned int iminor; 124:   125: uunsigned int imajor; 126:   127: 用来从一个inode中获得主设备号和次设备号 128:   129: ※ 字符设备的注册 130:   131: n内核内部使用struct cdev结构来表示字符设备。为此我们的代码应包含linux/cdev.h, 132: 其中定义了这个结构以及与其相关的一些辅助函数。 133:   134: ※ The classic way to register a char device driver is with: 135:   136: int register_chrdev; 137:   138: int unregister_chrdev; 139:   140: ※ open方法 141:   142: n在大部分驱动程序中,open完成如下工作: 143:   144: u检查设备相关错误(诸如设备未就绪或相似的硬件问题)。 145:   146: u假如是首次打开,初始化设备。 147:   148: u标别次设备号,如有必要更新f_op指针。 149:   150: u分配和填写要放在filp-private_data里的数据结构。 151:   152: u增加使用计数。 153:   154: container_of; 155:   156: struct scull_dev dev; / device information / 157:   158: dev = container_of; 159:   160: filp-private_data = dev; / for other methods / 161:   162: ※ release方法的作用正好与open相反。这个设备方法有时也称为close。它应该: 163:   164: n 使用计数减1。 165:   166: n 释放open分配在filp-private_data中的内存。 167:   168: n 在最后一次关闭操作时关闭设备。 169:   170: ※ The scull driver introduces two core functions used to manage memory in the Linux kernel. These functions, 171: defined in linux/slab.h, are: 172:   173: void kmalloc; 174:   175: void kfree; 176:   177: ※ 在scull中,每个设备都是一个指针链表,其中每个指针都指向一个scull_qset结构。默认情况下,每一个这样的结构通过一个中间指针数组最多可引用4000000个字节。使用了一个有1000个指针的数组,每个指针指向一个4000字节的区域。 178:   179: ※ 量子是什么??P65 每个量子占用4000个字节 180:   181: ※ 使用宏和整数值同时答应在编译期间和加载阶段进行配置,这种方法和前面选择主设备号的方法类似。对于驱动程序中任何不确定的或与策略相关的数值,我们都可以使用这种技巧。 182:   183: ※ read和write 184:   185: ssize_t read; 186:   187: 从设备拷贝数据到用户空间 188:   189: ssize_t write; 190: 从用户空间拷贝数据到设备上。 191:   192: ※ 参数buff是指向用户空间的缓冲区,这个缓冲区或者保存要写入的数据,或者是一个存放新读入数据的空缓冲区。 193:   194: unsigned long copy_to_user; 195:   196: unsigned long copy_from_user; 197:   198: ※ 这两个函数还检测用户空间的指针是否有效。 199:   200: ※ Read方法 201:   202: n假如返回值等于最为count参数传递给read系统调用的值,所请求的字节数传输就成功完成了。这是最好的情况。 203:   204: n假如返回值是正的,但是比count小,只有部分数据成功传送。这种情况因设备的不同可能有许多原因。大部分情况下,程序会重新读数据。例如,假如你用fread函数读数据,这个库库函数会不断调用系统调用直至所请求的数据传输完成。 205:   206: n假如返回值为0,它表示已经到达了文件尾。 207:   208: n负值意味着发生了错误。值就是错误编码,错误编码在linux/errno.h中定义。 209:   210: ※ 与read相似,根据如下返回值规则,write也可以传输少于请求的数据量: 211:   212: n假如返回值等于count,则完成了请求数目的字节传送。 213:   214: n假如返回值是正的,但小于count,只传输了部分数据。再说明一次,程序很可能会再次读取余下的部分。 215:   216: n假如值为0,什么也没写。这个结果不是错误,而且也没有什么缘由需要返回一个错误编码。再说明一次,标准库会重复调用write。以后的章节会介绍阻塞型write,我们会对这种情形最更详尽的考察。 217:   218: n负值意味发生了错误;语义与read相同。
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
阅读(1194) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~