Bran的内核开发指南(3)

来源:互联网 发布:阿里云登录远程桌面 编辑:程序博客网 时间:2024/05/16 19:48
导读:
  在一般的C语言编程实例中,main()函数是程序的入口。为了保持你的编程习惯,让你熟悉内核开发,本指南将仍旧将main()函数作为程 序入口。正如上一章所提到的,我们尽力使汇编代码最少。但在后面的章节中,我们不得不回到汇编代码“start.asm”中去添加终端服务程序,以调用C 函数。
  在这一章节中,我们将建立“main.c”和一个包含了公共函数原型的头文件——“system.h”。在“main.c”中,我们依然将 使用一个main()函数以作为你的C程序入口。按照一个内核开发的规则,我们不需要从main()函数正常返回。许多操作系统使用main来初始化内核 和子系统,加载外壳(shell)程序,最后main()函数将进入一个空闲循环中。当多任务系统中没有其它的任务要被运行的时候,空闲循环就会被使用。 下面是一个含有最基本的main()函数和指南后面部分将用到的函数体的示例代码“main.c”
  #include <system.h >
  /* 你需要亲自将这些代码实践一遍 */
  unsigned char *memcpy(unsigned char *dest, const unsigned char *src, int count)
  {
  /* 在这里添加代码,将count指定大小的数据从src复制到
  * dest,最后返回dest */
  }
  unsigned char *memset(unsigned char *dest, unsigned char val, int count)
  {
  /* 在这里添加代码,将在dest中count指定大小的空间用
  * val指定的字符代换。最后返回dest */
  }
  unsigned short *memsetw(unsigned short *dest, unsigned short val, int count)
  {
  /* 这里和上面的功能一样,
  * 在局部变量都是无符号短整型时,
  * 你可以把上面的代码完整地复制下来就行了。
  * */
  }
  int strlen(const char *str)
  {
  /* 这里需要添加一个遍历数列str的循环,
  * 当发现当前位是“/0”时,循环结束,这样通过循环计数器就可以得出一个字符串的长度。
  * 最后返回字符串的长度。 */
  }
  /* 我们在后面将使用这个函数来读取I/O端口以获得键盘等设备的数据。
  * 我们在这里实际上使用的是被称为“在线汇编”的开发方式。
  * */
  unsigned char inportb (unsigned short _port)
  {
  unsigned char rv;
  __asm__ __volatile__ ("inb %1, %0" : "=a" (rv) : "dN" (_port));
  return rv;
  }
  /* 我们将使用这个函数来写I/O端口以向设备发送数据。
  * 这将在下一章中被用来改变字符模式光标位置。
  * 我们将再次使用在线汇编,因为有些工作是用C语言实现不了的。
  * */
  void outportb (unsigned short _port, unsigned char _data)
  {
  __asm__ __volatile__ ("outb %1, %0" : : "dN" (_port), "a" (_data));
  }
  /* 下面是一个非常简单的main()函数。它所做的仅仅是一个死循环。
  * 这就像我们的“空闲”循环。*/
  void main()
  {
  /* 你能在这后面添加命令 */
  /* 保留这个回环入口。如果你不小心删除了下面这个循环语句,
  * 这在“start.asm”中也是一个死循环。*/
  for (;;);
  }
  main.c:我们内核虽小但重要的开始
  在编译之前,我们需要在“start.asm”中添加两行。我们需要让NASM知道main()函数在一个外部文件中,并且我们需要在汇编文件中调用main()函数。打开“start.asm”,找到“stublet”所在的行。就在这行的后面,添加如下内容:
  extern _main
  call _main
  我们停下想想。在C语言中,我们描述为“main”,为什么在这里main前面要加下划线呢?这是因为GCC编译器在编译时要在所有函数和变量名前加一个下划线。而当我们在汇编代码中引用一个C中的函数或变量时,我们必须在其前加上一个下划线。
  现在似乎可以编译了吧,但我们还没有弄“system.h”呢。很简单,你只要创建一个空白的文本文件,取名为“system.h”。将所有 函数(memcpy, memset, memsetw, strlen, inportb, outportb)的原型添加到其中。在这里使用宏定义来避免使用文件包含是明智的。这还能避免在头文件中多次使用#ifndef, #define, #endif来做申明。我们将在指南中每个C源程序中包含“system.h”,以便你能方便地按需使用内核中函数。你还可以按照实际情况,随意地扩充这 个函数库。“system.h”的内容如下:
  #ifndef __SYSTEM_H
  #define __SYSTEM_H
  /* MAIN.C */
  extern unsigned char *memcpy(unsigned char *dest, const unsigned char *src, int count);
  extern unsigned char *memset(unsigned char *dest, unsigned char val, int count);
  extern unsigned short *memsetw(unsigned short *dest, unsigned short val, int count);
  extern int strlen(const char *str);
  extern unsigned char inportb (unsigned short _port);
  extern void outportb (unsigned short _port, unsigned char _data);
  #endif
  我们的全局头文件: 'system.h'
  下面,我们将学习怎么编译源代码。打开前面章节提到的“build.bat”,添加如下内容。注意,这里假定“system.h”文件在内核 代码的“include”文件夹中。这个命令将执行gcc编译器。在这些传递的参数中,“-Wall”将给你一些关于代码的提示。“-nostdinc” 和“-fno-builtin”的意思是我们使用的不是标准的C库函数。“-I./include”告诉编译器我们的头文件是在当前目录的 “include”文件夹中。“-c”告诉编译器先别忙着连接。正如前面章节提到的,“-o main.o”是指定编译器产生的文件。简而言之,我们是在使用对内核开发最适宜的选项把“main.c”编译成“main.o”。
  小贴示:在windows系统中。右键单击批处理文件并选择“编辑”来编辑它。
  gcc -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -I./include -c -o main.o main.c
  把上面的内容添加到"build.bat"中
  不要忘记按照我们在“build.bat”中提到的指令!最后,你需要把“main.o”添加到需要连接起来构建内核的目标文件列表中。如果你正在奋力地编写像memcpy这样有实际效力的函数,可以看看这个示例的解决方案。

本文转自
http://rammaker.cosoft.org.cn/store/bkerndev_zh_CN/Docs/creatingmain.htm  
原创粉丝点击