LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 955|回复: 5

操作设备文件时,系统调用和库函数在具体运用中有很大的区别吗?

[复制链接]
发表于 2005-3-25 10:41:02 | 显示全部楼层 |阅读模式
又要麻烦各位大侠了   

这是我的一段设备驱动程序(我是2.6的内核)
  1. #include <linux/kernel.h>
  2. #include <linux/module.h>
  3. #include <linux/fs.h>
  4. #include <asm/uaccess.h>         //for put_user()

  5. int init_module(void);
  6. void cleanup_module(void);

  7. static int device_open(struct inode *, struct file *);
  8. static int device_release(struct inode *, struct file *);
  9. static ssize_t device_read(struct file *, char *, size_t, loff_t *);
  10. //static ssize_t device_write(struct file *, const char *, size_t, loff_t *);

  11. #define SUCCESS 0
  12. #define DEVICE_NAME "leo"
  13. #define BUFSZ 80
  14. char msg[BUFSZ];
  15. char *pmsg;
  16. static struct file_operations fops = {
  17.   read : device_read,
  18.   open : device_open,
  19.   release : device_release
  20. };

  21. static int major;

  22. int init_module(void)
  23. {
  24.   if((major=register_chrdev(0, DEVICE_NAME, &fops)) < 0){
  25.     printk("Registering the character device failed with %d\n",major);
  26.     return major;
  27.   }
  28.   printk("<1>I was assigned major number %d\n",major);

  29.   return 0;
  30. }

  31. void cleanup_module(void)
  32. {
  33.   int ret=unregister_chrdev(major, DEVICE_NAME);
  34.   if(ret < 0)
  35.     printk("Error in unregister_chrdev: %d\n", ret);
  36. }

  37. //called when a process tries to open the device file  
  38. static int device_open(struct inode *inode, struct file *file)
  39. {
  40.   try_module_get(THIS_MODULE);
  41.   strcpy(msg, "Hello World!");
  42.   pmsg = msg;
  43.   return SUCCESS;
  44. }

  45. //called when a process closes the device file.
  46. static int device_release(struct inode *inode, struct file *file)
  47. {
  48.   module_put(THIS_MODULE);
  49.   return SUCCESS;
  50. }

  51. //called when a process already opened the dev file and attemts to read from it.

  52. static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset)
  53. {
  54.   int bytes_read = 0;

  55.   while ( length && *pmsg  ){
  56.     put_user(*pmsg++,buffer++);
  57.     length--;
  58.     bytes_read++;
  59.   }

  60.   return bytes_read;
  61. }
复制代码


重点不在驱动, 我把它编成模块,成功插入内核后,创建设备文件后,cat该设备文件,可以看到其内容。操作如下
  1. # insmod ./chardev.ko
  2. # mknod testdriver c 253 0
  3. # cat testdriver
  4. Hello World!
复制代码


然后我用系统调用编了一个测试程序,如下
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>

  5. #define DEV "/home/leo/test/os/driver/testdriver"

  6. int main()
  7. {
  8.   int fd,bytes_read;
  9.   char buf[100];

  10.   if((fd = open(DEV, O_RDONLY)==-1)){
  11.     perror("open device file");
  12.     exit(1);
  13.   }


  14.   bytes_read=read(fd, buf, sizeof(buf));
  15.   buf[bytes_read]=0;
  16.   fprintf(stderr, "fd = %d, bytes_read = %d",fd, bytes_read);
  17.   printf("Content Read: %s",buf);
  18.   return 0;
  19. }
复制代码

运行到read就阻塞了, 我百思不得其解,后来用strace看了一下, 发现它竟然在读取标准输入!
  1. # strace ./a.out
  2. execve("./a.out", ["./a.out"], [/* 59 vars */]) = 0
  3. uname({sys="Linux", node="leo", ...})   = 0
  4. brk(0)                                  = 0x804a000
  5. access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
  6. open("/etc/ld.so.cache", O_RDONLY)      = 3
  7. fstat64(3, {st_mode=S_IFREG|0644, st_size=85545, ...}) = 0
  8. mmap2(NULL, 85545, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40018000
  9. close(3)                                = 0
  10. open("/lib/libc.so.6", O_RDONLY)        = 3
  11. read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\360P\1"..., 512) = 512
  12. fstat64(3, {st_mode=S_IFREG|0755, st_size=1295056, ...}) = 0
  13. mmap2(NULL, 1223980, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x4002d000
  14. mprotect(0x40151000, 27948, PROT_NONE)  = 0
  15. mmap2(0x40152000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x124) = 0x40152000
  16. mmap2(0x40156000, 7468, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40156000
  17. close(3)                                = 0
  18. mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40158000
  19. mprotect(0x40152000, 4096, PROT_READ)   = 0
  20. set_thread_area({entry_number:-1 -> 6, base_addr:0x40158a90, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
  21. munmap(0x40018000, 85545)               = 0
  22. open("/dev/urandom", O_RDONLY)          = 3
  23. read(3, "\354\256\242\210", 4)          = 4
  24. close(3)                                = 0
  25. open("/home/leo/test/os/driver/testdriver", O_RDONLY) = 3
  26. read(0,
复制代码

我这才恍然大悟read为什么迟迟没有返回
后来我又用库函数重写了该测试程序,如下
  1. #include <stdio.h>

  2. #define DEV "/home/leo/test/os/driver/testdriver"

  3. int main()
  4. {
  5.         FILE *fp;
  6.   int bytes_read;
  7.   char buf[100];

  8.   if((fp = fopen(DEV, "r"))==NULL){
  9.     perror("fopen device file");
  10.     exit(1);
  11.   }


  12.   bytes_read=fread(buf, 1, sizeof(buf), fp);
  13.   buf[bytes_read]=0;
  14.   printf("bytes_read = %d, Content Read: %s",bytes_read,buf);
  15.   return 0;
  16. }
复制代码
这样就一切正常了,看strace的输出
  1. # strace ./a.out
  2. execve("./a.out", ["./a.out"], [/* 59 vars */]) = 0
  3. uname({sys="Linux", node="leo", ...})   = 0
  4. brk(0)                                  = 0x804a000
  5. access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
  6. open("/etc/ld.so.cache", O_RDONLY)      = 3
  7. fstat64(3, {st_mode=S_IFREG|0644, st_size=85545, ...}) = 0
  8. mmap2(NULL, 85545, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40018000
  9. close(3)                                = 0
  10. open("/lib/libc.so.6", O_RDONLY)        = 3
  11. read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\360P\1"..., 512) = 512
  12. fstat64(3, {st_mode=S_IFREG|0755, st_size=1295056, ...}) = 0
  13. mmap2(NULL, 1223980, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x4002d000
  14. mprotect(0x40151000, 27948, PROT_NONE)  = 0
  15. mmap2(0x40152000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x124) = 0x40152000
  16. mmap2(0x40156000, 7468, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40156000
  17. close(3)                                = 0
  18. mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40158000
  19. mprotect(0x40152000, 4096, PROT_READ)   = 0
  20. set_thread_area({entry_number:-1 -> 6, base_addr:0x40158a90, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
  21. munmap(0x40018000, 85545)               = 0
  22. open("/dev/urandom", O_RDONLY)          = 3
  23. read(3, "\226\224,\235", 4)             = 4
  24. close(3)                                = 0
  25. brk(0)                                  = 0x804a000
  26. brk(0x806b000)                          = 0x806b000
  27. open("/home/leo/test/os/driver/testdriver", O_RDONLY) = 3
  28. fstat64(3, {st_mode=S_IFCHR|0644, st_rdev=makedev(253, 0), ...}) = 0
  29. ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbffff23c) = -1 ENOTTY (Inappropriate ioctl for device)
  30. mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40018000
  31. read(3, "Hello World!", 4096)           = 12
  32. read(3, "", 4096)                       = 0
  33. fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
  34. mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40019000
  35. write(1, "bytes_read = 12, Content Read: H"..., 43bytes_read = 12, Content Read: Hello World!) = 43
  36. munmap(0x40019000, 4096)                = 0
  37. exit_group(0)                           = ?
复制代码


我一直搞不明白,为什么用系统调用读取设备文件时,read会被重定向到标准输入呢
各位大虾不吝赐教
发表于 2005-3-25 18:14:00 | 显示全部楼层
标准库中的函数在实现上也是系统调用的,只不过进行了必要的CACHE
引用beginning linux programming 中的"一般输入输出少用系统调用"的原因:
1.每次系统调用都要花些时间,建议一次调用完成更多的事.(标准库中进行了CACHE)
2.有些设备如磁代,在写入数据没有达到一定长度时会产生一个gap
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-3-26 10:18:59 | 显示全部楼层
这个我知道啊
但我奇怪为什么一用系统调用, 就会从标准输入读取数据
并且你注意没有,从strace的输出来看,open的返回值明明是3,为什么接下来会从0读呢?
而库函数编的程序就没有这个问题??????????
回复 支持 反对

使用道具 举报

发表于 2005-3-26 11:08:49 | 显示全部楼层
因为你的语句写得有问题:
  1. ((fd = open(DEV, O_RDONLY)==-1))
复制代码

如果你用 vim,记得用 % 来查看括号是否匹配。
回复 支持 反对

使用道具 举报

发表于 2005-3-26 17:49:26 | 显示全部楼层
Post by herberteuler
因为你的语句写得有问题:
  1. ((fd = open(DEV, O_RDONLY)==-1))
复制代码

如果你用 vim,记得用 % 来查看括号是否匹配。

herberteuler兄真是明察秋毫呀~~!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-3-26 22:11:22 | 显示全部楼层
我汗哪
原来括号放错了地方!!
我怎么就没在read之前把fd打出来看看呢?
为这事我检查了一遍又一遍
百思不得其解, 还以为系统调用又有什么神秘的特性呢

多谢herberteuler
多谢各位
多谢多谢
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表