linux-0.11调试向导

form : http://www.oldlinux.org/oldlinux/viewthread.php?tid=4352

这是我写的linux-0.11调试向导,其中有个文件
linux-0.11-debug.tar.gz是在赵博的linux-0.11-040327-rh9.tar.gz基础上修改的
,专门用来调试内核。

Linux 0.11调试向导
Harold Lee

Last Updated: 2005-11-2
一、介绍
赵炯的《Linux内核完全注释》对于想要学习内核的人来说,是本非常好的入门书。但是对于内核这样的系统软件,只是限于看书还远远不够,要想进一步提高,就必须得亲自调试内核。好在所需的工具都有了,现在就只剩下如何把这些工具组合起来,搭建一个调试系统,本文旨在帮助读者达到这个目地。
二、需要的东西
* 赵炯的《Linux内核完全注释》这本书是必需的,纸版或电子版的都可以。电子版可以到以下地址下载http://oldlinux.org/download/clk011c-1.9.5.pdf
oldlinux论坛也是个好地方,可以得到赵博的亲自指点,而且也有很多内核爱好者可以共同讨论。
http://www.oldlinux.org/cgi-bin/leoboard.cgi
* wxWindows-2.4.2
这是一个图形库,因为bochs对于X的支持不如对wxwindows的支持,为了能够顺利编译bochs,所以还是需要它。本文所使用的版本需为 2.4.2或以上。下载地址:

FXブログ 初心者でもわかるFX入門


* bochs-2.2:
一个能完全仿真x86的软件,本文所需版本为2.2或以上。
http://oldlinux.org/Linux.old/bochs/bochs-2.2/bochs-2.2.tar.gz
* linux-0.11的bochs image文件。
http://oldlinux.org/Linux.old/bochs/linux-0.11-devel-040923.zip
* 用于调试的linux-0.11
http://oldlinux.org/Linux.old/bochs/linux-0.11-debug.tar.gz
* Linux分发版本
基于你所在的平台,可以分为linux平台和windows平台,本文讨论 linux平台下的搭建。
linux平台先找一个目前流行的任何一个linux分发版本,记住把所有的开发包都装上。
三、编译
本节假设您都是在当前家目录下,解包,编译,安装。
(1)wxWindows
1. 解包
如果正常下载,应该会得到一个类似wxGTK-2.4.2.tar.bz2这样的包,通过下列命令解开它:
tar xvfj wxGTK-2.4.2.tar.bz2
在你的家目录下会有一个wxGTK-2.4.2的目录
2. 配置
进入刚解开的wxGTK-2.4.2目录,按照以下命令配置
./configure –prefix=/usr –with-gtk
3. 编译
接着在当前目录下运行这个命令:
make
4. 安装
首先切换到root用户下,如果你已经是root则不用切换,切换命令为:
su
输入你的root密码就可以切换到root用户下了。然后输入命令:
make install
你的wxWindows就安装好了。
(2)bochs
虽然bochs也提供已编译好的包,但是本文推荐自己编译。因为需要编译两个不同版本,后面会用到。编译bochs可能会遇到各种不同的问题,本文尽量把所有问题都交待清楚,以便您一次就能编译成功。
1. 解包
如果你按照正常下载,应该会获得一个名字为bochs-2.2.tar.gz的软件包,通过以下命令解开:
tar xvfz bochs-2.2.tar.gz
接着在你的家目录下就会有个bochs-2.2的目录了。
2. 配置
进入你刚才解开的bochs-2.2目录,在这个目录下,你会看到一个名叫configure的脚本文件,这个是unix世界软件包的标准配置文件。如何使用它,可以通过以下命令查看所有的配置选项:
./configure –help
这样你会得到一份详细的配置选项清单。本文打算编译两份不同的配置的版本。
第一个的版本为使用bochs自带的内部调试器。配置命令为
./configure –prefix=/opt/bochs/debug –with-wx –without-x –enable-plugins \
–enable-debugger –enable-disasm
配置注释:
* –prefix=/opt/bochs/debug
这个选项的意思是软件将被安装到哪个目录下
* –with-wx
这个选项的意思是使用wxWindows图形库,用作bochs的gui。
* –without-x
这个选项的意思是不要使用X图形库
* –enable-plugins
这个选项是必须要的
* –enable-debugger
这个选项是打开bochs的自带调试器
* –enbale-disasm
这个选项允许反汇编
3. 编译
接着在当前目录下运行命令:
make
4. 安装
首先切换到root用户下,如果你已经是root则不用切换,切换命令为:
su
输入你的root密码就可以切换到root用户下了。然后输入命令:
make install
这样你的bochs自带调试器的版本就编译好了。用以下命令作一个符号链接
ln -s /opt/bochs/debug/bin/bochs /usr/bin/bochsdbg
第二个版本为使用gdb-stub的版本
1. 清理
在配置第二个版本之前,需要把刚才编译的东西清理掉,输入以下命令:
make clean
2. 配置
然后配置,配置命令为
./configure –prefix=/opt/bochs/gdbstub –with-wx –without-x \
–enable-plugins –enable-disasm –enable-gdb-stub
配置注释:
* –enable-gdb-stub
这个选项打开gdb stub支持
* 其他选项同上,注意这个不同版本会安装到不同的目录下。

3. 编译
接着在当前目录下运行命令:
make
4. 安装
切换到root用户下,如果你已经是root则不用切换,切换命令为:
su
输入你的root密码就可以切换到root用户下了。然后输入命令:
make install
这样你的bochs使用外部调试器gdb的版本就编译好了。用以下命令作一个符号链接
ln -s /opt/bochs/gdbstub/bin/bochs /usr/bin/bochs
四、环境的搭建
(1). bochs的配置
使用bochs,还需要相应的image文件,linux-0.11-devel- 040923.zip这个包提供了我們需要的image文件,把这个包解开。会有一个linux-0.11-devel-040923目录产生,进入这个目录,因为里面的bochs配置文件是基于windows平台的,要想在linux平台用,还需要修改。输入以下命令:
find -name ‘*.bxrc’ | xargs sed -i -e ‘s@\(.*romimage.*\)\\\(.*\)@\1/\2@’ -e ‘s@^vgaromimage: \(.*\)@vgaromimage: file=\1@’
cp bochsrc-fdb.bxrc bochsrc-debug.bxrc
sed -i ‘s@\(^floppya: 1_44=\).*\(, status=inserted\)@\1″linux/Image”\2@’ bochsrc-debug.bxrc
cp bochsrc-fdb.bxrc bochsrc-gdbstub.bxrc
sed -i ‘s@\(^floppya: 1_44=\).*\(, status=inserted\)@\1″linux/Image”\2@’ bochsrc-gdbstub.bxrc
在bochsrc-gdbstub.bxrc中添加一行:
gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0

(2). linux-0.11调试版本
进入linux-0.11-devel-040923目录解开linux-0.11-debug.tar.gz,输入以下命令:
tar xvfz ../linux-0.11-debug.tar.gz
会有一个linux目录产生,这是一个经过修改可以在当前linux环境下编译,产生可调试的linux 0.11
内核。进入该linux目录,输入命令:
make
就开始编译内核了。
到此为止,所有的搭建工作都已做完了,剩下的就是开始调试了。
五、调试内核
调试工作主要在linux-0.11-devel-040923这个目录下进行,有两种调试方法:
第一种:使用bochs自带的调试器调试,输入命令:
bochsdbg -f bochsrc-debug.bxrc
首先,选择Debug菜单中的Debug Console子菜单,然后选择Simulate菜单的Start子菜单。可以开始调试
第二种:使用外部调试器gdb调试,输入命令:
bochs -f bochsrc-gdbstub.bxrc
只需选择Simulate菜单的Start子菜单,它会提示
Waiting for gdb connection on localhost:1234
端口是基于你的bochs配置文件的设置,然后另开一个终端,进入linux目录下的debug目录,输入命令:
gdb system
进入gdb调试界面,首先设置断点,比如
(gdb) b main
Breakpoint 1 at 0x6631: file init/main.c, line 110
然后远程调试
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000fff0 in sys_mkdir (pathname=0x0, mode=0) at namei.c:496
496        inode->i_mtime = inode->i_atime = CURRENT_TIME;
让程序跑起来
(gdb) c
Continuing.
Breakpoint 1, main () at init/main.c:110
110         ROOT_DEV = ORIG_ROOT_DEV;
OK,现在已经进入调试状态了。
在实际调试当中,常常需要把这两种方法结合起来,能够达到很好的效果。因为第一种方法非常直观所有的寄存器一目了然,
但是却是全汇编而且都单步的,对于内核后期的c语言文件,这样一步步走效率太低,所以需要第二种方法快速找到c语言文件
函数或语句的地址,然后在第一种方法中在该地址设置断点,可以直接跳到该地址执行,这样效率就高很多了。

形象解释PID算法+PID算法源代码

来至:http://www.ourdev.cn/bbs/bbs_content_all.jsp?bbs_sn=5043342

小明接到这样一个任务:
有一个水缸点漏水(而且漏水的速度还不一定固定不变),
要求水面高度维持在某个位置,
一旦发现水面高度低于要求位置,就要往水缸里加水。

小明接到任务后就一直守在水缸旁边,
时间长就觉得无聊,就跑到房里看小说了,
每30分钟来检查一次水面高度。水漏得太快,
每次小明来检查时,水都快漏完了,离要求的高度相差很远
,小明改为每3分钟来检查一次,结果每次来水都没怎么漏
,不需要加水,来得太频繁做的是无用功。几次试验后,
确定每10分钟来检查一次。这个检查时间就称为采样周期。

开始小明用瓢加水,水龙头离水缸有十几米的距离,
经常要跑好几趟才加够水,于是小明又改为用桶加,
一加就是一桶,跑的次数少了,加水的速度也快了,
但好几次将缸给加溢出了,不小心弄湿了几次鞋,小明又动脑筋,
我不用瓢也不用桶,老子用盆,几次下来,发现刚刚好,不用跑太多次,
也不会让水溢出。这个加水工具的大小就称为比例系数。

小明又发现水虽然不会加过量溢出了,有时会高过要求位置比较多
,还是有打湿鞋的危险。他又想了个办法,在水缸上装一个漏斗,
每次加水不直接倒进水缸,而是倒进漏斗让它慢慢加。这样溢出的问题解决了,
但加水的速度又慢了,有时还赶不上漏水的速度。
于是他试着变换不同大小口径的漏斗来控制加水的速度
,最后终于找到了满意的漏斗。漏斗的时间就称为积分时间 。

小明终于喘了一口,但任务的要求突然严了,
水位控制的及时性要求大大提高,一旦水位过低,
必须立即将水加到要求位置,而且不能高出太多,否则不给工钱。
小明又为难了!于是他又开努脑筋,终于让它想到一个办法,常放一盆备用水在旁边,
一发现水位低了,不经过漏斗就是一盆水下去,这样及时性是保证了,但水位有时会高多了。
他又在要求水面位置上面一点将水凿一孔,再接一根管子到下面的备用桶里这样多出的水会从上面的孔里漏出来。
这个水漏出的快慢就称为微分时间。

 

大学时代做机器人时用的PID算法源代码:

#define PID_Uint struct pid_uint
PID_Uint
{
int U_kk;
int ekk;
int ekkk;
int Ur; //限幅输出值,需初始化
int Un; //不灵敏区
//int multiple; //PID系数的放大倍数,用整形数据的情况下,提高PID参数的设置精度   固定为256
int Kp; //比例,从小往大调
int Ti; //积分,从大往小调
int Td; //微分,用巡线板时设为0
int k1; //
int k2;
int k3;
};

/********************************************************************
函 数 名:void Init_PID_uint(PID_uint *p)
功    能:初始化PID参数
说    明:调用本函数之前,应该先对Kp,Ti,Td做设置 ,简化了公式
入口参数:PID单元的参数结构体 地址
返 回 值:无
***********************************************************************/
void Init_PID_uint(PID_Uint *p)
{
p->k1=(p->Kp)+(p->Kp)*1024/(p->Ti)+(p->Kp)*(p->Td)/1024;
p->k2=(p->Kp)+2*(p->Kp)*(p->Td)/1024;
p->k3=(p->Kp)*(p->Td)/1024;
}
/********************************************************************
函 数 名:void reset_Uk(PID_Uint *p)
功    能:初始化U_kk,ekk,ekkk
说    明:在初始化时调用,改变PID参数时有可能需要调用
入口参数:PID单元的参数结构体 地址
返 回 值:无
***********************************************************************/
void reset_Uk(PID_Uint *p)
{
p->U_kk=0;
p->ekk=0;
p->ekkk=0;
}
/********************************************************************
函 数 名:int PID_commen(int set,int jiance,PID_Uint *p)
功    能:通用PID函数
说    明:求任意单个PID的控制量
入口参数:期望值,实测值,PID单元结构体
返 回 值:PID控制量
***********************************************************************/
int PID_common(int set,int jiance,PID_Uint *p)
{
int ek,U_k=0;
ek=jiance-set;
if((ek>(p->Un))||(ek<-(p->Un))) //积分不灵敏区
U_k=(p->U_kk)+(p->k1)*ek-(p->k2)*(p->ekk)+(p->k3)*(p->ekkk);
p->U_kk=U_k;
p->ekkk=p->ekk;
p->ekk=ek;
if(U_k>(p->Ur))                         //限制最大输出量,
U_k=p->Ur;
if(U_k<-(p->Ur))
U_k=-(p->Ur);
return U_k/1024;
}

开始arduino之旅 使用加速度模块 ADXL345

模块: arduino nano + ADXL345

ADXL345模块上延伸出8个引脚

分别为:    GND   VCC   CS   INT1    INT2   SDO   SDA   SCL

电源电压范围:2.0 V至3.6 V    这里我们使用 nano 的3.3V电源引脚

引脚连接

arduino nano                                       ADXL345

GND                                                           GND

3V3                                                            VCC

A4                                                            SDA

A5                                                            SCL

A4,A5啥逻辑都还没搞清…  还是在网上找的一篇陀螺仪的连接方法平凑起来的.

 

连接好硬件部分, 就可以开始捣腾软件部分

在网上找了个例子,  自己仔细研究每个配置量的意义.
// I2C (sparkfun breakout)
#define Register_ID 0
#define Register_2D 0x2D
#define Register_X0 0x32
#define Register_X1 0x33
#define Register_Y0 0x34
#define Register_Y1 0x35
#define Register_Z0 0x36
#define Register_Z1 0x37


#include
int ADXAddress = 0xA7 >> 1; // the default 7-bit slave address
int reading = 0;
int val=0;
int X0,X1,X_out;
int Y0,Y1,Y_out;
int Z1,Z0,Z_out;
double Xg,Yg,Zg;


void setup()
{
Wire.begin();
Serial.begin(9600);
delay(100);
// enable to measute g data
Wire.beginTransmission(ADXAddress);
Wire.send(Register_2D);
Wire.send(8); //measuring enable
Wire.endTransmission(); // stop transmitting

}

char str[512];
void loop()
{
//--------------X
Wire.beginTransmission(ADXAddress); // transmit to device
Wire.send(Register_X0);
Wire.send(Register_X1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress,2);
if(Wire.available() {
X0 = Wire.receive();
X1 = Wire.receive();
X1=X1< X_out=X0+X1;
}

//——————Y
Wire.beginTransmission(ADXAddress); // transmit to device
Wire.send(Register_Y0);
Wire.send(Register_Y1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress,2);
if(Wire.available() {
Y0 = Wire.receive();
Y1 = Wire.receive();
Y1=Y1< Y_out=Y0+Y1;
}
//——————Z
Wire.beginTransmission(ADXAddress); // transmit to device
Wire.send(Register_Z0);
Wire.send(Register_Z1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress,2);
if(Wire.available() {
Z0 = Wire.receive();
Z1 = Wire.receive();
Z1=Z1< Z_out=Z0+Z1;
}
//----------------轉換成 G 值
Xg=X_out/256.0;
Yg=Y_out/256.0;
Zg=Z_out/256.0;

Serial.print(“X= “);
Serial.print(Xg);
Serial.print(” “);
Serial.print(“Y= “);
Serial.print(Yg);
Serial.print(” “);
Serial.print(“Z= “);
Serial.print(Zg);
Serial.println(” “);

/*sprintf(str, “%d,%d,%d”, Xg, Yg, Zg);
Serial.print(str);
Serial.print(10, BYTE);
Serial.print(13, BYTE);*/
delay(200);

}

 

ADXAddress  不知道是在那个文档中看到的, 大概的意思就是, 在该芯片使用I2C方式时, 如果芯片接口CS口为低电平时使用0xA7地址访问芯片, 然而nanoWire使用的是7比特地址, 所以将总线地址向右移一位

Register_…   这些都是ADXL芯片中的寄存器地址, 可以在文档中查看到每个地址对应存储的信息

总的回过头在看程序是相当的简单,

但也觉得有点陌生, 可能不是自己亲自写的原因, 也有可能是自己本身对代码就不够了解.

arduino的一些接口确实太不熟悉了. 要是能有个全面的中文文档该多好.