【Linux】一些工具的简单使用,vim/gcc/gdb/make

网友投稿 315 2022-11-05


【Linux】一些工具的简单使用,vim/gcc/gdb/make

本篇博客将介绍linux下面一些简单工具的使用

1.vim编辑器

1.1安装vim

sudo apt-get install vim

需要注意的是,vim编辑器下不能使用​​CTRL+S​​​来保存文件,因为在linux中这个快捷键的作用是暂停该终端,整个系统都会卡住,这时候使用​​CTRL+Q​​取消暂停就可以了。

1.2文本操作

以下是命令模式下的一些文本批量化操作

yy 复制当前行,nyy复制n行p 粘贴再当前行的后面,np粘贴n次剪贴板的内容dd 剪切(删除)当前行,ndd操作n行u 撤销ctrl+r 重做shift+g 光标快速定位到文本末尾gg 光标快速移动到文本头n+shift+g 光标定位到文本的第n行shift+4 光标定位到该行末尾shift+6 光标定位到该行开头w,b 以单词为单位进行移动光标h,j,k,l 左、下、上、右shift+` 大小写快速切换r 替换光标所在处的字符,支持nrshift+r 批量化替换x 删除光标所在处的字符,nx删除n个

vim进入​​插入模式​​​的快捷键有​​a i o​​,分别对应不同的功能

1.3底行模式的操作

vim编辑器中​​底行模式​​的一些操作如下。在其他模式下按esc即退出到底行模式

:w "只保存:q "不保存退出:wq "保存并退出:reg "打开vim的寄存器面板:syntax on "开启语法高亮:set nu "显示行号:set nonu "取消行号显示:set tabstop=4 "设置tab的缩进,默认为8:set softtabstop=4 "softtabstop是“逢8空格进1制表符”,前提是你tabstop=8:set shiftwidth=4 "设置程序自动缩进所使用的空格长度:set autoindent "自动对齐上一行(这个选项会导致复制的时候代码排版混乱,可以考虑关闭,或者开启粘贴模式):set paste "开启粘贴模式:set mouse=a "设置鼠标模式,默认是a

上面的一些配置,写入​​.vimrc​​配置文件即可长时生效。

如果需要写入​​.vimrc​​​配置文件,需要先把​​:​​​和​​注释​​都去掉

2.gcc/g++编译器

g++操作和gcc是一样的,这里我们使用gcc作为演示

2.1linux下使用不同命令执行程序的几个阶段

第一步是预处理,只做文本操作

gcc -E test.c -o tset.i

在这个阶段会

展开头文件对define等等操作进行替换处理条件编译指令同时删除所有注释

编译操作

gcc -S test.i -o test.s

汇编操作

gcc -c test.s -o test.o

形成可执行程序

gcc test.o -o mytest

这三个命令的顺序就是​​ESc​​其中只有-c选项是小写的

正好就是键盘左上角esc按键的顺序

2.2代码和库

这里我们操作/编译的都是自己的代码。比如​​printf​​我是调用的c语言库中的函数,并没有自己完成一个打印的实现。

这时候就需要和系统的c语言库产生关联

c标准库的位置ls /lib64/libc*

上面的最后一步形成可执行程序​​mytest​​​时,系统会自动帮我们把这里的代码和库里面的方法连接起来,形成一个最终的可执行程序,并使用​​./mytest​​来执行输出结果

所以我们平时说的装环境就是需要安装语言的静态和动态库,这样才能正常利用库里面的函数进行代码的编译处理

同时我们在编译器里面写代码时的代码补全功能也是通过在库函数的头文件里面搜索来完成的。

2.3动态/静态链接&库

动态:linux(.so) windows(.dll)静态:linux(.a) windows(.lib)

网吧是全校所有同学共享的,你在网吧开的机子是和别人一起用的。 从学校去网吧(库),然后获得一台机子(库函数),打游戏(执行方法). 这就是一个动态的编译链接的过程,即为动态库。

如果学校允许带电脑,当你想打游戏的时候用的是自己的电脑,用的是自己的方法,这种情况就是用的静态库。每一个人拥有自己的电脑,这个电脑的功能和网吧里面的功能是一样的,当我们把库中的相关代码直接拷贝到自己的可执行程序中,即为静态链接

动态链接:所有人共享同一个资源

优点:可以节省资源;缺点:一旦库丢失,会导致所有程序失效

静态链接:都用的是自己的方法,将库里面的代码拷贝到自己的文件中

优点:不依赖任何库,程序可以独立运行缺点:浪费资源

查看链接状态,默认是动态链接

[muxue@bt-7274:~/GIT/raspi/code/TestProgram]$ ldd mytest linux-vdso.so.1 => (0x00007ffc0dd8b000) /$LIB/libonion.so => /lib64/libonion.so (0x00007f89bd66c000) libc.so.6 => /lib64/libc.so.6 (0x00007f89bd185000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f89bcf81000) /lib64/ld-linux-x86-64.so.2 (0x00007f89bd553000)

查看可执行程序的构成

[muxue@bt-7274:~/GIT/raspi/code/TestProgram]$ file mytestmytest: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=7cf0ffacfdaeadf8de9b4c0fea379b2a15c37e4c, not stripped

2.3.1手动指定进行静态链接

gcc test.c -o mytest1 -static

静态链接生成的可执行程序大小很大,动态链接的默认是​​8040​​左右的体积。

-rwxrwxr-x 1 muxue muxue 8416 Aug 7 07:34 mytest-rwxrwxr-x 1 muxue muxue 861288 Aug 7 07:44 hello

所以一般情况下我们都推荐使用动态链接,避免占用太大的空间

当我首次尝试这种方式的时候,出现了下面的报错

[muxue@bt-7274:~/GIT/raspi/code/TestProgram]$ gcc hello.c -o hello -static/usr/bin/ld: cannot find -lccollect2: error: ld returned 1 exit status

因为系统里面默认不会带​​.a​​的静态库,所以会报错。这时候需要我们手动安装一下。

sudo yum install -y glibc-staticsudo yum install -y libstdc++-static

安装成功!

Installed: glibc-static.x86_64 0:2.17-326.el7_9 Complete!

这时候执行就不会报错了!

[muxue@bt-7274:~/GIT/raspi/code/TestProgram]$ gcc hello.c -o hello -static[muxue@bt-7274:~/GIT/raspi/code/TestProgram]$

3.gdb调试

默认生成的可执行程序是无法调试的!在linux里面发布的可执行程序默认是​​release​​版本的,无法debug

需要添加一个​​-g​​选项进行编译

gcc test.c -o test_g -g

同时debug版本的可执行文件也会比release版本大一些,这大的空间里面存放的就是调试信息

-rwxrwxr-x 1 muxue muxue 8360 Aug 7 07:50 test-rwxrwxr-x 1 muxue muxue 9376 Aug 7 07:53 test_g

利用下面这个语句可以查看可执行程序的调试信息

readelf -S test | grep debug

可以看到debug版本包含了很多调试信息,而release版本里面没有

[muxue@bt-7274:~/GIT/raspi/vim/TestGdb]$ readelf -S test | grep debug[muxue@bt-7274:~/GIT/raspi/vim/TestGdb]$ readelf -S test_g | grep debug [27] .debug_aranges PROGBITS 0000000000000000 00001061 [28] .debug_info PROGBITS 0000000000000000 00001091 [29] .debug_abbrev PROGBITS 0000000000000000 00001122 [30] .debug_line PROGBITS 0000000000000000 00001164 [31]

3.1尝试调试一个简单的代码

以下是一些简单的gdb操作

b 行号:打断点info b:查看断点d 断点编号: 取消断点l 行号:显示代码l main:显示包含main的那一行r:run,开始运行程序,跳到第一个断点s:step,逐语句,对应vs的F11(进入函数)n:next,逐过程,对应vs的F10c:continue,跳转道下一个断点p:查看变量display / undisplay:常显示 或 取消常显示until 行号:跳转到指定行finish:执行完一个函数后停下bt:查看函数调用堆栈

提醒:编译的时候记得加上​​-g​​选项指定debug版本

下面是一个用于演式的示例代码

#include int Add(int a,int b){ printf("Add(a,b)\n"); return a+b;}int main(){ printf("hello wolrd!\n"); int ret=Add(1,20); printf("ret: %d\n",ret); return 0;}

演示如下

[muxue@bt-7274:~/GIT/raspi/vim/TestGdb]$ gdb test_gGNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7Copyright (C) 2013 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later

整体的操作并不是特别复杂,大家可以自己尝试一番,有问题可以评论提出

4.make/makefile

这是一个批量处理工具,我们可以通过make来批量编译一些代码,避免手动敲打命令行的出错问题。这在大型项目中非常重要。

makefile是当前路径下的一个普通文件,存放了如下内容:

依赖关系依赖方法

假设我们需要形成一个c语言的可执行文件

依赖关系:test -> test.c依赖方法:gcc test.c -o test

其对应的makefile如下

test:test.c gcc test.c -o mytest

注意,第二行的依赖方法必须tab缩进,不然无法正常调用!

编写好makefile后,直接在当前路径下执行make。系统会自动查找名称为makefile/Makefile的文件执行

[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ lsmakefile test.c[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ makegcc test.c -o test[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ lsmakefile test test.c[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ ./testhello wolrd!Add(a,b)ret: 21

我们还可以写一个清除指令,用于在编译后删除大量临时出现的可执行程序

.PHONY:cleanclean: rm -f test

在原本的makefile后追加这部分内容即可

通过​​make clean​​来清理文件

[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ lsmakefile test test.c[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ make cleanrm -f test[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ ls

4.1出现missing separator解决方案

当我执行make clean的时候出现了这个报错

[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ make cleanmakefile:4: *** missing separator. Stop.

这是因为在我的makefile中,依赖方法前面的缩进是4个空格,而不是1个tab

注意需要使用tab进行缩进,而不能手动打空格!

4.2make如何判断需不需要重新生成?

当我们在一个文件夹内执行过make之后,再次make,系统会提示当前的可执行程序test已经是最新版本,无需更新。

[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ lsmakefile test.c[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ makegcc test.c -o test[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ lsmakefile test test.c[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ makemake: `test' is up to date.[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$

那么系统是如何实别出来我们的原代码是否有过更改的呢?

4.2.1 stat时间戳

我们可以使用​​stat​​命令查看一个文件的时间戳

[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ stat test File: ‘test’ Size: 8440 Blocks: 24 IO Block: 4096 regular fileDevice: fd01h/64769d Inode: 1450818 Links: 1Access: (0775/-rwxrwxr-x) Uid: ( 1001/ muxue) Gid: ( 1001/ muxue)Access: 2022-08-07 17:18:40.463120772 +0800Modify: 2022-08-07 17:18:40.463120772 +0800Change: 2022-08-07 17:18:40.463120772 +0800 Birth: -[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ stat test.c File: ‘test.c’ Size: 201 Blocks: 8 IO Block: 4096 regular fileDevice: fd01h/64769d Inode: 1450788 Links: 1Access: (0664/-rw-rw-r--) Uid: ( 1001/ muxue) Gid: ( 1001/ muxue)Access: 2022-08-07 09:30:39.043992599 +0800Modify: 2022-08-07 09:30:38.544992699 +0800Change: 2022-08-07 09:30:38.544992699 +0800 Birth: -

这里可以看到,一个文件的时间戳分为3个,分别是Access查看、modify修改,Change更改。

第一个查看很好理解,那么modify和change有什么区别呢?

我们可以手动修改一个程序看看情况

[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ vim test.c[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ stat test.c File: ‘test.c’ Size: 207 Blocks: 8 IO Block: 4096 regular fileDevice: fd01h/64769d Inode: 1450788 Links: 1Access: (0664/-rw-rw-r--) Uid: ( 1001/ muxue) Gid: ( 1001/ muxue)Access: 2022-08-07 17:25:12.958082845 +0800Modify: 2022-08-07 17:25:12.808082859 +0800Change: 2022-08-07 17:25:12.808082859 +0800 Birth: -

这里我通过vim进入该文件,添加了一行注释,可以看到,相比于之前的时间,3个时间戳都被修改成了最新的时间。这是因为我们修改文件的时候一定会查看,也有modify和change

而如果我只是修改这个文件的权限,并不修改它的内容,会发生什么?

[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ chmod o-r test.c[muxue@bt-7274:~/GIT/raspi/vim/TestMake]$ stat test.c File: ‘test.c’ Size: 207 Blocks: 8 IO Block: 4096 regular fileDevice: fd01h/64769d Inode: 1450788 Links: 1Access: (0660/-rw-rw----) Uid: ( 1001/ muxue) Gid: ( 1001/ muxue)Access: 2022-08-07 17:25:12.958082845 +0800Modify: 2022-08-07 17:25:12.808082859 +0800Change: 2022-08-07 17:27:34.378068762 +0800 Birth: -

可以看到,只有change发生了变化。

​​文件=内容+属性​​,在这里的modify对应的就是内容修改,而change对应的是属性修改。而当我们修改文件内容的时候,会引起文件大小的变化,也是属性变化,所以修改内容也可能会引起change的变化!

了解了这个时间戳,那么系统是怎么判断是否需要重新生成就很简单了:比较依赖关系中左边的目标文件和右边源文件的​​modify​​​时间,如果源文件的​​modify​​时间早于目标文件,那么说明目标文件生成之后,源文件并没有发生更改,那么也无需再次生成

4.2.2 PHONY关键字的作用

在前面提到的​​clean​​​代码中,我们使用了​​.PHONY​​关键字来修饰clean。

这个关键字让clean作为一个伪目标,且总是被执行

怎么理解这个总是被执行?

当我们的源文件没有发生更改的时候,make不会重新生成,这个叫做总是不被执行

PHONY关键字的作用就是屏蔽系统对于​​modify​​时间的检查,每一次都会强制执行该语句的依赖方法。

一般情况下我们只有在​​clean​​​的时候才会使用​​.PHONY​​关键字来修饰

5.尝试编写一个简单的linux进图条

当我们在linux系统上下载一些软件的时候,总是可以看到用文字组成的进度条,这些进图条是怎么做出来的呢?

下面我们可以尝试用C语言写出一个简单的进度条。在这之前,我们需要了解一些概念

5.1 缓冲区

在我之前的​​C语言文件操作​​​博客中,提到了一个缓冲区的概念。简单来说,当我们​​printf​​一道字符串的时候,系统是先把这个字符串写入缓冲区,再把缓冲区的内容输出到屏幕上

比如下面这个代码,再linux环境中,​​\n​​会自动刷新缓冲区。

#include #include int main(){ while (1) { printf("hehe\n"); //在linux环境中,不带'\n'的时候,并不会打印(没有刷新缓存区) //而在VS环境中,带不带都会正常打印 sleep(1);//linux环境中,sleep函数的参数,单位是秒(VS是毫秒) // linux环境下,sleep函数需要小写,VS下是Sleep } return 0;}

如果我们去掉​​\n​​,系统则不会立即打印内容。

这时候需要我们手动用​​fflush(stdout)​​刷新一下缓冲区,现在程序会在一行中打印了

fflush(stdout);//手动刷新缓冲区

5.2 回车和换行

在我们日常生活中提到的换行一般指的是​​回车+换行​​

实际上,回车和换行是有区别的:

回车:光标回到该行的最前面换行:光标去到下一行,但是位置不变

在C语言中,​​\n​​​执行的就是​​回车+换行​​​,而​​\r​​是回车

那么我们就可以利用这个特性,来实现一个简单的倒计时

#include #include int main(){ int i=9; while (i>=0) { printf("%d\r",i); //在linux环境中,不带'\n'的时候,并不会打印(没有刷新缓存区) //而在VS环境中,带不带都会正常打印 fflush(stdout);//手动刷新缓冲区 sleep(1);//linux环境中,sleep函数的参数,单位是秒(VS是毫秒) // linux环境下,sleep函数需要小写,VS下是Sleep i--; } return 0;}

5.3 进度条

做好前面的准备工作后,现在我们就可以来打印一个简单的进度条了!

#include #include #include #define NUM 102#define STYLE '#'void process(){ char bar[NUM]; memset(bar, '\0', sizeof(bar)); const char *lable = "|/-\\";//在末尾打印一个转动的“小圆圈” int cnt = 0; while(cnt <= 100) { //默认是右对齐,使用-改位左对齐 printf("加载中:%-100s[%d\%][%c]\r", bar, cnt, lable[cnt%4]); fflush(stdout); bar[cnt++] = STYLE;//打印预定义的符号 usleep(200000); } printf("\n");}int main(){ process(); return 0;}

这样一个简单的进图条就搞定辣!

后记

linux中一些工具的使用可能不会有windows的编译器那么方便,比如GDB调试。但是在后续编写一些只有linux平台才能运行的代码的时候,我们必须学会使用这些工具,否则操作起来会非常麻烦!

感谢你看到最后,有任何问题都欢迎在评论区提出哦!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:linux 搭建mycat
下一篇:苹果官网查询序列号API(苹果官网查询序列号显示购买日期未验证)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~