当前位置: 首页 > 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.lryc.cn/news/595853.html

相关文章:

  • iOS组件化详解
  • 深入浅出控制反转与依赖注入:从理论到实践
  • SOLIDWORKS 2025电脑硬件配置指南-代理商硕迪科技
  • [pdf epub]《软件方法》电子书202507更新下载
  • 【ArcGIS Pro】设置临时存储文件夹(计算缓存数据存放位置)
  • 用org.apache.pdfbox 转换 PDF 到 图片格式
  • 前端自动化埋点:页面模块级行为跟踪与问题定位系统​​的技术设计方案
  • AngularJS 动画
  • 厌氧菌数据挖掘可行性评估报告
  • 类对马岛之魂 落叶交互
  • “闪存普惠”如何一步到位? 华为在商业市场破题
  • 【QT常用技术讲解】QSystemTrayIcon系统托盘
  • 【bug】 jetson上opencv无法录制h264本地视频
  • 华为服务器操作系统openEuler介绍与安装
  • 【LeetCode 热题 100】46. 全排列——回溯
  • Kafka灰度方案
  • Lua语言
  • LNMP平台部署
  • 数据库—修改某字段默认值
  • [08006][1033] ORA-01033: ORACLE 正在初始化或关闭--问题修复
  • 从ZooKeeper到KRaft:Kafka架构演进与无ZooKeeper部署指南
  • 第13天 | openGauss逻辑结构:表管理1
  • CanOpen--SDO 数据帧分析
  • RabbitMQ应用问题
  • 新手向:基于Python的剪贴板历史增强工具
  • MongoDB数据库详解-针对大型分布式项目采用的原因以及基础原理和发展-卓伊凡|贝贝|莉莉
  • Go 并发(协程,通道,锁,协程控制)
  • 基于 FFT + VMD 预处理的 1DCNN‑Informer 双支路并行、多头注意力融合分类模型
  • 【JS】获取元素宽高(例如div)
  • 力扣-链表相关题 持续更新中。。。。。。