Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1300210
  • 博文数量: 548
  • 博客积分: 7597
  • 博客等级: 少将
  • 技术积分: 4224
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-15 13:21
个人简介

嵌入式软件工程师&&太极拳

文章分类

全部博文(548)

文章存档

2014年(10)

2013年(76)

2012年(175)

2011年(287)

分类: LINUX

2011-03-06 20:28:00

 

  1. ==================
  2. 内核模块、函数
  3. ==================

  4. 一、"黑盒测试"
  5.     ---它就基于函数,函数本身相当于一个"黑盒子",用它来实现一些功能.我们就只关心它的参数(输入):怎么用?和返回值(输出):干什么事?

  6.    2、先在linux内核下打一个tag包:ctags -R .
  7.     然后在内核中看到一个内核函数可以根据相关目录来进行查看此函数干什么用.

  8.    3、"终端输出级别""printk输出级别"
  9.     ---我们使用终端默认输出的级别是7,小于7才可以在终端上显示;反之则不能在终端上看见(但其实显示了,只是超出级别没在终端上打印出来,可以用cat /proc/kmsg看到);
  10.      而,printk输出默认级别是4.

  11.     (:)
  12.      ---终端之所以设置输出级别是为了让屏幕"清晰"一些,其实还有很多输出信息是我们用不着就没必要在终端上打印出来.

  13. 二、linux中函数调用
  14.     1、内核中有很多"Symbo",它就是用来表示那些"漂浮"的类似宏定义(也就是函数),在内核驱动中需用某个"Symbo"时只需包含它需的头文件就可以调用.

  15.     2、内核经常在一个文件内用一个.c将"Symbo"实现,然后用"EXPORT_SYMBOL(函数名)"出来,还需包含一个.h的头文件,这样在内核中就可以被调用.

  16.     3、引用计数:当有模块(A)用到此模块(B),它的(B)引用计数就会加1,此时就必须先rmmod A后才能rmmod B才可以.

  17. 三、字符设备
  18.     注:老一套字符设备驱动注册方法:
  19.     1、注册:register_chrdev(major, *name, struct *fops)
  20.         (1).major:主设备号:
  21.             cat /proc/devices 就可以查看现在机子上所使用的设备号.
  22.             Doucmention/devices.txt是内核关于设备号使用的一些建议.
  23.         
  24.         (2)*name:驱动名字

  25.         (3)*fops:定义一个结构体指针作为它第三个参数.

  26.     2、注消:unregister_chrdev(major, *name)

  27.    注:新一套字符设备驱动注册方法:
  28.     1、"占位":register_chrdev_region(from, count, *name)
  29.         (1)from:设备号(设备号 = 主设备(major) + 次设备(mini))

  30.         (2)count:你有几个设备要预加就让内核预留几,也就是它的值.

  31.         (3)*name:驱动名字
  32.     2、初始化一个结构体:将结构体中的每个成员赋值:cdev_init()
  33.     3、将初始化好的结构体加到内核中去:cdev_add()
  34.     4、先注消(3):cdev_del()
  35.     5、再注消(1):unregister_chrdev_region(from, count)

  36.    注:自动分配设备号:alloc_chrdev_region(设备号地址, 最小设备号, 分多少个设备号, 新设备名字)

  37. 四、字符设备驱动怎么样让APP调用到?
  38.     1、先在内核中注册一个字符设备,它就会在含设备号表和fops的表中追加一项.

  39.     2、我们来实现fops这个结构体中各个成员;也就是针对设备的各种操作(open/read...).

  40.     3、当一个应用程序open某个设备时,它调POSIX库,将设备号传给内核,内核到设备表中去找,找到了就会去执行对应的fops,也就执行相对应的操作.


  41. 五、linux的头文件是递归嵌套,当找头文件应先到linux/include下去找.eg:#include <linux/errno.h>

  42. 六、在内核中用go...to...进行容错处理.




  43. ======================
  44.     面向对象
  45. ======================
  46. 一、container_of(ptr, type, member):它能让已知一个成员,找到与它相关的所有结构体
  47.     1:指向结构体中某个成员的指针.
  48.     2:结构体的类型.
  49.     3:成员名字.

  50. 二、filp->private_data:万能指针


  51. 三、ioctl:
  52.     1:ioctl宏: Documention/ioctl/ioctl.h:它就是代表一个数字

  53.     2:可变参(...): 可以传一个参数,也可以不传参数;

  54.     3:unsigned long 可以与一个指针互传数据(相等),因为都是32位.但用的时候要强转一个相同的类型.

  55. 四、阻塞式I/O(睡眠机制)
  56.     1:wait_event中有一个条件,是因为当应用程序有多个进程都进来被内核等待,这时内核让把这些进程放到一个先进先出的队列中去,条件是指内核唤醒队列的条件,当各个进程唤醒条件不一样,这样就不会出现"队列惊群".

  57.     2:init
  58.         wait_queue_head_t q;
  59.         init_waitqueue_head(&my_dev.q);

  60.     3:use
  61.         在read下:带中断机制的等待:
  62.             if(wait_event_interruptible(dev->q, dev->cur_size > 0))
  63.          return -ERESTARTSYS;

  64. 六、轮询:(select / poll)
  65.     用户调用select就有一个读集和一个写集,内核发现用户调用select就备一个table结构体,里面有两个集(集的个数取决与用户调select中参数状况),后驱动调用test_poll给内核table结构中两个集一个容器(两个对列),容器建好后,内核将select中第二个参数放到其中一个"槽",并规定这个槽为"读", 将第三个参数放到另一个"槽"中去,并称为"写"; test_poll返回一个mask给内核,如果mask是POLLIN,内核到读队列中去找到r_fset,并将其置1,然后在用户层调用FD_ISSET(fd, &r_fset)就知道是读,就可以做一些事;同理写.

  66. 七、异步通知:信号
  67.    A:应用程序:
  68.     1:注册信号(内核就会有一个结构体来维护这个信号)
  69.     2:设置属主(当多外应用程序也发这个信号,设置属主后就知道要这个信号是谁发的)
  70.     3:设异步通知(内核才会去调用.fasync:也是因为你要注册一个内核中没实现的信号才要去做)
  71.     4:写一个信号处理函数(它是由内核来调用的)
  72.    B:内核:
  73.     1:file_operations: .fasync->fasync_helper:初始化异步队列,并注册
  74.     2:kill_fasync:等事件来了发一个你刚注册的信号

  75. :内核中的一些潜规则:
  76.     1:__usr:表示这个参数来自用户提供,需考虑其合法性
  77.     2:__foo:表示一定有下foo函数来调用它,内核编程人员一般更改__foo,从而来更改foo,所以我们一般只管foo就一定是最新状态.
  78.     3:内核不能用浮点数
  79.     4:内核使用定长栈(一般为4K)
  80.     5:内核不能调用C库
  81.     6:内核采用GNU C标准
  82.     7:内核编程要考虑多进程或多线程的用户操作,要判断或保护一些临界资源
阅读(494) | 评论(0) | 转发(0) |
0

上一篇:补丁和内核编译

下一篇:内核目录

给主人留下些什么吧!~~