🪐1 初识Linux OS
Linux是一种开源的Unix-like操作系统内核,它是基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的操作系统。Linux内核最初是由芬兰程序员Linus Torvalds在1991年创建的,之后成为自由软件和开源社区的一个主要项目。
以下是Linux操作系统的一些主要特点和组成部分:
- 内核(Kernel): Linux内核是操作系统的核心,它管理系统资源,如处理器、内存、设备驱动程序等。Linus Torvalds最初编写了Linux内核,它是Linux操作系统的基础。
- Shell(命令解释器): Linux操作系统使用命令行界面(CLI),用户与系统交互通过Shell。用户可以通过输入命令执行各种任务,管理文件、进程、权限等。常见的Shell包括Bash(Bourne Again SHell)和Zsh。
- 文件系统: Linux支持多种文件系统,包括Ext4、XFS、Btrfs等。文件系统负责管理存储设备上的文件和目录,以及文件的权限、所有权等信息。
- 用户界面: 虽然Linux以CLI为主,但也支持图形用户界面(GUI)。常见的Linux桌面环境有GNOME、KDE、XFCE等,它们提供了直观的用户体验。
- 软件包管理系统: 大多数Linux发行版都有自己的软件包管理系统,用于安装、更新和删除软件。常见的包管理工具有APT(Debian/Ubuntu)、YUM(Red Hat/CentOS)、Pacman(Arch Linux)等。
- 网络功能: Linux具有强大的网络功能,支持各种网络协议和服务。它可以作为服务器运行各种网络服务,如Web服务器(Apache、Nginx)、邮件服务器(Postfix、Sendmail)、文件服务器(Samba)等。
- 多用户和多任务: Linux是一个多用户系统,多个用户可以同时访问同一台机器。它也是一个多任务系统,可以同时运行多个进程。
- 安全性: Linux以安全性而闻名,系统管理员可以设置用户权限、访问控制列表(ACLs)等来确保系统的安全性。
- 开源和自由: Linux是开源软件,其源代码可以被任何人查看、修改和分发。大多数Linux发行版是免费提供的,用户可以自由选择、使用和分发。
- 发行版(Distribution): 由于Linux内核是开源的,不同的组织和社区创建了各种Linux发行版,例如Ubuntu、Debian、Fedora、CentOS、Arch Linux等。每个发行版都有自己的特点和包管理系统。
总体而言,Linux操作系统是一个强大、灵活且可定制的操作系统,广泛应用于服务器、嵌入式系统、超级计算机等各种领域。
🪐2 Linux开发工具的解密与实战
🌍1. 实验目的
- 掌握gcc编译方法与使用;
- 掌握Linux调试器GDB的使用;
- 实践编译与调试技能,提升对软件开发环境的熟练度。
🌍2. 实验准备
- 获取 Linux 镜 像文件:下载适用于虚拟机的 Linux 镜像文件,比如 Ubuntu 21.04,从 Ubuntu 官方网站 获取。
- 安装虚拟化软件:安装 VMware Workstation 或 Virtualbox,两者提供简便的虚拟机管理和配置。
- 创建 Linux 虚拟机:打开虚拟化软件,按照指引创建新虚拟机。设置内存为 2GB 或更多,其余选项选择 默认值。加载下载好的 Linux 系统 ISO 镜像文件,启动虚拟机完成 Linux 系统安装。
🌍3. 实验内容
1.编译器gcc的使用
(1)编辑一个C语言程序文件 hello.c ,代码如下:
#include <stdio.h> main() { char name[20]; printf(“Please input your name :”); scanf(“% s”, name); printf(“Welcome % s !\n”, name); return 0; }
(2)编译文件: gcc -o hello hello.c。
(3)若有错误,修改hello.c的内容,然后再次编译,直至没有错误为止。
解:
(1)编辑一个C语言程序文件 hello.c ,代码如下:
#include <stdio.h> main() { char name[20]; printf(“Please input your name :”); scanf(“% s”, name); printf(“Welcome % s !\n”, name); return 0; }
在终端输入vim hello.c,将示例代码输入到hello.c结果显示如下:
退出保存,文件夹多了hello.c文件夹
(2)编译文件: gcc -o hello hello.c。
(3)若有错误,修改hello.c的内容,然后再次编译,直至没有错误为止。
在终端输入gcc -o hello hello.c结果显示报错如下所示:
回到源码hello.c,我们根据报错信息分段分析。
第一段:
hello.c:2:1: warning: return type defaults to ‘int’ [-Wimplicit-int] 2 | main() | ^~~~ hello.c: In function ‘main’: hello.c:5:10: error: stray ‘\342’ in program 5 | printf(���Please input your name:”); | ^ hello.c:5:11: error: stray ‘\200’ in program 5 | printf(���Please input your name:”); | ^ hello.c:5:12: error: stray ‘\234’ in program 5 | printf(��Please input your name:”); | ^ hello.c:5:13: error: ‘Please’ undeclared (first use in this function) 5 | printf(“Please input your name:”); | ^~~~~~ hello.c:5:13: note: each undeclared identifier is reported only once for each function it appears in hello.c:5:19: error: expected ‘)’ before ‘input’ 5 | printf(“Please input your name:”); | ^~~~~~ | ) hello.c:5:36: error: stray ‘\342’ in program 5 | printf(“Please input your name:���); | ^ hello.c:5:37: error: stray ‘\200’ in program 5 | printf(“Please input your name:���); | ^ hello.c:5:38: error: stray ‘\235’ in program 5 | printf(“Please input your name:��);
这一段显示源码的错误提示是因为代码中使用了不可见的特殊字符,导致编译器无法识别。可以很容易发现源码中的printf(“Please input your name:”);的双引号使用的是中文模式输入,故将其改成printf("Please input your name:");
第二段:
hello.c:6:9: error: stray ‘\342’ in program 6 | scanf(���%s”,name); | ^ hello.c:6:10: error: stray ‘\200’ in program 6 | scanf(���%s”,name); | ^ hello.c:6:11: error: stray ‘\234’ in program 6 | scanf(��%s”,name); | ^ hello.c:6:12: error: expected expression before ‘%’ token 6 | scanf(“%s”,name); | ^ hello.c:6:14: error: stray ‘\342’ in program 6 | scanf(“%s���,name); | ^ hello.c:6:15: error: stray ‘\200’ in program 6 | scanf(“%s���,name); | ^ hello.c:6:16: error: stray ‘\235’ in program 6 | scanf(“%s��,name); | ^
这一段显示源码的错误提示仍然是因为代码中使用了不可见的特殊字符,导致编译器无法识别。可以很容易发现源码中的scanf(“%s”,name);的双引号使用的是中文模式输入,故将其改成scanf("%s",name);
第三段:
hello.c:7:10: error: stray ‘\342’ in program 7 | printf(���Welcome %s!\n”,name); | ^ hello.c:7:11: error: stray ‘\200’ in program 7 | printf(���Welcome %s!\n”,name); | ^ hello.c:7:12: error: stray ‘\234’ in program 7 | printf(��Welcome %s!\n”,name); | ^ hello.c:7:13: error: ‘Welcome’ undeclared (first use in this function) 7 | printf(“Welcome %s!\n”,name); | ^~~~~~~ hello.c:7:22: error: ‘s’ undeclared (first use in this function) 7 | printf(“Welcome %s!\n”,name); | ^ hello.c:7:23: error: expected ‘)’ before ‘!’ token 7 | printf(“Welcome %s!\n”,name); | ^ | ) hello.c:7:24: error: stray ‘\’ in program 7 | printf(“Welcome %s!\n”,name); | ^ hello.c:7:26: error: stray ‘\342’ in program 7 | printf(“Welcome %s!\n���,name); | ^ hello.c:7:27: error: stray ‘\200’ in program 7 | printf(“Welcome %s!\n���,name); | ^ hello.c:7:28: error: stray ‘\235’ in program 7 | printf(“Welcome %s!\n��,name);
这一段显示源码的错误提示仍然是因为代码中使用了不可见的特殊字符,导致编译器无法识别。可以很容易发现源码中的printf(“Welcome %s!\n”,name);的双引号使用的是中文模式输入,故将其改成printf("Welcome %s!\n",name);
此时改后的代码为:
此时进行编译报错如下:
提示错误是由于没有定义main()的类型,故将main函数前加上int,最终修改版代码如下:
再次进行编译结果显示通过如下:
输入./hello运行结果如下:
2.使用GDB 调试程序BUG(教材12.7节)
(1)使用文本编辑器输入以下代码greet.c。程序试图倒序输出main 函数中定义的字符串,但结果没有显示。
#include <stdio.h> int display1(char *string); int display2(char *string); main() { char string[] = “Welcome to Linux !”; display1(string); display2(string); } int display1(char *string) { printf(“The original string is % s \n”, string); } int display2(char *string1) { char *string2; int size, i; size = strlen(string1); string2 = (char *)malloc(size + 1); for (i = 0; i < size; i++) { string2[size - i] = string1[i]; } string2[size + 1] =’’; printf(“The string afterward is % s\n”, string2); }
(2)使用gcc –g 的选项编译这段代码,运行生成的可执行文件,观察运行结果。
(3)使用gdb 调试程序,通过设置断点、单步跟踪,一步步找出错误所在。(调试过程需截图)
(4)纠正错误,更改源程序并得到正确的结果。
解:
在终端输入vim greet.c,输入以上示例为:
输入gcc -o greet greet.c进行编译结果如下:
回到源码greet.c,我们根据报错信息分段分析。
第一段:
greet.c:4:1: warning: return type defaults to ‘int’ [-Wimplicit-int] 4 | main() | ^~~~
这里错误提示为main()函数无类型,故在main()前加上int。
第二段:
greet.c: In function ‘main’: greet.c:6:17: error: stray ‘\342’ in program 6 | char string[] = ���Welcome to Linux!”; | ^ greet.c:6:18: error: stray ‘\200’ in program 6 | char string[] = ���Welcome to Linux!”; | ^ greet.c:6:19: error: stray ‘\234’ in program 6 | char string[] = ��Welcome to Linux!”; | ^ greet.c:6:20: error: ‘Welcome’ undeclared (first use in this function) 6 | char string[] = “Welcome to Linux!”; | ^~~~~~~ greet.c:6:20: note: each undeclared identifier is reported only once for each function it appears in greet.c:6:28: error: expected ‘,’ or ‘;’ before ‘to’ 6 | char string[] = “Welcome to Linux!”; | ^~ greet.c:6:37: error: stray ‘\342’ in program 6 | char string[] = “Welcome to Linux!���; | ^ greet.c:6:38: error: stray ‘\200’ in program 6 | char string[] = “Welcome to Linux!���; | ^ greet.c:6:39: error: stray ‘\235’ in program 6 | char string[] = “Welcome to Linux!��; |
这里错误提示为main()函数内的引号输入为中文模式下输入,需更改为英文状态。根据源码可以很容易发现其他的引号也均需要修改。
第三段:
greet.c: In function ‘display2’: greet.c:20:8: warning: implicit declaration of function ‘strlen’ [-Wimplicit-function-declaration] 20 | size = strlen(string1); | ^~~~~~ greet.c:20:8: warning: incompatible implicit declaration of built-in function ‘strlen’ greet.c:2:1: note: include ‘<string.h>’ or provide a declaration of ‘strlen’ 1 | #include <stdio.h> +++ |+#include <string.h> 2 | int display1(char *string); greet.c:21:19: warning: implicit declaration of function ‘malloc’ [-Wimplicit-function-declaration] 21 | string2 = (char *)malloc(size + 1); | ^~~~~~ greet.c:21:19: warning: incompatible implicit declaration of built-in function ‘malloc’ greet.c:2:1: note: include ‘<stdlib.h>’ or provide a declaration of ‘malloc’ 1 | #include <stdio.h> +++ |+#include <stdlib.h> 2 | int display1(char *string); greet.c:26:17: error: empty character constant 26 | string2[size+1]='';
这个错误提示包含了三个问题:
- 隐式声明函数strlen/malloc的警告:可以通过在代码中添加#include <string.h>和#include <stdlib.h>解决。
- 不兼容的隐式声明函数警告:这个问题是因为编译器默认隐式声明了strlen和malloc函数,但是实际上这些函数需要在头文件中声明。通过添加上述的头文件可以解决这个警告。
- 空字符常量错误:这个问题是因为在代码中有一个空的字符常量'',这是无效的。如果想要将一个字符数组的最后一个元素设为空字符,应该使用单引号括起来的空格字符,即' '。可以将代码中的空字符常量替换为' '来解决这个问题。
修改源码如下:
输入gcc -g test.c -o test进行编译结果如下:
输入./ greet
在23行(for循环处)设置断点:b 24;在29行(printf函数处)设置断点:b 29。此时输入info b查看断点设置情况。
输入r运行代码,也可输入n单步运行代码,继续单步运行代码数次,并使用命令查看,发现string2[size-1]的值正确。
继续程序的运行:c
在程序中,第24行代码为:string2[size-i] = string1[i];,这个语句是将字符串string1中的每个字符倒序复制到另一个字符串string2中。但是,在循环中,i的取值范围是从0到size-1,因此在第一次循环中,string2[size-i]实际上是string2[size-0],即string2的最后一个字符,而不是第一个字符。这会导致第一个字符无法被正确赋值。 为了解决这个问题,可以将循环的范围修改为从0到size。这样,在第一次循环中,string2[size-i]就会被正确地赋值为string1中的第一个字符,而不是最后一个字符。修改后的代码如下所示
重新进行编译,结果显示通过。
🌍4. 实验心得
- 编码注意事项: 在实验中,我学到了在编写代码时需要格外注意当前输入模式,避免在中文输入模式下输入英文符号。这是因为中文输入模式会导致符号被输入法转换,不直接对应ASCII码表中的字符。这种小细节的注意可以避免在后续编译和调试过程中因为字符转换引起的问题,确保代码的准确性和可靠性。
- 编译器选项的灵活运用: 通过使用gcc编译器,我掌握了如何通过命令行参数指定编译选项。具体而言,我了解到-Wall选项可开启所有警告信息,有助于发现潜在的错误和问题。同时,使用-g选项能够在编译时生成调试信息,提供更全面的调试支持。这种对编译选项的灵活运用不仅有助于提前发现潜在问题,也为后续的调试工作提供了更充分的信息支持。
- 高效调试技巧的应用: 通过实验,我学会了在Linux环境下使用GDB调试器进行代码调试的基本技巧。这包括使用break设置断点、run运行程序以及print打印变量值等命令。这些简洁而强大的调试命令使得在代码调试过程中能够更高效地定位和修复问题,提高了开发效率。通过掌握这些调试技巧,我能够更自信地处理代码中的错误和异常,确保程序的稳定性和可靠性。
📝总结
Linux操作系统的领域就像一片未被勘探的信息大海,引领你勇敢踏入开源系统的神秘领域。这是一场独特的学习冒险,从基本概念到系统架构,逐步揭示更深层次的操作系统原理、命令行工具和高级系统管理的奥秘。