Linux下工具的基本使用

网友投稿 604 2022-08-30

Linux下工具的基本使用

Linux下工具的基本使用

序言

这个博客算是其他工具的使用总结吧,里面包含gdp,动静态库的简单认识...其中gdp是我们今天的额难点,需要我们好好的了解一下.

gcc & g++ 编译器

这两个工具比较的简单,其中gcc是C语言的编译器的,g++是C++的编译器,我们这里用gcc来演示,它们的选项都是一样的.

程序编译

我们先来看看如何在Linux下运行一个C语言的代码.我们看到当前目录下有一个.c文件

我们要做的就是编译这个代码.

[bit@Qkj 08_07]$ gcc main.c

现在我们就可以看到了如何编译一个代码,可执行程序的文件名默认是a.out,当然我们也可以修改.

[bit@Qkj 08_07]$ gcc main.c -o mybin

我们都知道,一个代码要变成可执行程序要经过以下若干步骤,我先列出来,在Linux的环境下给大家演示,有些命令看不太懂没有关系,就看文件的变化就可以了

预处理编译汇编链接

预处理(预编译)

预处理大致包含四个方面,就是去注释,宏替换,头文件展开和条件编译.这个过程我们在之前就谈过.

#include #define M 100 int main() { // 打印一个宏 printf("宏 %d\n",M); #ifdef A printf("hello A\n"); #else printf("对不起,你没有定义 A\n"); #endif return 0; }

我们希望得到预处理后的结果的,下面的选项就可以.

[bit@Qkj 08_07]$ gcc -E main.c -o main.i

编译

编译器把代码翻译成汇编语言

[bit@Qkj 08_07]$ gcc -S main.i -o main.s

汇编

将汇编文件翻译成可重定位二进制文件,在Windows下是.obj文件.这时候我们这个文件还不能运行.

[bit@Qkj 08_07]$ gcc -c main.s -o main.o

链接

这个才是我想和大家分享的,链接一直是我们比较难理解的.我们可以从这个角度理解,一旦我们调用了一个函数,如果这是别人实现的函数,例如printf函数,编译器就会问,这个printf的函数在哪里?我们使用的printf如何和C库里面的函数链接出来.

[bit@Qkj 08_07]$ ls /lib64/libc*

动静态库

库分为动态库和静态库,等会我们感性的认识一下它们两个的区别.在Linux, .so后缀就是动态库,Windows下是.dll.静态库在Linux是.a,Windows下是.lib.

动态库 我们只是包含头文件,遇到标准库的函数,就去库里面找 动态链接静态库 编译器遇到了库里面的函数,编译器自动把这个函数给复制到你的可执行程序中 静态链接

注意,Linux默认是连接动态库,我们也推荐的是动态库.如果我们想要静态库连接,下面也行,加上一个选项就可以了

[bit@Qkj 08_08]$ gcc main.c -o s_a.out -static

上面的指令可能跑不过,原因就是Linux默认一般都是动态库,我们需要自己-静态库,记住在root用户下-,我们还没有添加sudo这个东西,一个是C库,一个是C++的

[root@Qkj ~]# yum install -y glibc-static [root@Qkj ~]# yum install -y libstdc++-static

ldd 指令

ldd可以观察可执行程序所依赖的库,

[bit@Qkj 08_08]$ ldd a.out

gdb 调试器

gcc/g++都是编译器,gdp才是我们的调试器关于这个调试器,大家知道它的基本的用法就可以了,我们先来用简单的指令.

debug & release

在Linux下,默认是release模式,这个模式不可以调试,而且编译器还会做一定程度的优化.gcc 的release 版本是交付给用户使用,debug里面包含了调试信息 ,体积上一定大于release模式.

使用下面的选项就可以得到debug模式的版本

至于多的那一部分,就是调试信息

调试

我们先把代码给放出来,先稍微的看两眼.

#include int add(int a) { int sum = 0; int i = 0; for(i=0;i

我们这里开始,直接开始调试.这个还是比较简单的,直接只用这个格式的指令就可以了. gbd debug版本

[bit@Qkj 08_08]$ gdb mybin_g

如果你不想调试了,可以直接q,退出调试.

显示代码,直接 l

它默认不是从第零行显示,如果我们想要从某一行显示,可以l后面跟上数字,注意,一般gdp会自动记录上一个命令,你可以直接按回车执行上一条命令.

打断点 b 行号,打断点很重要,我们在第十五行打断点

查看断点,info b

跳到下一个断点 c

取消断点 d 断点编号

程序跑起来,他会停留在第一个遇到的断点, 直接 r

查看变量,p

到下一行 n,可以理解成 逐过程,类似F10,也就是遇到函数不进入.

逐语句,s 进入函数 类似F11

常显示display对于下面我们总不能每次都p sum的值吧,这里有常显示的指令

取消常显示,undisplay,这里面用的是编号

查看堆栈 bt

until 10 跳到指定行

finish 跳出当前函数

make和Makefile

我们在window系统下学习C语言,一般会使用VSCode、VS这些软件,它们有一个统称叫做集成开发环境 ,在Vs2013中我们很容易写出几个 .c 文件,只需要编译一下就可以运行起来,那么在Linux环境下该怎么做?这就是需要make和makefile,这里需要先说明一下,在公司里面makefile是可以自动生成的,不需要我们手写,但是这里需要我们了解原理.

我这里就先说一下,不做详细的解释

make 是一条命令Makefile 是一个文件

环境准备

先创建几个文件,里面的内容就随便编写了.

[bit@Qkj 08_07]$ touch main.c mytest.c mytest.h

mytest.h

#ifndef __MYTEST_H__ //#ifndef __MYTEST_H__这个先不用管 #define __MYTEST_H__ #include void func(); #endif

编写mytest.c

#include "mytest.h"void func(){ prinf("func()\n");}

编写main.c

#include "mytest.h"int main(){ show(); return 0;}

Makefile/makefile

在谈make之前,我们需要有谈一下makefile,首先这是一个文件,我们先创建一个这个文件.注意Makefile和makefile都可以.

我们在本目录下创建一个Makefile(或者makefile)文件

[bit@Qkj 08_07]$ touch Makefile

那么这里就开始需要谈谈这个文件里面的内容了,我们想是不是可以吧自己想要的指令写道这个文件里面,然后通过某种方法可以简便的做法.那么在里面我们可以这么做.

mybin: main.c mytest.c gcc main.c mytest.c -o mybin

这里是我们今天的重点,我会好好的解释的

依赖关系&依赖方法

我们首先要知道一点:一个Makefile文件存在下面的东西,缺一不可

依赖关系依赖方法

那么这俩个究竟是什么东西,我们先来举一个例子.假设在学校里面,你缺生活费了,你给你把打了一个电话,说我是你孩子,所以需要给我打钱.这时候就出现了这两个东西.亲情就是依赖关系,打钱就是依赖方法.

这里面我们还可以这么做,我们编译程序的过程也可以控制出来.

make

make是一个指令,我们就是简单make一下就可以执行编译程序的功能.

我们只需要用一下make命令

清理解决方案

我们在VS里面看到过清理解决方案这种情况,在Linux中中也就是删除掉我们编译出来的程序,我们就是下面的做法.

这个地方还算是比较好的,如果我们生成的临时文件很多呢?总不能每一次都可以保证自己删除时完全的正确的吧,所以我们也把这个清理的程序放到makefile中.

这里面还有一点没有谈到的,不过先不要着急,这里面都会和大家讲.

make clean

现在就存在一个问题,为何事make clean,要知道上面我们编译程序可是一个make就可以了,难道clean有什么是特殊的?准确来说,是第一个依赖关系和依赖方法比较特殊,谁在第一个,make的就是谁,其余的前面都是make+目标文件.

伪目标

在现实生活中,存在一些孤儿院的孩子,它们不存父母,计算机中也存在相似的东西,这中称之为为目标,所谓伪目标就是没有依赖文件的目标文件.这里需要注意的,伪目标也是目标,不要把伪军不当军队.

总是可执行

现在还存在最后一个大问题,.PHONY是什么,它是必须的的吗?大家可以把**.PHONY**看作makefile里面的一个关键字.

一般情况下,我们用.PHONY修饰伪目标,被它总是修饰的总是被执行,那么什么是总是被执行呢?我们先来看看什么是总是不被执行的.注意,现在我们的.PHONY只是修饰的clean目标文件,我们先看看什么是总是不被执行的.

我们呢观察到,我们make了多次,编译器会告诉我们当前程序已经是最新的了,不需要执行了.这就是总是不被执行的.反之,如果我用.PHONY修饰编译程序的那部分目标文件,他就可以总是被执行了.不过我们不建议这么做,主要后面公司里面一个程序很大,明明已经是最新了的,为何还要重新编译,没必要.

一般我们学到基础那里就可以了,要是还要深入的学习,这里可以稍微了解一下,我们可以使用一些符号来表示目标文件和依赖文件

目标文件 :依赖文件

冒号左边 目标文件 也就是 $@冒号左边 依赖文件 也就是 $^

要是我们想要简省依赖方法就可以写下面的命令

gcc $^ -o $@

要是我们还想进一步省略,就要写下面的命令,不过这里我们就不建议了,毕竟以后的makefile又不是我们来写的.

解释一下

%.o 对应.c文件生成的.o文件%.c 本目录下所有的.c文件$< 所有的.c文件一一展开在gcc下生成对应的.o文件

stat 指令

这里面我们需要在看看这个指令,我记得前面好像说过一嘴,没说也没有关系,这里面在说说.statu可以看一下文件的属性,谈这个关键字主要是想看看编译器是如何知道你的代码是最新的?这是一个重点.

[bit@Qkj 08_07]$ stat main.c

在Linux中,文件的属性里面一般包含三个时间,我们都知道,文件=内容+属性

Access: 进入或读取文件的最新时间Modify: 修改文件内容的最新时间Change: 修改文件权限的属性的最新时间

也就是说,我们每执行一次操作,都会导致这三个时间的变换.例如我们如果修改一下文件的权限,这样就会导致Change时间被修改.

这里我们需要注意的是,如果我们修改文件的内容可能也导致文件的属性被修改,主要文件的大小也是属性的一部分,这一点要记住了.还有就是Access: 时间,在比较新的Linux内核,我们如果读取文件的话可能不会修改Access: 这个时间,主要是这种操作太过频繁,一般编译器会在执行了十次左右样的操作才会修改Access: 时间

现在我们就知道,总是不被执行的原理.在我们进行make的时候,编译器会先比对它们的Modify:时间,看看源文件时间是不是大于可执行程序的时间,大于就执行,否就不执行.如果别.PHONY修饰,那么就会自动忽略这一过程.

进度条

今天我们实现一个进度条的小玩意儿,很简单,我们先看看成果

缓冲区

在学习计算机时,你在很多地方有可能看到到缓冲区的字样,那什么是缓冲区啊?,我们先看一下现象

我是在Linux环境下演示,window环境下演示可能会没有效果。

#include int main() { printf("你好,缓冲区"); sleep(3); //在Linux环境下sleep的单位 秒 return 0; }

你会发现当我们执行程序时,会有大概几秒的延迟,可我们不是先执行的printf函数吗,后面才是sleep,实际上这也是事实,只不过当我执行printf后,我们想要输出的内容被放到一个缓冲区里面了,后面程序停留了3秒,当进程快要结束的时候,后面才会刷新缓冲区.下面我将仔细的说一下什么缓冲区

本质上 缓冲区就是一块内存,我们将想要输出的数据放到缓冲区中,当我们刷新缓冲区的时候会把他打印出来,我们可以把数据当成一个水池,缓冲区看作打水的的木桶,当水桶满了的时候或者我们就是想要一半的水,就把水倒出来。

什么情况下缓冲区会刷新

全缓冲缓冲区满了,会刷新行缓冲我们使用换行符,缓冲区也会刷新函数缓冲我们使用某个函数强制刷新程序退出,自动刷新

下面我们一一演示

全缓冲

我们演示一下,大家就懂了

#include int main(){ while(1) { printf("hello world"); sleep(3); } return 0;}

由于时间有些长,我们就不等了,结果是当缓冲区满了后,屏幕上会出现一屏幕的 hello world

行缓冲

我们这里的换行是指的 \n,它可以是缓冲区刷新

#include int main(){ while(1) { printf("hello world\n"); sleep(3); } return 0;}

函数缓冲

在C语言中提供了一个函数,它可以强制刷新缓冲区,这就是 ​​fflush()​​,它是stdio.h中的库函数,在一个C程序中,编译器会默认打开 3 个标准输入输出流,分别是下面的,这一点知道就可以了

stdinstdoutstderr

#include int main(){ while(1) { printf("hello world"); fflush(stdout); sleep(3); } return 0;}

\n 和 \r

有很多人都搞不懂这两个有什么区别,我们今天重点说一下

\n叫做 换行\r叫做 回车

我们举一个写文章的例子,当我们在“我叫张三,外号法外狂徒。”这句话再开一行时,看看他们之间的区别

有人可能会感到疑惑,这和我们用的不对啊,我们之前使用下面的代码时,都是再下一行的开头直接打印,实际上这是C语言的编译器默认将换行回车这两个浓缩到 \n了

printf("hello world\n");printf("hello world\n");

演示效果 \r

我们先演示一下效果

#include "ProcBar.h" int main() { int count = 10; while(count--) { printf("%d\r",count); sleep(1); } printf("hello word\n"); return 0; }

注意看,我们没有打印出9,8,7,6,5这样的数据,原因是缓冲区的因素,我们使用函数刷新一下缓冲区看看效果

#include "ProcBar.h" int main() { int count = 10; while(count--) { printf("%d\r",count); fflush(stdout); sleep(1); } printf("hello word\n"); return 0; }

进度条实现

有了上面的知识点,我们就可以写出自己的进度条了,由于多文件情况下阅读的体验不太好,我把代码写在一个文件中

usleep() 和 sleep()的作用一样,不过它的单位是 毫秒memset() 是 string.h库的一个函数,它可以把数组内部都初始化指定的 数据%-100s 设置100的字段,-表示 左对齐%d%% 是为了了打印 百分比 %是转换说明,所以用两个%代替一个% %% -> %详细的请:point_right:​​转换说明​​

#ifndef __INCLUDE_H__ //防止头文件被重复引用#define __INCLUDE_H__#include #include #endifvoid proBar(){ const char* p = "|/-\\"; int i = 0; char arr[101] = { 0 }; memset(arr, '\0', sizeof(arr)); while (i <= 100) { printf("[%-100s][%d%%][%c]\r", arr, i, p[i % 4]); fflush(stdout); arr[i++] = '#'; usleep(80000); } printf("\n");}int main(){ proBar(); return 0;}

这样,你的进度条就完成了,我们也可以给进度条添加颜色,C语言是支持的,不过这就要你自己去处理了

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

上一篇:IM系统的前世今生—2小时用Go快速搭建高性能、可拓展的IM系统
下一篇:openssl 下的对称加密和非对称加密(openssl漏洞)
相关文章

 发表评论

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