10.Linux 时间
文章目录
- 10.1 日历时间(Calendar Time)
- 1. **日历时间的表示**
- 2. **系统调用 `gettimeofday()`**
- 3. **系统调用 `time()`**
- 4. **`time()` 与 `gettimeofday()` 的关系**
- 总结
- 10.2 时间转换函数
- 1. **`ctime()` 示例**
- 2. **`asctime()` 示例**
- 3. **`strftime()` 示例**
- 4. **`strptime()` 示例**
- 5. **综合示例**
- 总结
- 10.2.1 将 `time_t` 转换为可打印格式
- **`ctime()` 函数**
- **`ctime_r()` 函数**
- 10.2.2 `time_t` 和分解时间之间的转换
- **分解时间(Broken-down Time)**
- **`gmtime()` 函数**
- **`localtime()` 函数**
- **`gmtime_r()` 和 `localtime_r()` 函数**
- **`mktime()` 函数**
- 总结
- **时间转换函数**
- **注意事项**
- 1. **`ctime()` 示例**
- 2. **`gmtime()` 示例**
- 3. **`localtime()` 示例**
- 4. **`mktime()` 示例**
- 5. **`mktime()` 自动调整字段示例**
- 6. **`strftime()` 示例**
- 总结
- 10.2.3 分解时间和打印格式之间的转换
- 1. **`asctime()` 函数**
- 2. **`asctime_r()` 函数**
- 3. **`strftime()` 函数**
- 4. **`strftime()` 的常用格式化说明符**
- 5. **示例程序**
- 总结
- 程序清单 10-2:返回当前时间的字符串的函数
- **`currTime()` 函数**
- 程序清单 10-3:将打印格式时间转换为分解时间
- **`strptime()` 函数**
- **示例程序**
- **运行示例**
- 总结
- 10.3 时区
- 1. **时区定义**
- 2. **为程序指定时区**
- 3. **`TZ` 环境变量的两种格式**
- 4. **示例程序**
- 5. **运行示例**
- 总结
- 10.4 地区(Locale)
- 1. **地区定义**
- 2. **地区类别**
- 3. **设置地区**
- 4. **示例程序**
- 5. **运行示例**
- 总结
- 10.5 更新系统时钟
- 1. **`settimeofday()` 系统调用**
- 2. **`stime()` 系统调用**
- 3. **`adjtime()` 函数**
- 4. **`adjtimex()` 系统调用**
- 10.6 软件时钟(Jiffies)
- 1. **Jiffies 的定义**
- 2. **Linux 内核中的 Jiffies**
- 3. **Jiffies 的影响**
- 总结
- 1. **`settimeofday()` 示例**
- 2. **`adjtime()` 示例**
- 3. **`stime()` 示例**
- 4. **获取和打印当前时间**
- 5. **综合示例**
- 总结
- 10.7 进程时间
- 1. **`times()` 系统调用**
- 2. **`clock()` 函数**
- 3. **示例程序**
- 4. **运行示例**
- 5. **程序说明**
- 总结
- 10.9 练习 10-1
- **问题描述**
- **1. `times()` 的周期计算**
- **2. `clock()` 的周期计算**
- **总结**
- **注意事项**
这段文字主要讨论了计算机程序中涉及的两种时间类型:真实时间和进程时间,并详细介绍了日历时间的概念及其在UNIX系统中的表示方式。
-
真实时间:
- 日历时间:从标准点(如UTC 1970年1月1日零点,称为Epoch)开始计算的时间,用于记录时间戳。
- 流逝时间:从进程启动开始计算的时间,适用于周期性操作或外部设备的时间度量。
-
进程时间:进程使用的CPU时间总量,用于性能检查和优化。
-
硬件时钟:计算机体系结构内置的时钟,帮助内核计算真实时间和进程时间。
-
日历时间的表示:
- UNIX系统内部使用自Epoch以来的秒数表示时间,存储在
time_t
类型的变量中。 - 32位系统中,
time_t
是有符号整数,时间范围从1901年12月13日到2038年1月19日,存在“2038年问题”。 - 64位系统可以缓解这一问题,但32位嵌入式系统和历史数据仍可能受到影响。
- UNIX系统内部使用自Epoch以来的秒数表示时间,存储在
总结:程序关注真实时间和进程时间,UNIX系统使用Epoch以来的秒数表示日历时间,32位系统存在2038年问题,64位系统可以解决这一问题。
10.1 日历时间(Calendar Time)
1. 日历时间的表示
- UNIX系统内部使用自Epoch(UTC 1970年1月1日零点)以来的秒数表示时间,存储于
time_t
类型的变量中。 - 32位系统中,
time_t
是有符号整数,时间范围为1901年12月13日20时45分52秒至2038年1月19日03:14:07。 - 2038年问题:32位系统在2038年后会溢出,可能导致时间计算错误。64位系统可以解决此问题,但32位嵌入式系统和历史数据仍可能受影响。
2. 系统调用 gettimeofday()
-
用于获取日历时间,精度可达微秒。
-
函数原型:
#include <sys/time.h> int gettimeofday(struct timeval *tv, struct timezone *tz);
- 返回值:成功返回0,失败返回-1。
- 参数:
tv
:指向struct timeval
的指针,存储时间值。tz
:历史遗留参数,应始终置为NULL
。
-
struct timeval
结构体:struct timeval {time_t tv_sec; // 自Epoch以来的秒数suseconds_t tv_usec; // 额外的微秒数 };
tv_usec
提供微秒级精度,但实际精度依赖于硬件实现。
-
tz
参数:- 早期用于获取时区信息,现已废弃。
- 如果提供,返回
timezone
结构体,包含tz_minuteswest
(与UTC的分钟偏移)和tz_dsttime
(夏令时标志)。
3. 系统调用 time()
-
返回自Epoch以来的秒数,与
gettimeofday()
的tv_sec
字段相同。 -
函数原型:
#include <time.h> time_t time(time_t *timep);
- 返回值:自Epoch以来的秒数,失败返回
(time_t)-1
。 - 参数:
timep
:如果不为NULL
,将秒数存储到timep
指向的位置。
- 返回值:自Epoch以来的秒数,失败返回
-
常见用法:
time_t t = time(NULL);
4. time()
与 gettimeofday()
的关系
- 历史原因:
- 早期UNIX提供
time()
,4.3BSD补充了更精确的gettimeofday()
。 time()
可以视为gettimeofday()
的简化版本。
- 早期UNIX提供
- 实现:
time()
可以通过调用gettimeofday()
实现。
总结
- 日历时间:UNIX系统使用自Epoch以来的秒数表示时间,32位系统存在2038年问题。
gettimeofday()
:获取日历时间,支持微秒级精度,tz
参数已废弃。time()
:返回自Epoch以来的秒数,是gettimeofday()
的简化版本。- 时区与夏令时:早期通过
tz
参数处理,现已废弃,时区信息需通过其他方式管理。
以下是一些关于时间转换函数的具体示例,帮助你更好地理解如何使用这些函数。
10.2 时间转换函数
1. ctime()
示例
将time_t
转换为固定格式的字符串。
#include <stdio.h>
#include <time.h>int main() {time_t now = time(NULL); // 获取当前时间printf("Current time: %s", ctime(&now)); // 转换为固定格式字符串并打印return 0;
}
输出:
Current time: Tue Oct 10 14:30:00 2023
2. asctime()
示例
将struct tm
转换为固定格式的字符串。
#include <stdio.h>
#include <time.h>int main() {struct tm tm_info = {.tm_year = 123, // 2023年(1900 + 123).tm_mon = 9, // 10月(0-11).tm_mday = 10, // 10日.tm_hour = 14, // 14时.tm_min = 30, // 30分.tm_sec = 0 // 0秒};printf("Custom time: %s", asctime(&tm_info)); // 转换为固定格式字符串并打印return 0;
}
输出:
Custom time: Tue Oct 10 14:30:00 2023
3. strftime()
示例
将struct tm
转换为自定义格式的字符串。
#include <stdio.h>
#include <time.h>int main() {time_t now = time(NULL); // 获取当前时间struct tm *tm_info = localtime(&now); // 转换为本地时间char buffer[80];// 自定义格式:YYYY-MM-DD HH:MM:SSstrftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_info);printf("Formatted time: %s\n", buffer);// 自定义格式:Day, Month Day, Yearstrftime(buffer, sizeof(buffer), "%A, %B %d, %Y", tm_info);printf("Formatted date: %s\n", buffer);return 0;
}
输出:
Formatted time: 2023-10-10 14:30:00
Formatted date: Tuesday, October 10, 2023
4. strptime()
示例
将字符串解析为struct tm
。
#include <stdio.h>
#include <time.h>int main() {const char *time_str = "2023-10-10 14:30:00";struct tm tm_info;// 解析字符串if (strptime(time_str, "%Y-%m-%d %H:%M:%S", &tm_info) != NULL) {printf("Parsed time: %d-%02d-%02d %02d:%02d:%02d\n",tm_info.tm_year + 1900, tm_info.tm_mon + 1, tm_info.tm_mday,tm_info.tm_hour, tm_info.tm_min, tm_info.tm_sec);} else {printf("Failed to parse time string.\n");}return 0;
}
输出:
Parsed time: 2023-10-10 14:30:00
5. 综合示例
结合time()
、localtime()
和strftime()
,获取当前时间并格式化输出。
#include <stdio.h>
#include <time.h>int main() {time_t now = time(NULL); // 获取当前时间struct tm *tm_info = localtime(&now); // 转换为本地时间char buffer[80];// 自定义格式:YYYY年MM月DD日 HH时MM分SS秒strftime(buffer, sizeof(buffer), "%Y年%m月%d日 %H时%M分%S秒", tm_info);printf("当前时间: %s\n", buffer);return 0;
}
输出:
当前时间: 2023年10月10日 14时30分00秒
总结
ctime()
:将time_t
转换为固定格式字符串。asctime()
:将struct tm
转换为固定格式字符串。strftime()
:将struct tm
转换为自定义格式字符串。strptime()
:将字符串解析为struct tm
。
通过这些函数,可以轻松地在不同时间格式之间进行转换,并处理时区、本地化等复杂性问题。
10.2.1 将 time_t
转换为可打印格式
ctime()
函数
- 功能:将
time_t
转换为固定格式的字符串。 - 函数原型:
#include <time.h> char *ctime(const time_t *timep);
- 参数:
timep
:指向time_t
的指针。
- 返回值:
- 成功时返回一个 26字节 的静态分配字符串,格式为
Wed Jun 8 14:22:34 2011\n\0
。 - 失败时返回
NULL
。
- 成功时返回一个 26字节 的静态分配字符串,格式为
- 特点:
- 自动考虑本地时区和夏令时(DST)设置。
- 返回的字符串是静态分配的,后续调用会覆盖其内容。
- 非线程安全。
ctime_r()
函数
- 功能:
ctime()
的可重入版本。 - 特点:
- 允许调用者提供缓冲区存储结果,避免静态分配问题。
- 线程安全。
10.2.2 time_t
和分解时间之间的转换
分解时间(Broken-down Time)
- 分解时间存储在
struct tm
中,包含以下字段:struct tm {int tm_sec; // 秒 [0-60],60 用于闰秒int tm_min; // 分 [0-59]int tm_hour; // 时 [0-23]int tm_mday; // 日 [1-31]int tm_mon; // 月 [0-11]int tm_year; // 年(自 1900 年以来的年数)int tm_wday; // 星期 [0-6],0 表示周日int tm_yday; // 一年中的第几天 [0-365]int tm_isdst; // 夏令时标志(>0:启用,0:未启用,<0:未知) };
gmtime()
函数
- 功能:将
time_t
转换为 UTC 时间 的分解时间。 - 函数原型:
#include <time.h> struct tm *gmtime(const time_t *timep);
- 返回值:
- 成功时返回指向静态分配
struct tm
的指针。 - 失败时返回
NULL
。
- 成功时返回指向静态分配
- 特点:
- 返回的
struct tm
是静态分配的,后续调用会覆盖其内容。 - 非线程安全。
- 返回的
localtime()
函数
- 功能:将
time_t
转换为 本地时间 的分解时间。 - 函数原型:
#include <time.h> struct tm *localtime(const time_t *timep);
- 返回值:
- 成功时返回指向静态分配
struct tm
的指针。 - 失败时返回
NULL
。
- 成功时返回指向静态分配
- 特点:
- 自动考虑本地时区和夏令时(DST)设置。
- 返回的
struct tm
是静态分配的,后续调用会覆盖其内容。 - 非线程安全。
gmtime_r()
和 localtime_r()
函数
- 功能:
gmtime()
和localtime()
的可重入版本。 - 特点:
- 允许调用者提供缓冲区存储结果,避免静态分配问题。
- 线程安全。
mktime()
函数
- 功能:将本地时间的分解时间转换为
time_t
。 - 函数原型:
#include <time.h> time_t mktime(struct tm *timeptr);
- 参数:
timeptr
:指向struct tm
的指针。
- 返回值:
- 成功时返回自 Epoch 以来的秒数。
- 失败时返回
(time_t)-1
。
- 特点:
- 忽略输入
tm_wday
和tm_yday
字段,计算后更新这些字段。 - 自动调整超出范围的字段值(如
tm_sec = 123
会被调整为tm_sec = 3
,并增加tm_min
)。 - 考虑时区和夏令时(DST)设置:
- 若
tm_isdst > 0
,视为夏令时。 - 若
tm_isdst == 0
,视为标准时间。 - 若
tm_isdst < 0
,自动判断是否启用夏令时。
- 若
- 转换完成后,更新
tm_isdst
字段以反映实际状态。
- 忽略输入
总结
时间转换函数
函数 | 功能 | 特点 |
---|---|---|
ctime() | time_t → 固定格式字符串 | 静态分配,非线程安全 |
ctime_r() | time_t → 固定格式字符串 | 可重入,线程安全 |
gmtime() | time_t → UTC 分解时间 | 静态分配,非线程安全 |
gmtime_r() | time_t → UTC 分解时间 | 可重入,线程安全 |
localtime() | time_t → 本地分解时间 | 静态分配,非线程安全 |
localtime_r() | time_t → 本地分解时间 | 可重入,线程安全 |
mktime() | 本地分解时间 → time_t | 自动调整字段,考虑时区和 DST |
注意事项
- 静态分配的函数(如
ctime()
、gmtime()
、localtime()
)是非线程安全的,需使用可重入版本(如ctime_r()
、gmtime_r()
、localtime_r()
)。 mktime()
会自动调整字段值,并考虑时区和夏令时设置。
通过这些函数,可以方便地在 time_t
、分解时间和字符串格式之间进行转换,并处理时区、夏令时等复杂性问题。
以下是一些具体示例,展示如何使用时间转换函数(如 ctime()
、gmtime()
、localtime()
和 mktime()
)来处理时间。
1. ctime()
示例
将 time_t
转换为固定格式的字符串。
#include <stdio.h>
#include <time.h>int main() {time_t now = time(NULL); // 获取当前时间printf("Current time: %s", ctime(&now)); // 转换为固定格式字符串并打印return 0;
}
输出:
Current time: Tue Oct 10 14:30:00 2023
2. gmtime()
示例
将 time_t
转换为 UTC 时间的分解时间。
#include <stdio.h>
#include <time.h>int main() {time_t now = time(NULL); // 获取当前时间struct tm *utc_time = gmtime(&now); // 转换为 UTC 时间printf("UTC time: %04d-%02d-%02d %02d:%02d:%02d\n",utc_time->tm_year + 1900, utc_time->tm_mon + 1, utc_time->tm_mday,utc_time->tm_hour, utc_time->tm_min, utc_time->tm_sec);return 0;
}
输出:
UTC time: 2023-10-10 12:30:00
3. localtime()
示例
将 time_t
转换为本地时间的分解时间。
#include <stdio.h>
#include <time.h>int main() {time_t now = time(NULL); // 获取当前时间struct tm *local_time = localtime(&now); // 转换为本地时间printf("Local time: %04d-%02d-%02d %02d:%02d:%02d\n",local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday,local_time->tm_hour, local_time->tm_min, local_time->tm_sec);return 0;
}
输出:
Local time: 2023-10-10 14:30:00
4. mktime()
示例
将分解时间转换为 time_t
,并自动调整字段值。
#include <stdio.h>
#include <time.h>int main() {struct tm tm_info = {.tm_year = 123, // 2023年(1900 + 123).tm_mon = 9, // 10月(0-11).tm_mday = 10, // 10日.tm_hour = 14, // 14时.tm_min = 30, // 30分.tm_sec = 0 // 0秒};// 将分解时间转换为 time_ttime_t t = mktime(&tm_info);printf("Seconds since Epoch: %ld\n", (long)t);// 打印调整后的分解时间printf("Adjusted time: %04d-%02d-%02d %02d:%02d:%02d\n",tm_info.tm_year + 1900, tm_info.tm_mon + 1, tm_info.tm_mday,tm_info.tm_hour, tm_info.tm_min, tm_info.tm_sec);return 0;
}
输出:
Seconds since Epoch: 1696948200
Adjusted time: 2023-10-10 14:30:00
5. mktime()
自动调整字段示例
测试 mktime()
自动调整超出范围的字段值。
#include <stdio.h>
#include <time.h>int main() {struct tm tm_info = {.tm_year = 123, // 2023年(1900 + 123).tm_mon = 9, // 10月(0-11).tm_mday = 32, // 32日(超出范围).tm_hour = 25, // 25时(超出范围).tm_min = 70, // 70分(超出范围).tm_sec = 120 // 120秒(超出范围)};// 将分解时间转换为 time_ttime_t t = mktime(&tm_info);printf("Seconds since Epoch: %ld\n", (long)t);// 打印调整后的分解时间printf("Adjusted time: %04d-%02d-%02d %02d:%02d:%02d\n",tm_info.tm_year + 1900, tm_info.tm_mon + 1, tm_info.tm_mday,tm_info.tm_hour, tm_info.tm_min, tm_info.tm_sec);return 0;
}
输出:
Seconds since Epoch: 1697036400
Adjusted time: 2023-11-01 02:10:00
6. strftime()
示例
将分解时间转换为自定义格式的字符串。
#include <stdio.h>
#include <time.h>int main() {time_t now = time(NULL); // 获取当前时间struct tm *local_time = localtime(&now); // 转换为本地时间char buffer[80];// 自定义格式:YYYY年MM月DD日 HH时MM分SS秒strftime(buffer, sizeof(buffer), "%Y年%m月%d日 %H时%M分%S秒", local_time);printf("当前时间: %s\n", buffer);return 0;
}
输出:
当前时间: 2023年10月10日 14时30分00秒
总结
ctime()
:将time_t
转换为固定格式字符串。gmtime()
:将time_t
转换为 UTC 分解时间。localtime()
:将time_t
转换为本地分解时间。mktime()
:将分解时间转换为time_t
,并自动调整字段值。strftime()
:将分解时间转换为自定义格式字符串。
通过这些示例,可以更好地理解如何使用这些函数处理时间。
10.2.3 分解时间和打印格式之间的转换
本节介绍如何在分解时间(struct tm
)和打印格式之间进行转换,主要涉及以下函数:
1. asctime()
函数
- 功能:将分解时间转换为固定格式的字符串。
- 函数原型:
#include <time.h> char *asctime(const struct tm *timeptr);
- 参数:
timeptr
:指向分解时间结构体struct tm
的指针。
- 返回值:
- 成功时返回一个 26字节 的静态分配字符串,格式为
Tue Dec 28 15:01:57 2010\n\0
。 - 失败时返回
NULL
。
- 成功时返回一个 26字节 的静态分配字符串,格式为
- 特点:
- 与
ctime()
类似,但asctime()
的输入是分解时间,而非time_t
。 - 本地时区设置对
asctime()
无影响,因为分解时间通常已通过localtime()
或gmtime()
处理。 - 返回的字符串是静态分配的,后续调用会覆盖其内容。
- 非线程安全。
- 与
2. asctime_r()
函数
- 功能:
asctime()
的可重入版本。 - 特点:
- 允许调用者提供缓冲区存储结果,避免静态分配问题。
- 线程安全。
3. strftime()
函数
- 功能:将分解时间转换为自定义格式的字符串。
- 函数原型:
#include <time.h> size_t strftime(char *outstr, size_t maxsize, const char *format, const struct tm *timeptr);
- 参数:
outstr
:存储结果的缓冲区。maxsize
:缓冲区的最大长度。format
:格式化字符串(如"%Y-%m-%d %H:%M:%S"
)。timeptr
:指向分解时间结构体struct tm
的指针。
- 返回值:
- 成功时返回写入的字符数(不包括终止空字节)。
- 失败时返回
0
。
- 特点:
- 提供对输出格式的精确控制。
- 不会自动添加换行符,除非
format
中包含换行符。
4. strftime()
的常用格式化说明符
以下是 strftime()
支持的格式化说明符的子集:
说明符 | 描述 | 示例 |
---|---|---|
%% | 百分号字符 | % |
%a | 星期几的缩写 | Tue |
%A | 星期几的全称 | Tuesday |
%b | 月份名称的缩写 | Feb |
%B | 月份全称 | February |
%c | 日期和时间 | Tue Feb 1 21:39:46 2011 |
%d | 一个月中的一天(2位数) | 01 |
%D | 美国日期格式(%m/%d/%y ) | 02/01/11 |
%F | ISO 日期格式(%Y-%m-%d ) | 2011-02-01 |
%H | 小时(24小时制,2位数) | 21 |
%I | 小时(12小时制,2位数) | 09 |
%j | 一年中的第几天(3位数) | 032 |
%m | 月份(2位数) | 02 |
%M | 分钟(2位数) | 39 |
%p | AM/PM | PM |
%R | 24小时制时间(%H:%M ) | 21:39 |
%S | 秒(2位数) | 46 |
%T | 时间(%H:%M:%S ) | 21:39:46 |
%u | 星期几编号(1-7,周一=1) | 2 |
%U | 一年中的周数(以周日计算) | 05 |
%w | 星期几编号(0-6,周日=0) | 2 |
5. 示例程序
以下程序演示了如何使用 asctime()
、ctime()
、gmtime()
、localtime()
和 strftime()
。
#include <stdio.h>
#include <time.h>
#include <locale.h>
#include "tlpi_hdr.h"#define SECONDS_IN_TROPICAL_YEAR (365.24219 * 24 * 60 * 60)int main() {time_t t;struct tm *gmp, *locp;struct tm gm, loc;struct timeval tv;t = time(NULL);printf("Seconds since the Epoch (1 Jan 1970): %ld", (long)t);printf(" (about %6.3f years)\n", t / SECONDS_IN_TROPICAL_YEAR);if (gettimeofday(&tv, NULL) == -1)errExit("gettimeofday");printf("gettimeofday() returned %ld secs, %ld microsecs\n", (long)tv.tv_sec, (long)tv.tv_usec);gmp = gmtime(&t);if (gmp == NULL)errExit("gmtime");gm = *gmp; // 保存本地副本printf("Broken down by gmtime():\n");printf("year=%d mon=%d mday=%d hour=%d min=%d sec=%d ",gm.tm_year, gm.tm_mon, gm.tm_mday, gm.tm_hour, gm.tm_min, gm.tm_sec);printf("wday=%d yday=%d isdst=%d\n", gm.tm_wday, gm.tm_yday, gm.tm_isdst);locp = localtime(&t);if (locp == NULL)errExit("localtime");loc = *locp; // 保存本地副本printf("Broken down by localtime():\n");printf("year=%d mon=%d mday=%d hour=%d min=%d sec=%d ",loc.tm_year, loc.tm_mon, loc.tm_mday, loc.tm_hour, loc.tm_min, loc.tm_sec);printf("wday=%d yday=%d isdst=%d\n", loc.tm_wday, loc.tm_yday, loc.tm_isdst);printf("asctime() formats the gmtime() value as: %s", asctime(&gm));printf("ctime() formats the time() value as: %s", ctime(&t));printf("mktime() of gmtime() value: %ld secs\n", (long)mktime(&gm));printf("mktime() of localtime() value: %ld secs\n", (long)mktime(&loc));exit(EXIT_SUCCESS);
}
总结
asctime()
:将分解时间转换为固定格式字符串。strftime()
:将分解时间转换为自定义格式字符串。- 格式化说明符:提供丰富的日期和时间格式化选项。
- 线程安全:使用可重入版本(如
asctime_r()
)以避免静态分配问题。
通过这些函数,可以灵活地在分解时间和打印格式之间进行转换,并满足不同的格式化需求。
程序清单 10-2:返回当前时间的字符串的函数
currTime()
函数
- 功能:返回当前时间的格式化字符串。
- 函数原型:
#include <time.h> #include "curr_time.h"char *currTime(const char *format);
- 参数:
format
:格式化字符串(如"%Y-%m-%d %H:%M:%S"
)。如果为NULL
,默认使用"%c"
。
- 返回值:
- 成功时返回指向静态分配字符串的指针。
- 失败时返回
NULL
。
- 实现:
#define BUF_SIZE 1000char *currTime(const char *format) {static char buf[BUF_SIZE]; // 静态分配的缓冲区time_t t;size_t s;struct tm *tm;t = time(NULL); // 获取当前时间tm = localtime(&t); // 转换为本地时间if (tm == NULL)return NULL;s = strftime(buf, BUF_SIZE, (format != NULL) ? format : "%c", tm); // 格式化时间return (s == 0) ? NULL : buf; }
程序清单 10-3:将打印格式时间转换为分解时间
strptime()
函数
- 功能:将字符串解析为分解时间。
- 函数原型:
#define _XOPEN_SOURCE #include <time.h>char *strptime(const char *str, const char *format, struct tm *timeptr);
- 参数:
str
:待解析的字符串。format
:格式化字符串(如"%Y-%m-%d %H:%M:%S"
)。timeptr
:指向struct tm
的指针,用于存储解析结果。
- 返回值:
- 成功时返回指向
str
中下一个未处理字符的指针。 - 失败时返回
NULL
。
- 成功时返回指向
- 特点:
- 类似于
scanf()
,支持灵活的格式匹配。 - 不区分大小写(如
Jan
和JAN
均可匹配)。 - 不修改未在
format
中指定的struct tm
字段。 - 不设置
tm_isdst
字段,需手动设置或由mktime()
自动确定。
- 类似于
示例程序
以下程序演示了 strptime()
和 strftime()
的用法。
#define _XOPEN_SOURCE
#include <time.h>
#include <locale.h>
#include <string.h>
#include "tlpi_hdr.h"#define SBUF_SIZE 1000int main(int argc, char *argv[]) {struct tm tm;char sbuf[SBUF_SIZE];char *ofmt;if (argc < 3 || strcmp(argv[1], "--help") == 0)usageErr("%s input-date-time in-format [out-format]\n", argv[0]);if (setlocale(LC_ALL, "") == NULL)errExit("setlocale"); // 使用本地化设置memset(&tm, 0, sizeof(struct tm)); // 初始化 tm 结构体if (strptime(argv[1], argv[2], &tm) == NULL)fatal("strptime"); // 解析输入字符串tm.tm_isdst = -1; // 未设置 DST,由 mktime() 自动确定printf("calendar time (seconds since Epoch): %ld\n", (long)mktime(&tm));ofmt = (argc > 3) ? argv[3] : "%H:%M:%S %A, %d %B %Y %Z"; // 默认输出格式if (strftime(sbuf, SBUF_SIZE, ofmt, &tm) == 0)fatal("strftime returned 0"); // 格式化输出printf("strftime() yields: %s\n", sbuf);exit(EXIT_SUCCESS);
}
运行示例
-
基本用法:
$ ./strtime "9:39:46pm 1 Feb 2011" "%I:%M:%S%p %d %b %Y" calendar time (seconds since Epoch): 1296592786 strftime() yields: 21:39:46 Tuesday, 01 February 2011 CET
-
自定义输出格式:
$ ./strtime "9:39:46pm 1 Feb 2011" "%I:%M:%S%p %d %b %Y" "%F %T" calendar time (seconds since Epoch): 1296592786 strftime() yields: 2011-02-01 21:39:46
总结
currTime()
:返回当前时间的格式化字符串,支持自定义格式。strptime()
:将字符串解析为分解时间,支持灵活的格式匹配。strftime()
:将分解时间转换为自定义格式的字符串。- 本地化支持:通过
setlocale()
设置本地化环境,影响时间格式的本地化表示。
通过这些函数,可以方便地在字符串和分解时间之间进行转换,并满足不同的格式化需求。
10.3 时区
时区是地理区域的标准时间,不同国家或地区可能使用不同的时区,甚至同一国家内的不同地区也可能使用不同的时区。此外,许多地区还采用夏令时(DST)来调整时间。为了处理这些复杂性,C语言函数库提供了对时区和夏令时的支持。
1. 时区定义
-
时区信息存储:
- 时区信息存储于
/usr/share/zoneinfo
目录下的文件中。 - 每个文件描述一个特定国家或地区的时区制度,例如:
EST
(美国东部标准时间)CET
(欧洲中部时间)UTC
(协调世界时)Turkey
(土耳其时间)Iran
(伊朗时间)
- 子目录用于对相关时区进行分组,例如:
/usr/share/zoneinfo/Pacific/Auckland
(新西兰奥克兰时间)/usr/share/zoneinfo/Pacific/Port_Moresby
(巴布亚新几内亚时间)
- 时区信息存储于
-
系统本地时间:
- 系统的本地时间由
/etc/localtime
文件定义,通常是一个指向/usr/share/zoneinfo
下某个文件的符号链接。
- 系统的本地时间由
-
时区文件格式:
- 时区文件的格式在
tzfile(5)
手册页中有详细描述。 - 时区文件可以通过
zic(8)
(时区信息编译器)工具创建。 - 使用
zdump(8)
命令可以显示指定时区文件的当前时间。
- 时区文件的格式在
2. 为程序指定时区
-
TZ
环境变量:- 通过设置
TZ
环境变量,可以为程序指定时区。 TZ
的格式为:<时区名称>
,其中时区名称是/usr/share/zoneinfo
下的文件路径。- 例如:
TZ=":Europe/Berlin"
(欧洲柏林时间)TZ=":Pacific/Auckland"
(新西兰奥克兰时间)
- 通过设置
-
时区的影响:
- 设置
TZ
环境变量会影响以下函数的行为:ctime()
localtime()
mktime()
strftime()
- 设置
-
默认时区:
- 如果未设置
TZ
环境变量,则使用/etc/localtime
定义的默认时区。 - 如果
TZ
为空或无效,则使用UTC
。
- 如果未设置
-
全局变量:
- 时区设置会初始化以下全局变量:
char *tzname[2]; // 时区名称和夏令时名称 int daylight; // 非零表示启用夏令时 long timezone; // UTC 与本地标准时间的秒数差
- 时区设置会初始化以下全局变量:
3. TZ
环境变量的两种格式
-
格式 1:时区文件路径
- 例如:
TZ=":Europe/Berlin"
- 这是 Linux 和其他 UNIX 系统的常见格式。
- 例如:
-
格式 2:SUSv3 标准格式
- 格式:
std offset[dst[offset][,start[/time],end[/time]]]
- 示例:
TZ="CET-1CEST-2,M3.5.0,M10.5.0"
CET-1
:标准时间比 UTC 提前 1 小时。CEST-2
:夏令时比 UTC 提前 2 小时。M3.5.0
:夏令时从 3 月最后一个星期日开始。M10.5.0
:夏令时到 10 月最后一个星期日结束。
- 格式:
4. 示例程序
以下程序演示了时区设置对时间函数的影响。
#include <time.h>
#include <locale.h>
#include "tlpi_hdr.h"#define BUF_SIZE 200int main(int argc, char *argv[]) {time_t t;struct tm *loc;char buf[BUF_SIZE];if (setlocale(LC_ALL, "") == NULL)errExit("setlocale"); // 使用本地化设置t = time(NULL); // 获取当前时间printf("ctime() of time() value is: %s", ctime(&t));loc = localtime(&t); // 转换为本地时间if (loc == NULL)errExit("localtime");printf("asctime() of local time is: %s", asctime(loc));if (strftime(buf, BUF_SIZE, "%A, %d %B %Y, %H:%M:%S %Z", loc) == 0)fatal("strftime returned 0"); // 格式化输出printf("strftime() of local time is: %s\n", buf);exit(EXIT_SUCCESS);
}
5. 运行示例
-
默认时区:
$ ./show_time ctime() of time() value is: Tue Feb 1 10:25:56 2011 asctime() of local time is: Tue Feb 1 10:25:56 2011 strftime() of local time is: Tuesday, 01 February 2011, 10:25:56 CET
-
指定时区:
$ TZ=":Pacific/Auckland" ./show_time ctime() of time() value is: Tue Feb 1 22:26:19 2011 asctime() of local time is: Tue Feb 1 22:26:19 2011 strftime() of local time is: Tuesday, 01 February 2011, 22:26:19 NZDT
总结
- 时区文件:位于
/usr/share/zoneinfo
,定义了不同地区的时区信息。 TZ
环境变量:用于为程序指定时区,影响时间函数的行为。- 时间函数:
ctime()
、localtime()
、mktime()
和strftime()
都会受到时区设置的影响。 - 夏令时:通过
TZ
环境变量或时区文件自动处理。
通过合理设置时区,程序可以正确地处理不同地区的时间,并自动适应夏令时调整。
10.4 地区(Locale)
地区(Locale)是指用户环境中依赖于语言和文化习俗的一个子集。它决定了程序如何显示和输入信息,例如日期、时间、货币、数字格式等。为了支持国际化(Internationalization,简称 I18N),程序需要能够适应不同地区的语言和文化习俗。
1. 地区定义
-
地区信息存储:
-
地区信息存储于
/usr/share/locale
(或/usr/lib/locale
)目录下的子目录中。 -
每个子目录代表一个特定地区,命名格式为:
language[_territory][.codeset][@modifier]
language
:双字母的 ISO 语言代码(如de
表示德语)。territory
:双字母的 ISO 国家代码(如DE
表示德国)。codeset
:字符编码集(如UTF-8
)。modifier
:修饰符,用于区分相同语言和国家的不同地区(如@euro
表示使用欧元)。
-
示例:
de_DE.utf8@euro
:德语,德国,UTF-8 编码,使用欧元。en_US
:英语,美国。fr_CH
:法语,瑞士。
-
-
地区匹配规则:
- 如果指定的地区名称与目录不匹配,系统会按以下顺序尝试匹配:
- 移除
codeset
。 - 移除
normalized codeset
(标准化字符编码集)。 - 移除
territory
。 - 移除
modifier
。
- 移除
- 如果仍未找到匹配,则使用默认地区
C
或POSIX
。
- 如果指定的地区名称与目录不匹配,系统会按以下顺序尝试匹配:
-
默认地区:
POSIX
地区是 SUSv3 定义的标准地区,使用 ASCII 字符集和英文格式。C
地区与POSIX
相同,用于历史兼容性。
2. 地区类别
每个地区子目录中包含多个文件,定义了不同类别的地区设置:
文件名 | 描述 |
---|---|
LC_CTYPE | 字符分类和大小写转换规则(如 isalpha() 的行为)。 |
LC_COLLATE | 字符排序规则(如 strcoll() 和 strxfrm() 的行为)。 |
LC_MONETARY | 货币格式化规则(如货币符号、小数点分隔符)。 |
LC_NUMERIC | 数字格式化规则(如小数点分隔符、千位分隔符)。 |
LC_TIME | 日期和时间格式化规则(如月份名称、日期格式)。 |
LC_MESSAGES | 消息格式(如“是/否”响应的翻译)。 |
- 额外类别(GNU 扩展):
LC_ADDRESS
:邮政地址格式。LC_IDENTIFICATION
:地区标识信息。LC_MEASUREMENT
:度量系统(如公制或英制)。LC_NAME
:人名和头衔格式。LC_PAPER
:标准纸张尺寸(如 A4 或信纸)。LC_TELEPHONE
:电话号码格式。
3. 设置地区
-
setlocale()
函数:- 功能:设置或查询程序的当前地区。
- 函数原型:
#include <locale.h> char *setlocale(int category, const char *locale);
- 参数:
category
:地区类别(如LC_ALL
、LC_TIME
、LC_MONETARY
等)。locale
:地区名称(如"de_DE"
)或空字符串(从环境变量获取)。
- 返回值:
- 成功时返回指向地区名称字符串的指针。
- 失败时返回
NULL
。
-
设置方法:
- 直接指定地区:
setlocale(LC_ALL, "de_DE.utf8");
- 从环境变量获取:
setlocale(LC_ALL, "");
- 直接指定地区:
-
环境变量:
LANG
:设置默认地区。LC_ALL
:覆盖所有其他LC_*
变量。LC_CTYPE
、LC_TIME
等:设置特定类别的地区。
-
优先级:
LC_ALL > LC_* > LANG
4. 示例程序
以下程序演示了地区设置对时间格式的影响。
#include <locale.h>
#include <time.h>
#include "tlpi_hdr.h"#define BUF_SIZE 200int main(int argc, char *argv[]) {time_t t;struct tm *loc;char buf[BUF_SIZE];if (setlocale(LC_ALL, "") == NULL)errExit("setlocale"); // 使用环境变量设置地区t = time(NULL); // 获取当前时间printf("ctime() of time() value is: %s", ctime(&t));loc = localtime(&t); // 转换为本地时间if (loc == NULL)errExit("localtime");printf("asctime() of local time is: %s", asctime(loc));if (strftime(buf, BUF_SIZE, "%A, %d %B %Y, %H:%M:%S %Z", loc) == 0)fatal("strftime returned 0"); // 格式化输出printf("strftime() of local time is: %s\n", buf);exit(EXIT_SUCCESS);
}
5. 运行示例
-
德语地区:
$ LANG=de_DE ./show_time ctime() of time() value is: Tue Feb 1 12:23:39 2011 asctime() of local time is: Tue Feb 1 12:23:39 2011 strftime() of local time is: Dienstag, 01 Februar 2011, 12:23:39 CET
-
混合地区:
$ LANG=de_DE LC_TIME=it_IT ./show_time ctime() of time() value is: Tue Feb 1 12:24:03 2011 asctime() of local time is: Tue Feb 1 12:24:03 2011 strftime() of local time is: martedi, 01 febbraio 2011, 12:24:03 CET
-
法语地区:
$ LC_ALL=fr_FR ./show_time ctime() of time() value is: Tue Feb 1 12:25:38 2011 asctime() of local time is: Tue Feb 1 12:25:38 2011 strftime() of local time is: mardi, 01 fevrier 2011, 12:25:38 CET
总结
- 地区:定义了语言和文化习俗相关的设置,影响程序的输入和输出格式。
setlocale()
:用于设置或查询程序的地区。- 环境变量:通过
LANG
和LC_*
变量控制地区的不同类别。 - 国际化支持:通过合理设置地区,程序可以适应不同语言和文化环境。
10.5 更新系统时钟
系统时钟的更新通常由系统工具(如网络时间协议 NTP 守护进程)维护,但 Linux 提供了两个系统调用用于手动更新系统时钟:settimeofday()
和 adjtime()
。
1. settimeofday()
系统调用
- 功能:设置系统的日历时间。
- 函数原型:
#define _BSD_SOURCE #include <sys/time.h> int settimeofday(const struct timeval *tv, const struct timezone *tz);
- 参数:
tv
:指向struct timeval
的指针,包含秒数和微秒数。struct timeval {time_t tv_sec; // 秒数suseconds_t tv_usec; // 微秒数 };
tz
:历史遗留参数,应始终设置为NULL
。
- 返回值:
- 成功时返回
0
。 - 失败时返回
-1
,并设置errno
。
- 成功时返回
- 特点:
- 直接设置系统时间,可能导致时间突然变化。
- 需要调用者具有
CAP_SYS_TIME
权限。 tv_usec
的微秒精度不一定被系统完全支持。
2. stime()
系统调用
- 功能:以秒为单位设置系统时钟。
- 函数原型:
#include <time.h> int stime(const time_t *t);
- 参数:
t
:指向time_t
的指针,表示自 Epoch 以来的秒数。
- 返回值:
- 成功时返回
0
。 - 失败时返回
-1
,并设置errno
。
- 成功时返回
- 特点:
- 与
settimeofday()
功能类似,但精度较低(仅秒级)。 - 需要调用者具有
CAP_SYS_TIME
权限。
- 与
3. adjtime()
函数
- 功能:逐步调整系统时钟,避免时间突然变化。
- 函数原型:
#define _BSD_SOURCE #include <sys/time.h> int adjtime(const struct timeval *delta, struct timeval *olddelta);
- 参数:
delta
:指向struct timeval
的指针,表示需要调整的时间。- 如果
delta->tv_sec
为正,系统时钟会逐渐加快。 - 如果
delta->tv_sec
为负,系统时钟会逐渐减慢。
- 如果
olddelta
:指向struct timeval
的指针,用于存储未完成的调整时间。- 如果为
NULL
,则不返回未完成的调整时间。
- 如果为
- 返回值:
- 成功时返回
0
。 - 失败时返回
-1
,并设置errno
。
- 成功时返回
- 特点:
- 适用于对时间进行微小调整(几秒钟误差)。
- 避免时间突然变化对应用程序的影响(如日志文件、数据库时间戳)。
- 需要调用者具有
CAP_SYS_TIME
权限。
4. adjtimex()
系统调用
- 功能:更通用和复杂的系统时钟调整接口,被 NTP 守护进程使用。
- 特点:
- 提供了比
adjtime()
更精细的控制。 - 详细信息可参考
adjtimex(2)
手册页和 NTP 规范。
- 提供了比
10.6 软件时钟(Jiffies)
软件时钟是内核中用于计时和调度的基本单位,其分辨率由内核常量 HZ
定义。
1. Jiffies 的定义
- Jiffies:软件时钟的基本单位,表示两次时钟中断之间的时间间隔。
HZ
:内核常量,表示每秒的时钟中断次数。- 例如,
HZ = 100
表示每秒 100 次时钟中断,每个 jiffy 为 10 毫秒。
- 例如,
2. Linux 内核中的 Jiffies
- 历史:
- Linux 2.4 及更早版本:
HZ = 100
(每个 jiffy 为 10 毫秒)。 - Linux 2.6.0:
HZ = 1000
(每个 jiffy 为 1 毫秒)。
- Linux 2.4 及更早版本:
- 可配置性:
- 自 Linux 2.6.13 起,
HZ
可配置为 100、250(默认)或 1000 赫兹。 - 自 Linux 2.6.20 起,增加了 300 赫兹选项,适用于视频帧率(25 帧/秒和 30 帧/秒)。
- 自 Linux 2.6.13 起,
3. Jiffies 的影响
- 精度:
- 更高的
HZ
值意味着更高的定时器精度和时间测量精度。
- 更高的
- 开销:
- 更高的
HZ
值会增加时钟中断的频率,导致更多的 CPU 开销。
- 更高的
总结
settimeofday()
:直接设置系统时间,可能导致时间突然变化。adjtime()
:逐步调整系统时间,适用于微小调整。- Jiffies:内核软件时钟的基本单位,
HZ
定义了时钟中断频率。 - 可配置性:
HZ
可根据需求配置为 100、250、300 或 1000 赫兹。
通过合理使用这些接口,可以有效地管理和调整系统时钟,同时避免对应用程序的负面影响。
以下是一些具体的代码示例,展示如何使用 settimeofday()
、adjtime()
和 stime()
来更新系统时钟。
1. settimeofday()
示例
设置系统时钟为指定的时间。
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>int main() {struct timeval tv;// 设置时间为 2023年10月10日 12:00:00tv.tv_sec = 1696946400; // 自 Epoch 以来的秒数tv.tv_usec = 0; // 微秒数if (settimeofday(&tv, NULL) == -1) {perror("settimeofday");exit(EXIT_FAILURE);}printf("System time set to 2023-10-10 12:00:00\n");return 0;
}
注意:
- 需要以 root 权限运行此程序。
- 直接设置系统时间可能导致应用程序出现问题(如日志时间戳不连续)。
2. adjtime()
示例
逐步调整系统时钟。
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>int main() {struct timeval delta, olddelta;// 设置需要调整的时间(加快 5 秒)delta.tv_sec = 5;delta.tv_usec = 0;if (adjtime(&delta, &olddelta) == -1) {perror("adjtime");exit(EXIT_FAILURE);}printf("Clock is being adjusted by 5 seconds.\n");if (olddelta.tv_sec != 0 || olddelta.tv_usec != 0) {printf("Previous adjustment remaining: %ld seconds, %ld microseconds\n",(long)olddelta.tv_sec, (long)olddelta.tv_usec);}return 0;
}
注意:
- 需要以 root 权限运行此程序。
adjtime()
适用于对时间进行微小调整,避免时间突然变化。
3. stime()
示例
以秒为单位设置系统时钟。
#include <stdio.h>
#include <time.h>
#include <stdlib.h>int main() {time_t t;// 设置时间为 2023年10月10日 12:00:00t = 1696946400; // 自 Epoch 以来的秒数if (stime(&t) == -1) {perror("stime");exit(EXIT_FAILURE);}printf("System time set to 2023-10-10 12:00:00\n");return 0;
}
注意:
- 需要以 root 权限运行此程序。
stime()
的精度较低(仅秒级)。
4. 获取和打印当前时间
在调整系统时间后,可以使用 gettimeofday()
获取当前时间并打印。
#include <stdio.h>
#include <sys/time.h>
#include <time.h>void print_current_time() {struct timeval tv;struct tm *tm_info;if (gettimeofday(&tv, NULL) == -1) {perror("gettimeofday");return;}tm_info = localtime(&tv.tv_sec);if (tm_info == NULL) {perror("localtime");return;}printf("Current time: %04d-%02d-%02d %02d:%02d:%02d.%06ld\n",tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec, (long)tv.tv_usec);
}int main() {print_current_time();return 0;
}
输出:
Current time: 2023-10-10 12:00:00.000000
5. 综合示例
结合 settimeofday()
和 adjtime()
,设置系统时间并逐步调整。
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <unistd.h>void print_current_time() {struct timeval tv;struct tm *tm_info;if (gettimeofday(&tv, NULL) == -1) {perror("gettimeofday");return;}tm_info = localtime(&tv.tv_sec);if (tm_info == NULL) {perror("localtime");return;}printf("Current time: %04d-%02d-%02d %02d:%02d:%02d.%06ld\n",tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec, (long)tv.tv_usec);
}int main() {struct timeval tv;// 设置时间为 2023年10月10日 12:00:00tv.tv_sec = 1696946400;tv.tv_usec = 0;if (settimeofday(&tv, NULL) == -1) {perror("settimeofday");exit(EXIT_FAILURE);}printf("System time set to 2023-10-10 12:00:00\n");print_current_time();// 逐步调整时间(加快 5 秒)struct timeval delta = {5, 0};if (adjtime(&delta, NULL) == -1) {perror("adjtime");exit(EXIT_FAILURE);}printf("Clock is being adjusted by 5 seconds.\n");sleep(2); // 等待 2 秒print_current_time();return 0;
}
输出:
System time set to 2023-10-10 12:00:00
Current time: 2023-10-10 12:00:00.000000
Clock is being adjusted by 5 seconds.
Current time: 2023-10-10 12:00:02.500000
总结
settimeofday()
:直接设置系统时间,适用于需要精确时间设置的场景。adjtime()
:逐步调整系统时间,适用于对时间进行微小调整。stime()
:以秒为单位设置系统时间,精度较低。- 注意事项:
- 需要 root 权限。
- 直接设置系统时间可能导致应用程序出现问题,建议谨慎使用。
10.7 进程时间
进程时间是指进程创建后使用的 CPU 时间总量,分为两部分:
- 用户 CPU 时间:在用户模式下执行的时间。
- 系统 CPU 时间:在内核模式下执行的时间(如系统调用、页错误处理等)。
进程时间可以通过系统调用 times()
和函数 clock()
来获取。
1. times()
系统调用
-
功能:获取进程的 CPU 时间信息。
-
函数原型:
#include <sys/times.h> clock_t times(struct tms *buf);
-
参数:
buf
:指向struct tms
的指针,用于存储时间信息。struct tms {clock_t tms_utime; // 用户 CPU 时间clock_t tms_stime; // 系统 CPU 时间clock_t tms_cutime; // 所有子进程的用户 CPU 时间clock_t tms_cstime; // 所有子进程的系统 CPU 时间 };
-
返回值:
- 成功时返回自过去某一时刻以来的时钟计时单元数。
- 失败时返回
(clock_t)-1
。
-
时钟计时单元:
- 时钟计时单元的单位是
clock tick
,可以通过sysconf(_SC_CLK_TCK)
获取每秒的时钟计时单元数。 - 在大多数 Linux 系统上,
sysconf(_SC_CLK_TCK)
返回100
。
- 时钟计时单元的单位是
2. clock()
函数
-
功能:获取进程的总 CPU 时间(用户 + 系统)。
-
函数原型:
#include <time.h> clock_t clock(void);
-
返回值:
- 成功时返回进程使用的总 CPU 时间,单位为
CLOCKS_PER_SEC
。 - 失败时返回
(clock_t)-1
。
- 成功时返回进程使用的总 CPU 时间,单位为
-
CLOCKS_PER_SEC
:- 在 POSIX.1 中,
CLOCKS_PER_SEC
定义为1000000
。 - 需要将
clock()
的返回值除以CLOCKS_PER_SEC
以获得秒数。
- 在 POSIX.1 中,
3. 示例程序
以下程序演示了如何使用 times()
和 clock()
获取进程时间。
#include <sys/times.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "tlpi_hdr.h"static void displayProcessTimes(const char *msg) {struct tms t;clock_t clockTime;static long clockTicks = 0;if (msg != NULL)printf("%s", msg);if (clockTicks == 0) { // 获取时钟计时单元clockTicks = sysconf(_SC_CLK_TCK);if (clockTicks == -1)errExit("sysconf");}clockTime = clock(); // 获取总 CPU 时间if (clockTime == -1)errExit("clock");printf("clock() returns: %ld clocks-per-sec (%.2f secs)\n",(long)clockTime, (double)clockTime / CLOCKS_PER_SEC);if (times(&t) == -1) // 获取进程时间errExit("times");printf("times() yields: user CPU=%.2f; system CPU=%.2f\n",(double)t.tms_utime / clockTicks, (double)t.tms_stime / clockTicks);
}int main(int argc, char *argv[]) {int numCalls, j;printf("CLOCKS_PER_SEC=%ld sysconf(_SC_CLK_TCK)=%ld\n\n",(long)CLOCKS_PER_SEC, sysconf(_SC_CLK_TCK));displayProcessTimes("At program start:\n");numCalls = (argc > 1) ? getInt(argv[1], GN_GT_0, "num-calls") : 10000000;for (j = 0; j < numCalls; j++)(void)getppid(); // 消耗 CPU 时间displayProcessTimes("After getppid() loop:\n");exit(EXIT_SUCCESS);
}
4. 运行示例
编译并运行程序:
$ ./process_time 10000000
CLOCKS_PER_SEC=1000000 sysconf(_SC_CLK_TCK)=100At program start:
clock() returns: 0 clocks-per-sec (0.00 secs)
times() yields: user CPU=0.00; system CPU=0.00After getppid() loop:
clock() returns: 2960000 clocks-per-sec (2.96 secs)
times() yields: user CPU=1.09; system CPU=1.87
5. 程序说明
displayProcessTimes()
:- 打印当前进程的用户和系统 CPU 时间。
- 使用
clock()
获取总 CPU 时间。 - 使用
times()
获取用户和系统 CPU 时间。
- 主程序:
- 调用
getppid()
消耗 CPU 时间。 - 在循环前后分别调用
displayProcessTimes()
,显示 CPU 时间的变化。
- 调用
总结
times()
:获取进程的用户和系统 CPU 时间,以及子进程的 CPU 时间。clock()
:获取进程的总 CPU 时间。- 单位转换:
times()
的时间单位是clock tick
,需除以sysconf(_SC_CLK_TCK)
转换为秒。clock()
的时间单位是CLOCKS_PER_SEC
,需除以CLOCKS_PER_SEC
转换为秒。
通过合理使用这些函数,可以测量和分析进程的 CPU 时间消耗。
10.9 练习 10-1
问题描述
假设:
sysconf(_SC_CLK_TCK)
返回的值是100
。times()
返回的clock_t
值是一个无符号的 32 位整数。clock()
返回的CLOCKS_PER_SEC
值是1000000
。
计算:
times()
返回的clock_t
值需要多久才能进入下一个从 0 开始的周期。- 对
clock()
返回的CLOCKS_PER_SEC
值执行相同的计算。
1. times()
的周期计算
clock_t
的范围:clock_t
是一个无符号的 32 位整数,其最大值为 (2^{32} - 1 = 4294967295)。
- 时钟计时单元:
sysconf(_SC_CLK_TCK)
返回100
,表示每秒有 100 个时钟计时单元。
- 周期时间:
- 周期时间 (T) 可以通过以下公式计算:
[
T = \frac{\text{最大值}}{\text{每秒的时钟计时单元数}} = \frac{4294967295}{100} \text{秒}
] - 计算结果:
[
T = \frac{4294967295}{100} = 42949672.95 \text{秒}
] - 转换为更易读的单位:
[
42949672.95 \text{秒} \approx 497.1 \text{天} \approx 1.36 \text{年}
]
- 周期时间 (T) 可以通过以下公式计算:
2. clock()
的周期计算
clock_t
的范围:clock_t
是一个无符号的 32 位整数,其最大值为 (2^{32} - 1 = 4294967295)。
- 时钟计时单元:
CLOCKS_PER_SEC
定义为1000000
,表示每秒有 1000000 个时钟计时单元。
- 周期时间:
- 周期时间 (T) 可以通过以下公式计算:
[
T = \frac{\text{最大值}}{\text{每秒的时钟计时单元数}} = \frac{4294967295}{1000000} \text{秒}
] - 计算结果:
[
T = \frac{4294967295}{1000000} = 4294.967295 \text{秒}
] - 转换为更易读的单位:
[
4294.967295 \text{秒} \approx 71.58 \text{分钟} \approx 1.19 \text{小时}
]
- 周期时间 (T) 可以通过以下公式计算:
总结
-
times()
的周期:- 最大值:4294967295 个时钟计时单元。
- 每秒的时钟计时单元数:100。
- 周期时间:约 497.1 天(1.36 年)。
-
clock()
的周期:- 最大值:4294967295 个时钟计时单元。
- 每秒的时钟计时单元数:1000000。
- 周期时间:约 71.58 分钟(1.19 小时)。
注意事项
times()
的周期较长:由于times()
的时钟计时单元较少(每秒 100 个),其周期时间较长(约 1.36 年)。clock()
的周期较短:由于clock()
的时钟计时单元较多(每秒 1000000 个),其周期时间较短(约 1.19 小时)。- 溢出风险:在长时间运行的进程中,
clock()
的返回值可能会溢出,导致时间计算错误。因此,建议使用gettimeofday()
或clock_gettime()
来测量经过时间。
相关文章:
10.Linux 时间
文章目录 10.1 日历时间(Calendar Time)1. **日历时间的表示**2. **系统调用 gettimeofday()**3. **系统调用 time()**4. **time() 与 gettimeofday() 的关系** 总结 10.2 时间转换函数1. **ctime() 示例**2. **asctime() 示例**3. **strftime() 示例**4…...
Java最常用的几种设计模式详解及适用业务场景
Java设计模式详解及适用业务场景 在软件开发中,设计模式是解决常见问题的最佳实践。通过采用这些设计模式,我们可以提高代码的可维护性、可读性和可扩展性。本文将介绍几种常见的Java设计模式,并结合具体代码示例,探讨它们适用的…...
运行fastGPT 第二步 安装宝塔面板 用于管理安装docker和其文件
if [ -f /usr/bin/curl ];then curl -sSO https://download.bt.cn/install/install_panel.sh;else wget -O install_panel.sh https://download.bt.cn/install/install_panel.sh;fi;bash install_panel.sh ed8484bec 上面运行以下,安装宝塔。如果不行,系…...
深度学习电影推荐-CNN算法
文章目录 前言视频演示效果1.数据集环境配置安装教程与资源说明1.1 ML-1M 数据集概述1.1.1数据集内容1.1.2. 数据集规模1.1.3. 数据特点1.1.4. 文件格式1.1.5. 应用场景 2.模型架构3.推荐实现3.1 用户数据3.2 电影数据3.3 评分数据3.4 数据预处理3.5实现数据预处理3.6 加载数据…...
CSS3的aria-hidden学习
前言 aria-hidden 属性可用于隐藏非交互内容,使其在无障碍 API 中不可见。即当aria-hidden"true" 添加到一个元素会将该元素及其所有子元素从无障碍树中移除,这可以通过隐藏来改善辅助技术用户的体验: 纯装饰性内容,如…...
如何在前端给视频进行去除绿幕并替换背景?-----Vue3!!
最近在做这个这项目奇店桶装水小程序V1.3.9安装包骑手端V2.0.1小程序前端 最近,我在进行前端开发时,遇到了一个难题“如何给前端的视频进行去除绿幕并替换背景”。这是一个“数字人项目”所需,我一直在冥思苦想。终于有了一个解决方法…...
【Linux】进程间通信IPC
目录 进程间通信 IPC 1. 进程间通信方式 2. 无名管道 2.1 特点 2.2 函数接口 2.3 注意事项 3. 有名管道 3.1 特点 3.2 函数接口 3.3 注意事项 3.4 有名管道和无名管道的区别 4. 信号 4.1概念 4.2信号的响应方式 4.3 信号种类 4.4 函数接口 4.4.1 信号发送和挂…...
go语言实现UTF8与GB2312内码转换
使用Go语言做个UTF-8转GB2312的代码,输入utf-8编码的文本,输出转换后的国标编码的hex内码 package mainimport ("fmt""os""strings""golang.org/x/text/encoding/simplifiedchinese""golang.org/x/text/transform&quo…...
【WPS】【WORDWORD】【JavaScript】实现微软WORD自动更正的效果
1.效果展示 2.核心代码展示 function readTableData(filePath) {let tableData {};let doc Application.Documents.Open(filePath); // 打开文档let table doc.Tables(1); // 获取第一个表格// 遍历表格,存储编号和描述的映射for (let i 1; i < table.Rows.…...
Excel数据叠加生成新DataFrame:操作指南与案例
目录 一、准备工作 二、读取Excel文件 三、数据叠加 四、处理重复数据(可选) 五、保存新DataFrame到Excel文件 六、案例演示 七、注意事项 八、总结 在日常数据处理工作中,我们经常需要将不同Excel文档中的数据整合到一个新的DataFrame中,以便进行进一步的分析和处…...
springBoot项目使用Elasticsearch教程
目录 一、引言(一)使用背景(二)版本库区别 二、引入依赖(一)springboot集成的es依赖(建议)(二)es提供的客户端库 三、配置(以yaml文件为例&#x…...
摄像头模块在狩猎相机中的应用
摄像头模块是狩猎相机的核心组件,在狩猎相机中发挥着关键作用,以下是其主要应用: 图像与视频拍摄 高清成像:高像素的摄像头模块可确保狩猎相机拍摄出清晰的图像和视频,能够捕捉到动物的毛发纹理、行为细节及周围环境的…...
栈溢出原理
文章目录 前言一、基本示例二、分析栈1. 先不考虑gets函数的栈情况2. 分析gets函数的栈区情况 三、利用栈1. 构造字符串2. 利用漏洞 前言 栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致与其相邻的栈中的变量的值被改变。…...
38.【3】CTFHUB web sql 报错注入
进入靶场 按照提示输入1 显示查询正确 既然是报错注入,先判断整形还是字符型注入 先输入1 and 11 再输入1 and 12 都显示查询正确,可知此为字符串型注入,不是数字型注入 然后就不会了 求助AI和其他wp 由以上2张搜索结果知updatexml是适用…...
SSE部署后无法连接问题解决
1. 问题现象 通过域名访问 https://api-uat.sfxs.com/sse/subscribe?tokenBearer%20eyJUxMiJ9.eyJhY2NvdW50IjoiYWRtaWZ0NvZGUiOiIwMDEiLCJyb2xidXNlcm5hbWUiOiLotoXnuqfnrqHnkIblkZgifQ.tlz9N61Y4 一直无法正常连接 2. 问题解决 nginx.conf进行配置 server {location /ss…...
sparkRDD教程之基本命令
作者:nchu可乐百香果 指导者:nchu-YoungDragon 1.前期准备 (1)从迅雷网盘上面下载这个项目,并且把scala,maven和java环境配置好 网盘链接: 分享文件:SparkRDD.zip 链接…...
【JavaScript】比较运算符的运用、定义函数、if(){}...esle{} 语句
比较运算符 !><> < 自定义函数: function 函数名(){ } 判断语句: if(判断){ }else if(判断){ 。。。。。。 }else{ } 代码示例: <!DOCTYPE html> <html> <head><meta charset&quo…...
ShardingSphere—SQL 路由与执行解析原理
在分布式数据库中,SQL 路由、解析及执行是核心机制,用于高效处理 SQL 请求并将其分发到合适的数据节点。在 ShardingSphere 中,这一过程分为三个主要阶段:SQL 路由、SQL 解析 和 SQL 执行,通过灵活的策略和优化机制实现…...
机器学习——什么是代价函数?
1.代价函数的定义 首先,提到代价函数是估计值和实际值的差,这应该是指预测值和真实值之间的差异,用来衡量模型的好坏。 在一元线性模型中,模型是直线,有两个参数,可能是斜率和截距。 通过调整这两个参数,让代价函数最小,这应该是说我们要找到最佳的斜率和截距,使得预测…...
k8s集群换IP
k8s集群搭建及节点加入时需要确定IP,但安装完成后设备移动到新环境可能出现网段更换或者IP被占用的情况,导致无法ping通节点或者无法打开原IP的服务。 解决方法为保持原有IP不更换,给网卡再加一个IP 这边使用两个ubuntu虚拟机模拟服务器和w…...
RPC 源码解析~Apache Dubbo
解析 RPC(远程过程调用)的源码可以帮助你深入理解其工作原理和实现细节。为了更好地进行源码解析,我们选择一个流行的 RPC 框架——Apache Dubbo 作为示例。Dubbo 是一个高性能、轻量级的开源 Java RPC 框架,广泛应用于企业级应用…...
【认识油管头部频道】ep5 “5-Minute Crafts”——DIY 和生活技巧
5-Minute Crafts 是一个非常受欢迎的 DIY 和生活技巧频道,它的火爆有多方面的原因: 1. 简单实用的内容 视频主要以解决日常生活中遇到的小问题为主,提供简单易学的技巧,吸引了想快速获取实用知识的观众。 2. 短视频形式 每个视…...
vue3 uniapp封装一个瀑布流组件
新增组件m-waterfall 这样就可以在页面直接使用 不用在引入了 <template><view class"m-waterfall"><view id"m-left-column" class"m-column"><slot name"left" :leftList"leftList"></slot&…...
基于Java的语音陪聊软件——支持聊天私聊-礼物系统-直播系统-缘分匹配-游戏陪玩
丰富的经验、成熟的技术,打造适合当下市场发展的语音交友软件源码。Java 语言凭借其独特的优势,为这款语音陪聊软件的稳健运行和持续发展奠定了坚实基础。它不仅融合了聊天私聊、礼物系统和直播系统等实用且有趣的功能,还创新性地引入了缘分匹…...
山石防火墙命令行配置示例
现网1台山石SG6000防火墙,配置都可以通过GUI实现。 但有一些配置在命令行下配置效率更高,比如在1个已有策略中添加1个host或端口。 下面的双引号可以不加 1 创建服务 1.1 单个端口 service "tcp-901"tcp dst-port 901 1.2 端口范围 servi…...
WordPress内容保护策略:如何反击并利用被盗内容
当你的网站开始获得大量流量时,内容盗窃成为不可避免的问题。除了通过高级的保护措施防止内容被盗,你还可以采取一些策略来反击内容盗窃,并从中获益。 1. 识别并封锁恶意IP地址 当你发现某些IP地址频繁访问并抓取你的网站内容时,…...
UDP报文格式
UDP是传输层的一个重要协议,他的特性有面向数据报、无连接、不可靠传输、全双工。 下面是UDP报文格式: 1,报头 UDP的报头长度位8个字节,包含源端口、目的端口、长度和校验和,其中每个属性均为两个字节。报头格式为二…...
【工具类】获取日出日落时间的Java工具类
博主介绍:✌全网粉丝22W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
ES7【2016】、ES8【2017】新增特性(六)
ES7【2016】新增特性 幂指数操作符 在ES7【2016】中新增了幂指数操作,幂指数操作符是**。它用于指数计算 基本语法:baseValue ** exponent 参数说明:baseValue是基数,exponent是指数。 let base 2; let exponent 4; let resul…...
SparX:一种用于层次视觉Mamba和变换器网络的稀疏跨层连接机制
摘要 https://arxiv.org/pdf/2409.09649 由于动态状态空间模型(SSMs)能够以线性时间计算复杂度捕获长距离依赖关系,Mamba在自然语言处理(NLP)任务中表现出了显著的性能。这激发了基于Mamba的视觉模型的快速发展&#…...
Spring Boot 中实现 WebSocket 的方式
在 Spring Boot 中实现 WebSocket 的方式主要有以下几种,每种方式适用于不同的场景和需求: 1. 基于 Spring WebSocket 的实现 特点: 原生支持 WebSocket,基于 Spring 提供的 API。使用 WebSocketConfigurer 和 WebSocketHandler 配置端点和消息处理逻辑。可以通过拦截器访…...
时序数据库TDengine 3.3.5.0 发布:高并发支持与增量备份功能引领新升级
近日,TDengine 3.3.5.0 版本正式发布,带来了多项重磅更新与优化,从功能拓展到性能提升,再到用户体验进行了全面改进。本次更新围绕用户核心需求展开,涵盖了开发工具、数据管理、安全性、可视化等多个层面,为…...
Elasticsearch:Jira 连接器教程第一部分
作者:来自 Elastic Gustavo Llermaly 将我们的 Jira 内容索引到 Elaasticsearch 中以创建统一的数据源并使用文档级别安全性进行搜索。 在本文中,我们将回顾 Elastic Jira 原生连接器的一个用例。我们将使用一个模拟项目,其中一家银行正在开发…...
HarmonyOS NEXT开发进阶(六):HarmonyOS NEXT实现嵌套 H5 及双向通信
文章目录 一、前言二、鸿蒙应用加载Web页面2.1 加载网络地址页面2.2 加载本地H5页面 三、实现Web组件 H5 层与鸿蒙应用层进行相互通讯3.1 鸿蒙应用向 H5 页面发送数据3.2 H5页面向鸿蒙应用发送数据 四、拓展阅读 一、前言 随着HarmonyOS NEXT的快速发展,越来越多的…...
Myeclipse最新版本 C1 2019.4.0
Myeclipse C1 2019.4.0下载地址:链接: https://pan.baidu.com/s/1MbOMLewvAdemoQ4FNfL9pQ 提取码: tmf6 1.1、什么是集成开发环境? ★集成开发环境讲究-站式开发,使用这个工具即可。有提示功能,有自动纠错功能。 ★集成开发环境可以让软件开…...
深度学习中PyTorch张量的重塑操作
深度学习中PyTorch张量的重塑操作 张量操作类型 在我们深入具体的张量操作之前,让我们先快速浏览一下主要的操作类别,这些类别包括我们将要介绍的操作。我们有以下高级类别的操作: 重塑操作元素级操作归约操作访问操作 有很多单独的操作&…...
lua下标是可以从0开始
故事背景,策划搞了一个功能配置表,我看居然是0开始的,功能也正常。于是测试了下,还真的可以。网上看了资料确实可以,但是也有需要注意的问题 local test {[0] 0} for k,v in pairs(test)doprint(k,v) endhttps://bl…...
从AI原理到模型演进及代码实践 的学习二
参考:全面解析:从AI原理到模型演进及代码实践-CSDN博客 训练过程 Transformer仅一个Encoder模块就可以工作,可以处理信息抽取、识别、主体识别等任务,比如 BERT(Bidirectional Encoder Representations from Transfor…...
计算机组成原理(计算机系统3)--实验二:MIPS64乘法实现实验
一、实验目标: 实际运用WinMIPS64进行试验,以期更了解WinMIPS64的操作; 更加深入地了解MIPS程序的语法; 深入地了解在计算机中乘法的实现以及加法与乘法之间的关系。 二、实验内容 按照实验报告指导,完成相关操作…...
WPS excel使用宏编辑器合并 Sheet工作表
使用excel自带的工具合并Sheet表,我们会发现需要开通WPS会员才能使用合并功能; 那么WPS excel如何使用宏编辑器进行合并 Sheet表呢? 1、首先我们要看excel后缀是 .xlsx 还是 .xls ;如果是.xlsx 那么 我们需要修改为 .xls 注…...
ASP.NET Core - 配置系统之配置添加
ASP.NET Core - 配置系统之配置添加 2. 配置添加 2. 配置添加 配置系统可以读取到配置文件中的信息,那必然有某个地方可以将配置文件添加到配置系统中。之前的文章中讲到 ASP.NET Core 入口文件中,builder(WebApplicationBuilder 对象) 中有一个 Config…...
【Redis】Redis 集群中节点之间如何通信?
【Redis】Redis 集群中节点之间如何通信? 一背景概述二通信协议Gossip 协议 三通信机制Gossip 消息类型(1).Ping消息(2).Pong消息(3).Meet消息(4).Fail消息 消息传播模式(1).反熵(Anti-entropy)(2).谣言传播(Rumor mongering) 四通信过程通信端口通信频率故障检测与…...
iOS - 内存对齐
1. 基本的内存对齐 // 对象内存对齐 struct objc_object {// isa 指针 8 字节对齐isa_t isa __attribute__((aligned(8))); };// 定义对齐常量 #define WORD_MASK 7UL // 字对齐掩码 #define WORD_SHIFT 3UL // 字对齐位移 #define WORD_SIZE 8 …...
RabbitMQ(四)
SpringBoot整合RabbitMQ SpringBoot整合1、生产者工程①创建module②配置POM③YAML④主启动类⑤测试程序 2、消费者工程①创建module②配置POM③YAML文件内配置: ④主启动类⑤监听器 3、RabbitListener注解属性对比①bindings属性②queues属性 SpringBoot整合 1、生…...
unity学习17:unity里的旋转学习,欧拉角,四元数等
目录 1 三维空间里的旋转与欧拉角,四元数 1.1 欧拉角比较符合直观 1.2 四元数 1.3 下面是欧拉角和四元数的一些参考文章 2 关于旋转的这些知识点 2.1 使用euler欧拉角旋转 2.2 使用quaternion四元数,w,x,y,z 2.3 使用quaternion四元数,类 Vector3.zero 这种…...
当PHP遇上区块链:一场奇妙的技术之旅
PHP 与区块链的邂逅 在技术的广袤宇宙中,区块链技术如同一颗耀眼的新星,以其去中心化、不可篡改、透明等特性,掀起了一场席卷全球的变革浪潮。众多开发者怀揣着对新技术的热忱与探索精神,纷纷投身于区块链开发的领域,试…...
Portainer.io安装并配置Docker远程访问及CA证书
Portainer.io安装并配置Docker远程访问及CA证书 文章目录 Portainer.io安装并配置Docker远程访问及CA证书一.安装 Portainer.io2.启动容器 二.docker API远程访问并配置CA安全认证1.配置安全(密钥)访问2.补全CA证书信息3.生成server-key.pem4.创建服务端签名请求证书文件5.创建…...
从漏洞管理到暴露管理:网络安全的新方向
在网络安全的快速发展中,传统的漏洞管理仍然是防御体系的基石。然而,面对日益复杂的威胁和不断增长的漏洞数量,单靠漏洞优先级评分(如VPR)已经无法满足现代安全需求。暴露管理这一全新的思维模式正在引领企业从被动应对…...
GraphRAG如何使用ollama提供的llm model 和Embedding model服务构建本地知识库
使用GraphRAG踩坑无数 在GraphRAG的使用过程中将需要踩的坑都踩了一遍(不得不吐槽下,官方代码有很多遗留问题,他们自己也承认工作重心在算法的优化而不是各种模型和框架的兼容性适配性上),经过了大量的查阅各种资料以…...
HTTP/HTTPS ⑤-CA证书 || 中间人攻击 || SSL/TLS
这里是Themberfue ✨上节课我们聊到了对称加密和非对称加密,实际上,单纯地非对称加密并不能保证数据不被窃取,我们还需要一个更加重要的东西——证书 中间人攻击 通过非对称加密生成私钥priKey和公钥pubKey用来加密对称加密生成的密钥&…...