Linux操作系统从入门到实战(十二)Linux操作系统第一个程序(进度条)
前言
在前文中,我们学习了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解析
变量定义
BIN=download
:指定最终生成的可执行文件名为download
;CC=gcc
:指定编译器为gcc
;SRC=$(wildcard *.c)
:通过wildcard
通配符匹配当前目录下所有.c
源文件(如process.c
、main.c
);OBJ=$(SRC:.c=.o)
:将SRC
中所有.c
后缀替换为.o
,自动生成目标文件列表(如process.o
、main.o
)。
核心规则
- 链接规则
$(BIN): $(OBJ)
:表示download
依赖所有.o
文件,通过gcc -o $@ $^
将目标文件链接为可执行文件($@
代表目标download
,$^
代表所有依赖的.o
文件); - 编译规则
%.o: %.c
:通配符规则,将任意.c
文件编译为同名.o
文件($<
代表依赖的.c
文件); - 清理规则
clean
:通过rm
删除编译生成的.o
和可执行文件,PHONY
声明clean
为伪目标(避免与同名文件冲突)。
- 链接规则
静默编译
规则前的@
符号用于隐藏命令本身的输出,只显示自定义提示(如“编译中:process.c → process.o”),使输出更简洁。
使用方法
- 编译程序:在终端执行
make
,自动完成编译和链接,生成download
可执行文件; - 运行程序:执行
./download
,即可看到动态进度条效果; - 清理文件:执行
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); // 符号长度为4
char 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); // 强制刷新
// 更新进度:填充一个字符,进度+1
bar[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
非常感谢您的阅读,喜欢的话记得三连哦 |