Linux的應用層到底層驅動的調用過程

應用層如何內核.mdhtml

1.從應用層打通內核:驅動

 

首先來講是設備號的引入,咱們經過 cat/proc/kallsyms |grep mydevice 能夠查看設備號,固然咱們也是能夠本身建立設備號,這是源於咱們在寫內核模塊的時候在程序中指定。設備號有了,他就能夠標識咱們具體的設備。那咱們應用層如何操做那?其實咱們應用層須要建立一個設備節點文件建立的方法是sudo mknod /dev/hello c 255 0 這樣咱們就能夠建立一個指定設備號的設備節點文件。經過設備號,使設備節點文件和內核中的設備驅動程序關聯了起來。這其實也就打通了。有了文件,咱們應用層就能夠用open啊,read啊去操做這個設備節點文件。真正體現了linux下一切皆文件啊!。
node

2.具體實現:

用戶空間經過mknod建立了一個設備文件hello(設備號) 對應在內核空間建立一個inode結構體(包含有設備號)與之對應
應用程序打開這個設備文件: 操做inode結構體,取出inode裏面的設備號 ==》 到內核的cdev鏈表中去查找這個設備號對應的cdev對象 ==》 找到
cdev對象裏面的fops ===》 找到fops裏面的open函數指針 ==》 這個函數指針指向咱們本身實行的open函數。
linux

3.實現一個驅動服務於多個設備:

按照之前的思路:咱們如今若是是一個服務,服務於多個設備,那麼問題就來了,顯然咱們是不會寫多個驅動程序的,可是有些數據是咱們必需要有多份的。好比咱們如今拿最簡單
的一個操做,咱們在驅動程序裏面註冊倆個設備號,那麼意味着咱們在用戶空間,有倆個設備節點文件,咱們在應用程序中打開倆份,都採用寫入的方式,寫數據,若是內核中咱們把
接受數據的緩衝區沒有從新備份,公用一份,結果就是數據的覆蓋。這些存儲的數據顯然是要區分的,那個設備就是那個設備,既然是服務於多設備,那設備號的建立也天然是建立多個
註冊多個,這些均可以很是容易的指定,具體能夠看代碼以及cdev顯然也是倆份,他們都在內核的鏈表中,可是注意cdev結構體中的fops指針都指向的是同一個,因此fops是使用的同一個
其實也很好理解fops裏面都是些讀寫操做的函數指針,不必區分。共用更加好。
,對於cdev還比較簡單,咱們在建立本身的cdev結構體的時候,指定參數就能夠多建立。
最後咱們乾脆把這些都要用到的東西打包個結構體,叫作設備結構體。咱們回顧上面,當咱們又一個設備使用驅動程序的時候,咱們指定一個相關數據接收,可是多個設備文件的時候
就出現了對應問題,那就是 :對應問題,咱們如何指定設備號對應的保存數據的變量(更加準確的是設備結構體中的變量,由於咱們封裝了)。在open的時候,咱們是很是清楚當前的
設備號的由於open的時候,系統函數給咱們傳遞了struct inode * inodep, struct file * filep 倆個參數,第一個參數是inode號,第二個是filep(這個結構體保存了我,文件的狀態,以及當前
decv。咱們經過inodep->i_cdev 獲取到當前的cdev的地址,而後經過container_of(ptr, type, member)根據結構體成員的地址從而獲取到整個結構體的首地址。
拿到了設備結構體的首地址,而後經過filep->private_data = dev;將地址保存到file結構體中,藉助file傳遞,由於file在讀寫函數中也調用了。到了讀寫函數中咱們首先用一個對應的結構體指針把傳來
的地址接收struct hello_device * dev = filep->private_data;哈哈哈,咱們在讀寫函數中拿到了咱們的數據,並且是依靠不一樣的decv,拿到不一樣的設備結構體。
web

完美@!
函數

  1. #include<linux/module.h>
  2. #include<linux/init.h>
  3. #include<linux/fs.h>
  4. #include<linux/cdev.h>
  5. #include<asm/uaccess.h>
  6. /*2017年1月12日19:49:34 張飛online*/
  7. /* 許可證聲明 */
  8. MODULE_LICENSE("GPL");
  9. #define NUM_OF_DEV 2
  10. int major =255;
  11. int minor =0;
  12. //設備結構體:將本身用的數據封裝
  13. struct hello_device {
  14. dev_t devno;//設備號
  15. struct cdev mycdev;// cdev
  16. char data[64];
  17. }hello_dev[NUM_OF_DEV];
  18. //hello_dev[0] //設備1
  19. //hello_dev[1] //設備2
  20. /*##############################################
  21. *應用程序的open調用驅動的 hello_open 依靠 設備號和cdev中的iops,具體實現
  22. 能夠看上面的文字
  23. *
  24. ##############################################*/
  25. staticint hello_open(struct inode * inodep,struct file * filep)
  26. {
  27. struct hello_device * dev;
  28. printk("-- %s called -- major = %d minor = %d\n", __FUNCTION__, imajor(inodep), iminor(inodep));
  29. /*inodep->i_cdev指明設備結構體中的mycdev地址,最終獲取到當前cdev對應的咱們本身定義的
  30. 設備結構體的地址,這是核心:就是依靠當前cdev獲取當前咱們的結構體地址
  31. */
  32. dev = container_of(inodep->i_cdev,struct hello_device, mycdev);
  33. /*
  34. 用file中的私有數據傳出,由於讀寫函數也傳入了file,並且file也是一 一對應的,當前文件的file
  35. */
  36. filep->private_data = dev;
  37. return0;
  38. }
  39. //應用程序的close調用驅動的hello_release
  40. staticint hello_release(struct inode * inodep,struct file * filep)
  41. {
  42. printk("-- %s called --\n", __FUNCTION__);
  43. return0;
  44. }
  45. staticssize_t hello_read(struct file * filep,char __user * buf,size_t size,loff_t* off)
  46. {
  47. /*
  48. 獲取到了當前file,而後用結構體指針承接,實現修改
  49. */
  50. struct hello_device * dev;
  51. dev = filep->private_data;
  52. //check param
  53. if(size <0|| size >64)
  54. return-EINVAL;
  55. if(copy_to_user(buf, dev->data, size))
  56. {
  57. //爲真,表示失敗
  58. printk("cp err\n");
  59. return-1;
  60. }
  61. else{
  62. return size;
  63. }
  64. }
  65. staticssize_t hello_write(struct file * filep,constchar __user * buf,size_t size,loff_t* off)
  66. {
  67. struct hello_device * dev = filep->private_data;
  68. //check param
  69. if(size <0|| size >64)
  70. return-EINVAL;
  71. if(copy_from_user(dev->data, buf, size))
  72. {
  73. return-1;
  74. }
  75. else{
  76. return size;
  77. }
  78. }
  79. struct file_operations hello_fops ={
  80. .owner = THIS_MODULE,
  81. .open = hello_open,
  82. .release = hello_release,
  83. .read = hello_read,
  84. .write = hello_write,
  85. };
  86. staticint cdev_setup(struct cdev * cdev,dev_t devno)
  87. {
  88. int ret;
  89. cdev_init(cdev,&hello_fops);
  90. cdev->owner = THIS_MODULE;
  91. ret = cdev_add(cdev, devno,1);
  92. if(ret <0)
  93. return-1;
  94. return0;
  95. }
  96. /* 模塊加載函數,當向內核中插入這個模塊的時候會被調用 */
  97. int hello_init(void)
  98. {
  99. int ret;
  100. //作初始化的動做
  101. printk("-- %s called --\n", __FUNCTION__);
  102. //1. 註冊設備號
  103. hello_dev[0].devno = MKDEV(major, minor);
  104. hello_dev[1].devno = MKDEV(major, minor+1);
  105. //註冊來兩個設備號,255 0 和255 1
  106. ret = register_chrdev_region(hello_dev[0].devno, NUM_OF_DEV,"hello-device");
  107. if(ret <0)
  108. {
  109. printk("register_chrdev_region err\n");
  110. goto err1;
  111. }
  112. //2. cdev結構體插入內核鏈表
  113. ret = cdev_setup(&hello_dev[0].mycdev, hello_dev[0].devno);
  114. if(ret <0)
  115. {
  116. printk("cdev_add err\n");
  117. goto err2;
  118. }
  119. ret = cdev_setup(&hello_dev[1].mycdev, hello_dev[1].devno);
  120. if(ret <0)
  121. {
  122. printk("cdev_add err\n");
  123. goto err3;
  124. }
  125. // 初始化用戶數據
  126. strcpy(hello_dev[0].data,"000000000000000000000000000000000000");
  127. strcpy(hello_dev[1].data,"111111111111111111111111111111111111");
  128. return0;
  129. /*
  130. 這裏是刪除內核鏈表中的cdev 和 釋放設備號,藉助goto,巧妙釋放所有
  131. */
  132. err3:
  133. cdev_del(&hello_dev[0].mycdev);
  134. err2:
  135. unregister_chrdev_region(hello_dev[0].devno, NUM_OF_DEV);
  136. err1:
  137. return-1;
  138. }
  139. /* 模塊的卸載函數,當從內核中把本模塊刪除的時候被調用 */
  140. void hello_exit(void)
  141. {
  142. //作和init_module相反的動做
  143. printk("-- %s called --\n", __FUNCTION__);
  144. cdev_del(&hello_dev[1].mycdev);
  145. cdev_del(&hello_dev[0].mycdev);
  146. unregister_chrdev_region(hello_dev[0].devno, NUM_OF_DEV);
  147. return;
  148. }
  149. //聲明hello_init爲模塊的加載函數
  150. module_init(hello_init);
  151. module_exit(hello_exit);
  152. MODULE_AUTHOR("farsight");
  153. MODULE_DESCRIPTION("This is a simple moudles");

相關文章
相關標籤/搜索