日期:2014-05-16  浏览次数:20633 次

LINUX字符设备驱动程序原理总结

LINUX字符设备驱动程序原理总结
2010年07月08日
  一)设备的输入/输出原理
  通常,任何数据都必须通过内核空间才能到达应用程序的缓冲上。例如:对一个设备的读操作会引起数据被至少复制两遍,一遍是将内容复制到内核缓冲中,另一遍是将其再次复制到用户缓冲中。这是为了保证数据的可靠性和安全性所付出的代价。
  但是,当字符设备驱动程序在低速字符设备上读写操作时,它通常直接将数据从用户空间的缓冲中复制到设备上。
  二)I/O和字符设备
  字符设备包括两种类型的设备: 1)低速的字符设备,也就是流设备,一般是终端和连续的端口。 在流设备上不能进行随机访问,也就是只能调用read和write来实现与该设备的通信。而不能使用mmap。由于read和write是同步的,当进程进行write和read调用时,它通常会阻塞,并要一直到操作完成后才能从系统调用里返回。这意味着在存储完成后驱动程序才会允许进程继续执行.本来可以用于运行代码的时间浪费在等待设备驱动程序上.
  用下面的例子说明这一点:
  time dd if=/dev/zero of=/dev/tty bs=1M count=10
  10+0 records in
  10+0 records out
  10485760 bytes (10 MB) copied, 1.99129 s, 5.3 MB/s
  real    0m1.997s
  user    0m0.000s
  sys     0m0.144s
  这里只向终端写了堆空字符串,整个命令用了1.997秒,而实际上花在CPU的时间却只有0.144s,结论是剩下的时间都用在了驱动程序的阻塞上。 2)高速的字符设备 在高速的字符设备上允许随机访问,也就是支持mmap系统调用,这使得应用程序可以查看该设备驱动程序必须提供的所有数据.一般是很大的一个存储区域.
  一个支持mmap系统调用的字符设备驱动程序,是不需要read和write调用的.因为它的读写操作会变得几乎像配给一大块内存一样简单.从而可以减少对系统调用的使用.
  mmap()函数用来将某个文件内容映射到内存中,对该内存区域的访问即是直接对该文件内容读写.调用成功返回映射区的内存起始地址,进程可直接操作起始地址为该值的有效地址,否则返回-1。
  一个简单的mmap示例:
  #include 
  #include 
  #include 
  #include 
  #include 
  #define ERROR(x) do{ perror(x);\
  exit (EXIT_FAILURE); } while(0)
  int
  main (int argc, char *argv[])
  {
  const int nbytes = 4096;
  void *ptr;
  int fd = open("/dev/zero", O_RDWR);  //打开/dev/zero设备
  if (fd == -1) ERROR("open");
  ptr = mmap(0, nbytes, PROT_READ|PROT_WRITE,
  MAP_PRIVATE, fd, 0); //调用mmap函数,将/dev/zero设备节点映射到*ptr中的地址,
  //这个内存区域大小为4096个字节,权限是可读/可写
  if (ptr == MAP_FAILED) ERROR("mmap");
  memset(ptr, 1, nbytes);        //填充*ptr这个内存区域为1
  munmap(ptr, nbytes);         //释放*ptr这个内存映像
  return 0;
  }
  三)块设备,文件系统和I/O
  1)块设备是磁盘及其他使用文件系统的存储设备的基础.
  2)要访问一个设备,可以有两种方法:直接访问或者通过文件系统访问.
  直接访问设备:
  .分区:fdisk
  .格式化:mkfs
  .对设备节点的复制:cp /dev/sda sda.img或者 dd /dev/sda f=sda.img bs=8M
  通过文件系统访问:
  .通过mount命令挂载:mount /dev/sda1 /mnt/disk1之后对/mnt/disk1进行访问
  用一个例子来说明两种访问方式的区别
  备份整个磁盘/dev/sda的内容
  直接访问方式:
  cp /dev/sda sda.img
  通过文件系统访问方式:
  mount /dev/sda1 /mnt/disk1
  tar cvzf /mnt/disk1 sda.tar.gz
  总结两种方式的区别:
  1)直接通过设备节点备份,可以备份磁盘的所有内容,包括启动块,而通过文件系统方式进行备份则不包括启动块等信息。
  2)直接通过设备节点备份是备份了磁盘的所有数据,即磁盘/分区为30GB,备份后的文件也是30GB,而通过对文件系统打包备份,则有多少数据则打包多少数据。
  四)缓冲区缓存和文件系统缓存
  1)缓冲区缓存用来存储块设备可写和可读的块,在操作系统中叫buffer,由块设备驱动程序进行管理.它有以下几个特点:
  .当一个进程要向一个块设备写数据时,首先数据会被复制到缓冲区缓存中的一个块里.块设备驱动程序不是马上被调用,当内核决定应该将数据块写回设备上时,它才会调用驱动程序.
  .内核原则上会尽可能久地将每个读写块的副本保存在缓冲区缓存中..优点一是优化了系统的性能,因为想从磁盘上读数据的进程,只要从高速存储器中读取就可以了..优点二是cache允许内核把临近的数据块连接起来,将它们合并成一个大的写磁盘,这样可以提高磁盘的利用率..优点三cache的应用减少了启动磁盘的次数,因为在被写回磁盘前,一个块已经更新过了,这样内核只需要执行一次写操作而不需要两次.
  .缺点是如果系统崩溃或者掉电,而数据如果还没写到设备上时,数据就会丢失.
  应用缓冲区缓存的例子:
  第一步查看当前系统中memory的buffers的大少,这里是40400
  [root@test1 ~]# free
  total       used       free     shared    buffers     cached
  Mem:        515600     221304     294296          0      40400     143780
  -/+ buffers/cache:      37124     478476
  Swap:      1052248          0    1052248
  第二步从/dev/zero上的一个字符设备复制到了4MB数据到ramdisk设备/dev/ram0上的一个块设备.
  [root@test1 ~]# dd if=/dev/zero f=/dev/ram0 bs=1k count=4096
  第三步查看当