【Linux】探索缓冲区的概念 | Git 三板斧 | 实现简易进度条

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

  爆笑教程只送有缘人 👉 《看表情包学Linux》

💭 写在前面本章我们先对缓冲区的概念进行一个详细的探究之后会带着大家一步步去编写一个简陋的 "进度条" 小程序过程还是挺有意思的虽然实现的过程表现得非常沙雕但它是本 Linux 专栏中第一个小程序。在讲解进度条的实现之前还会讲解一下 "回车和换行" 的区别。最后我们来介绍一下 Git着重讲解一下 Git 三板斧一般只要掌握三板斧就基本够用了。


Ⅰ. 缓冲区Buffer

0x00 引入发现 "缓冲区" 的存在

先说一下 unistd.h 库中的 sleep 函数它可以按照秒去休眠

💬 代码演示

#include <stdio.h>
#include <unistd.h>

int main(void) {
    printf("Helo,World!\n");  
    sleep(2);                 

    return 0;
} 

❓ 思考首先运行的是 A  处的代码还是 B 处的代码

这还用思考肯定打出 Helo, World先运行A 处代码然后运行 B 处代码休眠 2s

竞猜环节如果我们把 Helo,World 后面的 \n 给去掉此时是先执行 A 还是先执行 B 

 看样子是先执行 B 再执行 A 了but……

然而实际上无论你加不加 \n代码都是从上往下先运行的即先执行 printf 再执行 sleep

代码没有任何的循环判断跳转什么的操作那一定是 从上到下按顺序执行的要坚信自己

这就是所谓的 "顺序结构"也是我们的默认结构。

既然是从上到下按顺序执行可是我们运行代码观察到的现象就是 sleep 先休眠 2s 然后打印啊。

 怎么肥事啊

" 呵呵printf 已经执行了但是数据还没有刷新出来。"

💡 真像实际上printf 已经先执行了只是这个 "Helo,World" 没有立马被显示出来罢了

当我们 sleep 时也没有显示当我们 sleep 完甚至到程序退出后这个 "Helo,World" 才显示出来。

这个时候如果打印的消息如果没有立即被显示出来

在 sleep 执行期间它最后显示出来证明了它的存在

但是 sleep 2s 内它并没有显示出来那么问题来了 —— 这个 "Helo,World" 在哪

 难道有什么东西存着它吗没错有这就是  " 缓冲区 "

0x01 缓冲区的理解

 什么是缓冲区这个缓冲区在哪里缓冲区其实说白了就是一段内存空间。

既然是内存空间那我们就能理解刚才举的例子里的 "Helo,World" 数据是放在了内存空间里。

只要在内存里就没有打印出来所以我们 sleep 2s 时它一直在内存里 "躺平" 呢。

最后 return 退出的时候这个数据才显示出来所以才看到了我们现在看到的现象

缓冲区的理解就是一段内存空间。立马将内存中的空间显示出来 \rightarrow 刷新策略 

我们今天不探讨什么策略就往显示器打印这个点来说我们只关注一种策略 —— 行刷新 

所谓的行刷新就是你要输出的一个行字符串当中看它是不是一个完整行

如果是一个完整行就会立马刷新出来如果不是就不刷新让它去缓冲区一边凉快去

等缓冲区变满了或者程序退出了再或者碰到换行服务再把它一块送出去。

那么如何证明你一个文本是完整的一行呢

这也很简单只要你打印的内容包含 \n包含反斜杠 n 在内的之前的所有内容成为一行。

不是直接把数据刷到我们外设上 还是把数据先放到缓冲区里只不过因为你有 \n

它就立马根据刷新策略把内容给你刷新出来仅此而已。

❓ 如果我不想用 \n我就想让我的数据立马刷新出去立马显示出来呢

这里就说来话长了我们不得不说一下 stdinstdoutstderr 的知识。

  一般一个程序默认在启动的时候会默认打开三个输入输出流

#include <stdio.h>

extern FILE* stdin;
extern FILE* stdout;
extern FILE* stderr;

如何刷新呢我们还可以通过 fflush() 去强制刷新

#include <stdio.h>

int fflush(FILE* stream);

如果你仔细观察你会发现它的参数和我们 stdinstdout stderr 类型是一样的都是 FILE*

" 实际上你们所有的 printf 底层打印的都是往 stdout 里丢的 "

在没有 \n 时我们通过 fflush 让它打印完立马给我刷新

🚩 运行结果如下

Ⅱ. 实现一个简易 "进度条"

0x00 回车和换行的概念

 在实现简易 "进度条" 之前我们还需要讲解一下回车和换行的概念。

❓ 思考你认为回车和换行是一个概念吗

  • 回车将光标拨回到当前行的最开始最左侧
  • 换行新起一行并不影响光标的位置

我们所理解的 "换行"  并不是这里的换行想达到我们所理解的 "换行" 效果

即新起一行并将光标拨回最开始位置就需要

回车 + 换行

 下面我们正式来谈一谈 "回车 + 换行" 的问题。

回车对应的就是 \r而我们在 C语言中经常使用的 \n 其实就是 "回车 + 换行"。

0x01 先学会模拟 "倒计时"

为了写 "进度条"我们先来模拟一下 "倒计时" 

💬 代码演示从 9 开始倒计时

#include <stdio.h>
#include <unistd.h>

int main(void) {
  int cnt = 9;
  while (cnt) {
    printf("%d\r", cnt--);
    fflush(stdout);
    sleep(1);
  }

  return 0;
}

🚩 运行结果演示

0x02 开始实现简易进度条

我们想要实现的效果

我们先创建一个空文件夹并创建一个 process.c 文件

$ mkdir process_bar
$ touch process.c

然后我们形成一个 Makefile 文件

 

process:process.c
	gcc -o process process.c
.PHONY:clean
clean:
	rm -f process 

然后我们打开刚才创建的 process.c 文件我们实现出 '#' 的填充部分

定义一个 process() 函数用于实现进度条。我们假设 100 个单位定义一个宏 TIMES 表示然后创建 bar 数组存放因为最后要存 \0 所以这里我们需要多预留一个位置给它所以定义一个 TIMES+1 的宏名曰 NUM。为了方便我们索性使用 memset 将所有缓冲区空间设置为 \0

然后开始我们的计数操作创建一个 cnt 变量 while 它个 100 下每次打印 bar 中的 1 个然后用 # 填充 cnt 对应位置的 bar 元素。

然后让程序睡个一秒再继续走循环判断。

🚩 运行结果如下

 …… 额这进度条未免有些沙雕哈没关系这是初始版嘛很正常 ㅋㅋㅋㅋㅋ

emm现在显然有两个问题亟待解决

  • sleep 一秒这是打印地是否有些太慢……
  • 这个进度条是否有些奇葩哪有换行打的进度条雾草太摩登了。

我们先来解决第一个问题看看 usleep 函数问问那个男人

$ man 3 usleep

按照微秒为单位去休眠我们 usleep(20000) 能让它 2 秒内跑完。

20000\mu s = 20 ms = 0.02s

然后我们刚才还讲解缓冲区概念的时候还介绍了 fflush()  和 \r在这里就派上用场了。

和刚才的倒计时一样这里换行的主要原因还是我们 printf 用了 \n我们修改一下写的代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define TIMES 100
#define NUM TIMES+1

void process() {
  char bar[NUM];
  memset(bar, '\0', sizeof(bar));  // 将所有缓冲区空间设置为\0

  int cnt = 0;
  while (cnt <= TIMES) {
    printf("[%s]\r", bar);  // 改成\r
    fflush(stdout);         // 将数据立马显示出来

    bar[cnt++] = '#';   // 填充#作为进度图例

    usleep(20000);      // 休眠0.02s  
  }
  
  printf("\n");    // 跑完再让他换个行
}

int main(void) {
  process();
  return 0;
}

🚩 运行结果如下

不错有个进度条的样子了。但好像没有给进度条预留一块空间啊

现在的进度条是带着 [ ] 直接往后怼的我们可以给 [ ] 预留 100 个 字符空间

printf("[%100s]\r", bar);

我们来看看效果如何3,2,1 笑

哈哈哈哈哈哈太沙雕了虽然给 [ ] 预留空间了但是是从右往左反过来打的。

  从右往左阿拉伯兄弟直呼内行 

为什么会这样呢因为 C 语言默认的对齐方式是右对齐的如果想让它左对齐就要加 -

printf("[%-100s]\r", bar);

这下就没有问题了

下面我们来加上 "百分比"

百分比不就是我们定义的 cnt 变量么我们打印出来就行

printf("[%-100s] [%d %%]\r", bar, cnt);

最后我们再实现一下 "不断旋转的光标"就大功告成了。

 想做到不断旋转的视觉效果通过   | / - \ 这四个符号不断变化即可。

由于 \ 需要用转义才能表示所以需要 \\ 我们把它们存到变量中。

打印时访问我们定义的变量即可这里将  cnt % 4 就可以按顺序循环访问这四个字符了。

/* 不断旋转的光标:   | / - \   */
  const char* lable = "|/-\\";

  int cnt = 0;
  while (cnt <= TIMES) {
    printf("[%-100s] [%d%%] ... %c\r", bar, cnt, lable[cnt % 4]);  // 改成\r

🚩 运行结果如下

简易的进度条就大功告成了 ~

Ⅲ.  Git 介绍

0x00 引入git 是什么

Git 是一个分布式版本控制系统它允许多个人在同一个项目中进行协作。

它允许用户在开发过程中跟踪文件的更改并在需要时回滚到之前的版本。

这样可以在团队协作开发时避免冲突并保证项目的完整性。

0x01 在 Github 创建项目

 点击 Repositories 进入如下页面然后点击 New

创建 repository

在创建好的项目页面中复制项目的链接以备接下来进行下载

 创建好仓库后如果我想把代码提交到 git我们可以把 HTTPS 的内容复制下来。

0x02  git clone 克隆

复制好 url 后如果想把远端的仓库克隆到本地我们可以用 git clone 指令。

 创建一个放置代码的目录

git clone [url]        # 此处的 url 是刚刚建立好的项目的链接

第一次的时候会让你输入账号和密码

此时我们就能看到仓库的名字赫然纸上

这就是我们从远端拉去下来的我们所建立的项目。

你可以进 .git 仓库里看看看看就行不要对里面的东西做任何的修改

如果我们想把我们的代码提交上去比如我们创建一个 test.c 文件

如果你想在提交之前看看 本地仓库 远端仓库 之间的关系你可以输入 git status 查验

git status

 

0x03 三板斧之第一板斧 —— git add 添加

 如果想上传到远端我们就要使用 git add 指令来操作了。

git add [file name]

我们试着把刚才创建的 test.c 文件添加到我们本地的仓库

第一次使用 git 的时候可能会让你配置一下你的用户名和邮箱

添加到本地仓库之后我们再介绍一个 git commit 指令提交日志。

0x04 三板斧之第二板斧 —— git commit -m

git commit -m     # -m选项代表的是本次的提交日志
# 提交时应该表明提交日志、描述改动的详细内容务必培养这个好习惯。

提交日志要好好写不要瞎写因为这是要给人看的你写的一切殊不知……

 日志存在的目的是为了给人看的也是给自己看的。

写些什么呢写一写你做了什么东西比如

0x05  三板斧之第三板斧 —— git push 推入

刚才已经将 test.c 存入本地仓库了现在我们想要把它传送到远端仓库即远端服务器上

git push

需要填入用户名与密码同步成功后刷新 Github 页面就能看到代码改动啦。

🔺 总结 建立仓库 → git clone git add → git commit -m "日志内容"git push

📌 [ 笔者 ]   王亦优
📃 [ 更新 ]   2023.1.12
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限本文有错误和不准确之处在所难免
              本人也很想知道这些错误恳望读者批评指正

📜 参考资料 

C++reference[EB/OL]. []. http://www.cplusplus.com/reference/.

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

百度百科[EB/OL]. []. https://baike.baidu.com/.

比特科技. Linux[EB/OL]. 2021[2021.8.31 xi

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux

“【Linux】探索缓冲区的概念 | Git 三板斧 | 实现简易进度条” 的相关文章