编译运行内核里面的helloworld以及使用递归计算阶乘

来源:互联网 发布:禁止安装任何软件方法 编辑:程序博客网 时间:2024/06/14 16:40

/* * $Id: hello.c,v 1.5 2004/10/26 03:32:21 corbet Exp $ */
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual␣BSD/GPL");
static int hello_init(void) {
  printk(KERN_ALERT "Hello,␣world\n");
  return 0;
}
static void hello_exit(void) {
  printk(KERN_ALERT "Goodbye,␣cruel␣world\n");
}
module_init(hello_init); module_exit(hello_exit);


//Makefile
obj-m := hello.o

在软件开发中,make 是一个工具程序(Utility software),经由读取叫 做 “makefile” 的文件,自动化建构软件。它是一种转化文件形式的工具,转 换的目标称为 “target”;与此同时,它也检查文件的依赖关系,如果需要的 话,它会调用一些外部软件来完成任务。它的依赖关系检查系统非常简单, 主要根据依赖文件的修改时间进行判断。大多数情况下,它被用来编译源 代码,生成结果代码,然后把结果代码连接起来生成可执行文件或者库文 件。它使用叫做 “makefile” 的文件来确定一个 target 文件的依赖关系,然 后把生成这个 target 的相关命令传给 shell 去执行。

2.编译helloword程序
在终端输入:sudo make -C /lib/modules/$(uname -r)/build M=$(pwd) modules

3. 将编译好的该模块加载到内核
sudo insmod hello.ko

4.使用lsmod命令,可以看到内核里已经有该模块。

5.要想看到其输出,执行
tail /var/log/syslog
或者tail /var/log/kern.log

6. 之后,将该模块卸载
sudo rmmod hello.ko





二:使用递归计算阶乘

源代码:
//lab1.c
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int lab1_init(int n) {
 if(n == 1){
   return n;
 }
 else{
   return n*lab1_init(n-1);
 }
}
static int lab1_run(void){
 int n;
 n = lab1_init(3);
 printk(KERN_ALERT "%d\n",n);
 return 0;
}
static void lab1_exit(void) {
 printk(KERN_ALERT "lab1 exit.\n");
}
module_init(lab1_run);
module_exit(lab1_exit);

执行步骤和hello world类似
这个程序,当N的值过大时,(我尝试了1000),会造成系统崩溃死机。
原因是函数的调用存储在栈当中。在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中。这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的内存地址块。而在内核里面每个进程都有自己的核心栈,不能使用虚拟存储,只能占用物理存储。因此每个栈的空间只有几个K。很容易溢出,而且溢出没保护。



通过以上两个实验,我们知道,在内核代码的编写有以下几个不同
1.没有 main 函数。每个内核模块只注册自己以便来服务将来的请求, 并且它的初始化函数立刻终止. 换句话说, 模块初始化函数的任务是 为以后调用模块的函数做准备; 好像是模块说, “我在这里, 这是我能 做的.”
2.模块卸载时需要精心清理自己的足迹。一个终止的应用程序可以在释 放资源方面懒惰, 或者完全不做清理工作, 但是模块的退出函数必须 小心恢复每个由初始化函数建立的东西, 否则会保留一些东西直到系 统重启。
3.内核模块中的错误会很致命。在应用程序开发中段错误是无害的, 一 个调试器常常用来追踪错误到源码中的问题, 而一个内核错误至少会 杀掉当前进程, 如果不终止整个系统。 • 不能调用运行时库。作为一个程序员, 你知道一个应用程序可以调 用它没有定义的函数: 连接阶段使用合适的函数库解决了外部引用。 printf 是一个这种可调用的函数并且在 libc 里面定义。一个模块, 在 另一方面, 只连接到内核, 它能够调用的唯一的函数是内核输出的那 些; 没有库来连接. 在 hello.c 中使用的 printk 函数, 是在内核中定义 的 printf 版本并且输出给模块。
4.内核编程必须考虑并发问题。大部分应用程序, 多线程的应用程序是 一个明显的例外, 典型地是顺序运行的, 从头至尾, 不必要担心其他事
情会发生而改变它们的环境。内核代码没有运行在这样的简单世界中, 即便最简单的内核模块必须在这样的概念下编写, 很多事情可能马上 发生. 内核编程中有几个并发的来源。自然的, Linux 系统运行多个进 程, 在同一时间, 不止一个进程能够试图使用你的驱动. 大部分设备能 够中断处理器; 中断处理异步运行, 并且可能在你的驱动试图做其他 事情的同一时间被调用。几个软件抽象 (例如内核定时器) 也异步运 行。而且, Linux 可以在对称多处理器系统 ( SMP ) 上运行, 结果是你 的驱动可能在多个 CPU 上并发执行。最后, 在 2.6, 内核代码已经是 可抢占的了; 这个变化使得即便是单处理器会有许多与多处理器系统 同样的并发问题。
5.内核栈空间非常有限。应用程序存在于虚拟内存中, 有一个非常大的 堆栈区。堆栈, 当然, 是用来保存函数调用历史以及所有的由当前活跃 的函数创建的自动变量。内核, 相反, 有一个非常小的堆栈; 它可能小 到一个 4096 字节的页。你的函数必须与这个内核空间调用链共享这 个堆栈。因此, 声明一个巨大的自动变量从来就不是一个好主意; 如果 你需要大的结构, 你应当在调用时间内动态分配。


顺便吐槽一句。。。csdn的使用体验很不好。。。
0 0
原创粉丝点击