嵌入式Linux编程环境
本章的要求 嵌入式Linux编程环境 熟悉Linux系统下的开发环境 熟悉vi的基本操作 熟悉gcc编译器的基本原理 熟练使用gdb调试技术 熟悉makefile基本原理及语法规范 熟悉makefile基本原理函数
本章的主要内容 1 Linux下C语言编程概述 2 常用编辑器 3 gcc编译器 4 gdb调试器 5 make工程管理器
一、Linux下C语言编程概述 C语言最早是由贝尔实验室的Dennis Ritchie为了 UNIX的辅助开发而编写的,它是在B语言的基础上 开发出来的。尽管C语言不是专门针对UNIX操作系 统或机器编写的,但它与UNIX系统的关系十分紧密。 由于它的硬件无关性和可移植性,使C语言逐渐成 为世界上使用最广泛计算机语言。 为了进一步规范C语言的硬件无关性,1987年,美 国国家标准协会(ANSI)根据C语言问世以来各种 版本对C语言的发展和扩充,制定了新的标准,称 为ANSI C。ANSI C语言比原来的标准C语言有了很 大的发展。目前流行的C语言编译系统都是以它为 基础的。
C语言 C语言的特点 C语言的成功并不是偶然的,它强大的功能和它的可移植性让它能在各 种硬件平台上游刃自如。总体而言,C语言有如下特点。 C语言可移植性强。C语言适合多种操作系统,如DOS、Windows、Linux,也适合多种体系结构,因此尤其适合在嵌入式领域的开发。
二、常用编辑器 Linux编辑器 行编辑器(ed、ex) 行编辑器每次只能对单行进行操作,使用起来很不 方便 全屏幕编辑器(vi、emacs) 全屏幕编辑器可以对整个屏幕进行编辑,用户编辑 的文件直接显示在屏幕上 vi是Linux系统的第一个全屏幕交互式编辑程序 6
Linux编辑器vi的使用 vi的三个模式 终端启动 一般模式 底行模式 编辑模式 (1)一般模式 Vi或vim 一般模式 :、/、? (1)一般模式 用户在用vi编辑文件时,最初进入的为一般模式。在该模式中可以通过上下移动光标进行“删除字符”或“整行删除”等操作,也可以进行“复制”、“粘贴”等操作,但无法编辑文字。 (2)编辑模式 只有在该模式下,用户才能进行文字编辑输入,用户课按[ESC]键回到命令行模式。 (3)命令模式 在该模式下,光标位于屏幕的底行。用户可以进行文件保存或退出操作,也可以设置编辑环境,如寻找字符串、列出行号等。 i、I、o、O、a、A ESC ESC 底行模式 编辑模式 7
vi:一般模式-编辑模式-底行模式 一般模式 编辑模式 命令模式
Linux编辑器vi的使用 vi的基本流程 (1)进入vi,即在一般模式下键入vi hello(文件名)。此时 进入的是命令行模式,光标位于屏幕的上方。 (2)在一般模式下键入i进入到编辑模式,如图3.2所示。可以 看出,在屏幕底部显示有“插入”表示插入模式,在该模式下 可以输入文字信息。 (3)最后,在插入模式中,输入“Esc”,则当前模式转入命 令行模式,并在底行行中输入“:wq”(存盘退出)进入底行模 式(命令模式),如图3.3所示。 这样,就完成了一个简单的vi操作流程: 命令行模式→插入模式→底行模式。 9
vi的各模式功能键 (1)命令行模式常见功能键如表: (2)插入模式的功能键只有一个, 也就是Esc退出到命令行模式。 (3)底行模式常见功能键如表所示 : 10
vi用法 --- 编辑 新增 (append) -- a 从 光 标 所 在 位 置 後 面 开 始 新 增 资 料, 光 标 後 的 资 料 随 新 增 资 料 向 後 移 动。 -- A 从 光 标 所 在 列 最 後 面 的 地 方 开 始 新 增 资 料。 插 入 (insert) -- i 从 光 标 所 在 位 置 前 面 开 始 插 入 资 料, 光 标 後 的 资 料 随 新 增 资 料 向 後 移 动。 -- I 从 光 标列 的 第 一 个 非 空 白 字 符 前 面 开 始 插 入 资 料。 开 始 (open) -- o 在 光 标 所 在 列 下 新 增 一 列 并 进 入 输 入 模 式。 -- O 在 光 标 所 在 列 上 方 新 增 一 列 并 进 入 输 入 模 式。
vi用法 --- 修改和删除 x 删除光标所在字符。 dd 删除光标所在的行。 r 修改光标所在字符,r後接著要修正的字符 R 进入取代状态,新增资料会覆改原先资料, 直到按[ESC]回到指令模式下为止。 s 删除光标所在字符,并进入输入模式。 S 删除光标所在的列,并进入输入模式。
vi用法 --- 退出 <:q>不保存退出 <:q!>不保存强制性退出 <:w>保存编辑 <:w filename>存入文件filename 中 <:w! filename>强制性存入文件filename 中 <:wq>(<:x>)保存并退出(shift+zz)
vi用法 --- 光标移动 <b>移动到当前单词的开始 <e>移动到当前单词的结尾 <w>向前移动一个单词 <h>向前移动一个字符 <j>向上移动一行 <k>向下移动一行 <l>向后移动一个字符
vi用法 --- 替换操作 <r>替换光标所在的字符 <R>替换字符序列 <cw>替换一个单词 <ce>同<cw> <cb>替换光标所在的前一字符 <c$>替换自光标位置至行尾的所有字符 <C>同<c$> <cc>替换当前行
vi用法 --- 复制与粘贴 </yw>将光标所在单词拷入剪贴板 <y$>将光标至行尾的字符拷入剪贴板 <Y>同<y$> <yy>将当前行拷入剪贴板 <p>将剪贴板中的内容粘贴在光标后 <P>将剪贴板中的内容粘贴在光标前
三、gcc编译器 GNU CC(简称为gcc)是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C++和 Object C等语言编写的程序。gcc不仅功能强 大,而且可以编译如C、C++、Object C、 Java、Fortran、Pascal、Modula-3和Ada等 多种语言,而且gcc又是一个交叉平台编译器, 它能够在当前CPU平台上为多种不同体系结构 的硬件平台开发软件,因此尤其适合在嵌入 式领域的开发编译。
gcc编译过程
gcc编译器 gcc编译流程解析 gcc的编译流程分为了4个步骤,分别为: 预处理(Pre-Processing); 编译器将上述代码中的stdio.h编译进来,并且用户可以使用gcc的选项 “-E”进行查看,该选项的作用是让gcc在预处理结束后停止编译过程。 编译(Compiling); gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要 做的工作,在检查无误后,gcc把代码翻译成汇编语言。用户可以使用“- S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。 汇编(Assembling); 汇编阶段是把编译阶段生成的“.s”文件转成目标文件可使用选项“-c” 就可看到汇编代码已转化为“.o”的二进制目标代码了。 链接(Linking)。 系统把这些函数(printf)实现都被做到名为libc.so.6的库文件中去了 ,在没有特别指定时,gcc会到系统默认的搜索路径“/usr/lib”下进行 查找,也就是链接到libc.so.6库函数中去,这样就能实现函数 “printf”了,而这也就是链接的作用。完成了链接之后,gcc就可以生 成可执行文件。 函数库一般分为静态库和动态库两种。静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”。动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的libc.so.6就是动态库。gcc在编译时默认使用动态库。 19
gcc --- 常用选项 [root@localhost gcc] gcc -g hello1.c -o hello1 [root@localhost gcc] gcc hello1.c –I /root/workplace/gcc/ -o hello1
gcc --- 预处理阶段 /*源码文件*/ #include“stido.h” int main() { 在该阶段,对包含的头文件(#include)和宏定义 (#define、#ifdef等)进行处理 。 /* hello.i */ …… typedef int (*__gconv_trans_fct) (struct __gconv_step *, struct __gconv_step_data *, void *, __const unsigned char *, __const unsigned char **, __const unsigned char *, unsigned char **, size_t *); # 2 "hello.c" 2 int main() { printf("Hello! This is our embedded world!\n"); return 0; } /*源码文件*/ #include“stido.h” int main() { printf("Hello! This is our embedded world!\n"); return 0; } [root@localhost gcc]# gcc –E hello.c –o hello.i www.embedu.org
gcc ---编译阶段(1) 接下来进行的是编译阶段,在这个阶段中, gcc首先要检查代码等,以确定代码的实际要 做的工的规范性、是否有语法错误作,在检 查无误后,gcc把代码翻译成汇编语言。用户 可以使用“-S”选项来进行查看,该选项只 进行编译而不进行汇编,生成汇编代码。 [root@localhost gcc]# gcc –S hello.i –o hello.s
gcc ---编译阶段(2) /* hello.s */ .file "hello.c" .section .rodata .align 4 addl $15, %eax shrl $4, %eax sall $4, %eax subl %eax, %esp subl $12, %esp pushl $.LC0 call puts addl $16, %esp movl $0, %eax leave ret .size main, .-main .ident "GCC: (GNU) 4.0.0 200X0Y19 (Red Hat 4.0.0-8)" .section .note.GNU-stack,"",@progbits /* hello.s */ .file "hello.c" .section .rodata .align 4 .LC0: .string "Hello! This is our embedded world!" .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $8, %esp andl $-16, %esp movl $0, %eax
gcc ---汇编阶段 把编译阶段生成的.s文件转换成目标文件, 选项“-c” [root@localhost gcc]# gcc –c hello.s –o hello.o
gcc ---链接阶段 动态链接 静态链接 [root@localhost gcc]# gcc hello.o –o hello
gcc --- 警告选项(1) $ gcc –ansi warning.c –o warning warning.c: 在函数“main”中: warning.c:7 警告:在无返回值的函数中,“return”带返回值 warning.c:4 警告:“main”的返回类型不是“int” www.embedu.org
$ gcc –pedantic warning.c –o warning warning.c: 在函数“main”中: warning.c:5 警告:ISO C90不支持“long long” warning.c:7 警告:在无返回值的函数中,“return” 带返回值 warning.c:4 警告:“main”的返回类型不是“int” $ gcc –Wall warning.c –o warning warning.c:4 警告:“main”的返回类型不是“int” warning.c: 在函数“main”中: warning.c:7 警告:在无返回值的函数中,“return”带返回值 warning.c:5 警告:未使用的变量“tmp” www.embedu.org
gcc --- 优化选项 gcc可以对代码进行优化,它通过编译选项“-On” 来控制优化代码的生成,其中n是一个代表优化级 别的整数。 不同的优化级别对应不同的优化处理工作。如使用 优化选项“-O”主要进行线程跳转(Thread Jump) 和延迟退栈(Deferred Stack Pops)两种优化。 使用优化选项“-O2”除了完成所有“-O1”级别的 优化之外,同时还要进行一些额外的调整工作,如 处理器指令调度等。选项“-O3”则还包括循环展 开和其他一些与处理器特性相关的优化工作。 可能适合使用优化选项的场合:程序发行 不太适合使用优化选项的场合:程序开发
gcc --- 体系结构相关选项 www.embedu.org
四Gdb调试器gdb使用方法(1) gdb调试器是一款GNU开发组织并发布的UNIX/Linux 下的程序调试工具。虽然,它没有图形化的友好界 面,但是它强大的功能也足以与微软的VC工具等媲 美。 $ gcc -g test.c -o test $ gdb test GNU gdb Red Hat Linux (6.3.0.0-1.21rh) Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1". (gdb)
gdb调试器 gdb使用流程 首先,打开Linux下的编辑器vi,编辑如下代码 # vi test.c 在保存退出后首先使用gcc对test.c进行编译,注意一定 要加上选项“-g”,这样编译出的可执行代码中才包含调 试信息,否则之后gdb无法载入该可执行文件 # gcc -g test.c -o 接下来就启动gdb进行调试。注意,gdb进行调试的是可执 行文件,而不是如“.c”的源代码,因此,需要先通过 gcc编译生成可执行文件才能用gdb进行调试 #gdb test
(1)查看文件,在gdb中键入“l”(list)就可以查看所载入的文 件 可以看出,在gdb的启动画面中指出了gdb的版本号、使用的库文件等信息,接 下来就进入了由“(gdb)”开头的命令行界面了 (1)查看文件,在gdb中键入“l”(list)就可以查看所载入的文 件 (2)设置断点 ,在gdb中设置断点非常简单,只需在“b”后加入 对应的行号即可 (3)查看断点情况,在设置完断点之后,用户可以键入“info b” 来查看设置断点情况,在gdb中可以设置多个断点 (4)运行代码,gdb默认从首行开始运行代码,可键入“r”(run )即可 (5)查看变量值,在gdb中只需键入“p”+变量值即可 (6)单步运行,单步运行可以使用命令“n”(next)或“s”( step) (7)恢复程序运行,可以使用命令“c”(continue)恢复程序的 正常运行了
gdb使用方法(2) (gdb) l 1 #include <stdio.h> 2 int sum(int m); 3 int main() 4 { 5 int i,n = 0; 6 sum(50); 7 for(i = 1; i <= 50; i++) 8 { 9 n += i; 10 } 11 printf("The sum of 1~50 is %d \n", n ); 12 13 } 14 int sum(int m) 15 { 16 int i, n = 0; 17 for(i = 1; i <= m; i++) 18 { 19 n += i; 20 } 21 printf("The sum of 1~m is = %d\n", n); 20 } (gdb) b 6 Breakpoint 1 at 0x804846d: file test.c, line 6. (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x0804846d in main at test.c:6 (gdb) b 19 (gdb) c Breakpoin 2, sum(m=50) at test.c:19 19 printf(“The sum of 1-m is %d\n”, n); (gdb) bt #0 sum(m=50) at test.c:19 #1 0x080483e8 in main() at test.c:6 (gdb) r Starting program: /root/workplace/gdb/test Reading symbols from shared object read from target memory...done. Loaded system supplied DSO at 0x5fb000 Breakpoint 1, main () at test.c:6 6 sum(50);
gdb使用方法(3) (gdb) help (gdb) p n List of classes of commands: $1 = 0 (gdb) p i $2 = 134518440 (gdb) help List of classes of commands: aliases -- Aliases of other commands breakpoints -- Making program stop at certain points data -- Examining data files -- Specifying and examining files internals -- Maintenance commands … Type "help" followed by a class name for a list of commands in that class. Type "help" followed by command name for full documentation. Command name abbreviations are allowed if unambiguous. (gdb) n The sum of 1-m is 1275 7 for (i = 1; i <= 50; i++) (gdb) s sum (m=50) at test.c:16 16 int i, n = 0; (gdb) c Continuing. The sum of 1-50 is :1275 Program exited with code 031. (gdb) help call Call a function in the program. The argument is the function name and arguments, in the notation of the current working language. The result is printed and saved in the value history, if it is not void.
gdb工作环境相关命令
gdb设置断点与恢复命令
gdb中源码查看相关相关命令
gdb中查看运行数据的相关命令
make工程管理器
make工程管理器 工程管理器,顾名思义,是指管理较多的文 件 Make工程管理器也就是个“自动编译管理 器”,这里的“自动”是指它能构根据文件 时间戳自动发现更新过的文件而减少编译的 工作量,同时,它通过读入Makefile文件的 内容来执行大量的编译工作
makefile基本结构(1) makefile是make读入的惟一配置文件,因此 本节的内容实际就是讲述makefile的编写规 则。在一个makefile中通常包含如下内容: 需要由make工具创建的目标体(target),通常是 目标文件或可执行文件; 要创建的目标体所依赖的文件(dependency_file); 创建每个目标体时需要运行的命令(command),这 一行必须以制表符(tab键)开头。
makefile基本结构(2) makefile格式 Makefile规范 Makefile的使用 target: dependency_files < TAB >command /* 该行必须以tab键开头*/ Makefile规范 hello.o: hello.c hello.h gcc –c hello.c –o hello.o Makefile的使用 $ make hello.o gcc –c hello.c –o hello.o $ ls hello.c hello.h hello.o makefile
创建和使用makefile变量 用来代替一个文本字符串 变量定义的两种方式 变量使用$(VAR) 递归展开方式VAR=var OBJS = kang.o yul.o CC = gcc CFLAGS = -Wall -O -g david : kang.o yul.o gcc kang.o yul.o -o david kang.o : kang.c kang.h gcc -Wall -O –g -c kang.c -o kang.o yul.o : yul.c yul.h gcc -Wall -O –g -c yul.c -o yul.o OBJS = kang.o yul.o CC = gcc CFLAGS = -Wall -O -g david : $(OBJS) $(CC) $(OBJS) -o david kang.o : kang.c kang.h $(CC) $(CFLAGS) -c kang.c -o kang.o yul.o : yul.c yul.h $(CC) $(CFLAGS) -c yul.c -o yul.o
makefile变量 变量种类 用户自定义变量 预定义变量 自动变量 环境变量 OBJS = kang.o yul.o CC = gcc CFLAGS = -Wall -O -g david : $(OBJS) $(CC) $^ -o $@ kang.o : kang.c kang.h $(CC) $(CFLAGS) -c $< -o $@ yul.o : yul.c yul.h
makefile中常用的预定义变量 www.embedu.org
makefile中常见的自动变量 和环境变量 -Wall -O -g -Wall -O -g make在启动时会自动读取系统当前已经定义了的环境 变量,并且会创建与之具有相同名称和数值的变量 如果用户在makefile中定义了相同名称的变量,那么 用户自定义变量将会覆盖同名的环境变量 www.embedu.org
Makefile中的假目标 目标文件与目录下文件名重名冲突 在当前make工作目录下,假如出现与目标文件同名 的文件,比如clean,即: #ls Clean xx.x makefile # make clean Make : ‘clean’is up to date 解决方案-----假目标(Phony Target) 在makefile文件开头加入.PHONY声明该目标,make此 时不会把clean文件当做一个文件处理,而是作为概念 上的一个目标