实验二 Linux编程基础 一、实验目的 熟悉Linux下使用C语言编程的方法,通过在编程中使用进程控制系统调用功能,直观理解Linux下进程并发执行情况。 二、实验题目 使用vi编辑C语言源程序;分析例子程序并通过man命令熟悉相关系统调用,编写进程并发执行的程序;使用gcc、make等工具进行编译;执行编译生成的执行文件并分析结果。 三、背景材料 因实验用的Linux环境以字符界面为主,而Linux的字符界面并没有广泛使用的集成开发环境,经典的C语言开发模式还是使用文本编辑软件编写源程序,然后使用gcc和make工具进行编译链接,生成可执行文件。在编辑源程序环节,可以使用不同的文本编辑工具,例如在图形用户界面下可以使用gedit或者kedit;字符界面下可以使用vi、emacs、nano等编辑器;甚至可以在其它环境下编辑,然后通过ftp上传。 (一)编辑器vi的使用 vi或vim是Linux系统上经典的文本编辑工具,在字符界面下,广泛用于系统管理、程序开发。本实验要求熟悉vi基本操作,可以使用vi进行文本编辑。 1、调用vi 最常用的命令模式是:vi filename 2、vi的三种模式 Command(命令)模式,用于输入命令; Insert(插入)模式,用于插入文本; Visual(可视)模式,用于可视化的的高亮并选定正文。 3、文件的保存和退出 Command模式是vi的默认模式,如果处于其它模式时,要通过ESC键切换过来,接着再输入:号时,vi会在屏幕的最下方等待输入命令; :w 保存; :w filename 另存为filename; :wq! 保存退出; :wq! filename 注:以filename为文件名保存后退出; :q! 不保存退出; :x 应该是保存并退出 ,功能和:wq!相同 4、光标移动; 按ESC进入Command模式后,可以用下面的一些键位来移动光标; j 向下移动一行; k 向上移动一行; h 向左移动一个字符; l 向右移动一个字符; ctrl+b 向上移动一屏; ctrl+f 向下移动一屏; 向上箭头 向上移动; 向下箭头 向下移动; 向左箭头 向左移动; 向右箭头 向右移动; 对于 j、k、l和h键,还能在这些命令的前面加上数字,比如 3j,表示向下移动3行。 5、进入插入模式 i 在光标之前插入; a 在光标之后插入; I 在光标所在行的行首插入; A 在光标所在行的行末插入; o 在光标所在的行的上面插入一行; O 在光标所在的行的下面插入一行; s 删除光标后的一个字符,然后进入插入模式; S 删除光标所在的行,然后进入插入模式; 6、文本内容的删除 x 删除一个字符; #x 删除几个字符,#表示数字,比如3x; dw 删除一个单词; #dw 删除几个单词,#用数字表示,比如3dw表示删除三个单词; dd 删除一行; #dd 删除多个行,#代表数字,比如3dd 表示删除光标行及光标的下两行; d$ 删除光标到行尾的内容; J 清除光标所处的行与上一行之间的空格,把光标行和上一行接在一起; 7、可视模式 在最新的Linux发行版本中,vi提供了可视模式。按ESC键,然后按v就进入可视模式; 退出可视模式,还是用ESC键。可视模式提供了友好的选取文本范围,以高亮显示。 8、复制和粘帖的操作 复制:对于可视模式下选中的文本,可以按y进行复制;按yy可以复制当前光标所在行。此外,删除也带有剪切的意思,当我们删除文字时,相应内容会自动复制; 粘贴:可以把光标移动到某处,然后按p或shift+p键进行粘贴,其中,p 在光标之后粘帖;shift+p 在光标之前粘帖。 9、行号 可以在命令模式输入 :set number 来打开行号显示,输入 :set number 来关闭行号显示。 10、查找功能 在命令模式下,输入/或?就可以进行查找; /SEARCH 注:正向查找,按n键把光标移动到下一个符合条件的地方; ?SEARCH 注:反向查找,按shift+n 键,把光标移动到下一个符合条件的 11、替换功能 按ESC键进入命令模式; :s /SEARCH/REPLACE/g 注:把当前光标所处的行中的SEARCH单词,替换成REPLACE,并把所有SEARCH高亮显示; :%s /SEARCH/REPLACE 注:把文档中所有SEARCH替换成REPLACE; :#,# s /SEARCH/REPLACE/g 注:#号表示数字,表示从多少行到多少行,把SEARCH替换成REPLACE; 注:命令中g表示全局查找,在指定的替换范围之外,也会把SEARCH高亮显示。 (二)gcc和make的使用 Linux下编辑好的C语言程序,需要使用gcc编译工具(Linux系统上一般也可以通过cc别名调用)进行编译连接,生成可执行文件。对于复杂的应用,可能把多个源程序分别编译,最后连接形成执行文件;为了有效管理这类项目,make代码维护工具可以用来定义一系列规则来管理编译连接过程。 【注】Linux下的可执行文件没有特别的后缀名约定,而是通过设置“可执行”属性来区分;另外,Linux默认的执行文件搜索路径也不包括当前目录,所以编译生成的执行文件需要显示指明文件路径来执行。 1、gcc的使用 (1) 简单使用方法 假设一个C语言源程序名为demo.c,根据该文件生成执行文件的最简单命令为: gcc hello.c 该命令将生成默认名字为a.out的执行文件;如果希望生成执行文件的名字不同,可以在命令中通过参数指定: gcc -o hello hello.c 该命令将生成名字为demo的执行文件。 (2) 分解使用 实际上,gcc所做的工作包括预编译、编译、连接等多个环节,上述简单用法隐含了对这几个环节的调用,如果需要分开调用,可以通过不同的命令行参数指定: gcc -E hello.c -o hello.i -E参数指定进行预编译,预处理的宏定义插入到hello.i中; gcc -c hello.i -o hello.o -c参数指定编译为目标代码 hello.o,也可以通过源文件直接生成(gcc -c hello.c); gcc hello.o -o hello 将生成可执行文件,如果由多个源程序文件生成,一般需要在生成时指定多个目标代码模块,例如:gcc hello.o e1.o s2.o -o hello (3) 常用参数 gcc命令行的常用参数如下: -c 通知GCC取消链接步骤,即编译源码并在最后生成目标文件; -Dmacro 定义指定的宏,使它能够通过源码中的#ifdef进行检验; -E 不经过编译预处理程序的输出而输送至标准输出; -g3 获得有关调试程序的详细信息,它不能与-o选项联合使用; -Idirectory 在包含文件搜索路径的起点处添加指定目录; -llibrary 提示链接程序在创建最终可执行文件时包含指定的库; -O、-O2、-O3 将优化状态打开,该选项不能与-g选项联合使用; -S 要求编译程序生成来自源代码的汇编程序输出; -v 启动所有警报; -Wall 在发生警报时取消编译操作,即将警报看作是错误; -Werror 在发生警报时取消编译操作,即把报警当作是错误; -w 禁止所有的报警。 2、make的使用 (1) make工具简介 make是一种代码维护工具,在大中型项目中,根据程序各个模块的更新情况,自动的维护和生成目标。能很好的做到“不多、不重、不漏”: • 不多:只更新那些需要更新的文件,而不动那些并不过时的东西; • 不重:是指当make对某个文件进行更新时,即使有很多文件过时,也将只更新一次; • 不漏:是指他不会漏下任何应该更新的文件。 make在使用时有一系列的规则,将根据这些规则来解释其配置脚本,以达到设计目的。配置脚本的缺省名是makefile或Makefile,也可在命令行指定其它文件名。使用形式为: make [option] [macrodef] [target] option指出make的工作行为,具体参数可参考man手册页。 (2) makefile文件编写 make工具使用的关键是编写高效、简洁、正确的makefile,这需要学习和实践。虽然make工具的使用有较高的门槛,但是一旦掌握,其带来的灵活和便捷远远超过一般IDE所能提供的管理功能。 实际makefile文件编写涉及的知识很多,有专著专门最次讲解。此处只是介绍概貌,借助一个简单的例子来说明:假设一个项目hello,依赖于hello.o、e1.o、e2.o,而hello.o依赖于hello.c、hello.h,e1.o依赖于e1.c、hello.h,e2.o依赖于e2.c;这样形成一棵依赖关系树,父节点依赖于子节点。使用make进行维护时,会对这棵树进行一次遍历,如果发现子节点形成的时间晚于父节点形成的时间便开始调用makefile中指定的命令进行维护。针对此例子的makefile示例如下: hello: hello.o e1.o e2.o gcc -o hello hello.o e1.o e2.o main.o: hello.c hello.h gcc -c hello.c e1.o: e1.c hello.h gcc -c e1.c hello.h e2.o: e2.c gcc -c e2.c makefile放置在与项目源程序文件相同目录中,在该目录中执行make hello或者make命令(命令行未target时会默认使用makefile的第一个target),将会根据makefile中定义的依赖关系依次执行编译、连接各个命令,生成hello执行文件。 实际项目的makefile因为会使用大量的宏、条件变量以及缺省规则,对初学者而言较难理解。对于一般的开源项目,在编写makefile时,target的命名都会遵循一些预定俗称的规则,常见的用法包括: make all 编译项目中所有实际目标 make install 进行安装 make clean 清除所有中间文件 (3) 缺省规则和简单用法 make的缺省规则是系统或者自定义的一些规则,在定义时不指出全名,而是根据文件扩展名定义一类依赖关系和相关命令。例如,与c语言相关的缺省规则包括: .c: $(CC) $(FLAGS) -o $@ $< .c .o: $(CC) $(FLAGS) -c $@ $< 两条规则的含义为:对于.c扩展名的文件,调用编译器(通过环境变量CC定义,默认为gcc)生成与.c文件对应名字的执行文件;对于.o、.c扩展名配对的情况,调用编译器进行编译,生成与.c文件对应名字的.o目标文件。 根据这些缺省规则,可以在简单情况下不编写makefile使用make工具。例如,对于根据hello.c生成hello执行文件的情况,可以直接使用命令 make hello 效果等同于:gcc -o hello hello.c (三)需要用到的系统调用 实验要求在例子程序基础上编程验证Linux下进程并发情况,需要用到的系统调用和库函数在下面列出,详细的使用方法说明通过“man 2 系统调用名”或者“man 3 函数名”命令获取。 fork() 创建一个子进程,通过返回值区分是在父进程还是子进程中执行; wait() 等待子进程执行完成; sleep() 导致调用进程睡眠指定秒数; getpid() 获取当前进程id; getppid() 获取父进程id。 四、实验内容 为便于掌握Linux环境下进行C语言编程的基本技能,本实验提供源程序例子,实验内容要在给出的例子程序基础上,根据要求进行修改,对执行结果进行分析。 1、子进程对存储空间的复制 (1) 使用文本编辑器输入源程序 输入如下源程序: #include
#include
#include
int main(void) { int x, i; printf("Input a initial value for i: "); scanf("%d", &i); while((x=fork())==-1); if(x==0) /* child run */ { printf("When child runs, i=%d/n", i); printf("Input a value in child: "); scanf("%d", &i); printf("i=%d/n", i); } else /* parent run */ { wait(); printf("After child runs, in parent, i=%d/n", i); } } (2) 预测程序的执行结果 阅读程序,根据自己的理解,预期程序执行后的结果。 (3) 实际执行结果分析 编译生成执行文件,执行后记录结果,说明与预期的结果是否一致。分析为什么是这样的结果。 2、父子进程执行过程分析 (1) 按照给定框架编程 按照如下程序框架,完成源程序编写: // ① pid=fork(); // ② if(pid==0) { sleep(3); printf("Child: pid=%d, ppid=%d/n", getpid(), getppid()); } else { printf("Parent: Child=%d, pid=%d, ppid=%d/n", pid, getpid(), getppid()); wait(); printf("After Child ends./n"); } printf("In which process?/n"); // ③ (2) 预测程序的执行结果 阅读程序,根据自己的理解,预期程序执行后的结果。 (3) 实际执行结果分析 编译生成执行文件,执行后记录结果,说明与预期的结果是否一致。分析为什么是这样的结果。 (4) 修改程序并分析执行结果 把程序框架中最后一句输出语句(位置③处)分别移至位置①和②处,预期输出结果是什么?实际执行结果如何?分析原因。 (5) 修改程序验证父子进程关系 修改程序,使父进程先执行完成,验证子进程是否会一起终止?如果不是,前后子进程的父进程号是否变化?记录并分析结果。 五、实验报告书写要求 应在实验报告中说明如下事项: 1、所使用的文本编辑器; 2、编译生成执行文件的命令; 3、相关程序的名称及存储位置,以便指导教师抽查; 4、实验内容1的结果:包括预期结果和实际执行结果,以及结果分析; 5、实验内容2的结果:包括预期结果和实际执行结果,对结果的分析;按照要求进行修改后的预期结果、实际结果及分析。
Linux编程基础
最新推荐文章于 2021-05-07 08:43:58 发布