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

implement libtime on Windows

因为Windows的time命令和Linux的time命令不一样,尝试实现libtime

libtime.h

/** libtime.h - 跨平台时间测量库* 功能:执行外部命令并测量其运行时间和资源使用*/#ifndef LIBTIME_H
#define LIBTIME_H#include <stdio.h>
#include <stdlib.h>
#include <string.h>#ifdef __cplusplus
extern "C" {
#endif/* 时间测量结果结构体 */
typedef struct {double real_time;   // 实际运行时间(秒)double user_time;   // 用户CPU时间(秒)double sys_time;    // 系统CPU时间(秒)long   max_rss;     // 最大内存使用(KB)int    exit_status; // 命令退出状态码
} libtime_result;/* 输出格式选项 */
typedef enum {LIBTIME_FORMAT_DEFAULT,  // 默认格式LIBTIME_FORMAT_PORTABLE, // 便携格式LIBTIME_FORMAT_VERBOSE   // 详细格式
} libtime_format;/** 执行命令并测量时间* 参数:*   command - 要执行的命令字符串*   result  - 存储测量结果的结构体指针* 返回值:0=成功,非0=错误码*/
int libtime_execute(const char* command, libtime_result* result);/** 打印测量结果* 参数:*   result - 测量结果*   format - 输出格式*   stream - 输出流(stdout/stderr等)*/
void libtime_print(const libtime_result* result, libtime_format format, FILE* stream);/** 解析命令行参数* 参数:*   argc/argv - 命令行参数*   其他参数 - 输出解析结果* 返回值:0=成功,1=参数错误*/
int libtime_parse_args(int argc, char* argv[],libtime_format* out_format,const char** out_output_file,int* out_append,const char** out_custom_format,int* out_cmd_start);/* 释放动态分配的参数内存 */
void libtime_free_args(char**argv, int count);#ifdef __cplusplus
}
#endif#endif // LIBTIME_H#ifdef LIBTIME_IMPLEMENTATION#include <time.h>
#include <ctype.h>/* 平台相关实现 */
#if defined(_WIN32) || defined(_WIN64)
#define WIN32_LEAN_AND_MEAN
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0601
#endif
#include <windows.h>
#include <psapi.h>
#include <shellapi.h>/* Windows时间转换:FILETIME -> 秒 */
static double filetime_to_sec(const FILETIME* ft) {ULARGE_INTEGER ul;ul.LowPart = ft->dwLowDateTime;ul.HighPart = ft->dwHighDateTime;return (double)ul.QuadPart / 10000000.0; // 100纳秒为单位
}#else
/* Linux/macOS 实现 */
#include <unistd.h>
#include <sys/times.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>/* 获取系统时钟频率 */
static long get_clock_ticks() {static long ticks = 0;if (ticks == 0) {ticks = sysconf(_SC_CLK_TCK);}return ticks;
}#endif/* 执行命令并测量时间(核心实现) */
int libtime_execute(const char* command, libtime_result* result) {if (!command || !result) return -1;memset(result, 0, sizeof(libtime_result));#if defined(_WIN32) || defined(_WIN64)/* Windows 实现:通过 cmd.exe 执行命令,支持内置命令和PATH查找 */STARTUPINFOA si = {0};PROCESS_INFORMATION pi = {0};si.cb = sizeof(si);// 让子进程继承父进程的标准输入输出,确保命令输出能显示si.dwFlags = STARTF_USESTDHANDLES;si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);si.hStdError = GetStdHandle(STD_ERROR_HANDLE);// 使用 cmd /c 执行命令,自动处理PATH查找和内置命令char* cmd_line = (char*)malloc(strlen(command) + 8); // 预留 "cmd /c " 空间if (!cmd_line) return -1;sprintf(cmd_line, "cmd /c %s", command);// 高精度计时器LARGE_INTEGER start, end, freq;QueryPerformanceFrequency(&freq);QueryPerformanceCounter(&start);// 创建进程:继承句柄,不新建窗口BOOL success = CreateProcessA(NULL,               // 不指定程序名,由cmd处理cmd_line,           // 命令行NULL, NULL,         // 安全属性TRUE,               // 允许句柄继承(关键:确保输出能显示)0,                  // 无特殊标志(不新建窗口)NULL, NULL,         // 环境变量和工作目录&si, &pi);if (!success) {DWORD err = GetLastError();free(cmd_line);return err;}// 等待命令执行完成WaitForSingleObject(pi.hProcess, INFINITE);// 计算实际运行时间QueryPerformanceCounter(&end);result->real_time = (double)(end.QuadPart - start.QuadPart) / freq.QuadPart;// 获取退出状态DWORD exit_code;GetExitCodeProcess(pi.hProcess, &exit_code);result->exit_status = exit_code;// 获取CPU时间FILETIME creation, exit_time, kernel, user;GetProcessTimes(pi.hProcess, &creation, &exit_time, &kernel, &user);result->sys_time = filetime_to_sec(&kernel);result->user_time = filetime_to_sec(&user);// 获取内存使用PROCESS_MEMORY_COUNTERS_EX pmc;pmc.cb = sizeof(pmc);if (GetProcessMemoryInfo(pi.hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {result->max_rss = pmc.PeakWorkingSetSize / 1024; // 转换为KB}// 清理资源CloseHandle(pi.hProcess);CloseHandle(pi.hThread);free(cmd_line);return 0;#else/* Linux/macOS 实现:通过 /bin/sh 执行命令 */time_t real_start = time(NULL);pid_t pid = fork();if (pid == -1) {return errno; // fork失败}if (pid == 0) {// 子进程:执行命令(通过shell处理PATH和内置命令)execl("/bin/sh", "sh", "-c", command, (char*)NULL);exit(EXIT_FAILURE); // execl失败} else {// 父进程:等待并测量int status;waitpid(pid, &status, 0);result->exit_status = status;result->real_time = difftime(time(NULL), real_start);// 获取CPU时间和内存使用struct rusage ru;getrusage(RUSAGE_CHILDREN, &ru);result->user_time = (double)ru.ru_utime.tv_sec + ru.ru_utime.tv_usec / 1e6;result->sys_time = (double)ru.ru_stime.tv_sec + ru.ru_stime.tv_usec / 1e6;result->max_rss = ru.ru_maxrss;}return 0;
#endif
}/* 打印测量结果 */
void libtime_print(const libtime_result* result, libtime_format format, FILE* stream) {if (!result || !stream) return;switch (format) {case LIBTIME_FORMAT_PORTABLE:fprintf(stream, "real %f\nuser %f\nsys %f\n",result->real_time, result->user_time, result->sys_time);break;case LIBTIME_FORMAT_VERBOSE:fprintf(stream, "Command exited with status %d\n", result->exit_status);fprintf(stream, "Real time:     %.3f seconds\n", result->real_time);fprintf(stream, "User CPU time: %.3f seconds\n", result->user_time);fprintf(stream, "Sys CPU time:  %.3f seconds\n", result->sys_time);fprintf(stream, "Max RSS:       %ld KB\n", result->max_rss);break;default: // 默认格式fprintf(stream, "\n%.3f real         %.3f user         %.3f sys\n",result->real_time, result->user_time, result->sys_time);break;}
}/* 命令行参数解析辅助函数 */
static void print_help(const char* prog_name, FILE* stream) {fprintf(stream, "Usage: %s [options] command [arguments...]\n", prog_name);fprintf(stream, "Measure the execution time of a command.\n\n");fprintf(stream, "Options:\n");fprintf(stream, "  -p, --portability   Use portable output format\n");fprintf(stream, "  -v, --verbose       Use verbose output format\n");fprintf(stream, "  -o FILE             Write output to FILE\n");fprintf(stream, "  -a                  Append to FILE instead of overwriting\n");fprintf(stream, "  -f FORMAT           Use custom output format\n");fprintf(stream, "  --help              Display this help message\n");fprintf(stream, "  --version           Display version information\n");
}static void print_version(FILE* stream) {fprintf(stream, "libtime 1.0\n");fprintf(stream, "Cross-platform time measurement tool\n");
}/* 解析命令行参数 */
int libtime_parse_args(int argc, char* argv[],libtime_format* out_format,const char** out_output_file,int* out_append,const char** out_custom_format,int* out_cmd_start) {// 初始化默认值*out_format = LIBTIME_FORMAT_DEFAULT;*out_output_file = NULL;*out_append = 0;*out_custom_format = NULL;*out_cmd_start = -1;// 解析参数for (int i = 1; i < argc; i++) {if (strcmp(argv[i], "--help") == 0) {print_help(argv[0], stdout);exit(0);} else if (strcmp(argv[i], "--version") == 0) {print_version(stdout);exit(0);} else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--portability") == 0) {*out_format = LIBTIME_FORMAT_PORTABLE;} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {*out_format = LIBTIME_FORMAT_VERBOSE;} else if (strcmp(argv[i], "-o") == 0) {if (i + 1 >= argc) {fprintf(stderr, "Error: -o requires a filename argument\n");return 1;}*out_output_file = argv[++i];} else if (strcmp(argv[i], "-a") == 0) {*out_append = 1;} else if (strcmp(argv[i], "-f") == 0) {if (i + 1 >= argc) {fprintf(stderr, "Error: -f requires a format string argument\n");return 1;}*out_custom_format = argv[++i];} else if (argv[i][0] == '-') {fprintf(stderr, "Error: Unknown option '%s'\n", argv[i]);print_help(argv[0], stderr);return 1;} else {// 找到命令起始位置*out_cmd_start = i;break;}}// 检查是否有命令if (*out_cmd_start == -1) {fprintf(stderr, "Error: No command specified\n");print_help(argv[0], stderr);return 1;}return 0;
}/* 释放参数内存 */
void libtime_free_args(char**argv, int count) {if (!argv) return;for (int i = 0; i < count; i++) {free(argv[i]);}free(argv);
}#endif // LIBTIME_IMPLEMENTATION

time_cli.c

/** time.c - 跨平台 time 命令入口程序* 平台:Windows 使用 WinMain,Linux/macOS 使用 main*/#define LIBTIME_IMPLEMENTATION
#include "libtime.h"#if defined(_WIN32) || defined(_WIN64)
/* Windows 入口:处理宽字符命令行参数 */
#include <windows.h>/* 将宽字符命令行参数转换为多字节 */
static char** convert_wargv(int argc, wchar_t* wargv[], int* out_argc) {if (!wargv || !out_argc) return NULL;char**argv = (char**)malloc(sizeof(char*) * argc);if (!argv) return NULL;for (int i = 0; i < argc; i++) {// 计算所需缓冲区大小int len = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL, NULL);if (len <= 0) {libtime_free_args(argv, i);return NULL;}// 分配并转换argv[i] = (char*)malloc(len);if (!argv[i]) {libtime_free_args(argv, i);return NULL;}WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1,argv[i], len, NULL, NULL);}*out_argc = argc;return argv;
}/* Windows 标准入口函数 */
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd
) {// 获取宽字符命令行参数int wargc = 0;wchar_t**wargv = CommandLineToArgvW(GetCommandLineW(), &wargc);if (!wargv) {fprintf(stderr, "Error: Failed to parse command line\n");return 1;}// 转换为多字节参数int argc;char**argv = convert_wargv(wargc, wargv, &argc);LocalFree(wargv); // 释放宽字符参数if (!argv) {fprintf(stderr, "Error: Failed to convert command line arguments\n");return 1;}#else
/* Linux/macOS 入口:使用标准 main 函数 */
int main(int argc, char* argv[]) {// 直接使用标准C参数,无需转换
#endif// 解析命令行参数libtime_format format;const char* output_file;int append;const char* custom_format;int cmd_start;if (libtime_parse_args(argc, argv, &format, &output_file, &append, &custom_format, &cmd_start) != 0) {
#if defined(_WIN32) || defined(_WIN64)libtime_free_args(argv, argc);
#endifreturn 1;}// 构建完整命令字符串(拼接命令和参数)size_t cmd_len = 0;for (int i = cmd_start; i < argc; i++) {cmd_len += strlen(argv[i]) + 1; // 加空格}char* command = (char*)malloc(cmd_len + 1);if (!command) {fprintf(stderr, "Error: Out of memory\n");
#if defined(_WIN32) || defined(_WIN64)libtime_free_args(argv, argc);
#endifreturn 1;}command[0] = '\0';for (int i = cmd_start; i < argc; i++) {if (i > cmd_start) strcat(command, " ");strcat(command, argv[i]);}// 执行命令并测量时间libtime_result result;int ret = libtime_execute(command, &result);free(command);if (ret != 0) {fprintf(stderr, "Error: Failed to execute command (error code: %d)\n", ret);
#if defined(_WIN32) || defined(_WIN64)libtime_free_args(argv, argc);
#endifreturn 1;}// 准备输出流FILE* output = stderr;if (output_file) {output = fopen(output_file, append ? "a" : "w");if (!output) {fprintf(stderr, "Error: Failed to open output file '%s'\n", output_file);
#if defined(_WIN32) || defined(_WIN64)libtime_free_args(argv, argc);
#endifreturn 1;}}// 输出测量结果if (custom_format) {for (size_t i = 0; i < strlen(custom_format); i++) {if (custom_format[i] == '%' && i + 1 < strlen(custom_format)) {i++;switch (custom_format[i]) {case 'e': fprintf(output, "%.3f", result.real_time); break;case 'U': fprintf(output, "%.3f", result.user_time); break;case 'S': fprintf(output, "%.3f", result.sys_time); break;case 'P': fprintf(output, "%.0f", result.real_time > 0 ? (result.user_time + result.sys_time) / result.real_time * 100 : 0);break;case 'M': fprintf(output, "%ld", result.max_rss); break;case 'x': fprintf(output, "%d", result.exit_status); break;default:  fputc(custom_format[i], output); break;}} else {fputc(custom_format[i], output);}}fprintf(output, "\n");} else {libtime_print(&result, format, output);}// 清理资源if (output_file) fclose(output);
#if defined(_WIN32) || defined(_WIN64)libtime_free_args(argv, argc);
#endifreturn result.exit_status;
}

编译

  • gcc time_cli.c -lpsapi

测试输出

timecli.exe -f "=== 执行统计 ===\n实际时间: %e秒\n用户CPU: %U秒\n系统CPU: %S秒\n内存峰值: %M KB\nCPU使用率: %P\n退出代码: %x" dirtime.exe python selp.py     
def print_self_source():# __file__ 变量包含当前脚本的路径with open(__file__, 'r') as f:# 读取并打印文件内容print(f.read())if __name__ == "__main__":print_self_source()0.104 real         0.016 user         0.000 systime.exe -p ping baidu.comPinging baidu.com [182.61.244.181] with 32 bytes of data:
Reply from 182.61.244.181: bytes=32 time=102ms TTL=49
Reply from 182.61.244.181: bytes=32 time=125ms TTL=49182.61.244.181 的 Ping 统计信息:
^   数据包: 已发送 = 2,已接收 = 2,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒time.exe -v gcc --version
gcc (x86_64-posix-seh-rev0, Built by MinGW-Builds project) 15.1.0
Copyright (C) 2025 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.Command exited with status 0
Real time:     0.068 seconds
User CPU time: 0.016 seconds
Sys CPU time:  0.016 seconds
Max RSS:       5400 KB

作为库使用

#define LIBTIME_IMPLEMENTATION
#include "libtime.h"
#include <stdio.h>int main() {libtime_result result;const char* command;// 根据平台选择测试命令#ifdef _WIN32command = "dir"; // Windows 命令#elsecommand = "ls -l"; // Linux/macOS 命令#endifprintf("执行命令: %s\n", command);int ret = libtime_execute(command, &result);if (ret != 0) {fprintf(stderr, "命令执行失败,错误码: %d\n", ret);return 1;}// 打印详细时间统计printf("\n命令执行统计:\n");libtime_print(&result, LIBTIME_FORMAT_VERBOSE, stdout);return 0;
}

是bug吗?

  1. 对于阻塞的命令无法计算,比如ping baidu.com无法计算时间
  2. I create shit and bug program, damn

支持的格式符(没测试)

格式符含义示例输出
%e实际运行时间(秒,精确到毫秒)1.234
%U用户CPU时间(秒,精确到毫秒)0.120
%S系统CPU时间(秒,精确到毫秒)0.030
%PCPU使用率(百分比)12%
%M最大内存使用量(KB)1234
%x命令退出状态码0
%%输出百分号本身%
http://www.dtcms.com/a/340044.html

相关文章:

  • MyCAT基础概念
  • Python函数总结
  • week2-[一维数组]最大元素
  • 单细胞格式转换 rds 转成 h5ad
  • transformer模型初理解
  • Transformer、BERT、BEiT等模型相关八股及代码【自用】
  • HJ4 字符串分隔
  • 神经网络训练过程详解
  • 电流采样实现方法
  • JavaScript 代码保护与混淆
  • Vue2+Vue3前端开发_Day1
  • 端口映射原理操作详解教程:实现外网访问内网服务,本地路由器端口映射公网ip和软件端口映射域名2种方法
  • Qwen2.5-vl源码解读系列:LLM的Embedding层
  • MySQL常用函数
  • 首届机器人足球运动会技术复盘:从赛场表现看智能机器人核心技术突破
  • Wireshark获取数据传输的码元速率
  • 中科米堆CASAIM提供机加工件来料自动化测量尺寸方案
  • Origin绘制气泡图|科研论文图表教程(附数据格式模板)
  • 【HarmonyOS】H5 实现在浏览器中正常跳转 AppLinking 至应用
  • Java基础 8.19
  • 基于SpringBoot的停车场管理系统【2026最新】
  • C文件/Linux内核级文件理解
  • 软考网工选择题-1
  • 路由器详解
  • Windows 8.1 补丁 KB2919355 安装方法 详细步骤
  • 【Netty4核心原理⑫】【异步处理双子星 Future 与 Promise】
  • 【AI】算法环境-显卡、GPU、Cuda、NVCC和cuDNN的区别与联系
  • Stimulsoft 发布 2025.3 版本:支持在报表计算中解释运行 C# 脚本
  • Apache ShenYu网关与Nacos的关联及如何配合使用
  • 基于Envoy的AI Gateway测试环境搭建