第七章 系统信息与系统资源(7.1-7.2.3)

本文介绍了如何通过Linux系统调用和C库函数获取系统信息,如uname、sysinfo、hostname和sysconf,以及如何处理时间和日期,包括UTC时间、实时时钟和使用gettimeofday获取高精度时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        在应用程序当中,有时往往需要去获取到一些系统相关的信息,譬如时间、日期、以及其它一些系统相关信息,本章将向大家介绍如何通过 Linux 系统调用或 C 库函数获取系统信息,譬如获取系统时间、日期以及设置系统时间、日期等;除此之外,还会向大家介绍 Linux 系统下的/proc 虚拟文件系统,包括/proc 文件系统是什么以及如何从/proc 文件系统中读取系统、进程有关信息。

7.1 系统信息

7.1.1 系统标识 uname

        系统调用 uname()用于获取有关当前操作系统内核的名称和信息。

#include <sys/utsname.h>
int uname(struct utsname *buf);

/*函数参数:
    buf:struct utsname 结构体类型指针,指向一个 struct utsname 结构体类型对象。
返回值:
    成功返回 0;
    失败将返回-1,并设置 errno。*/

        uname()函数用法非常简单,先定义一个 struct utsname 结构体变量,调用 uname()函数时传入变量的地址即可。

struct utsname 结构体如下所示:

struct utsname {
    char sysname[];    /* 当前操作系统的名称 */
    char nodename[];    /* 网络上的名称(主机名) */
    char release[];    /* 操作系统内核版本 */
    char version[];    /* 操作系统发行版本 */
    char machine[];    /* 硬件架构类型 */
#ifdef _GNU_SOURCE
    char domainname[];/* 当前域名 */
#endif
};

        可以看到struct utsname 结构体中的所有成员变量都是字符数组,获取到的信息都是字符串。

7.1.2 sysinfo 函数

        sysinfo 系统调用可用于获取一些系统统计信息。

#include <sys/sysinfo.h>
int sysinfo(struct sysinfo *info);

/*函数参数:
    info:struct sysinfo 结构体类型指针,指向一个 struct sysinfo 结构体类型对象。
返回值:
    成功返回 0;
    失败将返回-1,并设置 errno。*/

        sysinfo()函数用法也非常简单,先定义一个 struct sysinfo 结构体变量,调用 sysinfo()函数时传入变量的地址即可。
        struct sysinfo 结构体如下所示:

struct sysinfo {
    long uptime;                /* 自系统启动之后所经过的时间(以秒为单位) */
    unsigned long loads[3];     /* 1, 5, and 15 minute load averages */
    unsigned long totalram;     /* 总的可用内存大小 */
    unsigned long freeram;      /* 还未被使用的内存大小 */
    unsigned long sharedram;    /* Amount of shared memory */
    unsigned long bufferram;    /* Memory used by buffers */
    unsigned long totalswap;    /* Total swap space size */
    unsigned long freeswap;     /* swap space still available */
    unsigned short procs;       /* 系统当前进程数量 */
    unsigned long totalhigh;    /* Total high memory size */
    unsigned long freehigh;     /* Available high memory size */
    unsigned int mem_unit;      /* 内存单元大小(以字节为单位) */
    char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding to 64 bytes */
};

 

7.1.3 gethostname 函数

        此函数可用于获取 Linux 系统主机名,与utsname 数据结构体中的 nodename 变量一样。

#include <unistd.h>
int gethostname(char *name, size_t len);

/*函数参数:
    name:指向用于存放主机名字符串的缓冲区。
    len:缓冲区长度。
返回值:
    成功返回 0;
    失败将返回-1,并会设置 errno。*/

7.1.4 sysconf()函数

        sysconf()函数是一个库函数,可在运行时获取系统的一些配置信息,譬如页大小(page size)、主机名的最大长度、进程可以打开的最大文件数、每个用户 ID 的最大并发进程数等。

#include <unistd.h>
long sysconf(int name);

        调用 sysconf()函数获取系统的配置信息,参数 name 指定了要获取哪个配置信息:

        ⚫ _SC_ARG_MAX:exec 族函数的参数的最大长度,exec 族函数后面会介绍,这里先不管!
        ⚫ _SC_CHILD_MAX:最大并发进程数,也就是同一个用户可以同时运行的最大进程数。
        ⚫ _SC_HOST_NAME_MAX:主机名的最大长度。
        ⚫ _SC_LOGIN_NAME_MAX:登录名的最大长度。
        ⚫ _SC_CLK_TCK:每秒时钟滴答数,也就是系统节拍率。
        ⚫ _SC_OPEN_MAX:一个进程可以打开的最大文件数。
        ⚫ _SC_PAGESIZE:系统页大小(page size)。
        ⚫ _SC_TTY_NAME_MAX:终端设备名称的最大长度。
        ⚫等        
        若指定的参数 name 为无效值,则 sysconf()函数返回-1,并会将 errno 设置为 EINVAL。否则返回的值便是对应的配置值。注意,返回值是一个 long 类型的数据。

7.2 时间、日期

7.2.1 时间的概念

       GMT 时间

        GMT(Greenwich Mean Time)中文全称是格林威治标准时间,这个时间系统的概念在 1884 年被确立,由英国伦敦的格林威治皇家天文台计算并维护。
        GMT 时间就是英国格林威治当地时间,也就是零时区(中时区)所在时间,譬如 GMT 12:00 就是指英国伦敦的格林威治皇家天文台当地的中午 12:00,与我国的标准时间北京时间(东八区)相差 8 个小时,即早八个小时,所以 GMT 12:00 对应的北京时间是 20:00。

        UTC 时间

        UTC(Coordinated Universal Time)指的是世界协调时间(又称世界标准时间、世界统一时间),是经过平均太阳时(以格林威治时间 GMT 为准)、地轴运动修正后的新时标以及以「秒」为单位的国际原子时所综合精算而成的时间,计算过程相当严谨精密,因此若以「世界标准时间」的角度来说,UTC 比 GMT 来得更加精准。

        GMT 与 UTC 这两者几乎是同一概念,它们都是指格林威治标准时间,也就是国际标准时间,只不过UTC 时间比 GMT 时间更加精准,所以在我们的编程当中不用刻意去区分它们之间的区别。在 Ubuntu 系统下,可以使用"date -u"命令查看到当前的 UTC 时间。

        在 Ubuntu 系统下,时区信息通常以标准格式保存在一些文件当中,这些文件通常位于/usr/share/zoneinfo目录下,该目录下的每一个文件(包括子目录下的文件)都包含了一个特定国家或地区内时区制度的相关信息,且往往根据其所描述的城市或地区缩写来加以命名,譬如 EST(美国东部标准时间)、CET(欧洲中部时间)、UTC(世界标准时间)、Hongkong、Iran、Japan(日本标准时间)等,也把这些文件称为时区配置文件,如下图所示:

        系统的本地时间由时区配置文件/etc/localtime 定义,通常链接到/usr/share/zoneinfo 目录下的某一个文件(或其子目录下的某一个文件):

如果我们要修改 Ubuntu 系统本地时间的时区信息,可以直接将/etc/localtime 链接到/usr/share/zoneinfo 目录下的任意一个时区配置文件,譬如 EST(美国东部标准时间),首先进入到/etc 目录下,执行下面的命令:

sudo rm -rf localtime
#删除原有链接文件
sudo ln -s /usr/share/zoneinfo/EST localtime
#重新建立链接文件

        上图是再使用 date 命令查看下系统当前的时间。  可以发现后面的标识变成了 EST,也就意味着当前系统的本地时间变成了 EST 时间.

7.2.2 Linux 系统中的时间

        点时间和段时间

        时间有两种方式:点时间和段时间;点时间顾名思义指的是某一个时间点,譬如
        当前时间是2021 年 2 月 22 日星期一 11:12 分 35 秒,所以这里指的就是某一个时间点;
        段时间来说,顾名思义指的是某一个时间段,譬如早上 8:00 到中午 12:00 这段时间。

        实时时钟 RTC

        操作系统中一般会有两个时钟,一个系统时钟(system clock),一个实时时钟(Real time clock),也
叫 RTC。系统时钟由系统启动之后由内核来维护,譬如使用 date 命令查看到的就是系统时钟,所以在系统关机情况下是不存在的;而实时时钟一般由 RTC 时钟芯片提供,RTC 芯片有相应的电池为其供电,以保证系统在关机情况下 RTC 能够继续工作、继续计时。

        Linux 系统如何记录时间

        Linux 系统在开机启动之后首先会读取 RTC 硬件获取实时时钟作为系统时钟的初始值,之后内核便开始维护自己的系统时钟。所以由此可知,RTC 硬件只有在系统开机启动时会读取一次,目的是用于对系统时钟进行初始化操作,之后的运行过程中便不会再对其进行读取操作了。
        而在系统关机时,内核会将系统时钟写入到 RTC 硬件、已进行同步操作。

        jiffies 的引入

        jiffies 是内核中定义的一个全局变量,内核使用 jiffies 来记录系统从启动以来的系统节拍数,所以这个变量用来记录以系统节拍时间为单位的时间长度,Linux 内核在编译配置时定义了一个节拍时间,使用节拍率(一秒钟多少个节拍数)来表示。 譬如常用的:
        节拍率为 100Hz(一秒钟 100 个节拍数,节拍时间为 1s /100)、
        200Hz(一秒钟 200 个节拍,节拍时间为 1s / 200)、
        250Hz(一秒钟 250 个节拍,节拍时间为 1s / 250)、
        300Hz(一秒钟 300 个节拍,节拍时间为 1s / 300)、
        500Hz(一秒钟 500 个节拍,节拍时间为 1s /500)等
        由此可以发现配置的节拍率越低,每一个系统节拍的时间就越短,也就意味着 jiffies 记录的时间
精度越高,当然,高节拍率会导致系统中断的产生更加频繁,频繁的中断会加剧系统的负担,一般默认情况下都是采用 100Hz 作为系统节拍率。

        内核其实通过 jiffies 来维护系统时钟,全局变量 jiffies 在系统开机启动时会设置一个初始值,上面也给大家提到过,RTC 实时时钟会在系统开机启动时读取一次,目的是用于对系统时钟进行初始化,这里说的初始化其实指的就是对内核的 jiffies 变量进行初始化操作。

7.2.3 获取时间 time/gettimeofday

        time 函数

        系统调用 time()用于获取当前时间,以秒为单位,返回得到的值是自 1970-01-01 00:00:00 +0000 (UTC)以来的秒数。

#include <time.h>
time_t time(time_t *tloc);

/*函数参数:
    tloc:如果 tloc 参数不是 NULL,则返回值也存储在 tloc 指向的内存中。
返回值:
    成功则返回自 1970-01-01 00:00:00 +0000 (UTC)以来的时间值(以秒为单位);
    失败则返回-1,并会设置 errno。*/

        time 函数获取得到的是一个时间段,也就是从 1970-01-01 00:00:00 +0000 (UTC)到现在这段时间所经过的秒数,所以你要计算现在这个时间点,只需要使用 time()得到的秒数加 1970-01-01 00:00:00即可。这并不需要我们手动去计算,可以直接使用相关系统调用或 C 库函数得到当前时间

        自 1970-01-01 00:00:00 +0000 (UTC)以来经过的总秒数,我们把这个称之为日历时间或 time_t 时间。

        gettimeofday 函数

        time()获取到的时间只能精确到秒,如果想要获取更加精确的时间可以使用系统调用 gettimeofday 来实现,gettimeofday()函数提供微秒级时间精度。

#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);

/*函数参数和返回值含义如下:
    tv:参数 tv 是一个 struct timeval 结构体指针变量,
        struct timeval 结构体在前面章节内容中已经给大家介绍过,具体参考示例代码 5.6.3。
    tz:参数 tz 是个历史产物,早期实现用其来获取系统的时区信息,目前已遭废弃,
        在调用 gettimeofday()函数时应将参数 tz 设置为 NULL。
    返回值:
    成功返回 0;
    失败将返回-1,并设置 errno。*/

timeval结构体如下:

struct timeval {
    long tv_sec;     /* 秒 */
    long tv_usec;    /* 微秒 */
};

测试
        使用 gettimeofday 获取自 1970-01-01 00:00:00 +0000 (UTC)以来的时间值

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
int main(void)
{
        struct timeval tval;
        int ret;
        ret = gettimeofday(&tval, NULL);
        if (-1 == ret) {
                perror("gettimeofday error");
                exit(-1);
        }
        printf("时间值: %ld 秒+%ld 微秒\n", tval.tv_sec, tval.tv_usec);
        exit(0);
}

结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值