Linux应用程序开发 基础知识Word下载.docx
《Linux应用程序开发 基础知识Word下载.docx》由会员分享,可在线阅读,更多相关《Linux应用程序开发 基础知识Word下载.docx(81页珍藏版)》请在冰豆网上搜索。
15.1.设备号
15.2.设备号的分配和释放
15.3.重要的数据结构
15.4.读和写
16.PCI设备
17.内核初始化优化宏
18.访问内核参数的接口
19.内核初始化选项
20.内核模块编程
20.1.入门
20.2.为模块添加描述信息
20.3.内核模块处理命令介绍
21.网络子系统
21.1.sk_buff结构
21.2.sk_buff结构操作函数
21.3.net_device结构
21.4.网络设备初始化
21.5.网络设备与内核的沟通方式
21.6.网络设备操作层的初始化
21.7.内核模块加载器
21.8.虚拟设备
21.9.8139too.c源码分析
21.10.内核网络数据流
22.备忘录
ListofTables
1.1.特殊字符的表示方法
Chapter
1.
C语言基础
TableofContents
Linux是使用C语言开发的,基于Linux平台的应用程序开发,C语言是首选的开发语言。
本章记录C语言的基本概念和基础知识。
1.1.
数据类型
整数类型(int),
各种整数数制表示法:
∙ddd,十进制表示法,d为0--9的整数,但不能以0开头。
如:
123,345。
∙0ooo,八进制表示法,以0(数字0)开头,o为0--7的整数。
010(八进制)=8(十进制),014(八进制)=12(十进制)。
∙0xhhh,十六进制表示法,以0x或0X开头,h为0--9、A、B、C、D、E、F。
0x10(十六进制)=16(十进制),0xA(十六进制)=10(十进制)。
∙以L或l结尾的数表示长整数(longint),编译器会以32位空间存放此数字,但GCC默认是以32位存放整数,所以此表示法在Linux下没什么作用。
1.2.
关键字
关键字是C语言本身保留使用的,不能用于变量和函数名。
autodoubleintstruct
breakelselongswitch
caseenumregistertypedef
charexternreturnunion
constfloatshortunsigned
continueforsignedvoid
defaultgotosizeofvolatile
doifstaticwhile
1.3.
变量等级
∙auto,内部变量,在函数内部声明。
只能在函数内部使用,它的生命周期从调用函数开始,到函数执行完时消失。
内部变量以堆栈存放,必须在函数执行时才会存在,这种方式称为声明。
auto可省略。
autointi=0;
/*可写成inti=0;
*/
内部变量的优缺点:
o内部变量只在函数内有效,能提高函数的安全。
o内部变量在函数结束时消失,不会长期占用内存空间,能提高内存的利用率。
o内部变量的缺点是生命周期短,函数运行结束后不能保留。
∙staticauto,内部静态变量,在函数内部定义,auto也可省略。
内部静态变量以固定地址存放,编译时就已分配置内在空间,这种方式称为定义。
由于有固定地址,函静态变量不会随函数的结束而消失。
static变量会一直保存在内存空间中,当函数再次执行时,上次保留的使用静态变量可以继续使用。
staticinti=0;
∙extern,外部变量,是在函数外定义的变量,可被多个函数存取。
在外部变量定义覆盖范围之内的函数内可以自由使用外部变量。
不在外部变量定义覆盖范围之内的函数要使用外部变量就要先使用extern关健字来声明外部变量。
inti;
/*外部变量定义,在main函数外*/
intmain(void)
{
i=1;
/*main()函数位于外部变量i定义的下面,不用声明可直接使用*/
printf("
%d\n"
i);
}
externinti;
/*外部变量i在main()函数之后定义,需用extern关键字声明后才能使用*/
i);
...
在另外的程序文件中我们也可以通过扩展声明使用其它程序文件中的外部变量。
程序1hello.c
#include<
stdio.h>
externinti;
//扩展声明外部变量
i=333;
printf("
externdes(void);
//扩展声明外部函数
des();
//外部变量定义
程序2hello1.c
//扩展声明其它程序文件中的外部变量
voiddes()
i++;
}
编译
debian:
~/c#gcchello.chello1.c
~/c#./a.out
333
334
外部变量有效范围总结:
o由外部变量定义的位置开始,至文件结尾。
o不在有效范围内的函数,也可通过extern扩展声明使用定义的外部变量,且可在多个函数中使用。
注意:
在各函数中使用的外部变量是一样的,对该变量的修改会影响到其它函数内的同一变量。
o可用extern扩展声明使用另外一个程序文件中的外部变量。
外部变量的优点是生命周期长,可在函数间共享数据和传输数据。
缺点是变量安全性较低,但可通过合理设置外部变量的有效范围提高安全性。
∙staticextern,外部静态变量,在函数外部定义,只供单一程序文件使用,即使其它程序文件定义了同样名称的变量,编译器也把它当成另外一个变量处理。
外部静态变量能有效隔离变量在一个程序文件中。
staticinti;
∙register,register变量是以寄存器(register)来存放变量,而不是一般内存。
只有内部变量才能使用register类型变量。
使用这种变量能加快变量的处理速度。
但缺点是要占用CPU寄存器。
registerinti;
registerintj;
变量等级的概念也同样适用于函数。
若想调用不在有效范围内的函数,则要用extern扩展声明函数的有效范围。
内部变量是以堆栈方式存放的,必须在函数执行时才会存在,所以称为声明(Declaration)。
其它如staticauto、extern和staticextern等级的变量,都是以固定的地址来存放的,而不是以堆栈方式存放的,在程序编译时就已分配了空间,所以称之为定义(Definition)。
1.4.
特殊字符的表示方法:
Table
特殊字符的表示方法
符号
ASCII字符(十六进制)
句柄符号
作用
\a
07
BEL
响铃
\b
08
BS
回格
\f
0C
FF
换页
\n
0A
LF
换行
\r
0D
CR
回车键
\t
09
HT
[tab]键
\v
0B
VT
空行
\0
00
NUL
空字符
\\
5C
\
反斜杠
\'
2C
'
单引号
\"
22
"
双引号
\?
3F
?
问号
1.5.
格式化字符串
∙%c,表示字符变量。
∙%s,表示字符串变量。
∙%f,表示浮点数变量。
∙%d,表示整数变量。
∙%x,表示十六进制变量。
∙%o,表示八进制变量。
1.6.
指针与数组
∙C语言中专门用来存放内存地址的变量叫指针(pointer)变量,简称指针。
∙&
运算符用来取得变量地址,
∙"
*"
运算符用来取得指针变量的值。
∙数组名就是地址变量,指向内存中存放第一个数组元素的地址。
数组元素编号从0开始,如a[0]表示数组a的第一个元素。
数组是内存中的连续区间,可根据声明类型存放多种数值类型。
inta[10];
声明一个有10个int元素的数组
charb[20];
声明一个有20个char元素的数组
指针示例:
int*p;
/*p是一个指针,p的内容是内存的地址,在这个地址中将存放一个整数。
数组名和指针都是用来存放内存地址的,不过数组名具有固定长度,不可变。
而指针与一般变量一样,其值是可变的。
1.7.
结构体
结构体是用户定义的由基本数据类型组成的复合式数据类型。
数组也是复合式数据类型,但二者是不同的,数组是相同类型数据的集合,而结构体是不同类型数据的集合。
如我们可以把一个人的姓名、性别,年龄组成一个单一结构体。
这样在程序处理时就把它当成一个独立对象进行处理。
结构体声明方法有两种,一种是分离式声明,一种是结合式声明。
分离式声明是先把声明结构体,在程序中再声明结构体变量。
结合式声明是把结构体声明和变量声明同时完成。
分离式声明示例
structperson{
charname;
charsex;
intage;
};
main(void){
structpersonworker;
结合式声明示例
}worker;
每个结构体可以表示一个工人的信息,如果要表示多个工人的信息,则可以用结构体数组。
structpersonworker[20];
//表示20个工人
结构体初始设置。
}worker={"
jims"
"
male"
30};
用"
."
和"
->
运算符存取结构体中的数据。
是直接存取法,"
为间接存取法,用于结构体指针。
如果p是一个指向person结构体的指针,则p->
name和(*p).name的结果是一样的。
1.8.
typedef--自定义类型名
结构体可以自定义数据类型,而typedef可以自定义新的类型名。
typedefchar*STRING;
//定义一个新的字符指针类型名STRING
STRINGa;
a="
abc"
;
theavalueis%s.\n"
a);
a为字符指针类型,自定义类型名通常以大写方式表示,以示区别。
#define与typedef的区别是:
#define只是单纯地进行变量替换,而typedef是创建新的类型名。
typedef的一个主要作用是简化声明,提高程序的可读性。
typedefstructperson{
}p
这样我们就定义一个新的结构体类型名p,在程序中我们可以使用它来声明变量。
如
pworker;
worker={"
1.9.
函数和宏
函数是C代码的集合,每个C程序由一个或多个函数组成,main()是一个特殊的函数,是C程序的入口,每个C程序必须有且只能有一个mian()函数。
ANSI函数定义:
类型函数名(类型参数1,类型参数2,...)
函数代码;
示例:
intfunc(inti,charc)
...
在程序中要使用我们设计开发的函数,需要先进行声明,函数声明的作用是把函数类型告诉编译器。
函数声明与定义差不多,只是不包括程序主体。
上面示例的函数在主程序中的声明方式如下:
voidmain()
inttotal;
intfunc(inti,charc);
//函数声明
total=int(xxx,xxx);
//声明后才能调用该函数
定义和声明中的参数类型(int,char)要相同,但名称(i,c)可以不同。
当函数没有返回值时,需声定义成void类型,调用者也要做void声明。
一般我们把函数的声明放在一个统一的文件中,这个文件叫头文件。
在程序中用#include命令把头文件包含进来。
在程序中调用函数前就不用再进行函数声明了。
头文件简化了函数声明的管理并使头文件可被多个程序重复使用。
大大提高C程序的开发效率。
例如:
我们最常使用的printf()函数,在使用前我们不需每次都进行声明操作,直接使用就可以啦。
但前提是我们要把stdio.h头文件包含进来。
printf()函数声明在stdio.h文件中已进行了声明。
1.10.
ANSI标准头文件
Linux系统头文件位于/usr/include中。
默认情况下编译器只在该目录下搜索头文件。
∙assert.h,定义assert宏,可用来检查程序错误。
∙ctype.h,
∙errno.h
∙float.h
∙limits.h
∙locale.h
∙math.h
∙setjmp.h
∙signal.h
∙stdarg.h
∙stddef.h
∙stdio.h
∙stdlib.h
∙string.h
∙time.h
2.
预处理
C语言在程序进行编译之前,会先将程序中以"
#"
标记的部份进行处理。
这种处理叫做预处理。
预处理主要的完成以下三个内容:
宏处理、头文件和条件式编译。
∙宏处理指令语法如下:
#define宏名字符串
#defineMAX200
宏指令语句尾不用加分号(;
)
宏定义可以用#undef命令取消,我们可以用该功能进行程序调试。
∙头文件处理是把头文件中的函数声明插入程序中。
∙条件式编译,编译器可根据条件式编译语句有选择地进代码块进行编译。
选择式编译指令如下:
#if表达式如果表示式结果不为0,则编译下面的程序
#ifdef宏名若宏名已被定义,则编译下面的程序
#ifndef宏名若宏名未定义,则编译下面的程序
#else前面条件不成立时,则编译下面的程序
#endif结束上列各种条件式编译
3.
使用GCC编译程序
直接生成a.out可执行文件
~/c#gcchello.c
编译hello.c程序,生成hello可执行文件:
~/c#gcc-ohellohello.c
生成.s的汇编代码文件。
~/c#gcc-Shello.c
4.
使用gdb调试程序
如果想利用gdb工具来调试程序,在编译程序时要使用-g选项。
~/c#gcc-gserial.c-oserial
调试serial程序。
~/c#gdbserial
GNUgdb6.5-debian
Copyright(C)2006FreeSoftwareFoundation,Inc.
GDBisfreesoftware,coveredbytheGNUGeneralPublicLicense,andyouare
welcometochangeitand/ordistributecopiesofitundercertainconditions.
Type"
showcopying"
toseetheconditions.
ThereisabsolutelynowarrantyforGDB.Type"
showwarranty"
fordetails.
ThisGDBwasconfiguredas"
i486-linux-gnu"
...Usinghostlibthread_dblibrary"
/lib/tls/libthread_db.so.1"
.
(gdb)list
8#include<
errno.h>
/*错误号定义*/
9
10intmain(void)
11{
12intfd,n,status,buffsize;
13structtermiosa;
14structtermios*oldtio;
15charm[255],*comm;
16
17fd=open("
/dev/ttyS0"
O_RDWR|O_NOCTTY|O_NDELAY);
(gdb)
gdb的list命令是列出程序源码。
下面介绍gdb下的各种操作。
∙list,列出程序源代码,一次只列出10行的内容。
list命令可以指定范围。
list5,10可列出第5行到第10行的内容。
∙run,执行程序。
按Ctrl+c可中断程序的执行。
∙shell,暂时退出gdb回到shell环境。
在shell环境用exit命令可以返回gdb。
∙break,设置断点,后跟行号则把断点设置在指定的行号,后跟函数名则把断点设置在函数。
如break6,breakfunction。
还可根据条件设置断点,如:
break9ifresult>
50。
这条命令的意思是,当运行到第9行时,如果result变量的值大于50,则中断程序。
(gdb)break6
Breakpoint1at0x8048634:
fileserial.c,line6.
∙watch,指定条件,如果成立则中断。
watchresult>
50。
当result的变量大于50时,马上中断程序。
∙print,打印变量值,如:
printresult。
∙whatis,查看变量类型,如:
whatisresult。
∙continue,从中断点继续运行程序。
∙step,从中断点开始单步运行,如果遇到函数,则进入函数单步运行。
∙next,从中断点开始单步运行,如果遇到函数,则运行函数,该命令不会进入函数单步运行,而是运行整个函数。
∙infobreakpoints,查看程序中所设置的所有中断点信息。
(gdb)infobreakpoints
NumTypeDispEnbAddressWhat
1breakpointkeepy0x08048634inmainatserial.c:
6
Enb字段是"
y"
,表示断点1现正生效。
∙disable/enable,控制中断点失效和启用。
disable1。
如果disable/enable命令后没有指定断点号,则该命令作用于所有已设置的断点。
(gdb)disable1
1breakpointkeepn0x08048634inmainatserial.c:
Enb字段由"
变成"
n"
,断点1暂时被禁止。
∙enableonce,使断点生效一次。
∙delete,删除断点。
delete1。
delete要指定断点号。
∙clear,删除断点。
clear6。
clear要指定设置断点的行号或函数名。
∙helpall,显示所有gdb环境的命令。
在gdb环境下,按tab键可自动补全命令。
直接按回车键可重复执行上一个操作。
按上下光标键可显示历史命令。
5.
Linux程序开发基础
5.1.
路径
在设置Linux的系统路径时,使用冒号分隔每个路径名。
如:
PATH="
/usr/local/sbin:
/usr/local/bin:
/usr/sbin:
/usr/bin:
/sbin:
/bin:
/usr/bin/X11"
在Linux中的程序有两种,一种是可执行程序,与Windows下的.exe文件类似,一种是脚本,与Windows下的.bat文件类似。
Linux中常用的程序存放路径有以下几个:
∙/bin,该路径存放系统启动时需要使用的程序。
∙/usr/bin,该路径存放用户需使用的标准程序。
∙/usr/local/bin,该路径存放本地安装的程序。
∙Linux使用斜杠"
/"
分隔路径名,而不是Windows的反斜杠"
。
∙Linux下的C编译器使用GCC,由于历史的原因,在POSIX兼容的操作系统中,C编译器都叫cc,所以Linux下也有一个cc命令,它是一个到gcc的软链接。
开发工具,多数位于/usr/bin或/usr/local/bin目录下。