分类: LINUX编程 |
从应用程序引出到操作系统我觉得比较自然。下面就从一个简单例子介
绍一个程序如何在操作系统中运行。
执行。那么,main函数是不是与其他函数有所区别,地位有些特殊呢?
不是的。main函数和其他函数地位一样。其实,我们完全可以做到让一个
c程序从任何地方开始执行。比如linux,它就没有main函数,大家都知道,
系统执行过启动的一段汇编后,就会跳转到位于init/main.c中的
start_kernel中开始执行。
一般用户用c语言开发时会调用一些库函数,编译成obj文件后,在链接过
程中把库函数的二进制代码链接进入程序,最后形成二进制可执行文件。
链接过程中,链接器会在用户程序前插入一些初始化的代码。uClinux下
是在crt0.s中(我移植的是uClibc库)。不管什么平台下什么形式的crt0.s,
这个文件最后几行代码中肯定有一个jmp(或者call或br等转移指令)
(或__uClibc_main)。这就是为什么你的程序都从main开始执行。如果你把
这个跳转标号改成任意一个标号,比如foo。而你的程序里面既有main,又
有foo,则这种情况下,程序就先从foo开始执行。所以,main函数和其他
函数一样,并没有特殊地位。
以flat格式可执行文件为例。uClinux下支持一种叫flat的可执行文件格式。
这种文件格式比较简单,基本上是平铺的,所以叫flat很形象。现在好像
uClinux-2.4.x内核的版本已经能够支持elf格式的文件执行了。不过为了
举例简单,我还是用flat格式举例。这里暂不分析flat文件格式,我们把注
意力放到参数传递上。uClinux开发用户程序,首先当然是编码,然后编译,
编译生成的文件是elf格式的,所以要用工具elf2flt将elf文件转换成flat,
假设这个工作已经完成。
参数。学过C语言的都知道,x,y作为参数会传递给main,其中argc=3,
argv[0]="foo",
在你执行一个程序的时候,操作系统会调用
程中把库函数的二进制代码链接进入程序,最后形成二进制可执行文件。
链接过程中,链接器会在用户程序前插入一些初始化的代码。uClinux下
是在crt0.s中(我移植的是uClibc库)。不管什么平台下什么形式的crt0.s,
这个文件最后几行代码中肯定有一个jmp(或者call或br等转移指令)
(或__uClibc_main)。这就是为什么你的程序都从main开始执行。如果你把
这个跳转标号改成任意一个标号,比如foo。而你的程序里面既有main,又
有foo,则这种情况下,程序就先从foo开始执行。所以,main函数和其他
函数一样,并没有特殊地位。
以flat格式可执行文件为例。uClinux下支持一种叫flat的可执行文件格式。
这种文件格式比较简单,基本上是平铺的,所以叫flat很形象。现在好像
uClinux-2.4.x内核的版本已经能够支持elf格式的文件执行了。不过为了
举例简单,我还是用flat格式举例。这里暂不分析flat文件格式,我们把注
意力放到参数传递上。uClinux开发用户程序,首先当然是编码,然后编译,
编译生成的文件是elf格式的,所以要用工具elf2flt将elf文件转换成flat,
假设这个工作已经完成。
参数。学过C语言的都知道,x,y作为参数会传递给main,其中argc=3,
argv[0]="foo",
在你执行一个程序的时候,操作系统会调用
do_execve(char
这个操作会根据文件路径打开文件,装入内存,argv就是放到命令行参数,envp是
环境变量参数。
是flat格式,就会调用load_flat_binary(),在fs/binfmt_flat.c中。有关参数,会根据一路传递下来的argv,envp首先处理一遍计算出参数的个数argc,envc。然后在函数create_flat_tables里面建立好参数表。整个函数代码如下:
{
(1)
(2)
(3)
(4)
(5)
(6)
(7)
(8)
(9)
(10)
(11)
(12)
(13)
(14)
(15)
(16)
(17)
(18)
(19)
(20)
(21)
(22)