百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 优雅编程 > 正文

c/c++获取linux系统时间的函数哪些是线程安全的?

sinye56 2025-01-08 17:16 7 浏览 0 评论

c/c++获取linux系统时间的函数有多种,但是在多线程编程时,我们需要知道他们的实现原理,从而判断是否是线程安全的。

time函数

  • 函数原型:
time_t time(time_t *tloc);
  • 它返回从 1970 年 1 月 1 日 00:00:00 UTC到当前时间的秒数。如果tloc不是NULL,则返回值也会存储在tloc指向的位置。
  • 线程安全分析:time函数本身是线程安全的。因为它主要是获取一个系统时间的整数值(time_t类型),没有涉及到共享的静态数据结构等可能导致数据竞争的情况。
  • 安全问题分析:通常情况下比较安全,但如果在使用time函数返回的time_t值时,没有正确地进行范围检查或者在传递给其他函数时没有考虑数据类型兼容性等问题,可能会导致程序错误。例如,将time_t值错误地解释为其他类型进行计算。
  • 内部实现原理:time函数在 Linux 系统中通常是通过系统调用(如gettimeofday系统调用的部分功能)来获取系统的时间戳。系统内部维护着一个高精度的时间计数器,time函数读取这个计数器的值并将其转换为从 1970 年 1 月 1 日 00:00:00 UTC 开始计算的秒数。

localtime和localtime_r函数

  • 函数原型:
struct tm* localtime(const time_t* timep);
struct tm* localtime_r(const time_t* timep, struct tm* result);

localtime函数将time_t类型的时间值转换为本地时间的struct tm结构,localtime_r是可重入版本

  • 线程安全分析:
    • localtime函数不是线程安全的。因为它使用一个静态的struct tm结构来存储转换后的时间,在多线程环境下可能会导致数据竞争。
    • localtime_r函数是线程安全的。它允许调用者提供一个struct tm结构来存储结果,避免了多个线程对共享数据的竞争。
  • 安全问题分析:
    • 对于localtime函数,如前面所述,在多线程环境中使用可能会导致数据错误。并且如果对localtime返回的struct tm结构指针进行非法访问(如超出结构范围访问),可能会导致缓冲区溢出等问题。
    • localtime_r函数在正确使用(提供合法的struct tm结构指针)的情况下相对安全,但如果提供的struct tm结构没有足够的内存空间或者被其他线程非法修改,也可能会出现问题。
  • 内部实现原理:

这些函数主要是根据系统的时间设置(包括时区等信息)来计算time_t值对应的年、月、日、时、分、秒等信息。对于localtime,内部使用静态struct tm结构存储结果,在计算过程中会填充这个结构。而localtime_r则是按照调用者提供的struct tm结构来填充结果,计算过程涉及到对时间戳进行数学运算,如先计算总天数来确定年份,再根据年份计算月份等,同时会考虑闰年等因素。


gmtime和gmtime_r函数

  • 函数原型:
struct tm* gmtime(const time_t* timep);
struct tm* gmtime_r(const time_t* timep, struct tm* result);

它们的功能类似于localtimelocaltime_r,但是gmtime函数是将time_t类型的时间值转换为格林威治标准时间(GMT)的struct tm结构,gmtime_r是可重入版本。

  • 线程安全分析:

gmtime函数不是线程安全的,原因和localtime类似,它使用静态struct tm结构。

gmtime_r函数是线程安全的,通过让调用者提供struct tm结构来存储结果避免数据竞争。

  • 安全问题分析:

对于gmtime函数,同样存在多线程数据竞争和非法访问struct tm结构导致的问题。

gmtime_r函数在正确使用时比较安全,但也要注意提供的struct tm结构的合法性。

  • 内部实现原理:

计算原理和localtime类似,不过是基于格林威治标准时间来计算。在计算过程中,不考虑本地时区和夏令时等因素,直接根据从 1970 年 1 月 1 日 00:00:00 UTC 开始的时间戳来确定年、月、日等信息,将这些信息填充到struct tm结构中。

gettimeofday函数

  • 函数原型:
int gettimeofday(struct timeval *tv, struct timezone *tz);

它返回当前日期和时间,tv参数用于存储秒数和微秒数,tz参数用于存储时区信息(在现代应用中通常设置为NULL,因为时区信息可以通过其他方式获取)。

  • 线程安全分析:

gettimeofday函数本身是线程安全的。它主要是获取系统的时间值和微秒值,没有涉及到共享数据结构导致的数据竞争。

  • 安全问题分析:

如果对struct timeval结构(包含秒数和微秒数)的使用不当,比如没有正确处理其成员变量或者在传递过程中修改了不该修改的部分,可能会导致程序错误。另外,在一些特殊情况下,如系统时间被恶意修改或者系统时钟出现异常,获取的时间数据可能不准确。

  • 内部实现原理:

在 Linux 内核中,系统维护着高精度的时钟硬件设备(如高精度定时器)。gettimeofday函数通过系统调用机制访问内核中的时间管理模块,读取当前时间的秒数和微秒数,然后将这些值填充到struct timeval结构中。它还可以根据系统的配置读取时区信息填充到struct timezone结构中,但通常在实际应用中不使用这个功能。


clock_gettime函数

  • clock_gettime函数的原型:
#include <time.h>
 int clock_gettime(clockid_t clk_id, struct timespec *tp);

其中:

  • clk_id参数用于指定要获取时间的时钟类型,常见的取值有以下几种123:
    • CLOCK_REALTIME:表示系统的真实时间,会随着系统时间的调整而改变,从 UTC 1970-1-1 00:00:00 开始计时 。
    • CLOCK_MONOTONIC:表示系统的运行时间,从系统启动这一刻起开始计时,不受系统时间被用户改变的影响,即单调递增的时间。
    • CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统 CPU 花费的时间。
    • CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统 CPU 花费的时间。
  • tp参数是一个指向struct timespec结构体的指针,用于存储获取到的时间信息,struct timespec结构体的定义如下:
struct timespec
 { 
time_t tv_sec; /* 秒 */
 long tv_nsec; /* 纳秒 */
 };

该函数的返回值为 0 表示成功,返回 - 1 表示失败,失败时会设置errno来指示错误原因,常见的错误码有:

  • EFAULT:tp指向的地址空间不可访问。
  • EINVAL:clk_id无效或不被支持。
  • ENOTSUP:系统不支持该时钟类型。
  1. 线程安全性分析
  • 结论:clock_gettime函数是线程安全的
  • 安全问题分析:
    • 该函数不会因为多个线程同时调用而出现数据竞争的情况。这是因为它在获取时间时,不会共享内部可能被修改的全局状态(如在localtime函数中存在的共享静态struct tm结构的问题)。每个线程调用clock_gettime获取的时间信息是独立的,不受其他线程的影响。
    • 然而,在使用clock_gettime函数返回的结果(struct timespec结构)时,如果多个线程同时访问和修改这个结构,可能会导致数据不一致。但这属于对返回结果的不当使用,而非函数本身的问题。例如,如果一个线程正在读取struct timespec结构中的时间值用于计算,而另一个线程同时修改了这个结构,就可能出现计算错误。
  1. 函数内部实现原理
  • 时钟源访问:在 Linux 系统中,clock_gettime函数是通过系统调用实现的。系统维护了多个不同的时钟源,clock_gettime函数根据传入的clockid_t参数(时钟 ID)来选择访问不同的时钟。例如,CLOCK_REALTIME是系统实时时钟,它可以获取当前系统时间,并且可以通过系统设置来改变;CLOCK_MONOTONIC是单调时钟,从系统启动开始单调递增,不受系统时间调整(如用户手动修改时间)的影响。
  • 时间读取机制:
    • 当clock_gettime函数被调用时,它通过内核提供的接口访问相应的时钟硬件设备(如高精度定时器)或者内核维护的时间计数器。对于高精度时钟,例如,内核可能会使用硬件定时器产生的高频中断来更新时间计数器。这些计数器的值经过一定的转换和处理后,被填充到struct timespec结构中返回给用户空间程序。struct timespec结构包含两个成员,tv_sec(秒数)和tv_nsec(纳秒数),以提供高精度的时间信息。
    • 以CLOCK_REALTIME为例,内核会将当前系统时间(包括从硬件时钟获取的时间,并考虑时区等因素)转换为秒数和纳秒数,填充到struct timespec结构。对于CLOCK_MONOTONIC,内核会根据系统启动后的时间计数(如基于 CPU 的时间戳计数器等)来生成秒数和纳秒数,确保时间是单调递增的。

相关推荐

程序员:JDK的安装与配置(完整版)_jdk的安装方法

对于Java程序员来说,jdk是必不陌生的一个词。但怎么安装配置jdk,对新手来说确实头疼的一件事情。我这里以jdk10为例,详细的说明讲解了jdk的安装和配置,如果有不明白的小伙伴可以评论区留言哦下...

Linux中安装jdk并配置环境变量_linux jdk安装教程及环境变量配置

一、通过连接工具登录到Linux(我这里使用的Centos7.6版本)服务器连接工具有很多我就不一一介绍了今天使用比较常用的XShell工具登录成功如下:二、上传jdk安装包到Linux服务器jdk...

麒麟系统安装JAVA JDK教程_麒麟系统配置jdk

检查检查系统是否自带java在麒麟系统桌面空白处,右键“在终端打开”,打开shell对话框输入:java–version查看是否自带java及版本如图所示,系统自带OpenJDK,要先卸载自带JDK...

学习笔记-Linux JDK - 安装&amp;配置

前提条件#检查是否存在JDKrpm-qa|grepjava#删除现存JDKyum-yremovejava*安装OracleJDK不分系统#进入安装文件目...

Linux新手入门系列:Linux下jdk安装配置

本系列文章是把作者刚接触和学习Linux时候的实操记录分享出来,内容主要包括Linux入门的一些理论概念知识、Web程序、mysql数据库的简单安装部署,希望能够帮到一些初学者,少走一些弯路。注意:L...

测试员必备:Linux下安装JDK 1.8你必须知道的那些事

1.简介在Oracle收购Sun后,Java的一系列产品就被整合到Oracle官网中,打开官网乍眼一看也不知道去哪里下载,还得一个一个的摸索尝试,而且网上大多数都是一些Oracle收购Sun前,或者就...

Linux 下安装JDK17_linux 安装jdk1.8 yum

一、安装环境操作系统:JDK版本:17二、安装步骤第一步:下载安装包下载Linux环境下的jdk1.8,请去官网(https://www.oracle.com/java/technologies/do...

在Ubuntu系统中安装JDK 17并配置环境变量教程

在Ubuntu系统上安装JDK17并配置环境变量是Java开发环境搭建的重要步骤。JDK17是Oracle提供的长期支持版本,广泛用于开发Java应用程序。以下是详细的步骤,帮助你在Ubuntu系...

如何在 Linux 上安装 Java_linux安装java的步骤

在桌面上拥抱Java应用程序,然后在所有桌面上运行它们。--SethKenlon(作者)无论你运行的是哪种操作系统,通常都有几种安装应用程序的方法。有时你可能会在应用程序商店中找到一个应用程序...

Windows和Linux环境下的JDK安装教程

JavaDevelopmentKit(简称JDK),是Java开发的核心工具包,提供了Java应用程序的编译、运行和开发所需的各类工具和类库。它包括了JRE(JavaRuntimeEnviro...

linux安装jdk_linux安装jdk软连接

JDK是啥就不用多介绍了哈,外行的人也不会进来看我的博文。依然记得读大学那会,第一次实验课就是在机房安装jdk,编写HelloWorld程序。时光飞逝啊,一下过了十多年了,挣了不少钱,买了跑车,娶了富...

linux安装jdk,全局配置,不同用户不同jdk

jdk1.8安装包链接:https://pan.baidu.com/s/14qBrh6ZpLK04QS8ogCepwg提取码:09zs上传文件解压tar-zxvfjdk-8u152-linux-...

运维大神教你在linux下安装jdk8_linux安装jdk1.7

1.到官网下载适合自己机器的版本。楼主下载的是jdk-8u66-linux-i586.tar.gzhttp://www.oracle.com/technetwork/java/javase/downl...

window和linux安装JDK1.8_linux 安装jdk1.8.tar

Windows安装JDK1.8的步骤:步骤1:下载JDK打开浏览器,找到JDK下载页面https://d.injdk.cn/download/oraclejdk/8在页面中找到并点击“下载...

最全的linux下安装JavaJDK的教程(图文详解)不会安装你来打我?

默认已经有了linux服务器,且有root账号首先检查一下是否已经安装过java的jdk任意位置输入命令:whichjava像我这个已经安装过了,就会提示在哪个位置,你的肯定是找不到。一般我们在...

取消回复欢迎 发表评论: