当前位置: 首页 > news >正文

Linux操作系统从入门到实战(十二)Linux操作系统第一个程序(进度条)

Linux操作系统从入门到实战(十二)Linux操作系统第一个程序(进度条)

  • 前言
  • 一、Makefile自动化构建
    • 完整Makefile代码
    • Makefile解析
    • 使用方法
  • 二、进度条核心实现
    • 1. 头文件 process.h
    • 2. 实现文件 process.c
    • 3. 主函数 main.c(场景模拟)
  • 三、效果
    • 效果展示


前言

在前文中,我们学习了Linux开发工具中make/Makefile的自动化构建知识,包括基础语法、推导过程与扩展规则

  • 今天,我们将基于这些知识,动手实现Linux环境下的第一个实战程序——动态进度条。这个程序虽简单,却能帮我们理解C语言输入输出、缓冲区机制、Makefile构建流程等核心知识点,为后续复杂程序开发打下基础。

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的Linux知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12879535.html?spm=1001.2014.3001.5482


一、Makefile自动化构建

要实现进度条程序,首先需要通过Makefile管理编译流程。一个清晰的Makefile能让我们更高效地编译、调试和清理代码。

完整Makefile代码

BIN=download   # 最终生成的可执行文件名
CC=gcc         # 编译器指定为gcc
SRC=$(wildcard *.c)  # 匹配当前目录下所有.c源文件
OBJ=$(SRC:.c=.o)     # 将所有.c文件替换为.o目标文件# 链接阶段:将所有目标文件合并为可执行文件
$(BIN): $(OBJ)@$(CC) -o $@ $^ @echo "链接完成:$^ → $@"# 编译阶段:将单个.c文件编译为.o目标文件
%.o: %.c@$(CC) -c $< @echo "编译中:$< → $@"# 清理编译产物(伪目标)
.PHONY: clean
clean:rm -f $(OBJ) $(BIN)@echo "清理完毕!"

Makefile解析

  1. 变量定义

    • BIN=download:指定最终生成的可执行文件名为download
    • CC=gcc:指定编译器为gcc
    • SRC=$(wildcard *.c):通过wildcard通配符匹配当前目录下所有.c源文件(如process.cmain.c);
    • OBJ=$(SRC:.c=.o):将SRC中所有.c后缀替换为.o,自动生成目标文件列表(如process.omain.o)。
  2. 核心规则

    • 链接规则 $(BIN): $(OBJ):表示download依赖所有.o文件,通过gcc -o $@ $^将目标文件链接为可执行文件($@代表目标download$^代表所有依赖的.o文件);
    • 编译规则 %.o: %.c:通配符规则,将任意.c文件编译为同名.o文件($<代表依赖的.c文件);
    • 清理规则 clean:通过rm删除编译生成的.o和可执行文件,PHONY声明clean为伪目标(避免与同名文件冲突)。
  3. 静默编译
    规则前的@符号用于隐藏命令本身的输出,只显示自定义提示(如“编译中:process.c → process.o”),使输出更简洁。

使用方法

  1. 编译程序:在终端执行make,自动完成编译和链接,生成download可执行文件;
  2. 运行程序:执行./download,即可看到动态进度条效果;
  3. 清理文件:执行make clean,删除所有编译生成的文件(.o和可执行文件)。

二、进度条核心实现

进度条的核心是动态刷新显示进度,需要解决三个问题:如何计算进度、如何实时更新显示、如何控制刷新频率。我们将通过process.h(声明)、process.c(实现)和main.c(场景模拟)三个文件实现。

1. 头文件 process.h

#ifndef PROCESS_H  // 头文件防护:防止重复包含
#define PROCESS_H// 进度条长度:100个字符+1个终止符'\0'
#define LENGTH 101
// 进度条填充字符
#define LABLE '*'// 依赖的头文件
#include <string.h>   // 字符串操作(strlen/memset)
#include <unistd.h>   // 延时函数(usleep)
#include <stdio.h>    // 输入输出(printf/fflush)// 函数声明
// 根据目标值和当前值刷新进度条
void FlushProcess(double target, double current);
// 简易进度条(带动态符号)
void Process();#endif  // PROCESS_H

解析

  • 宏定义LENGTH=101:进度条最多显示100个*,加上字符串终止符\0,总长度为101;
  • 头文件防护#ifndef PROCESS_H:避免同一头文件被多次包含导致的函数/变量重复定义;
  • 函数声明:明确对外提供的接口,方便其他文件调用。

2. 实现文件 process.c

#include "process.h"// 根据目标值和当前值刷新进度条
void FlushProcess(double target, double current)
{// 计算进度百分比(当前值/目标值*100)double rate = (current / target) * 100.0;// 进度条填充的字符数(取整数部分)int cnt = (int)rate;// 初始化进度条缓冲区char bar[LENGTH];memset(bar, 0, sizeof(bar));  // 清空缓冲区(避免脏数据)// 填充进度条:前cnt个字符为LABLE('*')for(int i = 0; i < cnt; i++)bar[i] = LABLE;// 打印进度条:// [%-100s]:左对齐100个字符(确保进度条长度固定)// [%.lf%%]:显示百分比(%%转义为%)// \r:回到行首(覆盖当前行,实现动态刷新)printf("[%-100s][%.lf%%]\r", bar, rate);// 强制刷新缓冲区(避免输出被缓冲不显示)fflush(stdout);
}// 简易进度条(带动态旋转符号)
void Process()
{// 动态旋转符号(循环显示:| / - \)const char *symb = "|/-\\"; int s_len = strlen(symb);  // 符号长度为4char bar[LENGTH];memset(bar, '\0', sizeof(bar));  // 清空缓冲区int cnt = 0;  // 当前进度(0~100)// 从0%到100%循环刷新while(cnt <= 100){// 打印格式:进度条 + 百分比 + 旋转符号printf("[%-100s][%d%%][%c]\r", bar, cnt, symb[cnt % s_len]);fflush(stdout);  // 强制刷新// 更新进度:填充一个字符,进度+1bar[cnt++] = LABLE;// 延时20ms(控制刷新速度,避免太快看不清)usleep(20000);  // 单位:微秒(20000us = 20ms)}// 进度完成后换行(避免后续输出覆盖)printf("\n");
}

核心解析

  • FlushProcess函数:根据“当前值/目标值”计算进度,用*填充进度条,通过printf打印;
  • \r(回车符):使光标回到行首,下次打印时覆盖当前行,实现“动态刷新”(区别于\n换行);
  • fflush(stdout):标准输出默认是“行缓冲”(遇到\n才输出),这里用fflush强制刷新,确保实时显示;
  • usleep(20000):延时20毫秒,控制进度条刷新速度(若不延时,进度会瞬间完成);
  • 旋转符号symb:通过cnt % 4循环显示| / - \,模拟“加载中”的动态效果。

3. 主函数 main.c(场景模拟)

#include "process.h"// 模拟下载场景参数
#define TARGET_SIZE 1024.0  // 目标文件大小(单位:M)
#define SPEED 1.0           // 下载速度(单位:M/100ms)// 模拟下载过程
void Download(double target, double step)
{double current = 0.0;  // 当前已下载大小while(current < target){// 刷新进度条(当前进度=current/目标)FlushProcess(target, current);// 模拟下载耗时(100ms)usleep(100000);  // 100000us = 100ms// 累加已下载大小current += step;}// 下载完成后换行(避免后续输出覆盖)printf("\n");
}int main()
{// 调用下载模拟函数Download(TARGET_SIZE, SPEED);return 0;
}

场景说明

  • 模拟“文件下载”场景:目标文件大小1024M,每次下载1M,耗时100ms;
  • 循环调用FlushProcess:每次下载后更新进度条,直到下载完成;
  • 最后printf("\n"):进度完成后换行,避免终端后续输出与进度条重叠。

三、效果

效果展示

在这里插入图片描述


以上就是这篇博客的全部内容,下一篇我们将继续探索Linux的更多精彩内容。

我的个人主页
欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的Linux知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12879535.html?spm=1001.2014.3001.5482

非常感谢您的阅读,喜欢的话记得三连哦

在这里插入图片描述

http://www.xdnf.cn/news/1168219.html

相关文章:

  • 北京养老金计算公式网页实现案例:从需求分析到架构设计
  • J2EE模式---前端控制器模式
  • Python 绘制各类折线图全指南:从基础到进阶
  • k8s:离线部署tomcatV11.0.9,报Cannot find /opt/bitnami/tomcat/bin/setclasspath.sh
  • zabbix“专家坐诊”第295期问答
  • 以太网基础⑥ ZYNQ PS端 基于LWIP的TCP例程测试
  • MATLAB软件使用频繁,企业如何做到“少买多用”?
  • MFC类Qt的自动布局框架
  • 力扣-链表相关题 持续更新中。。。。。。
  • UE5 UI ScrollBox 滚动框
  • 欧拉系统二进制部署Docker
  • Linux_Ext系列文件系统基本认识(一)
  • Fluent许可与网络安全策略
  • 【洛谷】用两个数组实现静态单链表、静态双向链表,排队顺序
  • 【C语言进阶】动态内存管理(1)
  • 赋能未来数学课堂——基于Qwen3、LangChain与Agent架构的个性化教辅系统研究
  • Vue + WebSocket 实时数据可视化实战:多源融合与模拟数据双模式设计
  • vscode目录,右键菜单加入用VSCode打开文件和文件夹(快速解决)(含删除)(脚本)
  • 华为服务器操作系统openEuler介绍与安装
  • 信息学奥赛一本通 1553:【例 2】暗的连锁
  • C++_Hello算法_队列
  • Grails(Groovy)框架抛出NoHandlerFoundException而不是返回404 Not Found
  • Android-API调用学习总结
  • 专题 前端面试知识梳理大全
  • Leetcode题解:209长度最小的子数组,掌握滑动窗口从此开始!!!
  • Android13重置锁屏(1)
  • 碰一碰发视频源码搭建:支持OEM
  • 现在希望用git将本地文件crawler目录下的文件更新到远程仓库指定crawler目录下,命名相同的文件本地文件将其覆盖
  • 【Tomcat】Tomcat线程池深度调优手册(终极版)
  • 用USBi仿真器的SPI模式和IIC模式来调试DSP应该怎么做?