在路上

来源:百度文库 编辑:神马文学网 时间:2024/04/29 01:19:58
开放实验室
西北工业大学同学、老师、校友和校外友人的网上交流平台。
我们曾经走过一样的岁月,也曾经为一样的事情流泪和欢笑,
我们宁愿忽视那些不同的想法和生活方式;
我们坚信,虽然我们的样子已经被社会打磨成千面,但是体温相似,情有共通。
欢迎光临 开放实验室登录 |注册 |帮助
《基于linux的C编程与内核导读》连载(5)
今天是本书第二章的第3、4两节,分别讲述内核的体系结构和源代码目录结构。欢迎大家指出其中错误或提出修改意见,谢谢!
内核体系结构
Linux内核主要由5个模块组成,它们分别是:进程调度模块、内存管理模块、文件系统模块、进程间通信模块和网络接口模块。
进程调度模块用来负责控制进程对CPU资源的使用;内存管理模块用于确保所有进程能够安全地共享机器主内存区,同时,内存管理模块还支持虚拟内存管理方式;文件系统模块用于支持对外部设备的驱动和存储,虚拟文件系统模块通过向所有的外部存储设备提供一个通用的文件接口,隐藏了各种硬件设备的不同细节;进程间通信模块子系统用于支持多种进程间的信息交换方式;网络接口模块提供对多种网络通信标准的访问并支持许多网络硬件。
内核源代码的目录结构
Linux核心源程序通常都安装在/usr/src/linux下,本人使用的是RedHat9.0,因此源代码的目录确切为/usr/src/linux2.4.20。其中2.4.20是内核的版本号,下面首先粗略讲一下Linux的内核版本命名规律。版本号的格式为“x.yy.zz”,其中x介于0到9之间,而yy、zz则介于0到99之间,通常数字越高便说明版本越新。另外,如果yy为偶数(例如2.0.30),则表明这是一个稳定地发行的核心,而如果yy为奇数(例如2.1.42),则表明这是一个开发中的核心。
核心源程序的文件按树形结构进行组织,在源程序树的最上层,即目录/usr/src/linux下有这样一些目录和文件:
(1)主要文件有:
?        COPYING:GPL版权申明。对具有GPL版权的源代码改动而形成的程序,或使用GPL工具产生的程序,具有使用GPL发表的义务,如公开源代码;
?        CREDITS:光荣榜。对Linux做出过很大贡献的一些人的信息;
?        MAINTAINERS:维护人员列表,对当前版本的内核各部分都有谁负责;
?        Makefile:第一个Makefile文件。用来组织内核的各模块,记录了个模块间的相互这间的联系和依托关系,编译时使用;仔细阅读各子目录下的Makefile文件对弄清各个文件这间的联系和依托关系很有帮助;
?        ReadMe:核心及其编译配置方法简单介绍;
?        Rules.make:各种Makefilemake所使用的一些共同规则;
?        REPORTING-BUGS:有关报告Bug 的一些内容。
(2)主要目录有:
?        Arch/:arch子目录包括了所有和体系结构相关的核心代码。它的每一个子目录都代表一种支持的体系结构,例如i386就是关于intel cpu及与之相兼容体系结构的子目录。PC机一般都基于此目录;
?        Include/:include子目录包括编译核心所需要的大部分头文件。与平台无关的头文件在 include/linux子目录下,与 intel cpu相关的头文件在include/asm-i386子目录下,而include/scsi目录则是有关scsi设备的头文件目录;
?        Init/:这个目录包含核心的初始化代码(注:不是系统的引导代码),包含两个文件main.c和Version.c,这是研究核心如何工作的好的起点之一。
?        Mm/:这个目录包括所有独立于 cpu 体系结构的内存管理代码,如页式存储管理内存的分配和释放等;而和体系结构相关的内存管理代码则位于arch/*/mm/,例如arch/i386/mm/Fault.c;
?        Kernel/:主要的核心代码,此目录下的文件实现了大多数linux系统的内核函数,其中最重要的文件当属sched.c;同样,和体系结构相关的代码在arch/*/kernel中;
?        Drivers/:放置系统所有的设备驱动程序;每种驱动程序又各占用一个子目录:如,/block 下为块设备驱动程序,比如ide(ide.c)。如果你希望查看所有可能包含文件系统的设备是如何初始化的,你可以看drivers/block/genhd.c中的device_setup()。它不仅初始化硬盘,也初始化网络,因为安装nfs文件系统的时候需要网络;
?        Documentation/:文档目录,没有内核代码,只是一套有用的文档,可惜都是English的,看看应该有用的哦;
?        Fs/:所有的文件系统代码和各种类型的文件操作代码,它的每一个子目录支持一个文件系统, 例如fat和ext2;
?        Ipc/:这个目录包含核心的进程间通讯的代码;
?        Lib/:放置核心的库代码;
?        Net/:核心与网络相关的代码;
?        Modules/:模块文件目录,是个空目录,用于存放编译时产生的模块目标文件;
?        Scripts/:描述文件,脚本,用于对核心的配置;
一般,在每个子目录下,都有一个 Makefile 和一个Readme 文件,仔细阅读这两个文件,对内核源码的理解很有用。
发布于Wednesday, August 24, 2005 1:30 PM 由zsb1107 |0 篇评论
《基于linux的C编程与内核导读》连载(4)
今天的内容是本书第二章的第1、2两节,主要讲研读内核代码的原因与方法。
内核研读入门
为什么要读linux内核代码
Linux的最大的好处之一就是它的源码公开。同时,公开的核心源码也吸引着无数的电脑爱好者和程序员;他们把解读和分析Linux的核心源码作为自己的最大兴趣,把修改Linux源码和改造Linux系统作为自己对计算机技术追求的最大目标。
那么是什么吸引如此多的程序员去解读和分析内核代码呢?或者换个问题,我们研读linux内核代码有哪些好处呢?我大致总结了一下,主要有以下原因:
(1)要想真正理解linux操作系统,并掌握其实现原理和方法,就必须研读内核代码。虽然我们通过阅读一些操作系统的经典书籍可以对linux的一些工作原理有了一个理论上的认识,但实际上对操作系统的真正组成和内部关系实现的理解仍不是很清晰。正如Linus Torvalds所说,要想真正理解一个软件系统的运行机制,就必须阅读其源代码。
(2)在阅读源代码的过程中,你可以从中学到很多的计算机的底层知识,如系统的引导,虚拟存储的实现机制,多任务机制,系统保护机制等等。
(3)我们将从操作系统的整体结构中,体会整体设计在软件设计中的份量和作用,以及一些宏观设计的方法和技巧:Linux的内核为上层应用提供一个与具体硬件不相关的平台;同时在内核内部,它又把代码分为与体系结构和硬件相关的部分,和可移植的部分;再例如,Linux虽然不是微内核的,但它把大部分的设备驱动处理成相对独立的内核模块,这样减小了内核运行的开销,增强了内核代码的模块独立性。
(4)而且我们还能从对内核源码的分析中,体会到它在解决某个具体细节问题时方法的巧妙:如Linux通过Botoom_half机制来加快系统对中断的处理等等许多细节。
(5)最重要的是在源码的分析过程中,我们将会被一点一点地、潜移默化地专业化。一个专业的程序员,总是把代码的清晰性,兼容性,可移植性放在很重要的位置。他们总是通过定义大量的宏,来增强代码的清晰度和可读性,而又不增加编译后的代码长度和代码的运行效率;他们总是在编码的同时,就考虑到了以后的代码维护和升级。甚至,只要分析百分之一的代码后,我们就会深刻地体会到,什么样的代码才是一个专业的程序员写的,什么样的代码是一个业余爱好者写的。而这一点是任何没有真正分析过标准代码的人都无法体会到的。
如何研读linux内核
由于内核代码的冗长,和内核体系结构的庞杂,所以分析内核也是一个很艰难,很需要毅力的事。那么究竟如何研读内核呢,我们在分析内核时应该注意哪些事项呢?
(1)要分析Linux内核源码,首先要对内核的系统结构有个大概的了解,弄清处各个模块之间的联系。这点将在2.3节详细介绍。
(2)其次要找到各个模块的位置,也即要弄懂源码的文件组织形式。这点将在2.4节详细介绍。
(3)在分析源代码之前首先要阅读相关的理论知识,对这段代码要实现的功能、以及实现此功能用到的方法弄清楚,这样在阅读源代码时就有的放矢了。
(4)另外在研读内核时尽量以linux如何完整实现一个功能为线索,如打开一个文件的全过程、执行一个可执行文件的全过程等。这样大家对内核就会有比较生动的一个认识。
(5)数据结构是研读内核的关键部分,就是说我们要弄清主要数据结构的组成,了解数据结构之间的联系,并且要了解主要函数对有关数据结构的操作。
另外,在研读Linux内核时最好随身准备几本工具书。首先是关于80386处理器结构和编程的资料,这方面我推荐《UNIX系统下的80386》。此书对于80386的任务切换机制、内存管理单元等知识和UNIX的进程管理、内存管理进行了详细的对比分析。另外,最好能准备几本关于操作系统的经典教材,这里我推荐A.S.Tanenbaum的书《Operation System:Design and Implementation》。这本书的作者就是著名的MINIX操作系统的创造者,其对操作系统的讲解非常深刻和细致。另外一个重要的原因就是Linus在最初开发linux时,正是参考了MINUX系统,就像Tanenbaum在书中写的那样“A Finnish student,Linus Torvalds,decided to write a MINIX clone.Thus was LINUX born.”。所以此书对于我们理解linux内核有许多有益的帮助。
发布于Tuesday, August 23, 2005 1:48 PM 由zsb1107 |2 篇评论
《基于Linux的C编程与内核导读》连载(3)
本部分是第一章的第3、4两节,分别讲DDD和makefile。图片暂时我还没有学会怎么插入, 呵呵,大家先将就着看吧。
使用DDD进行程序调试
GNU DDD是命令行调试程序,如GDB、DBX、WDB、Ladebug、JDB、XDB、Perl Debugger或Python Debugger的可视化图形前端。它特有的图形数据显示功能(Graphical Data Display)可以把数据结构按照图形的方式显示出来。DDD最初源于1990年Andreas Zeller编写的VSL结构化语言,后来经过一些程序员的努力,演化成今天的模样。DDD的功能非常强大,可以调试用C\C++、Ada、Fortran、Pascal、Modula-2和Modula-3编写的程序;可以超文本方式浏览源代码;能够进行断点设置、回溯调试和历史纪录编辑;具有程序在终端运行的仿真窗口,并在远程主机上进行调试的能力;图形数据显示功能(Graphical Data Display)是创建该调试器的初衷之一,能够显示各种数据结构之间的关系,并将数据结构以图形化形式显示;具有GDB/DBX/XDB的命令行界面,包括完全的文本编辑、历史纪录、搜寻引擎。
DDD是开源软件,用户可以去http://www.cs.tubs.de/softech/ddd/下载.rpm格式的DDD源码文件。
图1-4显示的是DDD的主窗口。它主要由选单栏、工具条、数据窗口、源文件窗口、机器码窗口、控制台和命令工具窗口等几部分组成。其中,数据窗口用于观察复杂的数据结构,在删除数据之后,显示仍然有效;源文件窗口显示源代码、断点和当前执行到达的位置,选择该窗口中的“Display”项,可以显示任意表达式的值;机器码窗口显示当前所选函数的机器代码,但仅对于GDB来说是可用的;在Debugger控制台里,用户可以与DDD内置调试器的命令行接口进行交互,等同于执行命令工具栏中的命令。
图1-4 DDD的主窗口图
运行机理
在设计DDD的时候,主创人员决定把它与GDB之间的耦合度尽可能降小。因为像GDB这样的开源软件,更新要比商业软件快。所以为了使GDB的变化不会影响到DDD,在DDD中,GDB是作为独立的进程运行的,通过命令行接口与DDD进行交互。
DDD的运行机理如图1-5所示。它显示了用户、DDD、GDB和被调试进程之间的关系。为了使响应时间变小,DDD和GDB之间的所有通信都是异步进行的。在DDD中发出的GDB命令都会与一个回调例程相连,放入命令队列中。这个回调例程在合适的时间会处理GDB的输出。例如,如果用户手动输入一条GDB的命令,DDD就会把这条命令与显示GDB输出的一个回调例程连起来。一旦GDB命令完成,就会触发回调例程,GDB的输出就会显示在DDD的命令窗口中。
图1-5 DDD的运行机理图
DDD在事件循环时等待用户输入和GDB输出,同时等着GDB进入等待输入状态。当GDB可用时,下一条命令就会从命令队列中取出,送给GDB。GDB到达的输出由上次命令的回调过程来处理。这种异步机制避免了DDD在等待GDB输出时发生阻塞现象,到达的事件可以在任何时间得到处理。DDD和GDB的分离使得DDD运行速度变慢,但这种方法还有很多好处。例如,用户可以把GDB调试器换成其它调试器,如DBX等。另外,还可以在不同的机器上运行GDB和DDD。
现在就用DDD来实际调试下面sample.c这段程序,为了节省空间,去掉了所有的注释。
/*********************sample.c*******************/
/*************this example is used to learn DDD**************/
#include
#include
#define MAXINPUTSTRINGSIZE 5
int n;
int factn;
char resultstring[100];
int getInt()
{
char * inputString;
int inputInt;
inputString = (char *) malloc (MAXINPUTSTRINGSIZE * sizeof(char));
printf("Enter the value:");
fgets(inputString, MAXINPUTSTRINGSIZE, stdin);
printf("You entered %s\n", inputString);
inputInt = atoi(inputString);
return inputInt;
}
int computeFact(int n)
{
int ac***=0;
while(n>1)
{
ac*** *= n;
n--;
}
return ac***;
}
char * buildResultString(int x, int factx)
{
char * resultString = (char *) malloc(100 * sizeof(char));
sprintf(resultString, "The factorial of %d is %d\n", x, factx);
return resultString;
}
void main(int argc, char * argv[])
{
char * outString;
n = getInt();
factn = computeFact(n);
outString = buildResultString(n, factn);
printf("%s\n",outString);
}
首先,使用下面的命令编译sample.c,切记要使用“-g”选项生成调试信息:
#gcc -g -o sample sample.c
接着运行sample程序,输入数值“5”后,可以看到如下结果:
You entered 5
The factorial of 5 is 0
可以看出,上面程序中是有错误的,需要进行调试。输入下面的命令启动DDD调试器,调试这个可执行程序:
#DDD sample
一段时间之后,DDD的主窗口就会出现。找到怀疑出错的地方,在相应的代码上设置断点(在有怀疑的行上单击鼠标左键,然后单击工具栏中的“Break”按钮)。然后单击命令工具栏上的运行按钮或在选单栏“Commands”里选择运行相关命令,如图1-6所示。
图1-6 断点的设置
在控制台中提示符下输入数字“5”后按回车键,就会运行到图3中箭头指示的位置。这时候检查可疑变量ac***的值,在控制台提示后输入下面的命令:
(gdb) display ac***
接着往下单步运行,多次点击工具栏中的“Step”按钮,观察变量ac***的结果。具体参考如下:
(gdb) step
43 while(n>1) {
1: ac*** = 0
(gdb)
44 ac*** *= n;
1: ac*** = 0
(gdb)
45 n--;
1: ac*** = 0
(gdb)
43 while(n>1) {
1: ac*** = 0
(gdb)
44 ac*** *= n;
1: ac*** = 0
(gdb)
45 n--;
1: ac*** = 0
可以看出问题出在ac***上。这时点击命令工具栏上的“Kill”按钮将程序断掉,把初始化ac***的那一句改为“int ac*** = 1;”。重新运行之后,发现结果正确。至此,调试过程完毕。
特殊功能
上面只是粗略地介绍了DDD调试的方法。实际上,DDD还有一些与众不同的功能,例如可视化显示数据结构(单个结构体、二叉树、链表等)和绘制数据集等。
图1-7就是按点集绘制的数组sval中保存的数据(首先在源码窗口选中要显示的数组,然后点击工具栏中的“Plot”按钮,即会出现绘制窗口)。用户也可以不按数据点集显示,在弹出窗口选单“Plot”下选择“Lines”,就可以显示成连线段。这个功能非常直观,对于调试程序有很大的帮助作用。
图1-7 绘制数据集
DDD包含的内容不止这些,由于篇幅的限制,这里就不多说了。希望能起到抛砖引玉的作用。如果用户想进一步学习,可以参考DDD的用户手册。
文件的编写
为什么要编写makefile文件
记得在学习过程中曾经看过网上的一则帖子,上面有这么一段话“make 是所有想在Linux系统上编程的用户必须掌握的工具。如果你写的程序中没有用到 make ,则说明你写的程序只是个人的练习程序,不具有任何实用的价”。也许这么说有点儿偏激,但 make 实在是应该用在任何稍具规模的程序中的。下面就举个简单的例子来说明原因。
假设我们有下面这样的一个程序,源代码如下:
/***********main.c****************/
#include "mytool1.h"
#include "mytool2.h"
void main()
{
mytool1_print("hello");
mytool2_print("hello");
}
/*********** mytool1.h ****************/
#ifndef _MYTOOL_1_H
#define _MYTOOL_1_H
void mytool1_print(char *print_str);
#endif
/************mytool1.c************/
#include "mytool1.h"
void mytool1_print(char *print_str)
{
printf("This is mytool1 print %s\n",print_str);
}
/*********** mytool2.h ****************/
#ifndef _MYTOOL_2_H
#define _MYTOOL_2_H
void mytool2_print(char *print_str);
#endif
/************mytool2.c************/
#include "mytool2.h"
void mytool2_print(char *print_str)
{
printf("This is mytool2 print %s\n",print_str);
}
当然由于这个程序是很短的我们可以这样来编译
gcc -c main.c
gcc -c mytool1.c
gcc -c mytool2.c
gcc -o main main.o mytool1.o mytool2.o
这样的话我们也可以产生main程序,而且也不时很麻烦.但是如果我们考虑一下如果有一天我们修改了其中的一个文件(比如说mytool1.c)那么我们难道还要重新输入上面的命令?也许你会说,这个很容易解决啊,我写一个SHELL脚本,让她帮我去完成不就可以了.是的对于这个程序来说,是可以起到作用的.但是当我们把事情想的更复杂一点,如果我们的程序有几百个源程序的时候,难道也要编译器重新一个一个的去编译?
为此,聪明的程序员们想出了一个很好的工具来做这件事情,这就是make.我们只要执行一下make,就可以把上面的问题解决掉.在我们执行make之前,我们要先编写一个非常重要的文件.--Makefile.对于上面的那个程序来说,可能的一个Makefile的文件是:
# 这是上面那个程序的Makefile文件
main:main.o mytool1.o mytool2.o
gcc -o main main.o mytool1.o mytool2.o
main.o:main.c mytool1.h mytool2.h
gcc -c main.c
mytool1.o:mytool1.c mytool1.h
gcc -c mytool1.c
mytool2.o:mytool2.c mytool2.h
gcc -c mytool2.c
有了这个Makefile文件,不过我们什么时候修改了源程序当中的什么文件,我们只要执行make命令,我们的编译器都只会去编译和我们修改的文件有关的文件,其它的文件她连理都不想去理的。
的基本规则
既然makefile文件这么重要,那么就让我们一步步来学习makefile的一些知识首先我们看一下makefiel文件的结构。Makefiel文件里主要是有关哪些文件(‘target’目的文件)是从哪些别的文件(‘dependencies’依靠文件)中产生的,用什么命令来进行这个产生过程。有了这些信息,make会检查磁碟上的文件,如果目的文件的时间戳(该文件生成或被改动时的时间)比至少它的一个依靠文件旧的话, make 就执行相应的命令,以便更新目的文件。(目的文件不一定是最后的可执行档,它可以是任何一个文件。)
makefile 一般被叫做“makefile”或“Makefile”。当然你可以在 make 的命令行指定别的文件名。如果你不特别指定,它会寻找“makefile”或“Makefile”,因此使用这两个名字是最简单的。
在Makefile中也#开始的行都是注释行。Makefile中最重要的是描述文件的依赖关系的说明。一般的格式是:
target: components
TAB rule
第一行表示的是依赖关系。第二行是规则.比如说我们上面的那个Makefile文件的第二行main:main.o mytool1.o mytool2.o表示我们的目标(target)main的依赖对象(components)是main.o mytool1.o mytool2.o
当倚赖的对象在目标修改后修改的话,就要去执行规则一行所指定的命令.就象我们的上面那个Makefile第三行所说的一样要执行 gcc -o main main.o mytool1.o mytool2.o
注意规则一行中的TAB表示那里是一个TAB键,若缺少系统则会提示错误“***missing separator.stop”,这是许多初学者容易忽略的地方,要注意!
变量
上面提到 makefiles 里主要包含一些规则。它们包含的其它的东 西是变量定义。
makefile 里的变量就像一个环境变量(environment variable)。 事实上,环境变量在 make 过程中被解释成 make 的变量。这些 变量是大小写敏感的,一般使用大写字母。它们可以从几乎任何 地方被引用,也可以被用来做很多事情,比如:
1.贮存一个文件名列表。在上面的例子里,生成可执行文件的 规则包含一些目标文件名做为依靠。在这个规则的命令行 里同样的那些文件被输送给 gcc 做为命令参数。如果在这 里使用一个变数来贮存所有的目标文件名,加入新的目标 文件会变的简单而且较不易出错。
2.贮存可执行文件名。如果你的项目被用在一个非 gcc 的系 统里,或者如果你想使用一个不同的编译器,你必须将所 有使用编译器的地方改成用新的编译器名。但是如果使用一个变量来代替编译器名,那么你只需要改变一个地方,其 它所有地方的命令名就都改变了。
3.贮存编译器旗标。假设你想给你所有的编译命令传递一组 相同的选项(例如 -Wall -O -g);如果你把这组选项存 入一个变量,那么你可以把这个变量放在所有呼叫编译器的地方。而当你要改变选项的时候,你只需在一个地方改 变这个变量的内容。
要设定一个变量,你只要在一行的开始写下这个变量的名字,后 面跟一个 = 号,后面跟你要设定的这个变量的值。以后你要引用这个变量,写一个 $ 符号, 后面是围在括号里的变量名。下面举一个简单的例子,其作用和gcc –Wall –O –g test test.c是一样的:
……
CFLAGS = -Wall -O –g
……
gcc $(CFLAGS) test test.c
……
Makefile有三个非常有用的变量.分别是$@,$^,$<代表的意义分别是: $@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件.
如果我们使用上面三个变量,那么我们可以简化我们的Makefile文件为:
# 这是简化后的Makefile
main:main.o mytool1.o mytool2.o
gcc -o $@ $^
main.o:main.c mytool1.h mytool2.h
gcc -c $<
mytool1.o:mytool1.c mytool1.h
gcc -c $<
mytool2.o:mytool2.c mytool2.h
gcc -c $<
函数
makefile 里的函数跟它的变量很相似——使用的时候,你用一个 $ 符号跟开括号,函数名,空格后跟一列由逗号分隔的参数,最后用关括号结束。例如,在 GNU Make 里有一个叫‘wildcard‘的函 数,它有一个参数,功能是展开成一列所有符合由其参数描述的文件名,文件间以空格间隔。你可以像下面所示使用这个命令:
SOURCES = $(wildcard *.c)
这行会产生一个所有以‘.c‘ 结尾的文件的列表,然后存入变量 SOURCES 里。当然你不需要一定要把结果存入一个变量。
另一个有用的函数是 patsubst ( patten substitude, 匹配替 换的缩写)函数。它需要3个参数——第一个是一个需要匹配的式样,第二个表示用什么来替换它,第三个是一个需要被处理的由空格分隔的字列。例如,处理那个经过上面定义后的变量,
OBJS = $(patsubst %.c,%.o,$(SOURCES))
这行将处理所有在SOURCES字列中的字(一列文件名),如果它的结尾是‘.c‘,就用‘.o‘把‘.c‘取代。注意这里的 % 符号将匹配一个或多个字符,而它每次所匹配的字串叫做一个‘柄’(stem)。 在第二个参数里,%被解读成用第一参数所匹配的那个柄。
好了,我们的Makefile也差不多了,如果想知道更多的关于Makefile规则可以查看相应的文档。
发布于Monday, August 22, 2005 2:13 PM 由zsb1107 |4 篇评论
《基于Linux的C编程与内核导读》连载(2)
下C编程基础
对于本章的读者,我们认为有一定的C语言编写基础,所以在此就不介绍C语言的一些基本知识了。这里我们重点介绍一下如何在Linux环境中编写C程序。
源代码编辑器VI的使用
原则上任何文本编辑器均可以编写C语言源代码,但在Linux下我还是推荐使用Vi进行源代码的编写。虽然对于初学者可能稍微有些困难,但是熟练掌握Vi的使用对于Linux用户来讲非常重要。
任何一台安装了Linux的计算机都会提供Vi软件,Vi有三种操作状态:命令模式、插入模式和末行命令模式,它们间的切换方式如图1-1:
图1-1:Vi三种模式之间的切换
快速入门
下面首先通过一个非常简单的例子让大家对Vi的使用有个大体的认识。本例子是编写一个C语言源文件HelloWorld.c。
1:在提示符启动Vi,编辑HelloWorld.c文件:
#Vi HelloWorld.c
2:接着进入Vi命令模式的主界面,如图1-2所示:
图1-2 VI的主界面
3:此时按A键进入插入模式,注意界面最左下面一行的提示信息变成了――插入――。
4:接下来输入我们的源代码,方法和任何文本编辑器一样。编辑好的HelloWorld.c文件如图1-3所示,可以发现Vi自动将一些关键字涂上了不同的颜色。
图1-3 输入源文件代码
5:编辑好源文件后,就需要保存它。首先按Esc键,从插入模式退回命令模式,接着按 :键进入末行模式,此时输入wq并按回车,则存盘并保存。
的常用命令
1:命令模式的常用命令
命令
说明
x
删除光标后的字符
dd
删除光标所在行
ndd
删除包括光标所在行的n行文本
u
恢复刚才被修改的文本
Y
复制当前行到编辑缓冲区
nY
复制当前行开始的n行到编辑缓冲区
p
将编辑缓冲区中的内容粘贴到光标后的一行
L
移至窗口的最后一行
G
移至该文件的最后一行
nG
移至该文件的第n行
Ctrl+f
向后翻一页
Ctrl+b
向前翻一页
2:插入模式的常用命令
命令
说明
a
从光标所在位置后面开始新增文本
A
从光标所在行最后面的地方开始新增文本
i
从光标所在位置前面开始插入文本
I
从光标所在列的第一个非空白字前面开始插入文本
O
在光标所在列下新增一行并进入输入模式
O
在光标所在列上方新增一行并进入输入模式
3:末行命令模式的常用命令
命令
说明
:q
结束编辑
:w
存盘
:q!
强制离开VI,放弃存盘
:wq
存盘并退出
/exp
往前查找字符串exp
exp
往后查找字符串exp
语言编译器gcc的使用
Linux系统上运行的GNU C编译器(GCC)是一个全功能的ANSI C兼容编译器,虽然GCC没有集成的开发环境,但堪称是目前效率很高的C/C++编译器。
使用 gcc
通常后跟一些选项和文件名来使用 GCC 编译器. gcc 命令的基本用法如下:
gcc [options] [filenames]
命令行选项指定的操作将在命令行上每个给出的文件上执行. 下一小节将叙述一些你会最常用到的选项.
下面举个简单的例子,编译我们上一节编写的HelloWorld.c源文件:
#gcc –o HelloWorld HelloWorld.c
此时会在原目录下看到生成了可执行文件HelloWorld,要看到程序输出结果,则只要输入./HelloWorld即可。
选项
GCC 有超过100个的编译选项可用. 这些选项中的许多你可能永远都不会用到, 但一些主要的选项将会频繁用到. 很多的 GCC 选项包括一个以上的字符. 因此你必须为每个选项指定各自的连字符, 并且就象大多数 Linux 命令一样你不能在一个单独的连字符后跟一组选项. 例如, 下面的两个命令是不同的:
gcc -p -g test.c
gcc -pg test.c
第一条命令告诉 GCC 编译 test.c 时为 prof 命令建立剖析(profile)信息并且把调试信息加入到可执行的文件里. 第二条命令只告诉 GCC 为 gprof 命令建立剖析信息.
当你不用任何选项编译一个程序时, GCC 将会建立(假定编译成功)一个名为 a.out 的可执行文件. 例如, 下面的命令将在当前目录下产生一个叫 a.out 的文件:
gcc test.c
你能用 -o 编译选项来为将产生的可执行文件指定一个文件名来代替 a.out. 例如, 将一个叫 count.c 的 C 程序编译为名叫 count 的可执行文件, 你将输入下面的命令:
gcc -o count count.c
常用选项含义:
-c GCC仅把源文件编译为目标代码。
-static 链接静态库,即执行静态链接。
-O GCC对源代码进行基本优化。这些优化在大多数情况下都会使程序执行得更快。
-On 指定代码优化的级别为n,n为{0,1,2,3}中的一个。如-O2选项告诉GCC产生尽可能小和尽可能快的代码。
-g 在可执行程序中包含标准调试信息。
-pedantic 允许发出ANSI/ISO C标准所列出的所有警告。
-wall 允许发出GCC所提供的所有有用的警告。
-MM 输出一个make兼容的相关列表。
-v 显示编译过程中的每一步用到的命令。
-L 指定所用函数库的路径。如果我们所用到的函数库是系统默认的函数库,则不需要指定路径系统的默认的三个函数库是/lib;/usr/lib;/usr/local/lib。如果我们使用的函数不在这默认库中,就要用到-L选项了。
例如:
#gcc –o mytest mytest.c –L /home/zsb/lib
以上仅仅是一些常用的选项含义,其他一些选项,如内核模块编译时用到的GCC选项,我们将在以后的章节用到它们时再详细介绍。
发布于Monday, August 22, 2005 1:55 PM 由zsb1107 |2 篇评论
《基于Linux的C编程与内核导读》连载(1)
本想把自己半年来编写的这本书放在CSDN上,不过为了给咱们的OL增添些人气,决定放在OL。今天贴上此书的第一部分:前言。 前言
本来没想把平时的一些学习笔记整理成一本书,不过前日一位老友看过原稿后坚持让要我整理一下,说这样一些精彩的想法应该整理出来让大家一起共享,于是也就有了这本书。
“学而不思则惘,思而不学则怠”两千多年前孔老夫子的话,让我有了深刻的体会。从下决心学习linux开始,我经历了好多的挫折,也因此有了一些教训和心得。刚开始时见一位学长在苦读linux内核,被其雄心壮志和不怕牺牲的精神所深深感染,遂跟其研读linux内核。但是在读的过程中却发现自己在本科时学的那点操作系统知识根本不够,所以又回头重学OS的理论原理。然而人生的转折总发生在惨痛失败后的反思,自己在研读内核一年后,自觉水平可以,于朋友面前也常常自夸。不过一日,一外系老兄闻名前来求助,让我帮其编写一个设备驱动。自己闭屋三日,写出来东西却错误百出,得到的是对自己深深地失望。痛定思痛后决定不能再纸上谈兵,“绝知此事要躬行”要利用自己的知识多应用,多编一些应用程序。
本书的思路也就是我反思的结果,学习任何一个系统都首先要有坚实的理论知识,随后在研读内核的基础上一定要多应用。光看内核不编程,日久便会迷茫;光编程不看内核,就不能掌握系统的实质。
本书分为八章,前两章是C编程和研读内核的基础知识。中间五章分别讲了进程管理、存储管理、文件系统、设备管理和网络编程,其中每一章都分为基础知识、内核导读和C语言编程三节。最后一章是一些有用的专题总结,包括shell程序设计、内核模块编程等。
另外要特别声明,本书是自己的学习笔记整理。既然是学习笔记,肯定借鉴了许多别人书上的成果和例子,在此向他们表示感谢。本书不用于商业出版,仅限于各位linux爱好者学习使用。
发布于Sunday, August 21, 2005 11:26 AM 由zsb1107 |6 篇评论
郁闷
孔老二说:知者过之,愚者不及,道之不明也。
看来我是愚者了,要不这个程序老是调不通啊!
发布于Sunday, August 21, 2005 10:23 AM 由zsb1107 |0 篇评论
Hard to believe! A top genius
Can you image a single person could bring forward the concept of computer and build a series of computers? But Konrad Zuse did it !
This German who lived form 1910 to 1995 built a series of  computers from Z1 to Z4 by himself. At that time he even didn‘t know the thought of Charles Babage who brought forward the concept of computer firstly in 1830s.
Especially Z3 which built in 1941 had all main characters of modern computer like memory and programme. It ran slower than ENIAC, but faster than MARK 1.  But Howard Hathaway Aiken built MARK  with the help of IBM and American Navy.  Eckert and Mauchly had a large team to build ENIAC.  Zuse was alone but more greatly!
I am really moved by the wisdom of our human being after reading Zuse‘s legend this evening.
发布于Saturday, August 20, 2005 9:50 PM 由zsb1107 |0 篇评论
Beauty
The red rose which my girl friend took back from the Kun Ming flower market. I do love her.
发布于Friday, August 19, 2005 3:27 PM 由zsb1107 |0 篇评论
My lilo runs correctly while my kernel has some troubles. ft!
These days I find my linux run a little slowly when some big processes running behind. My friend suggest me to download a new kernel because they think my problem can be solved by kernel-2.6. But I have another idea. Make some changes on the source codes by myself. I don’t think the schedule algorithm used in kernel-2.4.20 are proper for me, I have my own algorithm.
This morning I decided to write a new Lila to test my own kernel. I add some lines in lilo.conf like this:
Image=/boot/bzImage
Label=my_own_linux
Read_only
Root=/dev/hda5
Then type a command #/sbin/lilo –v to save my changes. Reboot my computer and three choices appeared in the screen: linux, my_own_linux and Dos. Of course I select the second, but unfortunately the system didn’t run correctly.
It was still running out the lab and another boy was watching chao ji nv sheng. So I came to watch with him let along the doggone kernel. I am not a YU MI, but to tell the truth that little girl is really handsome.
This morning is not frustrated totally because my lilo runs correctly at least !
_xyz