///a.c
#include<stdio.h>
void fun(void);
int var_public_b=1;//
int main()
{
printf ("main start\n");
var_public_b=6;//
fun();
printf ("main end\n");
}
///b.c
#include<stdio.h>
int var_public_b=2;//
void fun(void)
{
printf("var_public_b=%d\n",var_public_b);
}
编译结果:
/tmp/ccy3sTJJ.o:(.data+0x0): multiple definition of `var_public_b'
/tmp/ccmApdIp.o:(.data+0x0): first defined here
collect2: ld returned 1 exit status
对于a.c中,将int var_public_b=1;改为
int var_public_b;
或者
extern int var_public_b;
均可编译通过
(或者不改a.c,在b.c中,将int var_public_b=2;改为
int var_public_b;
也可编译通过)
以上可以看出
引用外部变量在做声明时,在linux c中,可以不必加extern,但是不能赋值
即可以有n个文件中,同时存在
int var_public_b;(a.c)
int var_public_b;(b.c)
int var_public_b;(c.c)
int var_public_b=1;(d.c)
这样的声明形式,但赋值的最多只能有一个,否则编译器是为重复定义
这是因为编译器将未初始化的全局变量作为弱符号处理
连接装载和库p92 p112
当然,规范的写法应该
extern int var_public_b;(a.c)
extern int var_public_b;(b.c)
extern int var_public_b;(c.c)
int var_public_b=1;(d.c)
声明外部函数时加不加extern都可以
a.c中void fun(void)或者extern void fun(void)效果是一样的
/*
函数A的定义必定包含是函数A的声明,所以同一个文件中,函数A是在被调用前定义的,可以不必再去声明
------因为声明的本意就是告诉编译器函数A的名字,参数类型,参数个数,返回值类型等,作为在调用到函数A时去检查一下调用格式是否正确的依据
-----而既然定义在声明之前,那么编译器在顺序编译到这个位置时,已经得到了这个依据,没必要再去逼着程序员写上那个函数头外加一个分号(即声明)
-----对于全局函数而言,声明函数不会分配空间,自然不会作为将函数名添加到符号表的依据---只有定义才会
*/
/*
变量B(全局变量)的声明和定义的区别用肉眼看上去,似乎没有函数的定义和声明的区别那么清晰,
----因为函数的定义有一个函数体,函数的声明没有函数体
---而变量的定义无体可写,那么只有初始化标志着某行代码作为变量的定义了,比如
int a=6;--表示这是此处的符号a一个强符号--表示定义
---那么这行必是定义,是需要分配内存的,因为需要将6塞进里面
如果是
int a;这行呢--未初始化---表示此处符号a是一个弱符号----他的作用什么??
----在其他地方已经有这么一行int a=6(此处的a是强符号)代码的情况下-----他的作用是声明
---在其他任何一个地方都没有一行int a=6和int a的情况下-----他的作用是定义
----在其他地方也有这么一行int a代码的情况下----即出现多个同名的弱符号------他的作用不定,同类型的弱符号,应该是谁要看编译顺序吧----猜测,反正有一个判断的规则
实际上,
int a(或int a=6)本来就是表示定义a,
而变量的声明是extern int a,
但是以前的程序员往往在声明时忘记打extern所以会在多个文件中产生同一个变量的定义
为了解决此问题,干脆将未初始化的全局变量(int a)当做common类型处理,这只是解决出现多个int a的办法,并不表示int a是声明---但可以理解为 "有时用来声明a"
实际上的int a是定义,“只不过定义在了common块中”----猜测
所以写程序时,就按照规范的做法声明变量时要用extern int a,定义变量时要用int a或int a=6-----------声明时不加extern也可,表示是定义(弱符号),
但编译器已用common机制解决重复定义的错误
extern int a 不是表示变量a在外部文件定义,而是表示声明全局符号a,至于a在本文件定义还是在外部文件定义,这个随便
extern int a 也可写成etern a
另外
----变量的声明和定义不易区分是对全局变量而言,对于函数体内的局部变量
如果出现两个int a,则必定是重复定义----第一个int a,不管有没初始化,都是定义a
common块和弱符号机制,是对全局变量而言的,所以对局部变量两个int a肯定出错
extern也是对全局变量而言的,所以你打算在一个函数体内如下
extern int a;a=6;int a则会出错
*/
refer to
谭浩强-c程序设计3 p196有对extern,static之解释
http://www.shuihan.com/article/179
符号的一个问题,记录一下
http://www.unixresources.net/linux/clf/linuxK/archive/00/00/71/60/716080.html
EXPORT_SYMBOL()
/********************************************************************************************/
下面例子解释的很清楚啦,一个模块test.ko调用另一个模块base.ko的符号base_func1-----------2011-12-8
/**base.c**/
#include <linux/kernel.h>
#include <linux/module.h>
void base_func1(void)
{
printk("Hello, in base_func1\n");
}
EXPORT_SYMBOL(base_func1);
static int __init mini2440_hello_module_init(void)
{
printk("Hello, base is installed !\n");
return 0;
}
static void __exit mini2440_hello_module_cleanup(void)
{
printk("Good-bye, base was removed!\n");
}
module_init(mini2440_hello_module_init);
module_exit(mini2440_hello_module_cleanup);
MODULE_LICENSE("GPL");
/**test.c**/
#include <linux/kernel.h>
#include <linux/module.h>
extern void base_func1();
static int __init mini2440_hello_module_init(void)
{
printk("Hello, test is installed !\n");
base_func1();
return 0;
}
static void __exit mini2440_hello_module_cleanup(void)
{
printk("Good-bye, test was removed!\n");
}
module_init(mini2440_hello_module_init);
module_exit(mini2440_hello_module_cleanup);
MODULE_LICENSE("GPL");
ifneq ($(KERNELRELEASE),)
obj-m := base.o test.o
else
KDIR := /opt/FriendlyArm/mini2440/linux-2.6.32.2
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif
将bash,ko和test.ko考到板子上执行
[root@FriendlyARM plg]# insmod base.ko
Hello, base is installed !
[root@FriendlyARM plg]# insmod test.ko
Hello, test is installed !
Hello, in base_func1
看第二个例子refer to
http://blog.csdn.net/songqqnew/article/details/6754583 里面那个多依赖的例子
由上现象得出结论,EXPORT_SYMBOL是解决模块之间符号调用的一个手段。
模块与模块之间的符号(函数或者变量)调用,需要在符号所在文件中将那个符号导出比如EXPORT_SYMBOL(base_func1)。其他模块仅声明一下那个符号是外部符号如extern void base_func1(),就可以使用了。
模块内部的但是不在同一个文件里的符号调用,不需要使用EXPORT_SYMBOL导出符号。仅需要在想使用那个符号的文件里声明一下是外部符号如extern void base_func1(),就可以使用了。
接着看符号base_func1在test.ko里的
<div>[root@localhost test2]# readelf -s test.ko</div><div> Num: Value Size Type Bind Vis Ndx Name
</div> 28: 00000000 0 NOTYPE GLOBAL DEFAULT UND base_func1
符号base_func1被认为是und--undefined,所以链接器加载此符号的时候会去外面()寻找。
看符号test2 test3在mymodule.ko里的什么位置---第二个例子的
[root@localhost test3]# readelf mymodule.ko -s
Num: Value Size Type Bind Vis Ndx Name
<div> 35: 00000000 16 FUNC GLOBAL DEFAULT 2 test2
36: 00000010 16 FUNC GLOBAL DEFAULT 2 test3</div>
Ndx=2,即text段---readelf -S,在本模块的代码段,所以连接器加载此符号的时候去本代码段寻找---模块内部符号
。
/********************************************************************************************/
上面第一个例子展示了符号所在文件编译成模块和引用符号的文件也编译成模块,那么需要在符号所在文件里将那个符号导出来 EXPORT_SYMBOL 。
如果两个文件都编译进内核则就不需要 EXPORT_SYMBOL了。因为内核本身是一个大的文件,他们都会编译进内核代码段,符号不必导出也会找得到。
其他情况都需要 EXPORT_SYMBOL。
但是即使那两个文件都编译进内核,但为了防止意外发生(比如被某某不小心将其中一个编译成了模块),也要将相关符号 EXPORT_SYMBOL。
/********************************************************************************************/