LINUX入门篇【6】----第一个LINUX小程序---进度条及相关知识讲解

前言:

本篇我们将开始尝试构建我们的第一个LINUX的小程序----进度条作为一个十分常见的程序,在我们之后的工程实践中也是需要多次运用,但是介于我们目前还没有去学习网络等方面的知识,没法独立的去利用程序去下载一个真正的程序,自然没法根据程序去进行一个真实的下载环境,但是我们依旧可以拿出一个模拟下载的进度条程序。

1.前期预备知识讲解:

1.自动化构建代码:

让我们先想想我们在使用VS的时候的一些细节:我们只需要点击重新生成解决方案就可以直接直接重新编译我们当前的代码,而且是多个文件一起编译,但是在vim中我们对于多个文件,必须一个一个去gcc/g++,这就非常麻烦和繁琐,所以我们要寻找一种方法让我们的vim编译器也可以做到只需要一次指令就可以同时将多个文件一起编译,在这里,我便要引入我们的make和makefile.
首先,make是指令,而makefile是我们需要创建的一个不能改变名字的一个文件,你可以这样理解makefile:它是一个在当前目录下存在的一个具有特定格式的文本文件。
我们首先要创建一个makefile文件,注意,我们创建的时候名字必须是Makefile/makefile这两个,大小写没影响,但是名字不能变,如下:
在这里插入图片描述
有了这个文件,我们就可以使用make指令来自动执行makefile文件里面的命令了。

依赖方法与依赖关系:

我们的makefile文件里面命令的本质实际上就是两个:依赖方法和依赖关系,我们代码之间主要是依靠一个一个文件来封存,所以本质上文件之间的关系就反映着代码之间的关系,如下:
在这里插入图片描述
在这里,我写好了main.c process.c process.h三个文件,其中两个源文件,一个头文件,我们在书写C语言的程序的时候就知道,我们都是直接将源文件进行编译成可执行程序的,又根据我们前面程序的翻译中知道,我们的头文件是以链接的形式直接拷贝或者地址链接到程序中的,所以我们不需要对其进行编译展开,由上述的逻辑,我们大致构建出了一个文件之间的依赖关系:
main.c process.c两个文件要被编译成一个可执行程序文件,而头文件是链接进行的,由此,我们的可执行程序文件与两个源文件就是依赖关系,而他们的依赖方法就是将这两个源文件编译成一个可执行程序,头文件自己链接,由此,我们可以这样去写我们的Makefile的指令:
在这里插入图片描述
第一行是依赖关系,指的是我们的关系是process可执行程序和我们的两个源文件之间的关系,第二行需要先TAB空格后开始写,主要写依赖方法:在这里我们的$^代表的是冒号之后的依赖的对象,而 $@代表的是冒号之前的依赖对象,当然你直接写名字也是可以的,主要是这样写更加简便,然后这条指令我不多说,就是上篇文章我们说过的编译指令,后面的@ echo(注意这里@和echo之间要留有空格,否则这条指令执行会全是乱码,不简洁),这条指令就是将双引号里面的内容呈现到屏幕上,这个指令我们之前也学到过,它同样可以将对应的字符输入到文件中。
由此,我们的编译指令便构建了出来,现在只需要我们输入make,就可以自动编译代码成为可执行程序,如下:
在这里插入图片描述
这样当我们去查看我们的目录,一个process可执行程序便出现在了列表中,这便是我们的可执行程序。
但是,一个程序光可以编译是不够的,我们还需要它进行程序的清理,也就是要自动删除我们的全部程序文件,跟gcc/g++一样,倘若一个一个删就会太麻烦,所以我们同样使用利用程序将其一次清理指令全部执行的命令,即我们需要使用.PHONY伪代码来进行,这是由于我们的删除文件是没有依赖对象的,删除不需要跟其他文件搭建关系,但是我们的依赖关系如果不建立,依赖方法又是没法执行的,故我们通过使用伪代码来解决这个问题,.PHONY的特点在于:
.PHONY没有依赖关系这么一说,它会直接执行对应的依赖方法,即无论目标对象是否存在都会重新生成,不会在于文件新旧的问题,这是因为目标对象与其同名文件之间没有一定必要的相互关系。
故在这里我们可以这样总结:
.PHONY修饰的文件本身是和普通文件没有明显去别的,它只是总是被执行它对应的依赖方法,并且这种执行是强制性的。
由此,我们可以这样接着编译指令构建我们的删除指令,如下:
在这里插入图片描述

在这里,我们的clean即我们的对象,我们执行的指令当输入这个对象的时候,即当我们输入make clean的时候,我们就会去执行清理可执行程序process的指令rm -f,这样,我们就可以自动去清理多个程序文件,而不需要一个一个去清理了,如下:
在这里插入图片描述

make/makefile的自主推导性:

对于下面的代码:

mybin:code.ogcc code.o -o mybin
code.o:code.sgcc -c code.s -o code.o
code.s:code.i gcc -S code.i -o code.s
code.i:code.cgcc -E code.c -o code.i  

此代码是可以执行的,虽然扫描文件的时候从上到下执行,但是由于makefile的自主推导性,它是这样去分析这些代码的:
从尾部向上依次按照文件的编译过程去找是否存在对应的文件,知道形成可执行程序,所以,我们的文件是不怕顺序问题的,主要是该有的逻辑我们是不能有缺失的,否则会影响程序的编译。
在这里插入图片描述

2.ACM时间与通过时间对文件新旧的判断:

我们倘若已经生成了一个process可执行文件,我们要是再次对其编译可以么?
倘若我们这样去执行,就会跳出这样的回应:
在这里插入图片描述
这条指令的意思就是说,文件已经更新到最新版本时间,不支持再次编译了,也就是说,此时文件是旧的,LINUX是可以分辨出来并且拒绝重复编译的,那么,LINUX是如何去区分一个文件是否被修改从而确定其是否可以重新编译的呢?
这就不得不提到我们的ACM时间了。
何为ACM时间呢?让我们stat任意一个文件来查看文件的详细信息,如下:
在这里插入图片描述
你会发现,有三个时间:
Access:读写访问时间,当我们打开文件读取内容时,就会修改这个时间
Modify:文件内容修改时间,当我们进入文件并修改文件内容时,就会修改这个时间
Change:文件属性修改时间,文件的属性基本随着内容的修改也在不断的被修改着

根据他们三个之间的特点,他们相应的关系如下:
ACM分别对应着三个时间的首字母,当C时间被修改时(比如文件的三个读写权限)它只会影响自己,另外两个时间不被修改,当我们去修改M时间时,则A时间和C时间都会由此而被改变,而修改A时间时,对应的C文件的属性也会被修改。
那,文件访问的本质是什么呢?
首先文件是被存储在磁盘上的,也就是说,访问文件本质上就是访问磁盘,这个过程的效率是很低的,很耗费时间,会影响我们程序的执行。由此,在LINUX中,我们是不会重复去编译文件的,因为LINUX有着庞大的文件,倘若依次修改磁盘,会导致出现大量的磁盘的IO操作,这严重影响了系统的效率给,故LINUX会尽可能的减少文件的修改次数。
所以回过来,我们的文件的新旧是如何被系统分辨出来的呢?
它主要依靠的实际上就是文件的修改时间,但是时间并非本质,通过时间对比出来的新旧才为本质,源文件通过与可执行文件的修改时间进行对比:
首先第一次的时候,我们一定是先有源文件,然后通过编译得到可执行程序,此时我们会得出结论,源文件的修改时间一定是小于可执行程序文件的修改时间的,然后从第二次开始,到后面之后的很多次修改,我们修改文件的时候,先去修改源文件,此时当我们再去编译的时候,此时的源文件的修改时间反而大于可执行程序的修改时间了,故此时两者的大小关系发生了变化,LINUX系统识别到了这种变化,意识到文件做出了修改,故此时认为文件为新,执行重新编译的指令,但旧文件就不会执行这个指令,由此,便通过源文件和可执行文件的修改时间的大小关系的变化去分辨新旧文件。
但是,这条规律在大部分情况下都没问题,问题的产生不仅仅是修改新文件就能解决的,有些历史问题,需要重新清理项目才可以解决。
那如何修改文件的时间呢?
我们有时对代码已经足够完美了,没必要修改的情况下,我们还想让其重新编译,这时我们就需要去更新我们的3个时间去让文件变成新的,这里我们就需要我们的touch指令来修改文件的时间戳:
touch -a/-c/-m:分别对应更新a,c,m三个时间,倘若不加,就是对整体的文件的三个时间进行更新。
在这里要补充一下:对于.PHONY的伪目标,它是无视时间的,只要调用就必定执行,没有新旧判断这一说。

好了,现在我们已经掌握了vim编译器软件,gcc/g++编译代码,自动化构建代码,我们接下来就开始正式进行我们的进度条小程序的书写。

2.进度条程序:

说到进度条,我在这里以LOL的进度条举例子,在我们进入LOL游戏之前的10人英雄界面,我们就有一个进度条和一个不断旋转的小圆圈,入下:
在这里插入图片描述

由于实在找不到旋转的图标了,但是我们大致看到右下角的这个进度数字标识,再加上我们的进度条和一个360度旋转的小光标,这便是我们整个进度条程序的全部组成部分,如下:
在这里插入图片描述
好了,大致的铺垫都完事了,现在让我们开始正式书写程序。

1.缓冲区:

在LINUX中提供了一个接口头文件<unistd.h>,通过它可以让我们使用sleep函数,让程序休眠一段时间(秒数),首先我们要明白,在C/C++语言中,针对标准输出,给我们提供了默认的缓冲区,主要是输出缓冲区,在stdout开启的同时stdin,stdeer也会随着stdout一同开启。
所以,本质上我们打印的本质是先将要打印的内容放到stdout的输出缓冲区中,然后立刻刷新输出缓冲区打印出内容,倘若我们延时sleep前刷新stdout,我们就会立刻打印出内容,而不是等了几秒才打印,而我们平时使用的换行符\n实际上也是一种强制刷新的方式,所以我们回车每次都可以强制刷新缓冲区。
C/C++为我们提供了fflush()函数来强制刷新缓冲区,利用它我们就可以不断刷新缓冲区从而实现动态进行程序的效果

2.回车换行:

注意,回车和换行是两个完全不同的概念,你可以去看我们的键盘。我们所谓的回车键是这样的:
在这里插入图片描述
注意一个细节,这里的回车键先向下再向左,但我们实际上的回车就是直接回到最左边,所以我们键盘上实际的回车键是先向下再回车而不是单纯回车的意思,这个不要理解错了。所以,为什么我们在程序输入的时候Enter会直接跳到下一行的最开始而不是直接下一行,这正是因为这个键位是回车加换行的意思,两个命令同时触发了,**在C语言中,换行就是‘\n’,而回车就是’\r’.**故倘若我们向让我们的光标一直停在一个位置开头,就每一次都回车一次让光标一直在最左边的开头位置即可。
同时注意,我们其实每一个向屏幕输入的数字也好,字母也好,各种符号也好,他们的本质都是以字符形式被识别和存储的,而根据我们占位符的不同去识别和理解这个字符对应的数据到底是什么,所以这才有ASCII表的存在,就是为了转化字符的。

程序代码如下:

有了上面的知识的铺垫,我们的进度条自然而然就出来了,代码如下:
process.c文件(函数文件)
本来是想给源码的,但是太麻烦了,就直接上图片了,抱歉~~~~~!!!
在这里插入图片描述
这个就是我们的函数代码,我称之为进度条最终版本,在这个版本里,我们主要要实现一个进度条配合程序实时的跟进,而不是进度条只是自己向后运行而不考虑实际的下载情况,所以我在这里采取的是传入比率的方式带入程序进行运算,我在这里定义了一个静态数组和一个静态的整型,其中静态数组是保证进度条一直向前进行的,由于我们的进度条是函数形式,出了函数进度条的数组就会被销毁(当然,倘若你定义一个全局变量的数组也可以),同时,为了保证我们的进度条加载卡住的时候我们的小光标依旧在选旋转,我们的这个整型变量配合上一个字符数组来保证我们小光标的实时更新,以告诉用户我们还在加载而不是卡住了,打印的过程中的那个\033[31;44是我加上的输出的字符的颜色和背景色,这个上网就能查到,注意看我fflush的位置,我是先让打印结果进入缓存区后,再刷新,这样就起到了在我主函数的延时之前先刷新字符串打印出我们的进度条。
在最后加载完成的时候,补上加载完成的提示语句,此时rate为100%,即为进度条加载完毕。
process.h文件(头文件)
在这里插入图片描述
main.c文件(主函数文件)
在这里插入图片描述
我们在这里配合着我们的头文件一起看主函数文件,头文件不多说,其实主函数文件也没什么说的,基本的代码大家都能看懂,但是在这里我想说一说,我们的进度条一般是作为回调函数来调用使用的,而不是直接放入程序内部,因为这个进度条可能需要实时更新,所以我们定义了一个函数指针并将其类型重定义为callback_t ,传入我们的加载download函数中为cb,配合着cb,这样,我们只需要传入我们每次想要使用的任意版本的进度条即可,这种方法效率很高,同时也很节省时间。usleep保证了时间的进一步缩小为毫秒为单位,更加接近真实的时间而不是sleep的慢速,同时我们还模拟了一个进度条卡住的情况,当rate大于百分之50的时候,由于total被限制为target的一半,故我们的rate也被限制在百分之50,从而就模拟了进度条卡住的情况。
通过这样写,我们的最终效果如下:
在这里插入图片描述
虽然很简陋,但是你依旧可以通过一些方式进一步优化这个建议的进度条**,不过在我看来,最重要的是一些我们缓冲区,延时函数,如何操作LINUX熟练的写程序,进度条为何利用回调函数来进行等等这些重要的知识才最为关键**

总结:

无论如何,我们的进度条代码算是完成了,这是我们的第一个LINUX的小程序,但不会是最后一个,写下了这个小程序,让我们更加熟练的使用LINUX去写代码,这才是我们的关键所在,学习LINUX不仅仅是会写代码,同时要对整个生态,系统的一些很多知识有更多的理解和新的感受,这才是最关键的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/146470.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Devart dotConnect ADO.NET Data Providers Crack

开发数据相关 .NET 应用程序的终极解决方案&#xff1a;快速、灵活、全面、功能丰富、支持 ORM 的 ADO.NET 提供程序 概述 实体框架 连接字符串 博客 高性能 ADO.NET 数据提供程序 dotConnect 是基于 ADO.NET 架构和采用多项创新技术的开发框架构建的增强型数据连接解决方​​…

【配置环境】VS Code怎么使用JavaScript的Mocha测试框架和Chai断言库

一&#xff0c;环境 Windows 11 家庭中文版&#xff0c;64 位操作系统, 基于 x64 的处理器VS Code 版本: 1.83.1 (user setup)Node.js 版本&#xff1a;20.9.0 二&#xff0c;安装背景 在运行测试用例时遇到 ReferenceError: describe is not defined 错误&#xff0c;网上搜寻…

C/C++预定义宏、 #line 、#error、 #pragma和泛型选择

文章目录 预定义宏_ _func_ _是C语言的预定义标识符 #line和#error#pragma泛型选择&#xff08;C11&#xff09;参考 预定义宏 C标准规定了一些预定义宏&#xff1a; _ _func_ _是C语言的预定义标识符 C99 标准提供一个名为_ _func_ _的预定义标识符&#xff0c;它展开为一…

ElasticStack日志分析平台-ES 集群、Kibana与Kafka

一、Elasticsearch 1、介绍&#xff1a; Elasticsearch 是一个开源的分布式搜索和分析引擎&#xff0c;Logstash 和 Beats 收集的数据可以存储在 Elasticsearch 中进行搜索和分析。 Elasticsearch为所有类型的数据提供近乎实时的搜索和分析&#xff1a;一旦数据被索引&#…

《向量数据库指南》——TruLens + Milvus Cloud构建RAG深入了解性能

深入了解性能 索引类型 本例中,索引类型对查询速度、token 用量或评估没有明显影响。这可能是因为数据量较小的关系。索引类型对较大语料库可能更重要。 Embedding 模型 text-embedding-ada-002 在准确性(0.72,平均 0.60)和答案相关度(0.82,平均0.62)上优于 MiniLM Embeddin…

Office Word 中的宏

Office Word 中的宏 简介宏的使用将自定义创建的宏放入文档标题栏中的“自定义快速访问工具栏”插入指定格式、内容的字符选中word中的指定文字查找word中的指定文字A&#xff0c;并替换为指定文字B插入文本框并向内插入文字word 表格中的宏操作遍历表格中的所有内容批量设置表…

CTF-PWN-堆- 【off-by-one】

文章目录 堆的off-by-one利用思路Asis CTF 2016 b00ks libc 2.31IDA源码main输入名字creat函数dele函数edit函数print函数reeditor name函数 思路exp思路 堆的off-by-one off-by-one指的是单字节缓冲区溢出&#xff08;off-by-one 是可以基于各种缓冲区的&#xff0c;比如栈、…

解决公网下,k8s calico master节点无法访问node节点创建的pod

目的&#xff1a;解决pod部署成功后&#xff0c;只能在node节点访问&#xff0c;而master节点无法访问 原因&#xff1a;集群搭建时&#xff0c;没有配置公网进行kubectl操作&#xff0c;从而导致系统默认node节点&#xff0c;使用内网IP加入k8s集群&#xff01;如下&#xff…

八股文-TCP的三次握手

TCP协议是一种面向连接、可靠传输的协议&#xff0c;而建立连接的过程就是著名的三次握手。这个过程保证了通信的双方能够同步信息&#xff0c;确保后续的数据传输是可靠和有序的。本文将深入解析TCP三次握手的步骤及其意义。 漫画TCP的三次握手 TCP连接的建立采用了三次握手的…

VSCode 使用CMakePreset找不到cl.exe编译器的问题

在用vscode开发c项目的时候&#xff0c;使用预先配置的CMakePresets.json可以把一些特定的cmake选项固定下来&#xff0c;在配置时直接使用 "cmake --config --preset presetname"就可以进行配置&#xff0c;免去在命令行输入过多的配置参数。 但是在vscode中&#…

C++菜鸟日记2

关于getline()函数&#xff0c;在char和string输入的区别 参考博客 1.在char中的使用&#xff1a; 2.在string中的使用&#xff1a; 关于char字符数组拼接和string字符串拼接方法 参考博客 字符串拼接方法&#xff1a; 1.直接用 号 2.利用append&#xff08;&#xff0…

【观察】华为:数智世界“一触即达”,应对数智化转型“千变万化”

毫无疑问&#xff0c;数智化既是这个时代前进所趋&#xff0c;也是国家战略所指&#xff0c;更是所有企业未来发展进程中达成的高度共识。 但也要看到&#xff0c;由于大量新兴技术的出现&#xff0c;技术热点不停的轮转&#xff0c;加上市场环境的快速变化&#xff0c;让数智化…

Nacos 配置中心底层原理(1.X版本)

前言 Nacos 1.X版本 是长轮询 Nacos 2.X版本 是GRPC 长轮询 概念 客户端会轮询向服务端发出一个长连接请求&#xff0c;这个长连接最多30s就会超时&#xff0c;服务端收到客户端的请求会先判断当前是否有配置更新&#xff0c;有则立即返回&#xff0c;如果没有服务端会将这个…

upload-labs关卡9(基于win特性data流绕过)通关思路

文章目录 前言一、靶场需要了解的知识1::$data是什么 二、靶场第九关通关思路1、看源码2、bp抓包修改后缀名3、检查是否成功上传 总结 前言 此文章只用于学习和反思巩固文件上传漏洞知识&#xff0c;禁止用于做非法攻击。注意靶场是可以练习的平台&#xff0c;不能随意去尚未授…

【cpolar】Ubuntu本地快速搭建web小游戏网站,公网用户远程访问

&#x1f3a5; 个人主页&#xff1a;深鱼~&#x1f525;收录专栏&#xff1a;cpolar&#x1f304;欢迎 &#x1f44d;点赞✍评论⭐收藏 目录 前言 1. 本地环境服务搭建 2. 局域网测试访问 3. 内网穿透 3.1 ubuntu本地安装cpolar 3.2 创建隧道 3.3 测试公网访问 4. 配置…

Netty Review - 从BIO到NIO的进化推演

文章目录 BIODEMO 1DEMO 2小结论单线程BIO的缺陷BIO如何处理并发多线程BIO服务器的弊端 NIONIO要解决的问题模拟NIO方案一&#xff1a; &#xff08;等待连接时和等待数据时不阻塞&#xff09;方案二&#xff08;缓存Socket&#xff0c;轮询数据是否准备好&#xff09;方案二存…

医院数字化LIS(检验信息系统)源码

临床检验信息管理系统&#xff08;LIS&#xff09;是利用计算机连接医疗设备&#xff0c;通过计算机信息处理技术&#xff0c;将医院检验科或实验室的临床检验数据进行自动收集、存储、处理、提取、传输和交换&#xff0c;满足所有授权用户的功能需求。 一、系统概述 1.LIS&am…

Redis篇---第五篇

系列文章目录 文章目录 系列文章目录前言一、持久化有两种,那应该怎么选择呢?二、怎么使用 Redis 实现消息队列?三、说说你对Redis事务的理解前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,…

Axure基础详解二十二:随机点名效果

效果演示 组件 建立一个【中继器】&#xff0c;内部插入一个“文本框”。【中继器】每页项目数为1&#xff0c;开始页为1。 设置交互 页面载入时交互 给【中继器】新曾行&#xff0c;“name”数据列添加10行数据&#xff0c;填入相应的名字&#xff1b;“shunxu”数据列全部…

黑马程序员微服务 分布式搜索引擎3

分布式搜索引擎03 0.学习目标 1.数据聚合 **聚合&#xff08;aggregations&#xff09;**可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 什么品牌的手机最受欢迎&#xff1f;这些手机的平均价格、最高价格、最低价格&#xff1f;这些手机每月的销售…