找回密码
 注册
关于网站域名变更的通知
查看: 349|回复: 1
打印 上一主题 下一主题

Linux内核设计与实现之设备与模块

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2021-2-26 16:58 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x

& w2 L, j7 F' G3 @3 j5 W本章主要讨论与linux的设备驱动和设备管理的相关的4个内核成分,设备类型,模块,内核对象,sysfs。
7 e- b0 b/ f; s) z5 o
2 V2 s( g0 E% m  f) h7 a主要内容:0 r* i7 O$ e( s. R, M% k1 s  W6 _

9 u; f) }0 ~) \% t
  • 设备类型
  • 内核模块
  • 内核对象
  • sysfs
  • 总结
    ' M+ ~7 j  {, c7 v7 S* m

3 L3 e* C$ I1 {6 I: U; z1 ?9 D% a1 z4 _
1. 设备类型! z# W; S( S: w, s
linux中主要由3种类型的设备,分别是:
4 ?! `$ {' z: F) D8 t; Z8 O2 ]* z0 w9 B, R. [/ L1 F+ l4 `8 W6 ^
设备类型
代表设备
特点
访问方式
块设备硬盘,光盘随机访问设备中的内容一般都是把设备挂载为文件系统后再访问
字符设备键盘,打印机只能顺序访问(一个一个字符或者一个一个字节)一般不挂载,直接和设备交互
网络设备网卡打破了Unix "所有东西都是文件" 的设计原则通过套接字API来访问

2 `5 u1 `+ B- }% S
& C5 x& F7 x7 }) H9 r除了以上3种典型的设备之外,其实Linux中还有一些其他的设备类型,其中见的较多的应该算是"伪设备"。6 `" u* j; b1 J, H7 p% g
. }& \. [9 C8 U7 O" a4 a
所谓"伪设备",其实就是一些虚拟的设备,仅提供访问内核功能而已,没有物理设备与之关联。
# c6 ~$ D6 I7 Z* Z, F5 K$ E9 ?8 U" k/ S  @2 p0 k5 t5 R% g
典型的"伪设备"就是 /dev/random(内核随机数发生器), /dev/null(空设备), /dev/zero(零设备), /dev/full(满设备)0 p: K' U9 Z/ ?2 Y8 P3 j8 I7 D2 E0 u7 U

' u( g% s" D! {. B. U7 s; m! b" f
/ p5 ?1 A  c) m1 k( M0 ~3 X
# S9 ~+ S3 y  J& h7 k2. 内核模块
" @1 q# \( Y7 j& T1 V3 ^% M; B& }. k" p+ x$ N0 A  w6 [  l
Linux内核是模块化组成的,内核中的模块可以按需加载,从而保证内核启动时不用加载所有的模块,即减少了内核的大小,也提高了效率。
" \# g% t  b: R5 p( H) V3 i2 s
. C8 H( m. P0 E  J; r% T通过编写内核模块来给内核增加功能或者接口是个很好的方式(既不用重新编译内核,也方便调试和删除)。, \& e; c/ R4 _/ H4 t0 l& F/ u
1 c1 V# f2 Q5 A( y* a$ ^

9 M8 s8 }" @: A* d! |4 M' _% O: Q1 ~% t3 h0 E$ {' V& P
2.1 内核模块示例
$ |, ^% w, @3 E! C0 i0 k+ K& v9 C5 ~) c% _6 B
内核模块可以带参数也可以不带参数,不带参数的内核模块比较简单。
$ F5 g; O, A  L% \+ x7 I. ]+ Q% P- R+ {
我之前的几篇随笔中用于测试的例子都是用不带参数的内核模块来实验的。' L# K8 D9 P$ g! g2 D/ V$ p& |0 x
- V, X% R! o8 r2 {7 E) w) ^

2 G) i. C6 s2 |9 N2 n! Q
* q  q1 y& d& v  d2.1.1. 无参数的内核模块! g7 F* L- d5 B7 `3 }+ y/ e/ f

  p7 q/ y: V! y参考:9 N8 P  V7 @( _0 }3 T3 Y

6 y. a# I3 p. ^9 PLinux内核设计与实现之内核数据结构$ q5 H. V- o, ]; Q7 h

# M4 M4 `9 h/ R$ H0 vLinux内核设计与实现之中断下半部的处理3 M  F- V/ m9 o

2 O  k3 W& F2 g1 Y) |; e1 B7 f7 dLinux内核设计与实现之定时器和时间管理) r, T' [( _+ R( b" o$ H

  H, g  e/ V7 b2 {! F
1 \4 H, U. h! y$ H, p; T! t; w: S) K7 H) g& p
2.1.2. 带参数的内核模块# G% V1 H4 W2 z& g% p8 i6 V: w

% U. G* S4 T0 ?1 b& p. r构造带参数的内核模块其实也不难,内核中已经提供了简单的框架来给我们声明参数。
6 \1 P# Y9 F9 q' W8 W3 X, |1 ]' Y& v) J/ S
1. module_param(name, type, perm) : 定义一个模块参数; n- `8 C: E3 d2 p& o' x

* B( G0 x7 d1 l8 w. @+ 参数 name :: 既是用户可见的参数名,也是模块中存放模块参数的变量名8 x# w$ L( z* p! p" N0 r' G- A
7 V/ U3 R2 U6 o1 [8 _6 G  V
+ 参数 type :: 参数的类型(byte, short, int, uint, long, ulong, charp, bool...) byte型存放在char变量中,bool型存放在int变量中5 G7 u0 {" t$ @
. k- ?2 `/ A- Z( B, X+ i, t
+ 参数 perm :: 指定模块在 sysfs 文件系统中对应的文件权限(关于 sysfs 的内容后面介绍)7 Z5 D$ t4 L9 X3 o& {$ z
+ j+ P! w, h) P" r
7 @, N8 Y; P9 b' I: b" n; i4 F4 u

! q- ^- a8 p: I5 K7 mstatic int stu_id = 0;  // 默认id
  S6 Z+ R( i0 _* G1 f, p) p  @module_param(stu_id, int, 0644);% @0 v2 a2 u1 ?. Q& a0 C
3 ]* S7 f4 a& n* V/ V' \

- _; n* k( |+ w8 o0 V# z2. module_param_named(name, variable, type, perm) : 定义一个模块参数,并且参数对内对外的名称不一样# w4 M( m$ B5 }' l- I" o

6 @% R, K, l" e) P+ 参数 name :: 用户可见的参数名
! C; H! j0 [, z2 g3 Q' z
: l0 b  k5 T& |+ 参数 variable :: 模块中存放模块参数的变量名
; M; U1 o2 A3 S1 L6 |( v
6 e. Y- R2 k( B8 z: p3 Q+ 参数 type和perm :: 同 module_param 中的 type 和 perm
; n' E+ Y+ }1 E# C6 n0 A/ C, ~' }
, y8 I' T" z' g( i * J7 u* ^8 z* {0 ^

8 Z* N; J& m: V+ Q! u$ O7 Z7 g! Ystatic char* stu_name_in = "default name"; // 默认名字3 ~. i# u2 x& m. Z0 b/ ?' c
module_param_named(stu_name_out, stu_name_in ,charp, 0644);
3 d& C$ I4 p* \) F/* stu_name_out 是对用户开放的名称
" `- a% T4 y% c+ Z8 `7 _2 J" z * stu_name_in 是内核模块内部使用的名称
. D5 d1 u" A' D */
3 A) B6 b4 ?  B. @4 u, A 8 m& k: `  p  F3 D7 u8 Q
' H3 g; C, v. W
3. module_param_string(name, string, len, perm) : 拷贝字符串到指定的字符数组
4 @4 E. J6 q4 E4 Z9 H. M+ l( ^' H1 l+ E) h3 p! I; ?1 E
+ 参数 name :: 用户可见的参数名
6 J+ C* T) B9 f2 S( Q$ ]; b
# A( T) C6 S2 \+ 参数 string :: 模块中存放模块参数的变量名
: Q. F3 T9 H- M. s8 s$ _9 ~2 C
6 {! W  A2 ?9 k  }9 N+ 参数 len :: string 参数的缓冲区长度" T5 y8 a( R' w0 b1 S% i

, ~2 f6 ]" Y3 w4 X) y' c5 c8 v+ 参数 perm :: 同 module_param 中的 perm) {, e( U4 y: v2 [

; n* U" r5 \+ `! ^
" N: N) r4 c; ~( B6 i4 p# q% g; p0 _' _3 M1 x3 [
static char str_in[BUF_LEN];/ U! g; J2 T5 y* i9 Q* j
module_param_string(str_out, str_in, BUF_LEN, 0);9 ~! q) M" f1 Y/ T# ~3 m3 O
/* perm=0 表示完全禁止 sysfs 项 */
1 d0 R* a- {( z5 @ 6 X. d" r( ^" S! ?0 c
4 z; ?2 s' R$ n. {
4. module_param_array(name, type, nump, perm) : 定义数组类型的模块参数( e3 p- t7 J4 j; ?+ Y0 ^

! w8 w# S- b4 y' P+ 参数 name :: 同 module_param 中的 name4 y8 {) m* G* C0 e# u7 K
! H2 p6 M) x% D6 [8 Q
+ 参数 type :: 同 module_param 中的 type
) r, z5 e9 \% j& i: I) I  A4 A4 ~% _. z& x* v4 Q( B
+ 参数 nump :: 整型指针,存放数组的长度' w- L- `6 J  e$ X; _0 ]7 S

2 N2 a. ^9 f% X+ 参数 perm :: 同 module_param 中的 perm9 H6 R/ w9 T: [' v6 G6 _

6 u& T# e- n. Y' t; r0 q( B2 p% N ( \; g/ M  Y* d8 d4 e2 Q7 _
: t" z# h1 ?8 [
#define MAX_ARR_LEN 5
/ V: W+ R& H- v4 Astatic int arr_len;
# ?# k% y8 c* rstatic int arr_in[MAX_ARR_LEN];* d2 S/ E* ]( o
module_param_array(arr_in, int, &arr_len, 0644);
+ I) x1 i; \9 x( E+ U( R( X3 t 6 l9 T5 r6 k$ f6 j5 |3 S" v: p
- f) x* _# {' |7 }
5. module_param_array_named(name, array, type, nump, perm) : 定义数组类型的模块参数,并且数组参数对内对外的名称不一样! c. f# g7 _8 H
9 N1 D  X2 ]) X
+ 参数 name :: 数组参数对外的名称
, [7 y* F/ R% S# o% N, j2 E8 Y+ ]+ j6 Z. y# @
+ 参数 array :: 数组参数对内的名称
5 L3 g: m( Y- _* V3 i0 ~5 H+ ~1 k( L5 U. `+ ~  U+ T5 W
+ 参数 type,nump,perm :: 同 module_param_array 中的 type,nump,perm  R7 H3 G$ k& R$ ^% z; t* r
& A0 P' l! L3 V  x  ]1 z1 Q& E

% l7 f% [6 h) l: a$ W* p. Y5 u
3 U8 q1 f  o9 `; `6 T; t) J0 u, f#define MAX_ARR_LEN 58 x7 ]" F( S; Q, ?# m3 f9 i$ S
static int arr_len;
; A5 S* D1 N, `static int arr_in[MAX_ARR_LEN];
1 S) o5 z7 E- Y& a( T% l5 Pmodule_param_array_named(arr_out, arr_in, int, &arr_len, 0644);
8 |' o( c9 X7 n8 b) s
4 a, r5 K( s7 l2 E* H" l. L7 n& x: b. f  m3 X$ U  I% o+ ~4 t' N- k+ s
6. 参数描述宏
5 u# y0 x# c* j3 O5 A
+ G+ a4 U" z! Z+ f4 e可以通过 MODULE_PARM_DESC() 来给内核模块的参数添加一些描述信息。$ B0 \; q( r$ ?+ Y2 e

' h* n: h* g" m这些描述信息在编译完内核模块后,可以通过 modinfo  命令查看。* _: M8 o* r$ G% ~

# F# o$ F8 E& A+ b" w / Z- k7 e) U: y
% O# E4 L( q, }: m  i
static int stu_id = 0;  // 默认id
8 V. `, t6 o1 _, X) z8 umodule_param(stu_id, int, 0644);# ^% {, V0 S. K2 D
MODULE_PARM_DESC(stu_id, "学生ID,默认为 0");  // 这句就是描述内核模块参数 stu_id 的语句: F% F9 C" t/ o4 V1 Y

$ Q0 _1 g9 e7 U3 |
8 s) h/ L7 A% U3 X: L& _+ v% H4 y7. 带参数的内核模块的示例' {* y$ w7 h5 E
$ `3 u- E9 ?) E
示例代码:test_paramed_km.c8 k& g/ e# x4 O$ T8 }

; S% h9 h: ~$ G' J) {9 U1 h1 W; C定义了3个内核模块参数,分别是 int型,char*型,数组型的。
1 {% o2 _9 m, D& @1 k
& z  l: m/ b( `: y+ r8 h/ m复制代码- h( K* D' y( I; }3 R" D
#include<linux/init.h>* O! k, w! H, D8 X& _& t% G5 x
#include<linux/module.h>
  H$ S+ o, s. u$ C+ a6 V4 I#include<linux/kernel.h>
" ?& m6 y1 T  w, `
" n1 ?3 X9 J1 ~" _- h% r- wMODULE_LICENSE("Dual BSD/GPL");8 l; x% s+ F2 n2 N& `
. ~, A, @  Z* Z) b
struct student
/ u# ~6 P/ Y7 g& M' k- d2 Y{
1 Q8 Y' |( I5 C% q5 I    int id;
% Y" @' K+ G: p6 K5 W/ f    char* name;
4 I9 q9 v8 t9 {: j};, k2 J) x/ a9 {- A5 {+ K
static void print_student(struct student*);
) q4 F$ a; z8 x- V
$ q$ `  U, b, [2 c' p; {static int stu_id = 0;  // 默认id
8 ]; n4 h' C+ h: N; G5 Kmodule_param(stu_id, int, 0644);- v1 o- _% Z8 Z& I' w  g+ ^) J
MODULE_PARM_DESC(stu_id, "学生ID,默认为 0");
$ j; y0 |* q. C& A! c6 D9 ?! {$ u6 r9 @
static char* stu_name_in = "default name"; // 默认名字
$ Q; e) K- `) M$ A* _- f" Dmodule_param_named(stu_name_out, stu_name_in ,charp, 0644);
# p" U, y( Q% L; w+ Z, wMODULE_PARM_DESC(stu_name, "学生姓名,默认为 default name");
0 T7 [. U9 ^/ ?* ]3 f3 D0 Y: ^7 @6 I( u
#define MAX_ARR_LEN 5, D7 F6 S' C( A7 ]
static int arr_len;
' B$ i$ h: T8 Ustatic int arr_in[MAX_ARR_LEN];
. P9 |$ Y+ z7 z; \module_param_array_named(arr_out, arr_in, int, &arr_len, 0644);0 G( }4 Q8 B* Q/ Z" M: J2 k
MODULE_PARM_DESC(arr_in, "数组参数,默认为空");
; e" L/ b4 R+ c3 w- e) O2 Y/ d* \: f9 t
static int test_paramed_km_init(void)
) z2 B6 t& Y& P  F3 j" Y3 i) Q9 ^{
7 h$ H% ?& D5 @4 o& L    struct student* stu1;# R  E8 f) _' P, ?! x
    int i;! l& X: A0 ^4 l; N; H' x
    ( Y" y4 ^/ c3 P9 |2 q
    /* 进入内核模块 */
$ Q' \4 A' V5 |- b7 [/ I; h    printk(KERN_ALERT "*************************\n");8 U" q+ A+ \4 b) I* {% w3 ~4 w- m2 S
    printk(KERN_ALERT "test_paramed_km is inited!\n");9 J" l$ k- @: A( t
    printk(KERN_ALERT "*************************\n");5 F+ {; y" S! j0 c1 d" F% E" b
    // 根据参数生成 struct student 信息, p8 p" ]& ?2 Q" A& k) P
    // 如果没有参数就用默认参数, H2 a6 H: P7 n3 L" }
    printk(KERN_ALERT "alloc one student....\n");4 [& {  A1 u9 z! v, v$ L$ x
    stu1 = kmalloc(sizeof(*stu1), GFP_KERNEL);! B# m' Y# P& l1 {- L* o, S$ b
    stu1->id = stu_id;2 |  }( |( n+ j8 v3 U
    stu1->name = stu_name_in;
; T; {. F5 X# ~8 ]7 {  B0 o, R, y" }    print_student(stu1);
4 b! T8 G+ j/ ]' C6 P( s
+ }& Q& W2 K) `$ J- `    // 模块数组
" p( c6 J! Z  |. o    for (i = 0; i < arr_len; ++i) {' e" S7 t/ y9 @. j7 a
        printk(KERN_ALERT "arr_value[%d]: %d\n", i, arr_in);
3 _6 w! j  N# n0 W    }* w8 t- `% |5 w1 H0 u
1 _, z8 ~( y1 d4 Y0 s; X+ V, |
   
1 Q5 v# y0 \& D6 K$ g6 g6 H    return 0;
& D8 Q/ n" d, W4 J}+ @& K1 s- o5 k; f% T9 m1 `

4 O6 R) z+ p* ?/ x# M' j/ {static void test_paramed_km_exit(void)
# M6 f8 F+ N; h4 k  P+ p{/ i( T$ |8 }3 B. |+ P) q& [# G
    /* 退出内核模块 */
( h  O* ]0 _. j6 E    printk(KERN_ALERT "*************************\n");3 ]6 d  \2 y$ \$ _5 L
    printk(KERN_ALERT "test_paramed_km is exited!\n");
' ^' Z# e8 p" F5 ]# R    printk(KERN_ALERT "*************************\n");
& _% w7 k. q+ e+ e    printk(KERN_ALERT "\n\n\n\n\n");0 h: M5 M: k1 }1 s$ r) |, K
}
7 D" V) i7 t5 C" _" l& Y9 d( u1 @7 N6 [/ |' n; b
static void print_student(struct student *stu)
& A5 Q* n# R1 p+ h3 ?, L{0 S& K1 K& A! l  a4 g
    if (stu != NULL)$ R: w3 J( I2 z
    {
' k. p8 d4 ?' w/ I$ t. J: S        printk(KERN_ALERT "**********student info***********\n");
0 ~' q1 s* C8 z/ ~        printk(KERN_ALERT "student id   is: %d\n", stu->id);/ w" c8 n. T% Y
        printk(KERN_ALERT "student name is: %s\n", stu->name);% g5 ^' [! O. I) H
        printk(KERN_ALERT "*********************************\n");! k$ G4 u' o( V; G4 f" @* f
    }5 N) h- G' _% X' ^6 @
    else
, w6 M) i% a6 R( {4 G        printk(KERN_ALERT "the student info is null!!\n");    ! I& V) d7 n& I7 v; x
}/ l* m4 U6 B, k3 ]

' w3 W# G0 N5 ?+ Cmodule_init(test_paramed_km_init);
9 Q2 Q9 }! Y! G1 Xmodule_exit(test_paramed_km_exit);
! e$ m8 i8 U  s8 Z/ h7 j复制代码
. J( x9 a  U. i6 c
6 q* c5 y% @: ^4 O& e' q  T2 b4 S) t6 ]6 M6 f
上面的示例对应的 Makefile 如下:" [$ m0 h# V7 d" E4 \

5 g8 \: ?( R8 r% R6 s% f1 L复制代码" p6 }1 F) ~* v* W/ h
# must complile on customize kernel3 ?4 [6 X% F  o3 @: b; T
obj-m += paramed_km.o  p- U' U' V7 O5 c+ v% d( ]
paramed_km-objs := test_paramed_km.o6 X/ e2 m0 }2 |  U5 E* R5 |! Y. G# D
: |% |  [( S) l4 G& a8 j5 T
#generate the path  s9 c3 D) B& R  w7 U3 h8 c+ j
CURRENT_PATH:=$(shell pwd)
+ {/ k- j0 g  p% H; x' `#the current kernel version number
2 S5 x& u, z3 z' ?LINUX_KERNEL:=$(shell uname -r)
& c3 G; l6 w' t9 r, V) {5 c/ @5 O; m#the absolute path- `2 H. v) k# v: ?
LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)& Q  z  f7 l$ Y6 ~+ @( }9 \
#complie object! ?  I: ]7 \# ~- Q$ S
all:
" T, {8 K( X5 s3 f4 b    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules! \3 O& \( _& _4 M. y' E
    rm -RF modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned
$ a5 E8 {& B$ T1 G4 j#clean
( H$ c& S3 R5 u3 Eclean:4 G. l" r* G% }$ T: u
    rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned7 y+ g/ ]  w. _2 y2 t
复制代码
3 t7 z- p3 ^& R. i, Y + l" P$ x% W4 s* c$ k
内核模块运行方法:(我的运行环境是 CentOS 6.3 x86_64)) Q: t( G' z3 x3 ~; U+ T7 \

0 U9 m8 f7 j; B. h$ Q+ L* w复制代码! o7 l- W" `9 M7 Y$ d! {+ v
[root@vbox chap17]# uname -r
$ s" m7 n8 f1 s$ K* t2.6.32-279.el6.x86_64" K- L4 T/ \- B) H3 D! m: R0 K! J
[root@vbox chap17]# ll" e5 X& p/ i. Y/ Z3 ]7 X
total 8
) R- V" Y# x. |2 `  ]-rw-r--r-- 1 root root  538 Dec  1 19:37 Makefile
  f* @2 r) I, @/ M-rw-r--r-- 1 root root 2155 Dec  1 19:37 test_paramed_km.c' e& @+ Y6 _: w
[root@vbox chap17]# make    <-- 编译内核
* q9 H- p+ D2 {6 W3 S4 fmake -C /usr/src/kernels/2.6.32-279.el6.x86_64 M=/root/chap17 modules
& a1 }% M6 C) L: O* v; ]3 }make[1]: Entering directory `/usr/src/kernels/2.6.32-279.el6.x86_64'
8 b. y* T, o2 g# q4 [  CC [M]  /root/chap17/test_paramed_km.o
5 F" H7 O9 Q8 B  LD [M]  /root/chap17/paramed_km.o
5 {4 v3 d0 @; J6 d$ q- p  Building modules, stage 2.. v1 l; v8 w6 Z2 c) ]# N, K
  MODPOST 1 modules5 D+ h1 n5 {- Q
  CC      /root/chap17/paramed_km.mod.o
$ L) f; `& H& I6 t* Q  LD [M]  /root/chap17/paramed_km.ko.unsigned
7 d9 [9 R: T7 d2 T  T8 O# C* M  NO SIGN [M] /root/chap17/paramed_km.ko$ k" n6 q2 d7 ~7 f9 l. V
make[1]: Leaving directory `/usr/src/kernels/2.6.32-279.el6.x86_64'
8 s# L3 `( ~- f! [" k" U! \0 U/ E; n' a+ krm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned
# j+ T( a' j" ^3 r* X# n[root@vbox chap17]# ll   <-- 编译内核后,多了 paramed_km.ko 文件: g$ x- C( ]" L( X
total 124
. l, W) r/ N9 C! x2 y-rw-r--r-- 1 root root    538 Dec  1 19:37 Makefile2 n8 H+ F, z6 A( f* Z* {$ A
-rw-r--r-- 1 root root 118352 Dec  1 19:37 paramed_km.ko4 T/ n. h) e4 H0 X
-rw-r--r-- 1 root root   2155 Dec  1 19:37 test_paramed_km.c
8 b. P6 K8 w8 @" p# V
" ~5 l7 Y2 ~* d3 t  G5 d9 h<-- 通过 modinfo 命令可以查看对内核模块参数的注释( b$ |" l. z2 q, N
[root@vbox chap17]# modinfo  paramed_km.ko8 K8 V( r$ b9 b+ F
filename:       paramed_km.ko
; s& ^9 P  ^2 i0 [license:        Dual BSD/GPL
; h1 D) _* e  D1 N$ T2 Osrcversion:     C52F97687B033738742800D
; M; ^, Q. q- e$ [8 qdepends:
* L! m4 K! f. x1 a1 Uvermagic:       2.6.32-279.el6.x86_64 SMP mod_unload modversions
) P8 X' ~/ `) j- [parm:           stu_id:学生ID,默认为 0 (int)+ q( ?; R8 y) G+ C; Z) k: m
parm:           stu_name_out:charp
+ h" c' O/ j8 d: c. k& lparm:           stu_name_in:学生姓名,默认为 default name( R' c2 r" b# a3 r/ }& `# K
parm:           arr_out:array of int
# E2 ], c. P. l( s' ^; Z9 Yparm:           arr_in:数组参数,默认为空  r2 O2 X( l% A

7 }4 b: @2 {; z/ x, w8 S; t2 }$ {) o<-- 3 个参数都是默认的
" u5 z: D' D( j; D% W; ^. E[root@vbox chap17]# insmod paramed_km.ko
! f* ]) x+ }6 n[root@vbox chap17]# rmmod paramed_km.ko- ^' q0 }1 W1 v9 D
[root@vbox chap17]# dmesg | tail -16  <-- 结果中显示2个默认参数,第3个数组参数默认为空,所以不显示7 a2 H( k5 C' M
*************************3 C/ Y% i, Q; d' o
test_paramed_km is inited!
' K1 v' x& H6 }8 u2 v5 {*************************
( v' ~1 E+ V6 t' ialloc one student....' W9 ]' s; V: Z
**********student info***********
' O8 }# |, z8 B0 C% V% z1 W( E: jstudent id   is: 0" U& x3 {, v3 ]) I3 ?( m6 ]
student name is: default name7 j  }8 O- o/ F# G1 m- x: }8 g3 \& Z
*********************************3 u; x. M8 l3 P: q' U5 s
*************************
3 ~; v& R8 a* I2 _( T5 dtest_paramed_km is exited!: w; B" v6 q& w) W  ~. n, r
*************************
( I0 z$ P# }5 d4 p# P( m# E5 o8 `  U# S- s3 P
<-- 3 个参数都被设置" m/ P; q3 M6 o
[root@vbox chap17]# insmod paramed_km.ko stu_id=100 stu_name_out=myname arr_out=1,2,3,4,54 [' \2 ^7 f+ o  k
[root@vbox chap17]# rmmod paramed_km.ko& ]0 J; I7 m4 ^% `
[root@vbox chap17]# dmesg | tail -216 Z; P  T! X7 \6 p( y
*************************2 G! y- F  S8 l0 F" M9 V/ F# M* Y' i. Z
test_paramed_km is inited!
/ p2 r8 E: _" L6 q8 ^9 Y: Q*************************
% A  A4 K  U2 H' y% Ealloc one student....
6 j/ d6 W0 T6 g3 j% F/ m' Z**********student info***********6 m0 |( L8 ~1 I& m3 @
student id   is: 100
' ^$ c1 [4 Y- c0 k8 |student name is: myname
  T3 F, N: s7 u' E6 N. B9 g*********************************; c2 _- ]* r% ~
arr_value[0]: 18 [6 m+ }: p3 }& k% V
arr_value[1]: 2# }9 f. Y2 L5 N* v7 r
arr_value[2]: 30 V1 T) @- D% F# G+ g
arr_value[3]: 4) q" c* ?* U* [0 y& w+ S0 N
arr_value[4]: 5
9 }# D; m5 J' \*************************
$ k; x% U- ^  Z8 xtest_paramed_km is exited!
5 X1 f8 k7 U/ p, e*************************
8 o( c5 a( O! c7 k4 t复制代码
1 G$ n' H/ m7 F; t; w; ] " R+ ]1 D) L0 C) ^$ b0 c

9 l* U6 S% K4 `. p2.2 内核模块的位置3 r  ^( `/ o- N6 c" J
2.2.1.  内核代码外3 }9 k6 ^: L* p, A7 I/ u
% Q; o5 o  a' C% q* }8 v
上面的例子,以及之前博客中内核模块的例子都是把模块代码放在内核之外来运行的。
( T0 F4 x9 G7 ~. x- E; h' u; e7 D. R# E( T% b4 y
9 F# G4 B* S+ e7 N

2 {7 H3 n% \; n" W1 m3 L- J& I, h2.2.2. 内核代码中
; E/ F0 z' P% w, f8 Z
+ l9 ^" t" G+ `' q内核模块的代码也可以直接放到内核代码树中。% B' L7 J  E6 G! O6 M6 ?

, r) c' M$ c4 I如果你开发了一种驱动,并且希望被加入到内核中,那么,可以在编写驱动的时候就将完成此驱动功能的内核模块加到内核代码树中 driver 的相应位置。( R% r3 l+ A. G3 k4 n; p
4 u# |8 I  W4 m: e; F" k) J
将内核模块加入内核代码树中之后,不需要另外写 Makefile,修改内核代码树中的已有的 Makefile 就行。
  M- W' k6 \0 A- T; G6 U5 a5 O' e# J
比如,写了一个某种字符设备相关的驱动,可以把它加到内核代码的 /drivers/char 下,* w1 z* `; ~/ i$ w* ~3 m$ U

$ Y$ T7 C: |% s) `4 \5 O同时修改 /drivers/char下的Makefie,仿照里面已有的内容,增加新驱动的编译相关内容即可。9 |+ i# u: X5 z. a7 W
2 J, n$ ^. G: q

* q# {5 i+ _7 ^7 O4 d  Y% C$ Z0 w9 P& g+ g3 E
之后,在编译内核的时候会将新的驱动以内核模块的方式编译出来。1 @9 G0 t2 V- o: u0 n1 ]4 X5 T
$ ^/ k) a+ ^0 O9 T" {

/ S3 K/ B% q2 z6 B- t2 b) i
1 B3 A4 d; U& G8 m# [2.3 内核模块相关操作1 e' h6 g$ \$ e1 C' P/ z0 \& }
2.3.1. 模块安装4 {1 m1 X  [- w& w8 J1 Z
7 D" B3 ?! x$ _; r' r
make modules_install  <-- 把随内核编译出来的模块安装到合适的目录中( /lib/modules/version/kernel )
( f. S: \% V; i4 u4 d/ a
6 E- r! w, M; y4 e; N& i4 B" @* M. ?3 Y
( t# F" k: a. G/ _& M. T/ _9 v2.3.2. 模块依赖性. _% x! l6 ]$ A+ u2 ?
5 {( b" n7 |2 K) h  C# t8 U
linux中自动生产模块依赖性的命令:# _- q. M" `! _
8 T% s+ e! e+ O  J6 T, A
depmod     <-- 产生内核依赖关系信息
" T7 b/ @* q; o9 ndepmod -A  <-- 只为新模块生成依赖信息(速度更快)
7 Z% m4 f9 d  Z4 B* x0 G& b1 q # w8 Y& R& z( Q" Z) `9 y

, ?% Y  r! d4 Q/ B: x2 W0 q2.3.3. 模块的载入7 _& ~/ {& a5 }& N* e
& o1 k8 {* W4 z1 n" K" M
内核模块实验时已经用过:% p$ ]" C! O: a* z& j
& W* y$ H6 Q7 @- L; d: E1 y# M* O9 H
insmod module.ko8 X" q; f' S0 Z4 U% O
0 k5 z5 h1 }3 N0 w4 v2 l1 i
<-- 推荐使用以下的命令, 自动加载依赖的模块
  k- o) i. n3 H3 u* e3 a2 Lmodprobe module [module parameters]& k1 S5 c7 D: u4 `3 T
4 W* S! z6 d! Q+ Z
% `! j: b/ k- m% g5 ~: C" J! \2 r, L# M/ G
2.3.4. 模块的卸载
9 {1 w1 o$ F( B0 X5 O2 x( y) V- v# U5 h
内核模块实验时已经用过:
# B5 ?+ F' g' N, ~% P% g' [
- W# p3 b5 V9 h4 X7 V5 b! e8 Lrmmod module.ko
5 K: o$ J/ {# v: ?8 A2 q; x1 ^. @
; l' g# f& A' w- Z2 M<-- 推荐使用以下的命令, 自动卸载依赖的模块
/ g5 f# I! R& b: Qmodprobe -r module- T) ?' w, Q" K$ K7 b  p

6 @+ x. O6 Z! Y/ c1 d( Z, u& g
8 }4 {3 |( I; |& F( E- M$ r% p: m2.3.5. 模块导出符号表% g$ W9 v: c& _  w
. H. }. `% i: s2 k
内核模块被载入后,就动态的加载到内核中,为了能让其他内核模块使用其功能,需要将其中函数导出。6 ]% H# E/ }" b. J- t0 _5 a" j; q
4 P# W0 Q4 u; [
内核模块中导出函数的方法:) B2 H" w% p3 r* p' ~$ c' x8 O
9 V6 C6 U7 b2 ^, j- m) j
EXPORT_SYMBOL(函数名)       <-- 接在要导出的函数后面即可
- r, |# y8 q# C: SEXPORT_SYMBOL_GPL(函数名)   <-- 和EXPORT_SYMBOL一样,区别在于只对标记为GPL协议的模块可见
, L/ v: o& V+ o4 U  R! R
: g6 F; O8 g; s2 O- @  l2 e/ h
1 H/ i- F* C/ j# b# L内核模块导出符号表 示例:" Q: C5 a& W4 p- J
" H% i& G: {- A5 m; W0 O
+ 首先编写一个导出函数的模块 module_A: test_module_A.c$ o% ~, `' U* u

4 d- k* F' S) ?  o, |: m  {5 \7 h4 v复制代码$ I( K: W1 \1 S1 l! L5 @7 o
#include<linux/init.h>: Q/ ~# }6 v  s: N7 `, u; A
#include<linux/module.h>( y$ A" Z/ F0 T3 s7 {  h
#include<linux/kernel.h>. ~  U! s7 y9 |- p$ a2 g
7 }# h% g( ~: j' ~& c% d
MODULE_LICENSE("Dual BSD/GPL");" Y8 |% c' z; D" \* C" x

8 k6 e& ]1 }/ w3 c$ ystatic int test_export_A_init(void)) Y& l8 t# c7 z" r! C
{
. ?# X$ o, p; S9 N3 C    /* 进入内核模块 */
& `1 Y& H6 u. \2 M, @4 {2 c. u    printk(KERN_ALERT "*************************\n");
5 K; K( N6 x- _    printk(KERN_ALERT "ENTRY test_export_A!\n");1 [# {  K& T. O) a/ c* p7 {  h
    printk(KERN_ALERT "*************************\n");
" ^  M/ h- J4 s$ `    printk(KERN_ALERT "\n\n\n\n\n");
& @7 [# H( q! V/ w; [5 R5 f0 m' @    return 0;
9 ?4 c4 G8 o- I1 n- I) j) \2 D}2 ~6 ]2 |/ N2 x+ l& W  Y
1 I4 b' p7 p9 F% J$ B% d" @
static void test_export_A_exit(void)& z4 J6 V% O; ]9 ^  R' c
{
9 U) V% M& R& D' [    /* 退出内核模块 */$ [; [) q: U: ^! j; t
    printk(KERN_ALERT "*************************\n");2 {% C) [. M% X. G2 o3 p
    printk(KERN_ALERT "EXIT test_export_A!\n");
: f! T. O- j+ d+ d; o* X) u# S    printk(KERN_ALERT "*************************\n");
) i) U4 R% l7 G8 {    printk(KERN_ALERT "\n\n\n\n\n");
2 Y8 v; i$ d; {3 E- E}2 _: v2 ]6 O; J) D# _; g

' c. L% v" Q4 Y" ?3 M/* 要导出的函数 */
" J5 |. G/ v+ H# i. rint export_add10(int param)
, \2 S6 h: _: Y, b! v  m{
, v) L  w! P/ p# R6 a0 D. E    printk(KERN_ALERT "param from other module is : %d\n", param);  r& |/ y. y/ P% z  W# f. s
    return param + 10;! L8 q; H& n; {5 F3 ]* T
}9 l1 c# F2 j& K: b3 Y
EXPORT_SYMBOL(export_add10);. Z$ v! j0 `, O7 h
: j) ^4 ]& k: ^2 B& b6 L
module_init(test_export_A_init);
( Q5 a2 g/ w( m; rmodule_exit(test_export_A_exit);
9 {# m) \) g: R# u  {' u复制代码( x& M, ^8 k  ~5 e) M" E2 Q
* B1 {# I5 D" {7 o" T! X

- t1 x) T4 P3 F- M! O7 Z* ptest_module_A.c 的 Makefile
& w. s$ P  f% l: G1 U
; ], x- h6 K0 k7 i, P8 H复制代码: h9 Y3 L' H4 x- Q% g/ B; t
# must complile on customize kernel( d& {2 J6 ]! I% b- [! t3 R3 d
obj-m += export_A.o
( @% {6 x0 q7 J. kexport_A-objs := test_export_A.o
' m# Q- D  p. O
6 A6 G5 }" o& b; Q#generate the path( n* f& @3 ~9 b9 }" E& V; \
CURRENT_PATH:=$(shell pwd)
  l4 ?6 O+ E, \# x. Q) `#the current kernel version number
( e6 x) U5 {8 c/ Y9 ?: ALINUX_KERNEL:=$(shell uname -r)8 O2 s; z  ~$ T
#the absolute path
. Q5 F' y; }0 ]) r2 XLINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)
9 G2 L: s4 J1 j. Z* x#complie object! Q+ J) E  ~+ x$ D1 F
all:' ?# N. J: L; t2 K
    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
- o% T# G) h- A3 [& I    rm -rf modules.order .*.cmd *.o *.mod.c .tmp_versions *.unsigned
! ?( r$ Q8 G' B$ S#clean
- K0 k! }$ D$ t: _7 rclean:  P, }5 k2 x) E6 E3 k# a
    rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned
/ ~# ~5 p" k- w0 {3 Z  ]复制代码- ^9 ]! f5 T- m+ F$ q; P
) Z' ~- q6 O- V3 X

: P6 d2 Y: q3 G5 Z+ 再编写一个内核模块 module_B,使用 module_A 导出的函数 : test_module_B.c& f; Q( T  ]8 u* ]6 O3 v& j1 C4 U2 K3 n( Q
+ P1 b2 ~. z* M7 m
复制代码
, E! {. q9 Z6 I/ [#include<linux/init.h>
/ F2 h* ~1 B0 P1 L; @' g#include<linux/module.h>! S* c; V, P$ f' v# ]
#include<linux/kernel.h>
. ?' }" W. L1 j$ O
% f0 t1 [/ U2 wMODULE_LICENSE("Dual BSD/GPL");$ _. b; |' j, D3 P, D$ Y* F6 h, r
! n! X- J0 r; g3 J3 _: x
extern int export_add10(int);   // 这个函数是 module_A 中实现的
/ r+ w) [! W8 j& @2 I  b; x1 \# l+ I
static int test_export_B_init(void). f1 L- ]* `, R
{% Q8 Y5 F2 b* |# y$ s7 n
    /* 进入内核模块 */
* |+ L# ~- R& P0 @+ H7 a  E3 @    printk(KERN_ALERT "*************************\n");
5 t, i3 D1 B' x( h    printk(KERN_ALERT "ENTRY test_export_B!\n");
& d' l8 m) s; }2 d4 K, p$ k7 z    printk(KERN_ALERT "*************************\n");3 z- [8 r2 Z( C
    printk(KERN_ALERT "\n\n\n\n\n");
+ ~3 x. P7 p$ \$ v+ C4 O
4 y6 ?2 X' t! E! d9 H& H    /* 调用 module_A 导出的函数 */. m) O) P! P+ N! }6 P% Q
    printk(KERN_ALERT "result from test_export_A: %d\n", export_add10(100));
  G: p, H8 s4 O$ t% Q5 |   
& X: n' M1 v" k- k" S    return 0;, \* r* Z3 U- ]
}0 [; O6 |, g6 n( A! ?

$ h/ D' Y0 P, n4 sstatic void test_export_B_exit(void)1 ]! R! S- O$ X0 S1 F
{
& k6 \3 b9 I: A5 K0 Y2 r% e. W    /* 退出内核模块 */5 J$ X- v$ i5 g2 v
    printk(KERN_ALERT "*************************\n");# I) X: {; Q( H" v
    printk(KERN_ALERT "EXIT test_export_B!\n");
) {& [. O/ l6 t$ D% E( P' _    printk(KERN_ALERT "*************************\n");4 f  K2 }) |2 o% N! R" \0 |
    printk(KERN_ALERT "\n\n\n\n\n");: q3 g. q( A! u
}+ v0 R7 e1 {: Q' f8 K
- s0 [  n5 X* v0 i
module_init(test_export_B_init);
& c8 G" F) T. C. }/ a! C/ }module_exit(test_export_B_exit);: l( l, K: u' ~% x" }' l, @
复制代码2 p1 z+ c# c: Q; i; N! T5 [
, b6 ^6 b) |8 j

7 ?  v' h- g5 _" X8 ytest_module_B.c 的 Makefile
, A9 j7 Y  C) U( N& J$ i6 B% v2 H- v6 A; s( r3 f# A
复制代码
9 E. k- ?7 H; f9 L# must complile on customize kernel$ a, I" d4 i+ x2 _+ d
obj-m += export_B.o* I7 v  H( f- J/ s4 H
export_B-objs := test_export_B.o
3 X+ S9 M. P; a# D) }/ z! d- M) j6 n0 n! }8 V" i/ ]
#generate the path" `& K; ?5 f2 q4 g+ a7 G
CURRENT_PATH:=$(shell pwd)
" M# u7 x9 a+ M. D/ G#the current kernel version number
$ Y' O" u; q2 ~6 w3 N  ^+ B- }LINUX_KERNEL:=$(shell uname -r)
% X* d; H7 F& p$ M#the absolute path
' j" |9 n: I: e! a! t4 _6 U8 l! E8 ?% ~LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)& M0 L2 V1 S: u6 F5 d& G5 y7 f
#complie object4 q  c4 C! J8 U8 h- k
all:" J3 |, I6 Q9 |2 r, |, L  x
    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules+ t1 P- `2 Y! g3 _: T# _$ ~) s: Q
    rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned
# X  S/ `0 X6 g; Z. E; D4 {#clean
' V) N' v1 K! |3 G: m0 U* [0 r6 o$ Lclean:$ U& t6 k# w9 }0 g6 f
    rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned
0 \( ]( H. q3 @6 }0 Q9 y3 F+ H. u- E, \复制代码
* s4 T8 |+ P9 Z! |
2 f1 G" E* I8 O2 v3 v! @# |0 o. ?5 X& \& n7 o' k" q
+ 测试方法2 l  S3 O2 e- x6 C

% @- S' T' H; |$ U. h: _1. 将 test_export_A.c 和对应的 Makefile 拷贝到 module_A 文件夹中7 H0 U5 `- `- N2 n4 C( d; b( Y* O
. W+ z9 {6 }( |' g; i8 F8 i
2. 将 test_export_B.c 和对应的 Makefile 拷贝到 module_B 文件夹中8 z. V0 ]. D3 E5 d- G  Q

/ }6 w5 n4 S$ k- @0 q" u3. 编译 module_A 中的 test_export_A.c5 z, {. I1 E! {+ Z6 w

# M9 R. G' w2 }, v) W4. 将编译 module_A 后生成的 Module.symvers 拷贝到 module_B 文件夹中3 t. a# ^6 \, w5 M7 ?: R6 t6 l% v
" F9 o2 P2 l; n- q9 T
5. 编译 module_B 中的 test_export_B.c( k; S+ s! {# R
& v  a& g( p+ z& R$ Q! Q
6. 先安装 模块A,再安装模块B
, |0 K+ N' C; S- f
% u8 r1 t9 r3 `+ n7. dmesg 查看log( t% Q! C( a6 e9 t5 @( f5 B5 ?
* s; [1 R9 L* B& b
8. 用 rmmod 卸载模块B 和 模块A (注意卸载顺序,先卸载B再卸载A)
9 h3 T& N) A% U. j7 Z5 j
: p+ _/ e$ _3 s# m3 |$ f& M复制代码
+ U: U, Q* ^! q+ i2 r% F[root@vbox chap17]# ll
& t+ k" s  k* K2 ]5 @' x3 r& Dtotal 8
4 S; P6 M* P3 e( U1 C: a  j; l; ndrwxrwxr-x 2 root root 4096 Dec  7 22:14 module_A
2 L/ \" i- m& n' r. u0 _  J( udrwxrwxr-x 2 root root 4096 Dec  7 22:14 module_B4 E* x- Z, d8 K3 w
[root@vbox chap17]# ll module_A: ~* d: C1 L" g( J4 Y# @. B$ i
total 8
$ N- K# |" X( ^% R: Q/ N1 b-rw-r--r-- 1 root root 517 Dec  7 21:58 Makefile
+ z8 \1 |) W* |0 }- |/ z-rw-r--r-- 1 root root 893 Dec  7 21:58 test_export_A.c
% l2 S/ B( f# B1 }- I* Q! i7 k4 p[root@vbox chap17]# ll module_B
4 k3 }! N+ m8 {9 Atotal 8  l6 v2 `! g# t9 J$ V
-rw-r--r-- 1 root root 532 Dec  7 21:58 Makefile
* W0 `3 N$ i  f+ G( ?$ A) `. p" |-rw-r--r-- 1 root root 830 Dec  7 21:58 test_export_B.c# X* ^% \0 w& \% p
* r% h7 }" @7 r0 b  i
[root@vbox chap17]# cd module_A// ?, r3 H, `; F7 a: N; |: z
[root@vbox module_A]# ll1 i5 Q3 M! |0 P3 i. S5 b/ I
total 8, y8 D* J5 M- g+ o7 A
-rw-r--r-- 1 root root 517 Dec  7 21:58 Makefile1 z! \0 M0 W5 w7 ]4 w" T5 a* m% _
-rw-r--r-- 1 root root 893 Dec  7 21:58 test_export_A.c* M$ }; w, b7 B3 R) ~
[root@vbox module_A]# make
  P8 O: j% ?' }- Wmake -C /usr/src/kernels/2.6.32-279.el6.x86_64 M=/root/chap17/module_A modules
7 `4 `% |/ b+ W6 [1 Qmake[1]: Entering directory `/usr/src/kernels/2.6.32-279.el6.x86_64'( ~! z% S3 S6 C, n
  CC [M]  /root/chap17/module_A/test_export_A.o3 Z" u& U8 t9 T
  LD [M]  /root/chap17/module_A/export_A.o" @3 R0 {, N8 W  @; O2 [
  Building modules, stage 2.
9 v* q  F& _! c7 S/ ?9 P' n) M  MODPOST 1 modules
+ m% P4 Y0 q5 N" n7 g  CC      /root/chap17/module_A/export_A.mod.o
, G+ q( r4 y9 _8 K  LD [M]  /root/chap17/module_A/export_A.ko.unsigned
( F0 q3 f7 o! I  J( N  NO SIGN [M] /root/chap17/module_A/export_A.ko" U6 s4 u1 Z& p' V# o
make[1]: Leaving directory `/usr/src/kernels/2.6.32-279.el6.x86_64'' @: A) U, Y  T4 X9 j
rm -rf modules.order .*.cmd *.o *.mod.c .tmp_versions *.unsigned
5 k/ d6 D$ L& c[root@vbox module_A]# ll* o& P9 @4 `2 Y( x$ ?
total 120% B) y8 o8 m  H: M% F
-rw-r--r-- 1 root root 110452 Dec  7 22:31 export_A.ko, u2 M) _/ Y7 |/ \
-rw-r--r-- 1 root root    517 Dec  7 21:58 Makefile
7 Y; j3 I$ H1 x9 V5 S  {+ b& [5 N-rw-r--r-- 1 root root     69 Dec  7 22:31 Module.symvers6 V5 Y# D# D# E7 h" H
-rw-r--r-- 1 root root    893 Dec  7 21:58 test_export_A.c
$ |, r( ?4 q+ T( t( T9 y
% [! x9 P8 i; C# B3 M: D[root@vbox module_A]# cd ../module_B
; G  y  S$ {# h; R% i[root@vbox module_B]# ll7 E9 U- H: l& V2 y5 L
total 8
7 ?6 }2 p2 }" B-rw-r--r-- 1 root root 532 Dec  7 21:58 Makefile, C3 @0 Z  S" n$ ?
-rw-r--r-- 1 root root 830 Dec  7 21:58 test_export_B.c
& ^, T/ {3 [9 s& i[root@vbox module_B]# cp ../module_A/Module.symvers .
) n3 ~  `0 q* ]- R# [- H[root@vbox module_B]# ll1 r/ ^8 ^! ~* k; Q
total 12
+ {# c2 j& X7 l/ Q- P-rw-r--r-- 1 root root 532 Dec  7 21:58 Makefile9 J4 S- O* Z6 E& h! K( f. H
-rw-r--r-- 1 root root  69 Dec  7 22:32 Module.symvers# _3 @6 ~! q1 T3 E
-rw-r--r-- 1 root root 830 Dec  7 21:58 test_export_B.c; r' z6 F$ w8 }5 b- z
[root@vbox module_B]# make
" g) e% u5 B9 |1 t: ^3 hmake -C /usr/src/kernels/2.6.32-279.el6.x86_64 M=/root/chap17/module_B modules. S. I3 q9 K- }5 a
make[1]: Entering directory `/usr/src/kernels/2.6.32-279.el6.x86_64'7 J+ E% c4 m+ y, Q1 I, T% p
  CC [M]  /root/chap17/module_B/test_export_B.o. u) o1 r* B. F0 m
  LD [M]  /root/chap17/module_B/export_B.o
7 L7 W% i+ {' O0 Q* b  Building modules, stage 2.
  w, U+ h5 x9 g* k5 ]% f  MODPOST 1 modules5 W  h9 }5 S" W8 g) U& F7 o: d$ L
  CC      /root/chap17/module_B/export_B.mod.o
1 J/ b, _$ p& v9 W1 }" @1 g0 f" V# @, D& e  LD [M]  /root/chap17/module_B/export_B.ko.unsigned/ B+ Z- f- |, x1 O
  NO SIGN [M] /root/chap17/module_B/export_B.ko# h, {) l  ^: g
make[1]: Leaving directory `/usr/src/kernels/2.6.32-279.el6.x86_64'
' I8 m8 K. P7 _  r. p# rrm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned' C* L% [+ v$ O8 V
[root@vbox module_B]# ll( K& R! N" f. x! e  E1 ?
total 116* `( G  U/ C% F% k/ C, {
-rw-r--r-- 1 root root 108596 Dec  7 22:32 export_B.ko* D0 N9 y+ W# Z9 u( X' C
-rw-r--r-- 1 root root    532 Dec  7 21:58 Makefile: O& @! I1 D; l: O; N2 r4 [) y
-rw-r--r-- 1 root root    830 Dec  7 21:58 test_export_B.c+ m- Y5 |" X- Z2 q. A+ ?
- U* p5 Z9 V6 Q+ h7 ^) P
[root@vbox module_B]# insmod ../module_A/export_A.ko / `2 y, N1 o4 Q1 a$ f- s
[root@vbox module_B]# insmod export_B.ko
% B4 E3 n6 s* Z( C  S; p[root@vbox module_B]# dmesg | tail -181 s4 H) `% M1 f4 d
*************************
9 O, `6 ^0 v7 s* EENTRY test_export_A!
( }" e/ ?' \* n" }8 J3 y*************************4 H1 r8 ~/ L9 `# }3 y% t: O
. S8 H2 t, T; f% [
; H& f, e5 Z4 ^) b
0 {! j7 `5 x. A8 }# [& @% C
) ^. c6 M, H9 S3 ~6 J

' ?' l/ R+ ?, V*************************
" T1 M( s; y! a' nENTRY test_export_B!
& N* B; q, c: z) }. n; i*************************
& Q8 Q! d, H3 |& J' r+ l
: e1 H5 t, v- ?; [1 j9 s
. b* n* \0 ~  u7 ]5 \+ J5 H+ Z) s% M( F2 S
0 p' g' N7 L, |4 Q
$ v2 P' U$ S: ~  A& f( @
param from other module is : 100
+ I1 G( r! j0 E4 X; |2 G4 u7 @result from test_export_A: 110
5 @+ B& p, `, ?
' N, S' U4 w6 i: g4 O" M5 H8 W! I[root@vbox module_B]# rmmod export_B. b9 ^3 @2 _5 r4 N- F9 ~7 H
[root@vbox module_B]# rmmod export_A
8 ~# u6 W, p* b复制代码# G3 [8 X& N+ {0 V9 w* B! q% s

  k9 F, ^3 I; @0 A7 J: J2 H2 o  H/ r7 V2 ?$ F
注:5 W( [) g/ N: k9 m$ n
1. 必须把编译模块A后生成的 Module.symvers 拷贝到module_B 中再编译模块B,否在模块B找不到模块A导出的函数
, E! m- I# F3 w' h  W, K/ l- ^  a* ^. q+ u% m& c2 R
2. 先安装模块A,再安装模块B。
* u8 t! B: Z+ B
+ g& f( v" K4 `: d; F3. 先卸载模块B,再卸载模块A。
* C1 `6 B. Y) W4 j& q" b3 X0 I
" p" e2 e8 |' E) N+ Z9 P4. 安装卸载如果不按照上面的顺序,会有错误提示,大家可以试试看。
9 W6 I3 A  |3 J& X: }7 c% ]. L3 g- h9 D, b) Z$ d( }& M, n
5. 我实验的系统是 CentOS6.3 x86_64! V3 ?9 V. H  D+ r3 h# |, N

; s, u2 M" n1 f5 h. j0 ?6 P ; [, I' A7 b4 x9 f. [0 a

- y4 a- X+ ^. W8 \4 r& l
* G6 g9 f8 j$ `$ q* I$ }9 M' x. z3 l& F
3. 内核对象
# x' c8 p9 x+ L2 z7 _0 Q2.6内核中增加了一个引人注目的新特性--统一设备模型(device model)。
1 Q6 y  a! E5 }2 o! v. ~4 ?3 V& L3 W0 I+ x6 A; x; f
统一设备模型的最初动机是为了实现智能的电源管理,linux 内核为了实现智能电源管理,需要建立表示系统中所有设备拓扑关系的树结构,+ c1 J/ e6 J$ u. i6 l7 q$ t

( Y, E9 k6 i; q$ }- Y& M/ S) B这样在关闭电源时,可以从树的节点开始关闭。6 ]; l# X3 J5 \2 k" h" Z% u
: f, d3 N/ x2 X0 q- w; k3 L

3 S4 U" n: [( F/ C. o& g
5 N% `/ _- `: P9 A实现了统一设备模型之后,还给内核带来了如下的好处:
4 O  U, w" x& H, f) {9 f* k. ^! @. ?2 [+ W" }, N* P5 i7 ]/ }
1. 代码重复最小化(统一处理的东西多了)! x4 o' b; M) y; _  U# ^3 h: i
) E& p$ b$ h2 d- f% P, B& h
2. 可以列举系统中所有设备,观察它们的状态,并查看它们连接的总线) `! g$ W! \+ V1 l

! ^& Q3 H1 Y$ J/ e! w3. 可以将系统中的全部设备以树的形式完整,有效的展示出来--包括所有总线和内部连接
, y% X+ T0 ^" O% g5 c9 Y" _6 u- t" D% R$ z  c6 J3 H
4. 可以将设备和其对应的驱动联系起来,反之亦然
' k, {% {2 @$ E, ?
0 w' J1 e  M8 _3 P  N5. 可以将设备按照类型加以归类,无需理解物理设备的拓扑结构
  I. g* C. z# ?7 E: H- C. G" P# c! g* T3 ^# T; r$ z. G
6. 可以沿设备树的叶子向其根的反向依次遍历,以保证能以正确的顺序关闭设备电源, u3 a# V+ x, l! Q9 D
- ?; [( P; r! a+ J/ p; y2 S1 o

9 D+ ?% K, X8 H" c; O7 L4 M  U9 @7 c4 p1 `& [! H9 ?; w- h
3.1 kobject 简介, S/ F8 q* m8 @. c# h
统一设备模型的核心部分就是 kobject,通过下面对kobject结构体的介绍,可以大致了解它是如何使得各个物理设备能够以树结构的形式组织起来的。% \6 }) z; u4 u4 u
7 V, @9 p$ e: m

8 U6 b. b4 Y1 m
6 D/ d2 d9 {  X3 V: `5 l, k8 e3.1.1. kobject
9 E/ N9 m( l% v( B1 _" _1 ^
$ Y, t$ F' P* G, H7 Z2 W9 c( hkobject的定义在 <linux/kobject.h> 中
% f0 p" m; q; n! L; ~
6 `4 n! z" E) s1 `; n9 w复制代码1 E6 x; Z2 X& v
struct kobject {) K8 u# g2 V: }6 U  ~- B
    const char        *name;                   /* kobject 名称 */
& n! Q: P1 h! G1 x3 ?% C8 Y, d0 _    struct list_head    entry;               /* kobject 链表 */; _! C: |7 l0 t
    struct kobject        *parent;             /* kobject 的父对象,说明kobject是有层次结构的 */
6 K' j8 ~/ x4 J5 G" h4 ^* g6 ]    struct kset        *kset;                   /* kobject 的集合,接下来有详细介绍 */
6 Y1 a# x- V! P" {5 o0 \  [% e: R    struct kobj_type    *ktype;              /* kobject 的类型,接下来有详细介绍 */
7 {' D+ @' t8 Y9 D  |) p  ?' S: m, w    struct sysfs_dirent    *sd;                 /* 在sysfs中,这个结构体表示kobject的一个inode结构体,sysfs之后也会介绍 */1 Q, q$ w6 }  y2 {' D* ]' e6 Y- b
    struct kref        kref;                    /* 提供 kobject 的引用计数 */
% m- q  \" j& T3 x, P0 t; O    /* 一些标志位  */9 G. y( v5 _1 T3 K& [5 u+ n+ n
    unsigned int state_initialized:1;      
5 ^) G7 ^2 v) i4 g    unsigned int state_in_sysfs:1;0 t  \6 a5 H$ c" L8 e
    unsigned int state_add_uevent_sent:1;
% s: o& a  K/ t9 A( \    unsigned int state_remove_uevent_sent:1;
3 r7 w; N6 d8 x7 t; @    unsigned int uevent_suppress:1;
% A8 v4 i! [5 n};( ?0 R" H& |  u) f9 c5 Q! x
复制代码
+ A; i6 W4 _6 U' j- a # B" A5 e; p& T6 @

- k+ U: a+ O/ p- O' ]0 Mkobject 本身不代表什么实际的内容,一般都是嵌在其他数据结构中来发挥作用。(感觉有点像内核数据结构链表的节点)+ ]# [+ ~8 o7 z& [
) X0 p+ S& c0 N: F
比如 <linux/cdev.h> 中的 struct cdev (表示字符设备的struct)
* ]8 w/ p, D  H
; K8 g# s/ Q$ m, s$ _: S& e复制代码
4 Y+ P" d6 N3 u$ B( ^struct cdev {
% W1 M) s; |9 S# s0 f% P) \: E    struct kobject kobj;    /* 嵌在 cdev 中的kobject */5 I3 ?3 L1 i4 S
    struct module *owner;   
$ i" C& |0 N; W  k" d    const struct file_operations *ops;
. ?* |& Z3 ?: x    struct list_head list;
* R' l/ Z5 S# I9 O& r    dev_t dev;
! N4 Y4 R+ \3 y; O, i    unsigned int count;( n! P- z2 i$ k
};
) }, I+ Y$ Z& Z0 d8 B9 x复制代码
$ i9 G& M  Y5 t# h' P
$ \  U" R& A; f+ H3 h1 f
$ m  ~6 E9 R7 ]cdev中嵌入了kobject之后,就可以通过 cdev->kboj.parent 建立cdev之间的层次关系,通过 cdev->kobj.entry 获取关联的所有cdev设备等。
7 \8 l2 g" g6 O7 c  w
4 J5 v' m8 W9 Y) c; P6 G" R总之,嵌入了kobject之后,cdev设备之间就有了树结构关系,cdev设备和其他设备之间也有可层次关系。
9 t5 I! L% n+ s; i2 k+ W, U" N" a
$ Y6 Y& P0 {/ Z( `; _7 v
8 o8 x; Z( D& E9 _$ U/ |6 n, n7 J$ I- \4 n6 N
3.1.2. ktype% U6 ^( c- V- _7 w
2 H- X& w9 W2 n1 P
ktype是为了描述一族的kobject所具有的普遍属性,也就是将这一族的kobject的属性统一定义一下,避免每个kobject分别定义。
! w+ e% q' ^/ V2 U9 T" e0 M5 L% @# i; R5 q( k' D6 H
(感觉有点像面向对象语言中的抽象类或者接口)4 [( N2 m: e( L" e

/ c' E& p( O0 s5 ]/ |! A
) C% W- e+ R# @1 D1 y8 e# K8 S1 {( b- f  q) T
ktype的定义很简单,参见<linux/kobject.h>
7 H: G& p1 t7 |3 r3 S: ^* x. w& s% e+ L
struct kobj_type {) W  l# R" P; \: s" |
    void (*release)(struct kobject *kobj);  /* kobject的引用计数降到0时触发的析构函数,负责释放和清理内存的工作 */- F, M( p1 W" x' A9 c  ^
    struct sysfs_ops *sysfs_ops;            /* sysfs操作相关的函数 */% I) d, \4 L0 G
    struct attribute **default_attrs;       /* kobject 相关的默认属性 */. W! `$ V# F. I  S. S) j$ K8 {  O: i
};
. k5 W7 c2 Y0 n7 P# F( n9 b
& R  z6 ]" b0 P$ N) ^: I. `5 t' t) O4 r4 Y: X% I4 J
3.1.3. kset/ s# T& I  A  c
# _! M3 N+ \( l! d9 I' W* g# s: o
kset是kobject对象的集合体,可以所有相关的kobject置于一个kset之中,比如所有“块设备”可以放在一个表示块设备的kset中。& F) w0 V5 L' i5 ~6 {

. h# l8 t) ?: O& N. T/ h- Q 7 Q4 F3 C! k& N2 [) J
- A* }0 x! ]1 L
kset的定义也不复杂,参见 <linux/kobject.h>
5 J% y; J$ A. T9 F; y+ H2 L0 H. M& ^2 x, u7 c
复制代码* G* h6 f5 b$ Z' l$ M, u8 [; f0 L0 n+ D! w
struct kset {
  B. e/ ]  a0 P5 S/ u! {% B, y    struct list_head list;    /* 表示kset中所有kobject的链表 */
5 s& |$ i  `$ ?! w    spinlock_t list_lock;     /* 用于保护 list 的自旋锁*/* ]: `2 J3 e6 z. o: u" K9 e. b! q
    struct kobject kobj;      /* kset中嵌入的一个kobject,使得kset也可以表现的像一样kobject一样*/
1 |' B% c8 T7 H5 `/ \    struct kset_uevent_ops *uevent_ops;  /* 处理kset中kobject的热插拔事件 提供了与用户空间热插拔进行通信的机制 */
& l8 l7 K, G8 e+ s' n7 E4 n};
  Y0 i9 A; j) H; _2 R  p3 U复制代码) w* V9 A, R; M: Y$ m

- ~* A* Z8 ^. ?$ F5 {. H4 Q  ]
, E- X) b% L/ k" q* C" z
9 v: A) T) ~. |
1 S) M1 i! i4 ?& S% _0 |
9 X" y& c" d4 ?$ {! ]: e
  p% u- O9 G3 U4 u: ^% \3.1.4. kobject,ktype和kset之间的关系
5 {/ s  b1 A# t# a; C' m! h; M" D  q! |& S. s. S* D1 S, p
这3个概念中,kobject是最基本的。kset和ktype是为了将kobject进行分类,以便将共通的处理集中处理,从而减少代码量,也增加维护性。
" W5 f% r: ^  x: \+ N6 f7 |; W9 p4 {2 Y) w4 U9 L; ?
这里kset和ktype都是为了将kobject进行分类,为什么会有2中分类呢?
8 p$ I2 m3 @  k0 t5 Y& O; M; g6 N: i+ ~5 T8 L5 t
从整个内核的代码来看,其实kset的数量是多于ktype的数量的,同一种ktype的kobject可以位于不同的kset中。
; t  k# G% u% _7 V# h6 y2 y$ c' z6 E
做个不是很恰当的比喻,如果把kobject比作一个人的话,kset相当于一个一个国家,ktype则相当于人种(比如黄种人,白种人等等)。) z5 Y( B3 H6 h& X: p4 m! [
% x. I. [+ S: e) v3 l
人种的类型只有少数几个,但是国家确有很多,人种的目的是描述一群人的共通属性,而国家的目地则是为了管理一群人。
+ C4 T9 u( x( \) Q
& A" n' V6 n7 H! @# k同样,ktype侧重于描述,kset侧重于管理。# w3 f! Y2 i3 y" c4 T  n2 M- X9 U
$ O6 r3 W" x  y# V! n
! x5 m( v% O* J' r+ O

* c) K$ i3 f! B( f/ N& |7 B3.1.5. kref
. q, D& N' B& j- T
% q: A2 B+ C2 E, }6 s* N! [kref记录kobject被引用的次数,当引用计数降到0的时候,则执行release函数释放相关资源。
. ]- {3 C  B* h. ~, V6 _0 a/ w4 d9 J
# s6 }1 x/ t) J) q/ o

5 s! D5 @  F5 R: H7 zkref的定义参见:<linux/kref.h>
$ ]% X& q# O- r9 p0 d
4 H9 K+ Y( U, F/ e: S0 N0 R复制代码  o$ C' c1 {! _4 Y7 H
struct kref {
" i0 C5 j# z; `$ N- c4 a    atomic_t refcount;  /* 只有一个表示引用计数的属性,atomic_t 类型表示对它的访问是原子操作 */
# h- ]8 T* t) E- _$ j# m5 p. I9 R; ~};0 A: M0 T$ ^. A0 s7 A- ]
4 m: `4 u5 ?- R. O* k
void kref_set(struct kref *kref, int num);  /* 设置引用计数的值 */
& b" o5 @, o$ s* k& B# r% S( T3 B' t2 wvoid kref_init(struct kref *kref);          /* 初始化引用计数 */9 h7 r1 |, j$ J
void kref_get(struct kref *kref);           /* 增加引用计数 +1 */
) U! b# d  X7 P' v/ M( ]int kref_put(struct kref *kref, void (*release) (struct kref *kref)); /* 减少引用计数 -1 当减少到0时,释放相应资源 */
' c5 l% N1 G: w. R6 O4 b复制代码0 H$ x# C4 ^2 ]7 m
上面这些函数的具体实现可以参考内核代码  lib/kref.c, ~  x/ w2 S" p4 ?; t

: Y9 z0 o, ?2 H0 L! ^
0 O6 J) C. e2 B! L$ X# \. u" q
+ u( \) W. u* V7 |  _+ P5 o3.2 kobject 操作
" K6 g- [3 g$ s6 Ekobject的相关都在 <linux/kobject.h> 中定义了,主要由以下一些:
) R* U3 N* \8 P9 w9 F: O6 z
5 N5 ]2 O5 F( \复制代码- ?4 Z" g+ M, i
extern void kobject_init(struct kobject *kobj, struct kobj_type *ktype);  /* 初始化一个kobject,设置它是哪种ktype */
- ?2 F5 D% N* R3 K, g5 T5 ~extern int __must_check kobject_add(struct kobject *kobj,
; z; h! ]6 {8 e, ~: T                    struct kobject *parent,
( W) v+ m  L* g* d                    const char *fmt, ...);   /* 设置此kobject的parent,将此kobject加入到现有对象层次结构中 */- B! R. p! L5 {0 Y1 m& l& s
extern int __must_check kobject_init_and_add(struct kobject *kobj,: d' y8 T6 G5 Q( [$ N. w
                         struct kobj_type *ktype,
2 C& y" r. s- R( C! {                         struct kobject *parent,
. ^) R3 |1 O  p                         const char *fmt, ...); /* 初始化kobject,完成kobject_add 函数的功能*/* c" h1 C3 C) d  r  r0 ^- C
# k1 C" P1 t* {( z/ a
extern void kobject_del(struct kobject *kobj); /* 将此kobject从现有对象层次结构中取消 */
+ u/ U% p: E2 ]  u$ U
+ j& L& J9 z+ qextern struct kobject * __must_check kobject_create(void); /* 创建一个kobject,比kobject_init更常用 */  M: l* e, r/ q, ^' M% ~0 t
extern struct kobject * __must_check kobject_create_and_add(const char *name,
# {# V6 B; e- z! ?                        struct kobject *parent); /* 创建一个kobject,并将其加入到现有对象层次结构中 */, C& H( S* I# Y2 z# ^/ w: A* t
: b9 |, f$ a6 b' z5 U; U8 U6 w
extern int __must_check kobject_rename(struct kobject *, const char *new_name);  /* 改变kobject的名称 */
( u6 Q# K0 Y1 Q% K% n( xextern int __must_check kobject_move(struct kobject *, struct kobject *); /* 给kobject设置新的parent */
. _- Q  w& R: B  q8 C# L2 n' t: b! d* ^4 _* T) v7 ^
extern struct kobject *kobject_get(struct kobject *kobj); /* 增加kobject的引用计数 +1 */
9 C' \0 \6 m5 J6 u4 x- k( hextern void kobject_put(struct kobject *kobj);            /* 减少kobject的引用计数 -1 */
2 Y0 z1 `9 z( J9 J: L/ T, R) c
& o" C/ s' U5 F- m% x9 j& Yextern char *kobject_get_path(struct kobject *kobj, gfp_t flag);  /* 生成并返回与给定的一个kobj和kset相关联的路径 */+ v5 h' o- Z3 L1 T+ b
复制代码2 J  a) G- L  m, K
上面这些函数的具体实现可以参考内核代码  lib/kobject.c3 p+ h+ j* E3 a

* s" a, g: {, ]1 \# q8 ~$ g ! I7 X4 U% C( |. @
6 k1 m9 ~; Z6 Y/ h: x
4. sysfs6 K+ z/ z" O! G) E% b1 N
sysfs是一个处于内存中的虚拟文件系统,它提供了kobject对象层次结构的视图。
) x$ I- {+ B1 b2 g1 u, \7 a) E; V% A: }3 _- p
可以用下面这个命令来查看 /sys 的结构
) L# J+ ]+ o: @+ x6 b& U2 T- L/ f: W& |: O9 u+ b2 I. n' Q1 W
tree /sys       # 显示所有目录和文件8 |/ t9 W1 @5 N- d! N+ D7 D6 a
或者
9 @4 v- S! ]' B5 D& ]( F- P& y* ^9 Xtree -L 1 /sys  # 只显示一层目录
- e# }3 J  L2 |, w 6 Z' s: J, L" a( X! L* I) f

. Z8 Q3 O1 X) w8 g$ J4.1 sysfs中的kobject7 k$ \9 F$ O+ _
既然sysfs是kobject的视图,那么内核中肯定提供了在sysfs中操作kobject的API。
2 X$ _/ \! i( z2 F* n' Q# V3 C
- T# U: l. }( f  Z+ Pkobject结构体中与sysfs关联的字段就是 「struct sysfs_dirent    *sd; 」这是一个目录项结构,它表示kobject在sysfs中的位置。2 J: ~6 F! u1 {: e" C3 f, \
1 u9 F! u- q2 v& E" [' u  c
关于目录项,可以参考:Linux内核设计与实现之虚拟文件系统7 M- a* ^5 a5 `5 G7 @% C

! r% M2 c/ b  c' h0 Z/ m, L
& z$ u: D8 ~2 q1 o# r$ E8 O4 ~8 d5 _2 ~3 o& ]3 p% g
4.1.1. sysfs中添加和删除kobject非常简单,就是上面介绍的 kobject操作中提到的
* b+ S  `3 f7 x0 `5 P" W) G
* G1 r; O7 y" k9 V) F复制代码
* X( \5 H0 K$ {7 p' g, vextern int __must_check kobject_add(struct kobject *kobj,
% {  V. Y, v9 Z* h7 Y4 f% w; \                    struct kobject *parent,
: d9 G* R2 G! G3 J2 B$ D' E$ a                    const char *fmt, ...);   /* 设置此kobject的parent,将此kobject加入到现有对象层次结构中 */
: w, O6 P9 I/ q1 [) u  a2 ]extern int __must_check kobject_init_and_add(struct kobject *kobj,
/ Z4 e. P; W2 m1 M                         struct kobj_type *ktype,
+ e$ s% K0 B& C( \                         struct kobject *parent,$ S7 `$ v* J( D5 O  C  T
                         const char *fmt, ...); /* 初始化kobject,完成kobject_add 函数的功能*/
; T% D) Q! ]2 ^* @8 g- X. V( N7 ]) l% E* z1 A6 T
extern void kobject_del(struct kobject *kobj); /* 将此kobject从现有对象层次结构中取消 */5 N3 }* b# n8 |! q
... ...等等
) t* C& V1 P; m3 Z0 O& g复制代码
& n$ S3 h% Y0 _" J: f7 x# Z 7 a8 y6 ?. j& Z( `4 g2 a( t5 \; E  p
6 T- B1 z0 y2 j" W1 ^" ?
添加了kobject之后,只会增加文件夹,不会增加文件。
  S- {( |! P6 S0 u. x  S. i6 E2 w3 a8 X5 ]
因为kobject在sysfs中就是映射成一个文件夹。  D6 F# G5 p0 D

' N% J/ g: ]% a' h8 t4 m
- M( E+ W( O( |' |. `
; s2 z" L" N  y- B添加删除kobject的示例代码如下:+ F" q1 Y. e+ {/ P) l# `7 o  g

3 X# W9 l" R+ X6 {+ Q复制代码
6 T  }9 _" q) H5 O0 o' X. `/******************************************************************************
. ]6 |3 w4 ^+ [& B, H* \- T* l: {0 M1 V * @File    : test_kobject.c1 G6 T9 S2 l2 {  I$ r$ M, N7 e- ?2 X
* @author  : wangyubin1 j) o$ \' A3 F4 ]$ R0 q
* @date    : Tue Dec 24 09:49:53 2013& V2 p. l. M1 S! H
* , g4 r; k/ l0 _+ j; F+ v' ^" M7 f
* @brief   : 测试 kobject的创建和删除
  ?" B3 F4 `- M' O1 m( }: x6 y. V * history  : init
9 ?& h/ k. b. n: C: d' N ******************************************************************************/
# R/ D$ y9 C( @$ h# N$ `' [0 C; [: z. T8 x7 d7 O9 h
#include<linux/init.h>) _& S! t% N/ T
#include<linux/module.h>
: t2 J0 b6 n+ f7 g. \$ y' i+ S#include<linux/kernel.h>, V; E/ @6 y. G3 V
#include<linux/kobject.h>% e. X8 ^# B2 g5 q$ @4 ?
6 Y4 H. e- I: W- b& R8 a
MODULE_LICENSE("Dual BSD/GPL");5 a  W+ r5 }, I: O; E. s, r
$ U, \1 x2 N7 ^  W% e
struct kobject* kobj = NULL;
9 [2 S( A9 [8 @$ x
; h% Y! p+ v, T- e6 a$ jstatic int test_kobject_init(void)$ w+ h2 H0 |( e5 ^
{
7 A' ?& m. w1 W5 Y. w    /* 初始化kobject,并加入到sysfs中 */   
- V# S  J. l$ v0 T& F9 v5 U    kobj = kobject_create_and_add("test_kobject", NULL);
! x% b' H  q0 O0 v# F3 F4 q, E2 R    / `+ s) _6 i& P
    /* 进入内核模块 */; a1 F( f. G) D$ m8 ~
    printk(KERN_ALERT "*************************\n");9 X. }; _* [; o5 \
    printk(KERN_ALERT "test_kobject is inited!\n");  g& x$ w' y- D( m' H2 o
    printk(KERN_ALERT "*************************\n");
0 L& l" f6 v( n1 }( a  B! D" F( x4 A    ( R; ?: a  `' \4 f. a
    return 0;
) G& b9 b: ]! [/ O}/ V7 r9 I* ?1 t# a7 F

3 L% G/ @' k: ?- Kstatic void test_kobject_exit(void)9 E. L7 S9 e% c7 I2 ]2 t
{
& y, O( ^& B) `3 r8 f    /* 如果 kobj 不为空,则将其从sysfs中删除 */
& g/ x5 [* \/ Q1 ]/ F2 d" }    if (kobj != NULL)( g. }! T5 ^$ o4 A
        kobject_del(kobj);
. v3 U. F! ?. t7 d; e' z8 p    /* 退出内核模块 */- ^; x6 x! J- ~( i0 G( x
    printk(KERN_ALERT "*************************\n");5 c& s' k/ w8 S6 [( ]4 z. g- ?- A
    printk(KERN_ALERT "test_kobject is exited!\n");/ L' K- l/ n1 O7 G/ D  N/ p* [
    printk(KERN_ALERT "*************************\n");; ?% B! m+ W5 m4 ]4 B6 E7 u
    printk(KERN_ALERT "\n\n\n\n\n");
+ `5 ^" c4 u. R}" y4 V2 ], [* ?# l' W. ]9 q
! o* [/ a3 g9 N8 }
module_init(test_kobject_init);3 R1 U. h& X; K# Z( N8 X# ~& m
module_exit(test_kobject_exit);
  m, C0 G! A- `5 j: U复制代码" d7 `' {9 D* E, G3 \5 v# k3 z

9 I' |* O. {8 D. p
, Y& P2 }% ^/ u9 p1 l+ t对应的Makefile
0 c+ c; E, I- I( u  B1 `3 Z6 S$ y0 h; M* m; U4 V- L
复制代码
5 f  `7 w7 V+ q% }5 C; q2 S# must complile on customize kernel
# M9 T' E* \* P  Y& S$ n% @obj-m += mykobject.o
" d3 W! H: F& v& S3 O$ l( g6 ~mykobject-objs := test_kobject.o
. g  M/ E  U$ i- J4 I9 g  ]2 v+ A/ L7 `% s$ W
#generate the path: w3 ]9 i! E5 T0 f
CURRENT_PATH:=$(shell pwd)3 i1 l% g' V4 O  ], J8 a
#the current kernel version number
, R( E( J$ ?: q2 U# K8 i6 B+ lLINUX_KERNEL:=$(shell uname -r)
2 C4 V7 w& b  f# n' T#the absolute path
& _, m# D6 }' x9 x' R; E5 k* \7 jLINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)' h5 @* n7 A/ E! S: v* o/ o
#complie object* ^8 V4 U5 J+ o: }0 y7 m' O( X1 H
all:
; {. ^5 L% ~3 f# A    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
6 W- n2 ?/ @2 D9 E5 D/ S    rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned" O( X) S* r. J. C  T' ^, @! C
#clean, K: q( m; ]3 d4 M) a" D
clean:
# N0 [3 R2 q" S8 U    rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned
7 \" l" K( l; c2 k5 B. {0 L8 j0 A+ H复制代码
, H* W2 ]3 `* f* D- { 5 M9 [0 R$ R+ X# i
) f0 R8 g& }. }9 D
测试方法:(我使用的测试系统是:Centos6.5 x86)
( R: S% ^7 {% v- m% v  w
# l/ b2 s' E. N5 [复制代码
& g0 i+ T. x3 S[root@localhost test_kobject]# ll                             <-- 开始时只有2个文件,一个测试代码,一个Makefile
: ~  M9 n) C4 m4 `total 85 x. x0 Y1 }: t3 _, M
-rw-r--r-- 1 root root 533 Dec 24 09:44 Makefile- Z7 I8 I3 o" P( x; b
-rw-r--r-- 1 root root 908 Dec 24 09:44 test_kobject.c
/ T5 V# E! l6 \7 X' W[root@localhost test_kobject]# make                           <-- 编译用于测试的内核模块  z/ }; s0 m9 N$ n7 m" p7 S' g9 I
make -C /usr/src/kernels/2.6.32-431.el6.i686 M=/home/wyb/chap17/test_kobject modules2 R( m3 m3 ?* b- E
make[1]: Entering directory `/usr/src/kernels/2.6.32-431.el6.i686'# p6 M5 n3 _# o$ X3 a* H
  CC [M]  /home/wyb/chap17/test_kobject/test_kobject.o
. y& k, B% [5 B* |/ z0 I, `# w  LD [M]  /home/wyb/chap17/test_kobject/mykobject.o, ~4 D( V0 ]1 a5 v8 w
  Building modules, stage 2.  r# ~% H" T; [' W& }
  MODPOST 1 modules
9 B' c; l0 C  U( b' |" u  CC      /home/wyb/chap17/test_kobject/mykobject.mod.o9 U3 ^0 {: X( Z( j) M% V$ A
  LD [M]  /home/wyb/chap17/test_kobject/mykobject.ko.unsigned
5 @/ v7 W% {7 S2 ]' m! {5 C5 q; f  NO SIGN [M] /home/wyb/chap17/test_kobject/mykobject.ko$ s' y. n. n* z9 A
make[1]: Leaving directory `/usr/src/kernels/2.6.32-431.el6.i686'6 v* [, g" R. d
rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned6 s# y; H+ e9 z$ H! b
[root@localhost test_kobject]# ll                             <-- 编译后多出来的一个内核模块 ***.ko1 v4 {+ @6 v4 V) p& {
total 100" M! V, E- G# ?; I
-rw-r--r-- 1 root root   533 Dec 24 09:44 Makefile1 ~' n5 |" A" g( V
-rw-r--r-- 1 root root 91902 Dec 24 09:54 mykobject.ko
/ ^$ v  r5 F7 [/ d6 s* k-rw-r--r-- 1 root root   908 Dec 24 09:44 test_kobject.c& v9 C& F9 I# B& L/ X6 |. n& g, C+ f! p
[root@localhost test_kobject]# ll /sys/                        <-- 安装内核模块 mykobject.ko 之前的 sysfs结构
: B' F( |6 n/ A( N! {' Ytotal 0
7 [6 @6 ]7 _: S* |0 v% a  ddrwxr-xr-x  2 root root 0 Dec 24 09:28 block: g* C3 Q1 f& T
drwxr-xr-x 17 root root 0 Dec 24 09:28 bus
, o: X& A) i. Y2 M0 Q: Kdrwxr-xr-x 40 root root 0 Dec 24 09:28 class5 @* M5 ^0 `) e- u
drwxr-xr-x  4 root root 0 Dec 24 09:28 dev" h* \" K6 y9 B1 x; t
drwxr-xr-x 12 root root 0 Dec 24 09:28 devices# W0 c6 K- f& g+ ?8 S% g) ^! C5 ~
drwxr-xr-x  4 root root 0 Dec 24 09:28 firmware
$ u( l+ V7 u' |7 H$ r  vdrwxr-xr-x  3 root root 0 Dec 24 09:28 fs
* e: k& B# V  {drwxr-xr-x  2 root root 0 Dec 24 09:44 hypervisor: Z4 }" c" m" F) b; y$ @
drwxr-xr-x  5 root root 0 Dec 24 09:28 kernel
+ [- f: ^2 o' L: U% jdrwxr-xr-x 84 root root 0 Dec 24 09:46 module( I6 g; A% \0 n/ k9 D) I: ?
drwxr-xr-x  2 root root 0 Dec 24 09:44 power
$ p2 f- H7 }. K( E2 u1 A+ G0 y. ?" R[root@localhost test_kobject]# insmod mykobject.ko              <-- 安装内核模块
. Y9 z' y. ]4 S' @/ a" R) G. _[root@localhost test_kobject]# ll /sys/                         <-- 安装后,sysfs中多了一个文件夹 test_kobject
+ O. b7 R# u9 n, O! @& X( Htotal 0' C% V; `. w0 ?) \# }- G
drwxr-xr-x  2 root root 0 Dec 24 09:28 block0 N8 @  b+ @' O
drwxr-xr-x 17 root root 0 Dec 24 09:28 bus
7 t  H# {% O5 U  Y0 l$ I/ Wdrwxr-xr-x 40 root root 0 Dec 24 09:28 class
* G5 b$ d" V% d8 g2 ^, \drwxr-xr-x  4 root root 0 Dec 24 09:28 dev
% e7 a; F3 f( X" Ddrwxr-xr-x 12 root root 0 Dec 24 09:28 devices' h4 ]$ W  q  Y
drwxr-xr-x  4 root root 0 Dec 24 09:28 firmware( x8 @  c* C; P+ _' m- c; V3 K
drwxr-xr-x  3 root root 0 Dec 24 09:28 fs
/ T' c5 W) S9 r5 Q* idrwxr-xr-x  2 root root 0 Dec 24 09:44 hypervisor
4 s! M' X  X, ^' ^  |drwxr-xr-x  5 root root 0 Dec 24 09:28 kernel
9 g. V. q3 H; A5 Qdrwxr-xr-x 85 root root 0 Dec 24 09:54 module
3 d& @6 R( b1 |$ kdrwxr-xr-x  2 root root 0 Dec 24 09:44 power
; X. S% g. a# m/ S3 adrwxr-xr-x  2 root root 0 Dec 24 09:55 test_kobject. W3 M" }1 T. [( d2 z$ [2 O
[root@localhost test_kobject]# ll /sys/test_kobject/             <-- 追加kobject只能增加文件夹,文件夹中是没有文件的
( _& k9 e- X1 O8 h/ [( |6 {8 e+ M9 B* Stotal 0" Q4 g  ~) v0 `/ K
[root@localhost test_kobject]# rmmod mykobject.ko                <-- 卸载内核模块1 T$ t/ x# f  U' V( v$ V" G# J
[root@localhost test_kobject]# ll /sys/                          <-- 卸载后,sysfs 中的文件夹 test_kobject 也消失了
" s7 _) e# S' c4 s* e) R6 Ttotal 0  n2 y, _' q! F4 a
drwxr-xr-x  2 root root 0 Dec 24 09:28 block
  a1 I2 p; m5 l# l$ T5 j: d$ v+ sdrwxr-xr-x 17 root root 0 Dec 24 09:28 bus. E8 h( L2 S9 V
drwxr-xr-x 40 root root 0 Dec 24 09:28 class( b# w: [/ @7 m: d" U' `/ G
drwxr-xr-x  4 root root 0 Dec 24 09:28 dev
" R* N9 }) }# I) I" t  T# Zdrwxr-xr-x 12 root root 0 Dec 24 09:28 devices" C# Z( L. {# U; ^2 T" k7 |! @; d
drwxr-xr-x  4 root root 0 Dec 24 09:28 firmware
" S8 M- B- P3 h: @drwxr-xr-x  3 root root 0 Dec 24 09:28 fs
' f  I* e  {8 m! ?& e/ A) c9 |% F0 ?drwxr-xr-x  2 root root 0 Dec 24 09:44 hypervisor
8 ^# R. Z/ {- T; Bdrwxr-xr-x  5 root root 0 Dec 24 09:28 kernel
; c+ P7 r/ B! P8 J7 i0 D8 t$ _drwxr-xr-x 84 root root 0 Dec 24 09:55 module3 Z& K! _4 ?  p0 Q! H. k. A5 W& ]
drwxr-xr-x  2 root root 0 Dec 24 09:44 power2 a5 V  v" k) a' s! X* }
复制代码
* R6 Z! K! l* k" h
+ {+ ]! m$ H8 O; H8 H8 [- p" B' U. P1 u- H+ z" E3 ^
4.1.2. sysfs中添加文件, p6 |, C+ \2 O
) ^$ }% \; s$ s  m- Q* x
kobject是映射成sysfs中的目录,那sysfs中的文件是什么呢?( L& V) Z* E, f; z1 M7 K

9 J4 s1 Z) U' t, J( x2 O- X; n2 q( s其实sysfs中的文件就是kobject的属性,属性的来源有2个:
2 F" x" L/ g8 x- N5 f  t- g+ X& l( s& |
+ 默认属性 :: kobject所关联的ktype中的 default_attrs 字段
7 \, ?/ q4 U/ f
' k8 M$ d' p! X( I2 O* a( k+ x" L默认属性 default_attrs 的类型是结构体 struct attribute, 定义在 <linux/sysfs.h>) g  S2 P$ |4 m) o; M" v3 a
& D0 s' {, b6 _7 _5 M
struct attribute {/ C: P4 ]* c: ]
    const char        *name;   /* sysfs文件树中的文件名 */  V% R( h0 p- I7 @5 H
    struct module        *owner; /* x86体系结构中已经不再继续使用了,可能在其他体系结构中还会使用 */
* b/ |& ?+ d, S) H0 u' G    mode_t            mode;   /* sysfs中该文件的权限 */, D( \, e. q1 f/ P5 g6 q
};2 L3 k. n; h0 N$ J8 k
! J2 Q$ w0 W1 ~

' a% [* D% w( L# T! [: ?ktype中的 default_attrs 字段(即默认属性)描述了sysfs中的文件,还有一个字段 sysfs_ops 则描述了如何使用默认属性。; Z. ^" i2 t/ N0 ?

% @. _, b3 s$ estruct sysfs_ops 的定义也在 <linux/sysfs.h>
3 v& A6 O- t/ E# E6 M8 T1 }7 {8 i7 X1 V9 k+ d
复制代码6 j. q6 L$ Q0 G5 L$ ^: g
struct sysfs_ops {- ?; i0 k+ F. P4 B9 P# i4 Z) C
    /* 在读sysfs文件时该方法被调用 */
0 c' Q% e& `: O+ b1 c* \6 M    ssize_t    (*show)(struct kobject *kobj, struct attribute *attr,char *buffer);
! H/ p) _* `, `- u% P9 B    /* 在写sysfs文件时该方法被调用 */
7 R/ ~+ V0 e5 A* p6 t/ w    ssize_t    (*store)(struct kobject *kobj,struct attribute *attr,const char *buffer, size_t size);% k! P+ v+ Z" M% A9 X# p& M9 n
};
7 v) U9 b/ L7 g  q/ ^8 A复制代码, V7 |% y$ Y. \3 z4 J
2 z& g' r4 b' u9 e. N
8 e. L% n. H4 ~- y; H( V  [9 R9 S
show 方法在读取sysfs中文件时调用,它会拷贝attr提供的属性到buffer指定的缓冲区8 Z& `' J; q# H* r& t+ V% ?: _

2 u" v9 P2 ?2 Y6 k1 Z# [) Sstore  方法在写sysfs中文件时调用,它会从buffer中读取size字节的数据到attr提供的属性中: G* _+ B! }; l* F3 K; ~
! I1 _; o# A5 j' e/ T+ {
( b  M2 h3 `* h" t

" V9 W" p4 ]- y1 N& f5 v2 {* N+ J增加默认属性的示例代码: q8 w+ F& L1 D% s9 S

3 N1 x, Y( F5 p( x复制代码2 V$ i! c. \$ K
/******************************************************************************/ z4 T& x$ V# Q! ~, X( R7 j) \! c
* @file    : test_kobject_default_attr.c8 e1 Y/ P6 M+ {' A8 i; K& k: J* I' V
* @author  : wangyubin
# u! b3 o9 Y* u+ K * @date    : Tue Dec 24 10:28:09 2013
& ?# Y( X; ]/ X  l, z7 W$ |9 z * & d3 Y" O1 Z5 [6 K/ e6 [# b4 q/ x
* @brief   : 测试 kobject 的默认属性的创建和删除- ]0 D/ ~# m. W  Q0 W3 h  U
* history  : init8 ?; B* y. z/ o  z6 y
******************************************************************************/
+ A' I! H. |. @, E' D- [. C/ E# V; V! v/ g1 H  n3 [9 g# M
#include<linux/init.h>4 ]! F( P! T' M3 h: l
#include<linux/module.h>
% G. f5 |, V/ G  }#include<linux/kernel.h>
! o" Q  L' Y7 f: \; [#include<linux/kobject.h>
0 h% d& F4 j$ S+ ~4 @2 {+ c8 ]& a#include<linux/sysfs.h>
! _5 E+ w& ^8 \" |8 w6 o: N. |# ^3 L6 U0 x1 D" b% K
MODULE_LICENSE("Dual BSD/GPL");
9 q; n, `! A. u5 g9 B( X" X, r! M% E0 J; f8 _4 F! E
static void myobj_release(struct kobject*);4 E& x0 i/ h( C8 K
static ssize_t my_show(struct kobject *, struct attribute *, char *);
6 A# s. W5 t' ]( v1 @static ssize_t my_store(struct kobject *, struct attribute *, const char *, size_t);
/ ~" `* c' a; o" ]7 C& J
6 F9 U; ~8 X) h# w% r" p4 I/ {$ j/* 自定义的结构体,2个属性,并且嵌入了kobject */
" Y7 q0 i, ^  J, R/ `struct my_kobj 9 ~2 z; G; }  i
{
; {' k3 k4 a8 T2 y4 g) T) M- n    int ival;+ g; {% @* p; c  ]+ |$ B. f
    char* cname;
+ w4 k/ L+ |! S+ j& U7 d* z    struct kobject kobj;2 e! A) y0 \1 p9 \6 u
};
& k0 W" n! y( Z/ v$ e; A9 E) ^3 n+ }0 w# H9 D  W. c
static struct my_kobj *myobj = NULL;$ {! I" [7 H4 h: G

7 Z1 V  |: f" Y& I5 u/ [+ C3 v5 n/* my_kobj 的属性 ival 所对应的sysfs中的文件,文件名 val */# ?. V# L% w/ _0 j- F7 B
static struct attribute val_attr = {+ N: x$ E+ b8 j6 u8 _  Y* R" |
    .name = "val",
1 x: V% b: O: c  a3 {    .owner = NULL,
- }# H' E7 G# Y' U, h* i3 p, k' ~    .mode = 0666,
  R! @" A5 [4 L! U};8 G" j. Z; I$ \2 H1 i: j7 @) N( k
) {: ^! L7 w# M* Q9 P$ z& V8 W
/* my_kobj 的属性 cname 所对应的sysfs中的文件,文件名 name */
8 i  P  c" _: Vstatic struct attribute name_attr = {; f* y$ ]3 c/ n' \2 b% G
    .name = "name",1 L6 B! `" B  N- h& L6 B% r9 I
    .owner = NULL,, |* B/ X0 r# x' l5 R3 \
    .mode = 0666,  `$ u# w* Y% L! O) y6 j* A
};9 R) h  O9 {. J1 _% Y* @+ Q( Z

, _; v$ u( S/ o+ f. a& L* r' e% tstatic int test_kobject_default_attr_init(void)
1 C9 h9 R1 M$ O{
. E, H8 O2 h  X% t5 O! ]. F    struct attribute *myattrs[] = {NULL, NULL, NULL};
8 S% v# h" k8 I8 W7 K- I! Y    struct sysfs_ops *myops = NULL;6 O+ {. ^6 _9 q6 L2 u
    struct kobj_type *mytype = NULL;1 d, w1 X  n3 c0 r' o! w/ x7 u

) G5 `$ @# W  z8 l8 m0 A* j5 A    /* 初始化 myobj */' I& A+ d1 d$ |' S0 H+ O3 z
    myobj = kmalloc(sizeof(struct my_kobj), GFP_KERNEL);
/ X1 i# g! k7 H# X4 p    if (myobj == NULL)0 Q" F& l, _2 r% z8 k
        return -ENOMEM;' _5 o2 ~! R8 ?5 K. q. s8 ]0 }
" n. r6 L, b* Y% z: V& a4 x2 |
    /* 配置文件 val 的默认值 */
- h& y) j' w. R    myobj->ival = 100;
5 `1 Z3 e! i" L6 d" d    myobj->cname = "test";
8 ~' h& w! t' u1 M3 k; ?. q. L9 S1 T$ [% B2 c
    /* 初始化 ktype */
. P! V0 p* I  l4 _+ x  H  D    mytype = kmalloc(sizeof(struct kobj_type), GFP_KERNEL);
. S6 u3 x9 O8 k3 J# Q. ^    if (mytype == NULL)  N! w* ]' w& M4 `+ G7 a
        return -ENOMEM;
* V& ]& ?& A0 y9 ~1 Y5 t; z5 _3 a' ^8 A
    /* 增加2个默认属性文件 */5 p6 ~( Z) a- m+ j4 c+ R: x
    myattrs[0] = &val_attr;
) r5 u4 y) p. ]: a    myattrs[1] = &name_attr;  L$ ^/ \! _  ~

. l" }! B4 J+ r9 v) A    /* 初始化ktype的默认属性和析构函数 */
; X4 T: H5 _! L* ~3 l! S    mytype->release = myobj_release;8 P0 }' v5 _2 J& q" i6 B* o' L6 t
    mytype->default_attrs = myattrs;6 ~7 `9 c7 ?* u: S% S: c
* R' A1 O2 P! g5 j
    /* 初始化ktype中的 sysfs */
6 C+ Y7 @' x% O) l" U- f% ?% b    myops = kmalloc(sizeof(struct sysfs_ops), GFP_KERNEL);9 c2 b$ {6 ], h0 M: S, Y# C
    if (myops == NULL)
. `. h( u$ k5 E- B) ~7 ]        return -ENOMEM;! ?/ h- ?4 H1 r- ?

4 @9 x" Y- x/ z# M    myops->show = my_show;
' D$ X, p0 D. t3 K  V) ~5 I    myops->store = my_store;( e9 U( \4 b# H1 ^* D& J) |/ t
    mytype->sysfs_ops = myops;
: K9 b/ e) T: k5 \/ E6 ?" b# I! n
7 Y$ h1 ]2 P! d& @: G" y) I$ I7 @4 X! g    /* 初始化kobject,并加入到sysfs中 */
- W8 D3 J- O! f# i3 }; }    memset(&myobj->kobj, 0, sizeof(struct kobject)); /* 这一步非常重要,没有这一步init kobject会失败 */" y; Q3 a+ s9 ~% x3 }
    if (kobject_init_and_add(&myobj->kobj, mytype, NULL, "test_kobj_default_attr"))
6 U% H  m, R; Z2 N+ ?        kobject_put(&myobj->kobj);/ R6 Q, g3 y! }5 T
( Q; U- u, s3 j
    printk(KERN_ALERT "*************************\n");
9 C8 t- m! Z5 Q2 L  b' o    printk(KERN_ALERT "test_kobject_default_attr is inited!\n");
' X4 A* |- p! }    printk(KERN_ALERT "*************************\n");4 W5 D6 J, J9 u' g# a* Z2 q
    ( C& n' N, H0 f# i# ?( y/ G3 t0 v
    return 0;! `2 U9 o0 ]1 V$ t& Z! B
}5 ]/ v' c, {5 \8 D" C# M6 i
: D4 K" X3 T' ~% v
static void test_kobject_default_attr_exit(void)
3 R6 H3 n) G7 M{) c9 z! X/ X, `$ J
    kobject_del(&myobj->kobj);2 z& j' ?( B: X
    kfree(myobj);
- j5 g$ H2 J0 a/ h$ h- @    $ I" L, f7 ]- W; h
    /* 退出内核模块 */
- j: b. l2 ^& E% C    printk(KERN_ALERT "*************************\n");
7 N$ g4 e2 [  P  o5 J( T- @    printk(KERN_ALERT "test_kobject_default_attr is exited!\n");" @) ^3 L$ p3 i
    printk(KERN_ALERT "*************************\n");( W  X1 G4 d/ T! `! w7 J2 c* O: L
    printk(KERN_ALERT "\n\n\n\n\n");
2 i4 ?& m4 |& q3 R# P9 k' m}
8 I4 J( m% K) k9 H' g. q9 G2 O1 G; o
" O% X4 _! q9 c  O& `3 M4 W' _static void myobj_release(struct kobject *kobj) 2 w8 U) t3 H# C) ^0 j: O$ l
{& Z4 u) ~3 ]  F. \( q7 m) t1 m
    printk(KERN_ALERT, "release kobject");
# p7 V) _) G4 Y7 T4 ]; X3 ?    kobject_del(kobj);( P: G& i& H* ^
}
& ?- _8 a9 ^" C  L( z0 E
* y1 X% E1 O, T2 u/ \' ~0 ?/* 读取属性文件 val 或者name时会执行此函数 */7 U# }  A6 [1 p5 z  G
static ssize_t my_show(struct kobject *kboj, struct attribute *attr, char *buf) 4 f! A3 [3 j: j  K7 z
{! T4 c7 P& d( i
    printk(KERN_ALERT "SHOW -- attr-name: [%s]\n", attr->name);    & Y# {, k& _$ ~7 A( L
    if (strcmp(attr->name, "val") == 0)% e  r5 a0 y& a# E; {( |( m& E/ ^( S
        return sprintf(buf, "%d\n", myobj->ival);3 }6 I3 p$ T; O3 f. \4 j
    else
7 z0 Y' I# B# \% f' M        return sprintf(buf, "%s\n", myobj->cname);
4 b( J, z: T! h1 D  l: P' a}
" z3 N/ K" H% x- r+ l9 G$ B7 h. g7 W( t, M5 s; O
/* 写入属性文件 val 或者name时会执行此函数 */. w- Y" s7 E' |! ~4 Z3 U: h( F
static ssize_t my_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len)
: M7 i; o" l) h, L{
% y. x* ~  F! h6 F. u2 q- T    printk(KERN_ALERT "STORE -- attr-name: [%s]\n", attr->name);
% Y& _+ s' A* |    if (strcmp(attr->name, "val") == 0)0 P5 Z2 I( w) d- G" S
        sscanf(buf, "%d\n", &myobj->ival);
- r. n0 H! y# p. N' w% o# G/ c    else! N+ s$ S9 v2 Q1 ]/ v9 N0 J0 R
        sscanf(buf, "%s\n", myobj->cname);        : `% D) e( _, I! s( {
    return len;& m: U4 y. r5 ~
}
5 X" a, H% R+ ^% Z; x1 E5 n: [" k0 n$ {+ U: C% z6 k
module_init(test_kobject_default_attr_init);
5 Q+ y& Z) t& `1 @: _module_exit(test_kobject_default_attr_exit);
1 t. u8 T8 l8 V1 H  l9 S. A复制代码
+ n# d" N) M2 j2 L6 |2 ]. L" } ( Z) I! |  i) [) W
! G/ I- \' {9 q+ c9 `. i* Y- |
对应的Makefile如下:
; \& S) R2 l; i& t+ A% Z
9 d7 B7 M+ Z7 ~  a, ]  K复制代码
& ~: `9 Q- V8 p) I. f# must complile on customize kernel; Z) S2 n( j+ x8 N7 n3 }
obj-m += mykobject_with_default_attr.o
2 [# j; Y' d: g# z, Dmykobject_with_default_attr-objs := test_kobject_default_attr.o  T, y& T# J6 R6 E+ P: K, @6 E# Z
% L$ d8 B# K' a" w4 \5 C
#generate the path8 a/ T# F& t4 S5 ]1 P; X; U
CURRENT_PATH:=$(shell pwd)
* y! p5 K, o8 q  T) M  j' d#the current kernel version number
! q! f/ X1 D  n; x3 i5 `2 ]1 wLINUX_KERNEL:=$(shell uname -r)
) V/ U  J! d, C4 @4 F6 a% c#the absolute path; K7 j: j8 U+ ^. a3 ]) c* s
LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)
& m$ L: k7 a  R9 z#complie object# m1 a5 Z7 @1 e  n
all:
& G# ?2 F' _  X  b3 k2 t  n    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
0 V( Z& i: x5 u5 ^' B! H    rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned% C! T- A* I( |8 W
#clean
9 Q, d& J/ @9 m6 vclean:# b8 e. ?/ F2 ~* L
    rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned, {2 _9 Y4 e7 S1 ~4 C9 \1 i
复制代码6 ^- l  B, D/ X% y  p) o

5 p2 y7 u; h' Q$ P3 b0 O3 t' [6 E
测试方法:(我使用的测试系统是:Centos6.5 x86)2 I4 ^  u4 j# j$ M: D! v7 u9 V/ X

+ H4 o  z7 h" n2 q+ N4 h复制代码+ c7 {" `7 m; f. f2 R
############################ 编译 ########################################################+ ]' M3 c0 q. c! ~( M+ p
[root@localhost test_kobject_defalt_attr]# ll
1 i/ Z; O+ Y) T( }7 F# Utotal 8
/ S6 B1 t5 B9 E: e- @" b  h+ n-rw-r--r-- 1 root root  582 Dec 24 15:02 Makefile
+ U- ?5 t& \# F& O-rw-r--r-- 1 root root 4032 Dec 24 16:58 test_kobject_default_attr.c
4 n# f4 u) M( d$ I5 z% i[root@localhost test_kobject_defalt_attr]# make) z; [2 b) G) E( I9 T
make -C /usr/src/kernels/2.6.32-431.el6.i686 M=/home/wyb/chap17/test_kobject_defalt_attr modules
1 L7 P1 I* I5 @make[1]: Entering directory `/usr/src/kernels/2.6.32-431.el6.i686'
" D& ^7 P0 B" x2 X/ A# h! t  CC [M]  /home/wyb/chap17/test_kobject_defalt_attr/test_kobject_default_attr.o
1 P7 F" N- }! L+ }( @/ W5 I/home/wyb/chap17/test_kobject_defalt_attr/test_kobject_default_attr.c: In function ‘myobj_release’:; s# d) l$ x' A# \
/home/wyb/chap17/test_kobject_defalt_attr/test_kobject_default_attr.c:109: warning: too many arguments for format
: N: c& C+ P5 s5 k- X% V8 p0 \4 I  LD [M]  /home/wyb/chap17/test_kobject_defalt_attr/mykobject_with_default_attr.o2 W7 \& N6 b( Q+ a5 ^2 P) A  v' H" E! |
  Building modules, stage 2.7 y& q8 X# f# L# E
  MODPOST 1 modules$ L7 ]8 f, N+ h& J0 o& j: K
  CC      /home/wyb/chap17/test_kobject_defalt_attr/mykobject_with_default_attr.mod.o% A  }( w4 c" _7 n
  LD [M]  /home/wyb/chap17/test_kobject_defalt_attr/mykobject_with_default_attr.ko.unsigned9 R9 I! ?7 L# v4 i# p7 Z; `
  NO SIGN [M] /home/wyb/chap17/test_kobject_defalt_attr/mykobject_with_default_attr.ko6 L8 e, I) T* R- a4 K9 ~' M3 b0 N8 m) S
make[1]: Leaving directory `/usr/src/kernels/2.6.32-431.el6.i686'
; d3 U9 E; c( T5 O  c! U1 S- grm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned# w8 X1 y0 h2 V* c7 Z  ^* l  q! f+ d" k
[root@localhost test_kobject_defalt_attr]# ll+ J5 n! ]0 o1 {8 h7 i1 p7 I
total 104
; |' X; a1 t, }5 a) t; o& T-rw-r--r-- 1 root root   582 Dec 24 15:02 Makefile, D* u3 S5 w! \, b
-rw-r--r-- 1 root root 96805 Dec 24 16:58 mykobject_with_default_attr.ko
, t% M7 U) p3 g1 I# ^3 u" A9 ]6 v-rw-r--r-- 1 root root  4032 Dec 24 16:58 test_kobject_default_attr.c/ O2 }/ K0 d( m* |2 Q) X
: q* u: Y/ _; Q; o3 r" P0 [
############################ 安装 ########################################################
: P! p5 v* g1 N( H5 `7 w[root@localhost test_kobject_defalt_attr]# insmod mykobject_with_default_attr.ko 6 f& `9 r; A  q; p( j
[root@localhost test_kobject_defalt_attr]# ll /sys/                           <-- kobject对应的文件夹: u$ E, p, \- f  }7 K
total 08 ^* p" m7 d& g
drwxr-xr-x  2 root root 0 Dec 24 15:50 block4 A2 s# a; [+ N. v3 |1 K
drwxr-xr-x 17 root root 0 Dec 24 15:50 bus+ o% q4 q* k4 J4 ^0 M. t7 p  s& A
drwxr-xr-x 40 root root 0 Dec 24 15:50 class9 J7 B1 Z6 x! T" W0 a
drwxr-xr-x  4 root root 0 Dec 24 15:50 dev
6 r6 Z: L+ Q; Cdrwxr-xr-x 12 root root 0 Dec 24 15:50 devices
- L- E; ]$ k: J* ?4 c9 t; ~! Q4 R" ydrwxr-xr-x  4 root root 0 Dec 24 15:50 firmware
8 }2 W" c! l" j* B  ddrwxr-xr-x  3 root root 0 Dec 24 15:50 fs, C- p9 h$ F6 K, N
drwxr-xr-x  2 root root 0 Dec 24 16:06 hypervisor
6 f5 o5 I6 [* M, `" o/ x/ H  Wdrwxr-xr-x  5 root root 0 Dec 24 15:50 kernel8 Q. S! t2 X  i! o# l+ ?
drwxr-xr-x 85 root root 0 Dec 24 16:59 module
2 X; o$ Y. N" x& i4 m& ?drwxr-xr-x  2 root root 0 Dec 24 16:06 power: u9 F2 _# T8 o2 {
drwxr-xr-x  2 root root 0 Dec 24 16:59 test_kobj_default_attr* Q% Y6 n& k# z1 a/ e
[root@localhost test_kobject_defalt_attr]# ll /sys/test_kobj_default_attr/    <-- kobject的2个属性文件
* A# }1 c8 p- z3 ototal 00 Q) ?/ E7 Y3 s) s- l8 ~) f
-rw-rw-rw- 1 root root 4096 Dec 24 16:59 name) w* v/ i3 i2 d$ w
-rw-rw-rw- 1 root root 4096 Dec 24 16:59 val
  y6 O; D# Q6 \( {% R[root@localhost test_kobject_defalt_attr]# dmesg                              <-- dmesg 中只有初始化的信息
6 e2 m4 ]8 Z8 }, I" I*************************' C* G) \  v1 ?, D  \1 q
test_kobject_default_attr is inited!
/ x2 g" a" t! O, d* P' E*************************  _' m: z. ^) P
5 E3 F6 S8 J/ }( h1 J9 \" f
############################  读取属性文件 ###############################################
# F% j9 i9 X2 F! h2 a- ?. K- {( p[root@localhost test_kobject_defalt_attr]# cat /sys/test_kobj_default_attr/val  <-- 属性值就是我们在测试代码中输入的值. I$ n  D# A. s# q. q; j/ [
100
: k9 a/ E# e. o  R[root@localhost test_kobject_defalt_attr]# cat /sys/test_kobj_default_attr/name <-- 属性值就是我们在测试代码中输入的值
6 d# X: S- c* G7 htest
7 p! @  r: ?- V: M* R[root@localhost test_kobject_defalt_attr]# dmesg                              <-- dmesg 中多了2条读取属性文件的log
  H, Q0 W# b( }: RSHOW -- attr-name: [val]  f+ m5 D! j/ _* H) ^/ L
SHOW -- attr-name: [name]9 d/ c- W( _! C3 ^+ q
/ K9 {3 P+ ~& y
############################ 写入属性文件 ################################################
7 R7 \; G5 d/ _' ~% r[root@localhost test_kobject_defalt_attr]# echo "200" > /sys/test_kobj_default_attr/val         <-- val文件中写入 200
5 M) z  ^! H2 Q# c. `2 f6 F[root@localhost test_kobject_defalt_attr]# echo "abcdefg" > /sys/test_kobj_default_attr/name    <-- name文件中写入 adcdefg
: B7 A5 F' o3 N* e* R* a[root@localhost test_kobject_defalt_attr]# dmesg                              <-- dmesg 中又多了2条写入属性文件的log
5 V( [) ^7 k, p5 d1 CSTORE -- attr-name: [val]4 {4 j% E9 y/ K; i# H: E
STORE -- attr-name: [name]( l+ ?5 W! f7 {. F, [& M
[root@localhost test_kobject_defalt_attr]# cat /sys/test_kobj_default_attr/val     <-- 再次查看 val文件中的值,已变为200
# ~+ r' z6 y$ A2003 B! Y, e. V" W
[root@localhost test_kobject_defalt_attr]# cat /sys/test_kobj_default_attr/name    <-- 再次查看 name文件中的值,已变为abcdefg
$ t$ P" `: n5 Wabcdefg" u8 f4 O- m8 j- \7 F% e) R  j

* r9 }: z1 j7 o3 u  m2 c############################ 卸载 ########################################################
$ A6 j4 I! @& Q) N- j8 {8 l[root@localhost test_kobject_defalt_attr]# rmmod mykobject_with_default_attr.ko
7 |$ S3 c# |2 e8 |1 ?* I复制代码: K% P$ G" h4 ~- Z1 D; a4 |4 h
0 M% {% }: q( N0 V9 H# p( X
+ v3 e& q' n- A% Q9 f
( T. d+ r% T( ^  p- Q

4 ]: |1 f! G$ G: Q; j+ 新属性 :: kobject 自己定义的属性
- g% v/ {: Y! ]  q: J7 `# G: x, P
4 a1 Q; a" X$ u  h; H: S3 C一般来说,使用默认属性就足够了。因为具有共同ktype的kobject在本质上区别都不大,比如都是块设备的kobject,6 Y5 H5 @: Z4 u" Y. |2 c, Z

) Z3 S( h4 J4 q4 C% A- R. }它们使用默认的属性集合不但可以让事情简单,有助于代码合并,还可以使类似的对象在sysfs中的外观一致。6 [% W) F( Z% u; r7 {
. @8 C+ C2 V. q# @& C( @. Y: [+ Q

) v/ p4 e) {) ]% ]
/ [( Z6 _2 q1 u! e& O% t) \在一些特殊的情况下,kobject可能会需要自己特有的属性。内核也充分考虑到了这些情况,提供了创建或者删除新属性的方法。+ h4 H# v* i! I* R/ m

8 L- B% C- e( C% j在sysfs中文件的操作方法参见: fs/sysfs/file.c5 C% k# s$ p" @
2 T2 R' n4 h6 p8 [& V. Z  _" r) N
复制代码
" c, T0 F1 }0 M" J- o1 F+ o- |/* 文件的相关操作非常多,这里只列出创建文件和删除文件的方法 */
2 j7 A3 ~% e  M) E3 A9 a4 L7 A: M
/**
' T: z5 p/ [/ o8 v! e) v8 H4 Q, X6 A( q5 f1 y * 给 kobj 增加一个新的属性 attr
! l: h% ]% a9 f; N5 r3 [, H! n * kobj 对应sysfs中的一个文件夹, attr 对应sysfs中的一个文件+ W* c; g! z8 a9 Q5 Z5 _
*/
& r( ]$ I0 M. @$ p' ?int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
5 H( W" |* ~( Y8 j{9 y) K) U+ c" T/ W
    BUG_ON(!kobj || !kobj->sd || !attr);1 {+ t0 y% u# M  O" M9 y$ `

8 ^) O7 w+ T& c/ c3 A    return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
+ z: U) A2 w9 y3 S! u  N$ l" l" x( b8 ]( l6 h* u+ x
}( A8 ]& T2 W( e
) Z3 B1 x& T: S( M& \$ m
/**5 H9 D6 S9 U3 `6 Z+ b
* 给 kobj 删除一个新的属性 attr" b6 o5 a# b+ @( Q7 M& M
* kobj 对应sysfs中的一个文件夹, attr 对应sysfs中的一个文件) [1 Z& v3 X3 q7 C" X6 W" m
*/
1 e- i) E5 v; j4 {/ o; Wvoid sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
4 I1 V. `' f; N) M+ H{
0 V- `3 Z, c) H  c' N* G8 L    sysfs_hash_and_remove(kobj->sd, attr->name);
" R5 Q' T) b' W$ W) z9 }3 n1 U$ H}
9 T  e  g7 k  i  s复制代码1 B0 _" L" ]% R2 Y# r
- I) k6 o6 w$ p" g9 ]
0 _7 B+ a9 q8 y- m$ g4 L$ A: {& N
除了可以在sysfs中增加/删除的文件,还可以在sysfs中增加或者删除一个符号链接。
! M- _# R' o2 W# k/ G9 I2 r+ R1 C4 V7 T0 s3 G1 C* f' P' {/ [9 [
具体实现参见:fs/sysfs/symlink.c+ d7 Y) H- w  o! H7 L, ~$ j) _
7 s' P5 s$ M) \" x2 g" w
复制代码
6 c4 `) @; @& j7 N  m: P* E% W1 l: M( e/* 下面只列出了创建和删除符号链接的方法,其他方法请参考 symlink.c 文件 */
+ m8 C8 C7 y/ h* l) z& L; x  P4 z$ J& I6 a! @; F/ X( G/ F
/**' T/ x( Z5 I" i" q' f" B" P
* 在kobj对应的文件夹中创建一个符号链接指向 target
) J4 Z# g, Y, i5 b1 ? * 符号链接的名称就是 name! [' x, D5 k3 x4 t: m8 Z! T9 O  a, r
*/
. |: u. T( G" v" }, Fint sysfs_create_link(struct kobject *kobj, struct kobject *target,5 c. B$ b# A  k* q
              const char *name)
- [# R( {9 E) m  q  a' z{
! t4 [( R+ f3 A0 g" p) }    return sysfs_do_create_link(kobj, target, name, 1);$ J' C/ h% Q7 i5 L6 d
}$ F2 w* W+ M$ k" Z  o" U

; t7 T9 p/ E2 `  g& a+ `2 Q5 _3 d) B+ m) j- _7 I$ Q' Q) P
void sysfs_remove_link(struct kobject * kobj, const char * name)  D# V  _% g3 u! E* J  M
{, q$ \& [9 Q0 }. A
    struct sysfs_dirent *parent_sd = NULL;
  U' x4 U# v  a, |( n" h+ ^  D4 c0 u) v
    if (!kobj); d, D7 G% a8 |1 v; [/ t/ M
        parent_sd = &sysfs_root;
& ^" `+ w9 L& E& S! ^    else, R* a' O+ I! Z0 D3 X
        parent_sd = kobj->sd;* z6 b  C; w/ k5 l' t3 M. A, l6 O7 ~
, |" q8 a9 w5 |' W, y. f
    sysfs_hash_and_remove(parent_sd, name);. ~/ y/ Q" Q1 q2 v* |
}
) K" L$ u* ?% T: {8 M5 {; Z4 a- d/ S复制代码0 s, Y, |3 A. M, u0 v9 T

1 H( K/ m2 m8 j/ a8 A) F
* O4 K; g- ^2 o: e! t  ~) V增加新的属性的示例代码 (这里只演示了增加文件的方法,增加符号链接的方法与之类似)
4 M" x- P* |0 d: S
0 J# p1 Y, @8 [7 G5 y, w1 p9 Q' U复制代码: ], {+ J& W; \  Y5 C
/******************************************************************************! k$ K0 R+ q+ h- Z, y
* @file    : test_kobject_new_attr.c
3 J) z* y/ Q6 C2 l5 q * @author  : wangyubin3 n' t+ O0 ]& T
* @date    : Tue Dec 24 17:10:31 2013; A8 A/ F% ^/ \9 ^3 f7 z' @
*
* i+ I& h4 p# ` * @brief   : 测试 kobject 中增加和删除新属性+ o+ e2 A. w4 N1 N4 g/ G, A
* history  : init* v( c$ J% J4 A/ w# _
******************************************************************************/' @7 X) O7 Q( k1 X
- c. E. E6 t: V  e
#include<linux/init.h>4 f& ]+ M! j1 k/ f  _- g
#include<linux/module.h>! E4 O+ ]% R! K2 a/ S
#include<linux/kernel.h>1 |& {+ [" H+ F4 |3 S9 a, C
#include<linux/kobject.h>; i2 ?: [$ X# {- a
#include<linux/sysfs.h># S% O! D6 O' M" K7 a8 |. t. R) E
, z6 D8 ^: W3 @$ U$ o/ r
MODULE_LICENSE("Dual BSD/GPL");
% G+ y8 d9 q3 B. P0 Y5 V
9 ?5 I- n# S( o9 \0 H# M5 H( dstatic void myobj_release(struct kobject*);
$ Y# M$ j/ i) ?4 \3 Kstatic ssize_t my_show(struct kobject *, struct attribute *, char *);
( x* D* m8 W( }0 C8 ]7 p: Mstatic ssize_t my_store(struct kobject *, struct attribute *, const char *, size_t);
7 a2 f2 Q% [, v
+ E0 R9 s; o+ `7 \/ m1 Z/* 自定义的结构体,其中嵌入了kobject,通过属性 c_attr 来控制增加或者删除新属性 */
/ J4 _) V5 m: F" qstruct my_kobj # ]* y/ A8 k* M* D
{- a( I# M: W! N/ b& \0 o1 t
    int c_attr;                 /* 值为0:删除新属性, 值为1:增加新属性*/, I( H$ s6 d. }. L: L8 u
    int new_attr;% h# u0 |, ~8 n3 r. I8 m
    struct kobject kobj;5 C/ p& L- c6 U
};
" V) U) N9 p* B& E
! v) j  t9 _8 M2 \static struct my_kobj *myobj = NULL;4 C! ?) `1 W" t; r

! T9 y: K5 `. A  W/* my_kobj 的属性 c_attr 所对应的sysfs中的文件,文件名 c_attr */, d) T+ d# I) ^
static struct attribute c_attr = {
2 w7 u* Q0 T' E2 ]6 P    .name = "c_attr",2 f. _5 X+ S# x1 s' _+ G3 `6 ^
    .owner = NULL,8 G/ l' Y. }; t; z/ A& M
    .mode = 0666,
8 Z( G5 X! @3 z3 }0 b};
6 _0 x% k: y$ ?. A
4 }5 g0 o# I' y9 |7 F/ A/* 用于动态增加或者删除的新属性 */
+ X* Y9 @2 _( I2 Z$ N4 H: Kstatic struct attribute new_attr = {+ \: h$ w! P. F: F+ m; Q9 f
    .name = "new_attr",7 T9 n0 Z7 x% F/ w: O
    .owner = NULL,( R- P# X3 o9 [
    .mode = 0666,
5 ~/ ^! O8 B) [4 O};
& L8 W3 ]/ U% I7 m3 F
  ?0 r. n5 \5 `. O! \: Ostatic int test_kobject_new_attr_init(void)
# G2 }. ~4 |4 l{
- [2 m' s% u/ B/ `* Y9 j, O/ }    struct attribute *myattrs[] = {NULL, NULL};
5 w! z5 f; I  c' R! i, ~7 F& I    struct sysfs_ops *myops = NULL;6 F6 F, z  O6 |3 z  M: s
    struct kobj_type *mytype = NULL;/ I3 B! G6 U3 R8 w8 x) C
# |( h! U; s* D7 A9 `# T
    /* 初始化 myobj */
: B0 R, a9 Q3 N" ~    myobj = kmalloc(sizeof(struct my_kobj), GFP_KERNEL);- ?" E* Q$ j/ y8 `3 S3 Q
    if (myobj == NULL)/ W0 [: D. D$ f8 @
        return -ENOMEM;, `, ~7 r. _! e: R+ w

: l7 W' g7 E: e, F5 R' z6 k    /* 配置文件 val 的默认值 */
) u, B6 N  \) `; h- O    myobj->c_attr = 0;# N) {9 e( }% b& y2 M
  P, ~( U* u3 v; h5 O) \2 R
    /* 初始化 ktype */
1 K* A7 m# s2 M+ A    mytype = kmalloc(sizeof(struct kobj_type), GFP_KERNEL);
' |' c" Q/ z* }5 {    if (mytype == NULL)
  i. M5 G. ^, Q8 K! {' i+ @        return -ENOMEM;
$ Q8 [6 J+ f# K! e+ C- N0 O0 |! L% X6 c, F% Q* S
    /* 增加1个默认属性文件 */
0 l5 [3 t' p: b! n- b  h    myattrs[0] = &c_attr;$ T/ V2 E7 O* k) Z! J6 T; G3 H

1 \/ n" ~% ^( m5 S! ~- [, _4 J; W5 ^6 N    /* 初始化ktype的默认属性和析构函数 */4 T6 g+ H2 C5 s8 s+ d6 d6 z. u
    mytype->release = myobj_release;
9 G' r+ F: I- }' f$ v    mytype->default_attrs = myattrs;3 q, D7 v& w7 x; w1 v
( ?# u3 Q! N& j8 j; V
    /* 初始化ktype中的 sysfs */# ~; U* }# l: r) t/ p* ?; q
    myops = kmalloc(sizeof(struct sysfs_ops), GFP_KERNEL);. x# }" `! C/ ~
    if (myops == NULL)
8 b: N, x; W! l6 k5 Y        return -ENOMEM;5 b% M& {% T3 l4 X- v
. R3 M" x# U, ?( ^
    myops->show = my_show;
, r' i' U& ]4 T$ `' q5 w, I    myops->store = my_store;
5 e* p, Q: W( i    mytype->sysfs_ops = myops;) e+ z6 b4 J7 s& [3 v  d
0 i8 F; W5 p$ r; W  Y# w
    /* 初始化kobject,并加入到sysfs中 */4 C  t4 p7 X7 C+ X9 n) s
    memset(&myobj->kobj, 0, sizeof(struct kobject)); /* 这一步非常重要,没有这一步init kobject会失败 */
/ ^+ b; C% u9 F    if (kobject_init_and_add(&myobj->kobj, mytype, NULL, "test_kobj_new_attr"))
9 H3 p) g: j2 w" C* W$ K        kobject_put(&myobj->kobj);! _: ~) X+ h: f4 ^3 v2 z8 v7 [+ W
9 S& l7 G  [9 R7 g4 A' ^1 }; o
    printk(KERN_ALERT "*************************\n");
! A3 t8 F. H8 v- _- ?- J    printk(KERN_ALERT "test_kobject_new_attr is inited!\n");
/ ?6 o! i8 t0 w+ y    printk(KERN_ALERT "*************************\n");
, r  ~0 g) [4 S    # i% l* T% v; p2 x% n' p# r+ q/ f
    return 0;& S; k/ s9 H2 M6 f+ y7 a3 k4 [
}3 U% I3 G' g2 {  e# o

/ M& B3 I3 z' h- a; x4 V" Fstatic void test_kobject_new_attr_exit(void)
# j3 \  n: h# b! [! h% {; i, u{
3 v& _' z9 V4 U( S4 w; U; d    kobject_del(&myobj->kobj);
+ m! V& K% }& H* i    kfree(myobj);
% [8 N$ |9 {9 S- P2 `1 T    ( q+ V9 T5 I8 H; t6 H% `* j- p
    /* 退出内核模块 */
; ^1 t8 @3 C" T- I% n9 A    printk(KERN_ALERT "*************************\n");" A  S' }! D6 M1 ^, v& j% x
    printk(KERN_ALERT "test_kobject_new_attr is exited!\n");
' S1 c; W  U3 N) o4 r    printk(KERN_ALERT "*************************\n");
1 E* C1 u1 ]( `& P- i    printk(KERN_ALERT "\n\n\n\n\n");% t; Y* B9 M+ f6 k% `' H' W
}$ f* q# m& s; N- q: @. [
3 Q; ^; Z+ |! K4 Z
static void myobj_release(struct kobject *kobj) ; F* }2 N+ C+ o. S) T5 E
{
9 u; d$ p: A/ Y; Z& }( K  B% X    printk(KERN_ALERT "release kobject");
1 A" c* |. P$ K! {    kobject_del(kobj);
8 x, `3 o* n( Z+ z6 \+ X}
$ \' @+ A( _+ n, h/ N% P
7 {# A: w  I7 U( R4 C/* 读取属性文件 c_attr 或者 new_attr 时会执行此函数 */
. m- N2 J: h% V# H3 F# Pstatic ssize_t my_show(struct kobject *kboj, struct attribute *attr, char *buf) 7 x( H7 J( l! z: t" A
{6 ?1 C- X  @9 x8 p# w% R  i2 {
    printk(KERN_ALERT "SHOW -- attr-name: [%s]\n", attr->name);; M) B. o0 T8 T# }, q8 U3 b
    if (strcmp(attr->name, "c_attr") == 0)
, {* l) o  D! Z; E9 b8 c        return sprintf(buf, "%d\n", myobj->c_attr);
( J+ b0 X( `) _/ b# G, E; s  r9 U    else if (strcmp(attr->name, "new_attr") == 0)7 D+ `! ^/ e; ?7 g
        return sprintf(buf, "%d\n", myobj->new_attr);
& N+ z( o, R7 n8 j
  v- F  s0 n/ P5 L; N% U    return 0;
( j7 Z, O2 Z3 u9 j9 H6 m}
; F3 ~. l8 {+ Y$ R  f& U+ b. a0 G9 X3 H
/* 写入属性文件c_attr 或者 new_attr 时会执行此函数 */7 _) _' K8 Z6 I6 G: s
static ssize_t my_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) . ^( N; O2 c( @4 t
{$ R* {- l2 `! H
    printk(KERN_ALERT "STORE -- attr-name: [%s]\n", attr->name);# J; P7 V; @7 Q
    if (strcmp(attr->name, "c_attr") == 0)
) X0 x8 `5 [5 B        sscanf(buf, "%d\n", &myobj->c_attr);
0 B8 o7 }& _* K3 E! f% A2 }5 I1 J    else if (strcmp(attr->name, "new_attr") == 0)
2 g  \- T' D  d9 ?- h        sscanf(buf, "%d\n", &myobj->new_attr);
; N/ e& I" ?. D  w+ U9 a$ ]
% W. e* V& C$ D6 Z, X    if (myobj->c_attr == 1)     /* 创建新的属性文件 */3 N! w% {7 Y0 ]! H- `% c
    {  X; Y- z  S+ s6 N3 z
        if (sysfs_create_file(kobj, &new_attr))
$ \1 |* }, w* d1 ]4 Y: T            return -1;
4 F: u/ q! c5 ?, h; d2 T6 t$ |        else7 S1 H- `1 _/ h$ h* T
            myobj->new_attr = 100; /* 新属性文件的值默认设置为 100  */4 Q- l+ v/ L& N; z  U2 |& P- ]
    }
- n$ t, X1 _. C6 Z    ) g' V) N$ S' J
    if (myobj->c_attr == 0)     /* 删除新的属性文件 */
; R& r; x/ V8 }! t+ v2 W% @2 _        sysfs_remove_file(kobj, &new_attr);
+ _) `; y4 v- ?4 \   
3 ^# S9 Q( Q' u* x' O: B    return len;' I4 ~8 |5 H7 t! u" y  d2 V0 n! C$ }
}
4 H+ B- G3 |9 L& x( m
0 M" L" i. m$ Umodule_init(test_kobject_new_attr_init);
0 L; V: [. R" O7 dmodule_exit(test_kobject_new_attr_exit);
3 [1 f0 n7 y2 n6 P( r8 \复制代码" E. _% _4 O: A* j* H. ?" y9 e

9 K3 Z' F9 ?- c# d: u- ?
4 w, g2 z& y8 N* H对应的Makefile如下:: k1 u  i: K  k  D& F

5 k5 R5 ?5 r5 s; m2 `; g, ]0 ]复制代码% ?+ h5 T- K/ `# g( `; ?$ t* ?
# must complile on customize kernel
, O" `; k' D* |9 K$ ]% U7 aobj-m += mykobject_with_new_attr.o. H7 H% s9 X) E# N% _; L5 N3 P
mykobject_with_new_attr-objs := test_kobject_new_attr.o0 V4 o# m( r7 H. b( F2 o

" ]: u* r- l7 ~- t) ?#generate the path
, y- K. ^" r. `3 T9 ZCURRENT_PATH:=$(shell pwd)
' N! b( Z( ^* c! R* M3 q- _, v& C#the current kernel version number
3 e# t( ^; X; D0 t& W% C/ RLINUX_KERNEL:=$(shell uname -r)6 ^7 a& D6 r3 i# z
#the absolute path: I; @' I7 N! H6 c5 O% }
LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL); Q' A, c0 C7 i. J! w  A
#complie object( E5 M6 r. T$ Q1 `
all:& _2 y" M& Y4 j3 f4 A
    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules1 a$ Y. D& g5 y, L% K$ [
    rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned, S: M6 ]0 p( }, Q, O8 ]$ p
#clean
& N( b# p/ x* k: H% ~clean:. w5 e8 X8 j5 T) ?/ G8 o
    rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned
# u+ E; d% u( V* d" f/ R复制代码
2 Y& q6 Y0 ?# h; R/ [
& T+ w3 }: h0 l
4 a0 J8 J2 ~: c) b# x% \测试方法:(我使用的测试系统是:Centos6.5 x86)1 a& J  E) {- Z
$ l2 o$ b* c; P5 E. j& n% ?
根据测试代码,增加/删除新属性是根据默认属性 c_attr 的值来决定的。
& ~( J9 p9 c( I! g9 b' l: L0 Q6 [9 ~2 Z8 A& j* i
c_attr 设置为0 时,删除新属性 new_attr9 i: Y8 W8 x( t: E. b3 R2 b, q

! y" g/ A; V# B; m% _) Qc_attr 设置为1 时,新增新属性 new_attr' g& V: q* _7 g. e9 C

" a% t2 s) `6 N- |1 y复制代码
) x; K, G  F5 R* D############################ 编译,安装,卸载同测试默认属性时一样 #######################
: G2 W  m, k& y- k... 省略 ...
: y4 F7 B/ u* v5 ?3 c############################ 动态增加新属性文件 #########################################) x; Y  k2 L+ E3 Z- N5 R6 c
[root@localhost test_kobject_new_attr]# ll /sys/test_kobj_new_attr/            <-- 默认没有新属性 new_attr
2 j+ v! B0 U+ u5 S' ?* ^total 0
9 ]5 n. s+ v! X- ^" V-rw-rw-rw- 1 root root 4096 Dec 24 18:47 c_attr$ P+ n  R, |/ F+ a+ e" a
[root@localhost test_kobject_new_attr]# cat /sys/test_kobj_new_attr/c_attr     <-- c_attr 的值为0
0 [( J% q( w1 N) p  c0/ O. o1 o" U2 n1 p
[root@localhost test_kobject_new_attr]# echo "1" > /sys/test_kobj_new_attr/c_attr <-- c_attr 的值设为1
* t5 N$ Q/ B! G  f% C) n% u[root@localhost test_kobject_new_attr]# ll /sys/test_kobj_new_attr/            <-- 增加了新属性 new_attr, m; B- Y  G# o. C8 Z& g+ M
total 0
' D6 [- E! e' |7 i+ r1 ~-rw-rw-rw- 1 root root 4096 Dec 24 19:02 c_attr
+ A- }" `4 l( O3 Z* s/ M-rw-rw-rw- 1 root root 4096 Dec 24 19:02 new_attr
6 H/ i1 `1 g+ O; j3 f, a- s) y" s3 N" ]& T
############################ 动态删除属性文件 ###########################################4 @: A3 P$ g. {
[root@localhost test_kobject_new_attr]# echo "0" > /sys/test_kobj_new_attr/c_attr   <-- c_attr 的值为0
! b: a) \$ j- ~) A2 C) R[root@localhost test_kobject_new_attr]# ll /sys/test_kobj_new_attr/                 <-- 删除了新属性 new_attr
5 w* A. ^' q( H( G. _; Ototal 0
/ I, N8 u# w+ o: o-rw-rw-rw- 1 root root 4096 Dec 24 19:03 c_attr
( f) V% }9 ]- c. h, L复制代码
! y( s0 h. X1 I7 O$ o4 b
7 ]" a) r0 J. w. F2 n' V8 I1 E& @5 _
  G, V2 ], y7 r- ~4.1.3. sysfs相关约定$ o/ O" n' F5 C+ N) m* B" t0 I
, V; i. l' t; i6 E$ h1 Y0 u
为了保持sysfs的干净和直观,在内核开发中涉及到sysfs相关内容时,需要注意以下几点:0 u! B/ M* X- H- L6 D1 z

, t3 a; \+ l( k, x6 ]+ sysfs属性保证每个文件只导出一个值,该值为文本形式并且可以映射为简单的C类型+ u0 ^- r4 O$ Q+ M/ t
7 l" t+ U% R' C! p& w0 l
+ sysfs中要以一个清晰的层次组织数据  U! t2 V) b7 S* l/ J7 x7 D& X
# [* R0 f# |$ X0 L! Z
+ sysfs提供内核到用户空间的服务
- D$ I5 B. v. D( u+ E* k" s* I" u3 f/ _3 |( {
2 t8 U: y! u6 r* A. ]- k
" e$ p- R5 h" y& X: [
4.2 基于sysfs的内核事件
7 W' F0 s3 D7 _1 m+ J+ M3 `内核事件层也是利用kobject和sysfs来实现的,用户空间通过监控sysfs中kobject的属性的变化来异步的捕获内核中kobject发出的信号。/ S) T, j' k$ H( _, ^2 h! i
# C5 A. t8 x' _/ s
用户空间可以通过一种netlink的机制来获取内核事件。
  P: [  E! D" S: o0 W+ s8 D# C- B! U$ N/ J" m
内核空间向用户空间发送信号使用 kobject_uevent() 函数,具体参见: <linux/kobject.h>
: n/ p7 ]3 ]$ X3 l, j. g2 [" l2 k8 G* u# F6 A
int kobject_uevent(struct kobject *kobj, enum kobject_action action);9 E4 _4 Z, [9 g
3 b9 \9 k! z- W/ `0 ^

; ?8 a: \1 ~& _1 m- R下面用个小例子演示一些内核事件的实现原理:
* w- ^9 k+ f2 Y4 [! O
- i& V8 s5 S, j; |% x4.2.1. 内核模块安装或者删除时,会发送 KOBJ_ADD 或者 KOBJ_REMOVE 的消息1 {/ ^1 P8 x+ y! B, x/ l
" L) |1 t. Z3 o2 t- B" S8 n
内核模块的代码就用上面最简单的那个例子 test_kobject.c 的代码即可
7 `# G, V: l; U& d. `3 p, z
1 r$ L" ?2 [6 d( B& W! {: P6 X
2 t) ]+ S9 f' n% K. K
5 ^; Y7 A' u) D. U4 @4.2.2. 用户态程序: 通过 netlink机制来接收 kobject 的事件通知( r! _& H- ~/ r  {- |( N
0 r6 }9 Q* B: O+ L6 d
复制代码* i% O0 I9 x3 T
/******************************************************************************" z  n7 D2 R: y  A* C% v
* @file    : test_netlink_client.c6 P$ E: J6 H/ ?8 O2 c
* @author  : wangyubin
7 g+ K" w* g) a1 A% Y! H$ ?5 I * @date    : Tue Dec 24 19:48:54 2013
" p. m  F8 j' X# ~9 m6 ]- N2 ~$ c( q * , {$ X2 E+ ^, ^0 N% E6 q
* @brief   : 通过 netlink机制接收kobject发出的信号
  ~) X$ D) i- ?& Q * history  : init
1 y/ ]! p. |$ _ ******************************************************************************/0 m3 X2 e# T2 \5 J& D$ n5 g9 M

, |1 U. [  E3 {; h) k5 t#include <stdio.h>  
& Z) k7 k5 _# v7 Q/ t; b#include <stdlib.h>  6 @0 i0 d6 [3 {
#include <string.h>  
! s& G$ B1 h' E#include <errno.h>  
* s7 x: ?$ |. W+ [' c2 k#include <sys/types.h>  6 F8 j( J6 L; ]2 \; r$ K# Q$ u
#include <asm/types.h>  
* F- f2 B# z9 v3 u6 i& t#include <sys/socket.h>   
) H1 I# o. `+ y1 R9 j#include <linux/netlink.h>  
! X/ g9 \/ |: M/ O* u3 _
7 u* t1 G/ b1 o5 O" hvoid MonitorNetlinkUevent()  
: j( i" Z( ^7 \6 `5 B{  
1 E$ k8 a4 V7 u! t" U    int sockfd;  5 ]3 f5 E* x  P4 G) s4 x2 i
    struct sockaddr_nl sa;  
% R9 r* S/ X4 l" i& t    int len;  
5 t: s! _) m% r+ L: a0 \, f# j- K, C    char buf[4096];  * r  n, z/ a0 O# g8 I
    struct iovec iov;  ( ?& Q. \0 x  O' W. Q- m4 y
    struct msghdr msg;  # ^2 i/ `; L  J- L; L. K, W
    int i;  
+ W& z/ m% t, {; v7 o  V/ e  
% |) Y* X# \" g' T    memset(&sa,0,sizeof(sa));  
4 u- ]4 \& V) m# _3 }) @2 Q    sa.nl_family = AF_NETLINK;  
8 ^) j% x/ G4 F    sa.nl_groups = NETLINK_KOBJECT_UEVENT;  
' j0 y  M; `- y    sa.nl_pid = 0;//getpid(); both is ok  
- d! X( |( `: d$ O. q+ @! n    memset(&msg,0,sizeof(msg));  " b& U0 A2 x  [: A8 s; D
    iov.iov_base = (void *)buf;  ' o! }! N5 T; K* z+ {( ?2 \
    iov.iov_len = sizeof(buf);  3 P0 S! U5 d( m% S5 f/ C& l" a
    msg.msg_name = (void *)&sa;  5 i5 _( B) D& p, V! W, N( l) N$ {
    msg.msg_namelen = sizeof(sa);    }: y$ y6 f0 n1 A. a- L9 Y
    msg.msg_iov = &iov;  
- Q- K1 s! p! {( d  E, z    msg.msg_iovlen = 1;  7 |; z) r; h0 R$ F0 n% J
  
0 V' M, N7 h1 c2 {( I0 w9 D6 e    sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);  
! n8 s5 W( k" m) L    if(sockfd == -1)  
9 u2 C2 i7 {1 _% k; K, M/ p        printf("socket creating failed:%s\n",strerror(errno));  
$ d! L, m9 v+ U    if(bind(sockfd,(struct sockaddr *)&sa,sizeof(sa)) == -1)  
6 X. e/ S% b3 N$ Q# {, i, q        printf("bind error:%s\n", strerror(errno));  
4 B% m0 L8 K# O/ K1 {- l    while(1) {  
+ t" n, x) y6 e& {  r; f8 V- c/ [      memset(buf, 0, sizeof(buf));  . C9 H4 a5 Z; E
      len=recvmsg(sockfd, &msg, 0);  
( C( U) E5 R1 r8 I      if(len < 0){}  
1 Y5 V1 k0 Z+ j' {! H+ c        //printf("receive error\n");  1 d4 H( f" M1 ]) Z; j! {9 `
      else if(len < 32||len > sizeof(buf))  
. [$ k* y8 U# r! @( d& B+ Y# b% n        printf("invalid message");  
! E: O: q' v! S) V/ O( c' }      for(i=0; i<len; i++)  7 s( R2 U  y' o, G( N9 d" L& b5 J
        if(*(buf+i) == '\0')  
5 G' I2 ?% e/ Y            buf = '\n';  ; [& h' J# H2 F) Y2 E! T8 i8 R
      printf("received %d bytes\n%s\n", len, buf);  
: v: e, }2 e( T    }  
4 ^. J- r6 Q1 |% I2 t/ a1 [}  ( @: o+ D( q1 p3 g0 _9 [

) j$ C- I. ~7 c# H/ L: sint main(int argc, char *argv[])/ p; W* I: z1 Y" @
{
2 y& j; R6 l6 t1 V& T5 Q! s* V    MonitorNetlinkUevent();
1 k8 d" B$ v4 G. W    return 0;( Y; ^  e% t2 U( e
}+ i2 [( b6 k( A
复制代码, t2 u& d2 W. R* s1 F' r
2 [+ q8 {* s; }$ r$ o8 l
. p) p  {8 J% o$ y- I7 |: T

1 |0 |: Z) I6 ]. a: o
. b. Z# _( R# I4 q  s. Z7 e/ E  v4.2.3. 测试方法:(我使用的测试系统是:Centos6.5 x86)7 N8 D& `4 l, l3 B3 C

  d' ?* ]# t- r复制代码
$ d7 X8 l3 I1 c7 ~. @' ^" W; }/ d, M############################ 编译并启动用户态程序 (窗口1)###################################3 W- S, ^7 q; y* x
[root@localhost test_kobject_event]# ll# a/ g1 U3 P' i4 b  ^
total 4
/ j$ ~. }; L0 h# Z4 P% L, [4 j-rw-r--r-- 1 root root 1846 Dec 24 20:36 test_netlink_client.c/ N& u) |# v" {% w1 Y% [0 w) A
[root@localhost test_kobject_event]# gcc -o test_netlink_client test_netlink_client.c     <-- 编译用户态程序9 G7 a1 _3 a" o0 f1 B( x  J
[root@localhost test_kobject_event]# ./test_netlink_client                                <-- 启动后等待内核kobject的事件到来+ H6 @# N  y. |$ V, }8 X
  P' S2 S( S" U, Y+ v
############################ 安装内核模块并查看用户态程序输出 (窗口2)#######################
3 w  A1 o" v1 O5 Y( B# p  \[root@localhost test_kobject]# insmod mykobject.ko                         <-- 在窗口2中安装内核模块,窗口1中会接收到 KOBJ_ADD 信号
9 I  e& B/ z& W1 q############################ 卸载内核模块并查看用户态程序输出 (窗口2)#######################" y2 Q3 R$ s( P" x
[root@localhost test_kobject]# rmmod mykobject.ko                          <-- 在窗口2中安装内核模块,窗口1中会接收到 KOBJ_REMOVE 信号
7 T) b+ O8 d* s, |  `复制代码
9 K  r2 [$ ]8 L9 @ % g( m3 R4 k" [& p9 r
$ g8 E: P. k; ^/ j0 ?. C
5. 总结; S* H& N% v' m0 |, Y0 y2 U& r
kobject加sysfs给内核带来的好处不是三言两语能够说得清楚的,上面的例子也只是通过简单的使用来直观的感受一下kobject,例子本身没有什么实际意义。
: e5 P9 G) k" D/ Q5 D+ O) [0 S& A  D5 N1 ^. V) Q% ]0 C* }
最后一个例子中使用了 netlink机制,我在之前的项目中使用过netlink来监控系统进程的I/O信息,对netlink有一些初步的了解。
  @4 s8 u' y! G
% ~, t1 Q* }  y9 T2 I. t! ]但是例子中只是用来获取一下 kobject的事件而已,这里主要为了说明kobject,netlink的相关内容以后有机会再补充。  f! l& G; p! j; H. m: v
  • TA的每日心情
    慵懒
    2020-6-13 15:46
  • 签到天数: 1 天

    [LV.1]初来乍到

    2#
    发表于 2021-2-26 18:08 | 只看该作者
    Linux内核设计与实现之设备与模块
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-24 13:55 , Processed in 0.250000 second(s), 26 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

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