建个网站花钱做百度推广,系统开发是什么,wordpress添加+下载,装饰公司营销型网站建设在应用程序当中#xff0c;有时往往需要去获取到一些系统相关的信息#xff0c;譬如时间、日期、以及其它一些系统相关信息#xff0c;本章将向大家介绍如何通过 Linux 系统调用或 C 库函数获取系统信息#xff0c; 譬如获取系统时间、日期以及设置系统时间、日期等#x…在应用程序当中有时往往需要去获取到一些系统相关的信息譬如时间、日期、以及其它一些系统相关信息本章将向大家介绍如何通过 Linux 系统调用或 C 库函数获取系统信息 譬如获取系统时间、日期以及设置系统时间、日期等除此之外还会向大家介绍 Linux 系统下的/proc 虚拟文件系统包括/proc 文件系统是什么以及如何从/proc 文件系统中读取系统、进程有关信息。除了介绍系统信息内容外 本章还会向大家介绍有关系统资源的使用譬如系统内存资源的申请与使用等。 ⚫ 用于获取系统相关信息的函数 ⚫ 时间、日期 ⚫ 进程时间 ⚫ 使程序进入休眠 ⚫ 在堆中申请内存 ⚫ proc 文件系统介绍 ⚫ 定时器。
系统信息
系统标识 uname
系统调用 uname()用于获取有关当前操作系统内核的名称和信息函数原型如下所示可通过man 2uname命令查看
#include sys/utsname.h
int uname(struct utsname *buf);
使用该函数需要包含头文件sys/utsname.h。 函数参数和返回值含义如下 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_SOURCEchar domainname[];/* 当前域名 */#endif
};
可以看到 struct utsname 结构体中的所有成员变量都是字符数组 所以获取到的信息都是字符串。
#include stdio.h
#include stdlib.h
#include sys/utsname.hint main(void)
{struct utsname os_info;int ret;/* 获取信息 */ret uname(os_info);if (-1 ret) {perror(uname error);exit(-1);}/* 打印信息 */printf(操作系统名称: %s\n, os_info.sysname);printf(主机名: %s\n, os_info.nodename);printf(内核版本: %s\n, os_info.release);printf(发行版本: %s\n, os_info.version);printf(硬件架构: %s\n, os_info.machine);exit(0);
}
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 */
};
#include stdio.h
#include stdlib.h
#include sys/sysinfo.hint main(void)
{struct sysinfo sys_info;int ret;/* 获取信息 */ret sysinfo(sys_info);if (-1 ret) {perror(sysinfo error);exit(-1);}/* 打印信息 */printf(uptime: %ld\n, sys_info.uptime);printf(totalram: %lu\n, sys_info.totalram);printf(freeram: %lu\n, sys_info.freeram);printf(procs: %u\n, sys_info.procs);exit(0);
}
gethostname 函数
此函数可用于单独获取 Linux 系统主机名 与 struct utsname 数据结构体中的 nodename 变量一样gethostname 函数原型如下所示可通过man 2 gethostname命令查看
#include unistd.h
int gethostname(char *name, size_t len);
使用此函数需要包含头文件unistd.h。 函数参数和返回值含义如下 name 指向用于存放主机名字符串的缓冲区。 len 缓冲区长度。 返回值 成功返回 0,失败将返回-1并会设置 errno。
sysconf()函数
sysconf()函数是一个库函数可在运行时获取系统的一些配置信息譬如页大小page size、主机名的最大长度、进程可以打开的最大文件数、 每个用户 ID 的最大并发进程数等。其函数原型如下所示
#include unistd.h
long sysconf(int name);
使用该函数需要包含头文件unistd.h。 调用 sysconf()函数获取系统的配置信息 参数 name 指定了要获取哪个配置信息参数 name 可取以下任何一个值都是宏定义 可通过 man 手册查询 ⚫ _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 终端设备名称的最大长度。 ⚫ …… 除以上之外还有很多这里就不再一一列举了可以通过 man 手册进行查看用的比较多的是_SC_PAGESIZE 和_SC_CLK_TCK在后面章节示例代码中有使用到。 若指定的参数 name 为无效值则 sysconf()函数返回-1并会将 errno 设置为 EINVAL。否则返回的值便是对应的配置值。注意返回值是一个 long 类型的数据。
时间、日期
在正式介绍这些时间、日期相关的系统调用或 C 库函数之前需要向大家介绍一些时间相关的基本概念譬如 GMT 时间、 UTC 时间以及时区等。地球总是自西向东自转东边总比西边先看到太阳东边的时间也总比西边的早。东边时刻与西边时刻的差值不仅要以时计而且还要以分和秒来计算这给人们的日常生活和工作都带来许多不便。GMT 时间 GMTGreenwich Mean Time 中文全称是格林威治标准时间 这个时间系统的概念在 1884 年被确立由英国伦敦的格林威治皇家天文台计算并维护并在之后的几十年向欧陆其它国家扩散。从 19 世纪开始因为世界各国往来频繁而欧洲大陆、美洲大陆以及亚洲大陆都有各自的时区所以为了避免时间混乱 1884 年各国代表在美国华盛顿召开国际大会通过协议选出英国伦敦的格林威治作为全球时间的中心点 决定以通过格林威治的子午线作为划分东西两半球的经线零度线本初子午线、零度经线 由此格林威治标准时间因而诞生所以 GMT 时间就是英国格林威治当地时间 也就是零时区中时区 所在时间 譬如 GMT 12:00 就是指英国伦敦的格林威治皇家天文台当地的中午 12:00与我国的标准时间北京时间东八区相差 8 个小时即早八个小时所以 GMT 12:00 对应的北京时间是 20:00。UTC 时间 UTCCoordinated Universal Time指的是世界协调时间又称世界标准时间、世界统一时间 是经过平均太阳时(以格林威治时间 GMT 为准)、地轴运动修正后的新时标以及以「秒」为单位的国际原子时所综合精算而成的时间计算过程相当严谨精密因此若以「世界标准时间」的角度来说 UTC 比 GMT 来得更加精准。
GMT 与 UTC 这两者几乎是同一概念它们都是指格林威治标准时间也就是国际标准时间只不过UTC 时间比 GMT 时间更加精准所以在我们的编程当中不用刻意去区分它们之间的区别。在 Ubuntu 系统下 可以使用date -u命令查看到当前的 UTC 时间。
时区
全球被划分为 24 个时区每一个时区横跨经度 15 度以英国格林威治的本初子午线作为零度经线将全球划分为东西两半球 分为东一区、东二区、东三区……东十二区以及西一区、西二区、西三区……西十二区而本初子午线所在时区被称为中时区或者叫零时区划分图如下所示
东十二区和西十二区其实是一个时区就是十二区东十二区与西十二区各横跨经度 7.5 度以 180 度经线作为分界线。 每个时区的中央经线上的时间就是这个时区内统一采用的时间称为区时。相邻两个时区的时间相差 1 小时。例如我国东 8 区的时间总比泰国东 7 区的时间早 1 小时而比日本东 9 区的时间晚 1小时。因此出国旅行的人必须随时调整自己的手表才能和当地时间相一致。凡向西走每过一个时区就要把表向前拨 1 小时(比如 2 点拨到 1 点)凡向东走每过一个时区就要把表向后拨 1 小时比如 1 点拨到 2 点。实际上世界上不少国家和地区都不严格按时区来计算时间。为了在全国范围内采用统一的时间一般都把某一个时区的时间作为全国统一采用的时间。例如我国把首都北京所在的东 8 区的时间作为全国统一的时间称为北京时间 北京时间就作为我国使用的本地时间 譬如我们电脑上显示的时间就是北京时间 我国国土面积广大由东到西横跨了 5 个时区也就意味着我国最东边的地区与最西边的地区实际上相差了 4、 5 个小时。 又例如英国、法国、荷兰和比利时等国虽地处中时区但为了和欧洲大多数国家时间相一致则采用东 1 区的时间。 譬如在 Ubuntu 系统下可以使用 date 命令查看系统当前的本地时间。
可以看到显示出来的字符串后面有一个CST字样 CST 在这里其实指的是 China Standard Time中国标准时间的缩写表示当前查看到的时间是中国标准时间也就是我国所使用的标准时间--北京时间一般在安装 Ubuntu 系统的时候会提示用户设置所在城市那么系统便会根据你所设置的城市来确定系统的本地时间对应的时区譬如设置的城市为上海那么系统的本地时间就是北京时间因为我国统一使用北京时间作为本国的标准时间。 在 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 时间美国东部标准时间。
Linux 系统中的时间
点时间和段时间
通常描述时间有两种方式点时间和段时间点时间顾名思义指的是某一个时间点譬如当前时间是2024 年 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 变量进行初始化操作所以由此可知 操作系统使用 jiffies 这个全局变量来记录当前时间当我们需要获取到系统当前时间点时就可以使用 jiffies 变量去计算当然并不需要我们手动去计算 Linux 系统提供了相应的系统调用或 C库函数用于获取当前时间譬如系统调用 time()、 gettimeofday()其实质上就是通过 jiffies 变量换算得到。
获取时间 time/gettimeofday
(1)time 函数 系统调用 time()用于获取当前时间以秒为单位返回得到的值是自 1970-01-01 00:00:00 0000 (UTC)以来的秒数其函数原型如下所示可通过man 2 time命令查看
#include time.h
time_t time(time_t *tloc);
使用该函数需要包含头文件time.h。 函数参数和返回值含义如下 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 时间。
(2)gettimeofday 函数 time()获取到的时间只能精确到秒如果想要获取更加精确的时间可以使用系统调用 gettimeofday 来实现 gettimeofday()函数提供微秒级时间精度函数原型如下所示可通过man 2 gettimeofday命令查看
#include sys/time.h
int gettimeofday(struct timeval *tv, struct timezone *tz);
使用该函数需要包含头文件sys/time.h。 函数参数和返回值含义如下 tv 参数 tv 是一个 struct timeval 结构体指针变量 struct timeval 结构体在前面章节内容中已经给大家介绍过 tz 参数 tz 是个历史产物早期实现用其来获取系统的时区信息目前已遭废弃在调用 gettimeofday()函数时应将参数 tz 设置为 NULL。 返回值 成功返回 0失败将返回-1并设置 errno。 获取得到的时间值存储在参数 tv 所指向的 struct timeval 结构体变量中该结构体包含了两个成员变量tv_sec 和 tv_usec分别用于表示秒和微秒所以获取得到的时间值就是 tv_sec秒 tv_usec微秒 同样获取得到的秒数与 time()函数一样也是自 1970-01-01 00:00:00 0000 (UTC)到现在这段时间所经过的秒数 也就是日历时间 所以由此可知 time()返回得到的值和函数 gettimeofday()所返回的 tv 参数中 tv_sec 字段的数值相同。
时间转换函数
通过 time()或 gettimeofday()函数可以获取到当前时间点相对于 1970-01-01 00:00:00 0000 (UTC)这个时间点所经过时间日历时间 所以获取得到的是一个时间段的长度但是这并不利于我们查看当前时间这个结果对于我们来说非常不友好那么本小节将向大家介绍一些系统调用或 C 库函数通过这些 API 可以将 time()或 gettimeofday()函数获取到的秒数转换为利于查看和理解的形式。(1)ctime 函数 ctime()是一个 C 库函数 可以将日历时间转换为可打印输出的字符串形式 ctime()函数原型如下所示
#include time.h
char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);
使用该函数需要包含头文件time.h。 函数参数和返回值含义如下 timep time_t 时间变量指针。 返回值 成功将返回一个 char *类型指针指向转换后得到的字符串失败将返回 NULL。 所以由此可知使用 ctime 函数非常简单只需将 time_t 时间变量的指针传入即可调用成功便可返回字符串指针拿到字符串指针之后可以使用 printf 将其打印输出。但是 ctime()是一个不可重入函数 存在一些安全上面的隐患 ctime_r()是 ctime()的可重入版本一般推荐大家使用可重入函数 ctime_r()可重入函数 ctime_r()多了一个参数 buf也就是缓冲区首地址所以 ctime_r()函数需要调用者提供用于存放字符串的缓冲区。
ctime 或ctime_r转换得到的时间是计算机所在地对应的本地时间譬如在中国对应的便是北京时间并不是 UTC 时间接下来编写一段简单地代码进行测试。
#include stdio.h
#include stdlib.h
#include time.hint main(void)
{char tm_str[100] {0};time_t tm;/* 获取时间 */tm time(NULL);if (-1 tm) {perror(time error);exit(-1);}/* 将时间转换为字符串形式 */ctime_r(tm, tm_str);/* 打印输出 */printf(当前时间: %s, tm_str);exit(0);
}
(2)localtime 函数 localtime()函数可以把 time()或 gettimeofday()得到的秒数变成一个 struct tm结构体所表示的时间 该时间对应的是本地时间。 localtime 函数原型如下
#include time.h
struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);
使用该函数需要包含头文件time.h localtime()的可重入版本为 localtime_r()。 函数参数和返回值含义如下 timep 需要进行转换的 time_t 时间变量对应的指针可通过 time()或 gettimeofday()获取得到。 result 是一个 struct tm 结构体类型指针 稍后给大家介绍 struct tm 结构体参数 result 是可重入函数localtime_r()需要额外提供的参数。 返回值 对于不可重入版本 localtime()来说成功则返回一个有效的 struct tm 结构体指针而对于可重入版本 localtime_r()来说 成功执行情况下返回值将会等于参数 result失败则返回 NULL。 使用不可重入函数 localtime()并不需要调用者提供 struct tm 变量而是它会直接返回出来一个 struct tm结构体指针然后直接通过该指针访问里边的成员变量即可虽然很方便但是存在一些安全隐患所以一般不推荐使用不可重入版本。使用可重入版本 localtime_r()调用者需要自己定义 struct tm 结构体变量、并将该变量指针赋值给参数result在函数内部会对该结构体变量进行赋值操作。 struct tm 结构体如下所示
struct tm {int tm_sec; /* 秒(0-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, 星期日 Sunday 0、星期一1…) */int tm_yday; /* 一年里的第几天(0-365, 1 Jan 0) */int tm_isdst; /* 夏令时 */
};从 struct tm 结构体内容可知该结构体中包含了年月日时分秒星期等信息 使用 localtime/localtime_r()便可以将 time_t 时间总秒数分解成了各个独立的时间信息 易于我们查看和理解。localtime 函数返回的 struct tm 结构中的 tm_year 成员表示的是自 1900 年以来的年份数因此在使用时需要加上 1900 才能得到实际的年份。这是 C 标准库的设计规范了解并正确使用这一点对于正确处理时间和日期非常重要。
3gmtime 函数 gmtime()函数也可以把 time_t 时间变成一个 struct tm 结构体所表示的时间与 localtime()所不同的是gmtime()函数所得到的是 UTC 国际标准时间并不是计算机的本地时间这是它们之间的唯一区别。gmtime()函数原型如下所示
#include time.h
struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);
同样使用 gmtime()函数需要包含头文件time.h。 gmtime_r()是 gmtime()的可重入版本 同样也是推荐大家使用可重入版本函数 gmtime_r。 关于该函数的参数和返回值这里便不再介绍与 localtime()是一样的。
(4)mktime 函数 mktime()函数与 localtime()函数相反 mktime()可以将使用 struct tm 结构体表示的分解时间转换为 time_t时间日历时间 同样这也是一个 C 库函数其函数原型如下所示
#include time.h
time_t mktime(struct tm *tm);
使用该函数需要包含头文件time.h。 函数参数和返回值含义如下 tm 需要进行转换的 struct tm 结构体变量对应的指针。 返回值 成功返回转换得到 time_t 时间值失败返回-1。
(5)asctime 函数 asctime()函数与 ctime()函数的作用一样也可将时间转换为可打印输出的字符串形式与 ctime()函数的区别在于 ctime()是将 time_t 时间转换为固定格式字符串、而 asctime()则是将 struct tm 表示的分解时间转换为固定格式的字符串。 asctime()函数原型如下所示
#include time.h
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);
使用该函数需要包含头文件time.h。 函数参数和返回值含义如下 tm 需要进行转换的 struct tm 表示的时间。 buf 可重入版本函数 asctime_r 需要额外提供的参数 buf指向一个缓冲区用于存放转换得到的字符串。 返回值 转换失败将返回 NULL成功将返回一个 char *类型指针指向转换后得到的时间字符串对于 asctime_r 函数来说返回值就等于参数 buf。
(6)strftime 函数 除了 asctime()函数之外这里再给大家介绍一个 C 库函数 strftime()此函数也可以将一个 struct tm 变量表示的分解时间转换为为格式化字符串并且在功能上比 asctime()和 ctime()更加强大它可以根据自己的喜好自定义时间的显示格式而 asctime()和 ctime()转换得到的字符串时间格式的固定的。 strftime()函数原型如下所示
#include time.h
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
使用该函数需要包含头文件time.h。 函数参数和返回值含义如下 s 指向一个缓存区的指针该缓冲区用于存放生成的字符串。 max 字符串的最大字节数。 tm 指向 struct tm 结构体对象的指针。 返回值 如果转换得到的目标字符串不超过最大字节数也就是 max则返回放置到 s 数组中的字节数如果超过了最大字节数则返回 0。 format 这是一个用字符串表示的字段 包含了普通字符和特殊格式说明符可以是这两种字符的任意组合。 特殊格式说明符将会被替换为 struct tm 结构体对象所指时间的相应值这些特殊格式说明符如下
说明符表示含义实例%a星期的缩写Sun%A星期的完整名称Sunday%b月份的缩写Mar%B月份的完整名称March%c系统当前语言环境对应的首选日期和时间表示形式%C世纪年/10020%d十进制数表示一个月中的第几天01-3115、 05%D相当于m/d/y01/14/21%e与%d 相同但是单个数字时前导 0 会被去掉15、 5
%F相当于%Y-%m-%d2021-01-14%h相当于%bJan%H十进制数表示的 24 小时制的小时范围 00-2301、 22%I十进制数表示的 12 小时制的小时范围 01-1201、 11%j十进制数表示的一年中的某天范围 001-366050、 285%k与%H 相同但是单个数字时前导 0 会被去掉范围 0-231、 22%l与%I 相同但是单个数字时前导 0 会被去掉范围 1-121、 11%m十进制数表示的月份范围 01-1201、 10%M十进制数表示的分钟范围 00-5901、 55%n换行符%p根据给定的时间值添加“AM”或“PM”PM%P与%p 相同但会使用小写字母表示pm%r相当于%I:%M:%S %p12:15:31 PM%R相当于%H:%M12:16%S十进制数表示的秒数范围 00-6005、 30%T相当于%H:%M:%S12:20:03%u十进制数表示的星期范围 1-7星期一为 11、 5%U十进制数表示当前年份的第几个星期范围 00-53从第 一个星期日作为 01 周的第一天开始%W十进制数表示当前年份的第几个星期范围 00-53 从第 一个星期一作为第 01 周的第一天开始%w十进制数表示的星期范围为 0-6星期日为 0%x系统当前语言环境的首选日期表示形式没有时间01/14/21%X系统当前语言环境的首选时间表示形式没有日期12:30:16%y十进制数表示的年份后两字数字21%Y十进制数表示的年份4 个数字2021%%输出%符号%
strftime 函数的特殊格式说明符还是比较多的不用去记它需要用的时候再去查即可 通过上表可知譬如我要想输出2021-01-14 16:30:25PM January Thursday这样一种形式表示的时间日期那么就可以这样来设置 format 参数
%Y-%m-%d %H:%M:%S%p %B %A
设置时间 settimeofday
使用 settimeofday()函数可以设置时间 也就是设置系统的本地时间函数原型如下所示
#include sys/time.h
int settimeofday(const struct timeval *tv, const struct timezone *tz);
首先使用该函数需要包含头文件sys/time.h。 函数参数和返回值含义如下 tv 参数 tv 是一个 struct timeval 结构体指针变量 struct timeval 结构体在前面章节内容中已经给大家介绍了需要设置的时间便通过参数 tv 指向的 struct timeval 结构体变量传递进去。 tz 参数 tz 是个历史产物早期实现用其来设置系统的时区信息目前已遭废弃在调用 settimeofday()函数时应将参数 tz 设置为 NULL。 返回值 成功返回 0失败将返回-1并设置 errno。 使用 settimeofday 设置系统时间时内核会进行权限检查只有超级用户root才可以设置系统时间普通用户将无操作权限。
总结
进程时间
进程时间指的是进程从创建后也就是程序运行后到目前为止这段时间内使用 CPU 资源的时间总数出于记录的目的内核把 CPU 时间进程时间 分为以下两个部分 ⚫ 用户 CPU 时间进程在用户空间用户态下运行所花费的 CPU 时间。有时也成为虚拟时间virtualtime。 ⚫ 系统 CPU 时间进程在内核空间内核态下运行所花费的 CPU 时间。这是内核执行系统调用或代表进程执行的其它任务譬如服务页错误所花费的时间。 一般来说进程时间指的是用户 CPU 时间和系统 CPU 时间的总和也就是总的 CPU 时间。 Tips进程时间不等于程序的整个生命周期所消耗的时间 如果进程一直处于休眠状态进程被挂起、不会得到系统调度那么它并不会使用 CPU 资源所以休眠的这段时间并不计算在进程时间中。
times 函数
times()函数用于获取当前进程时间其函数原型如下所示
#include sys/times.h
clock_t times(struct tms *buf);
使用该函数需要包含头文件sys/times.h。 函数参数和返回值含义如下 buf times()会将当前进程时间信息存在一个 struct tms 结构体数据中所以我们需要提供 struct tms 变量使用参数 buf 指向该变量。 返回值 返回值类型为 clock_t实质是 long 类型 调用成功情况下将返回从过去任意的一个时间点譬如系统启动时间 所经过的时钟滴答数其实就是系统节拍数 将(节拍数 / 节拍率)便可得到秒数返回值可能会超过 clock_t 所能表示的范围溢出 调用失败返回-1并设置 errno。 如果我们想查看程序运行到某一个位置时的进程时间或者计算出程序中的某一段代码执行过程所花费的进程时间都可以使用 times()函数来实现。 struct tms 结构体内容如下所示
struct tms {clock_t tms_utime; /* user time, 进程的用户 CPU 时间, tms_utime 个系统节拍数 */clock_t tms_stime; /* system time, 进程的系统 CPU 时间, tms_stime 个系统节拍数 */clock_t tms_cutime; /* user time of children, 已死掉子进程的 tms_utime tms_cutime 时间总和 */clock_t tms_cstime; /* system time of children, 已死掉子进程的 tms_stime tms_cstime 时间总和 */
};
以下我们演示了通过 times()来计算程序中某一段代码执行所耗费的进程时间和总的时间测试程序如下所示
#include stdio.h
#include stdlib.h
#include sys/times.h
#include unistd.hint main(int argc, char *argv[])
{struct tms t_buf_start;struct tms t_buf_end;clock_t t_start;clock_t t_end;long tck;int i, j;/* 获取系统的节拍率 */tck sysconf(_SC_CLK_TCK);/* 开始时间 */t_start times(t_buf_start);if (-1 t_start) {perror(times error);exit(-1);}/* *****需要进行测试的代码段***** */for (i 0; i 20000; i)for (j 0; j 20000; j);sleep(1); //休眠挂起/* *************end************** *//* 结束时间 */t_end times(t_buf_end);if (-1 t_end) {perror(times error);exit(-1);}/* 打印时间 */printf(时间总和: %f 秒\n, (t_end - t_start) / (double)tck);printf(用户 CPU 时间: %f 秒\n, (t_buf_end.tms_utime - t_buf_start.tms_utime) / (double)tck);printf(系统 CPU 时间: %f 秒\n, (t_buf_end.tms_stime - t_buf_start.tms_stime) / (double)tck);exit(0);
} 可以看到用户 CPU 时间为 1.9 秒系统 CPU 时间为 0 秒也就是说测试的这段代码并没有进入内核态运行所以总的进程时间 用户 CPU 时间 系统 CPU 时间 1.9 秒。 图中显示的时间总和并不是总的进程时间 前面也给大家解释过 这个时间总和指的是从起点到终点所经过的时间并不是进程时间这里大家要理解。时间总和包括了进程处于休眠状态时消耗的时间sleep 等会让进程挂起、 进入休眠状态 可以发现时间总和比进程时间多 1 秒其实这一秒就是进程处于休眠状态的时间。
clock 函数
库函数 clock()提供了一个更为简单的方式用于进程时间它的返回值描述了进程使用的总的 CPU 时间也就是进程时间包括用户 CPU 时间和系统 CPU 时间其函数原型如下所示
#include time.h
clock_t clock(void);
使用该函数需要包含头文件time.h。 函数参数和返回值含义如下 无参数。 返回值 返回值是到目前为止程序的进程时间为 clock_t 类型注意 clock()的返回值并不是系统节拍数如果想要获得秒数请除以 CLOCKS_PER_SEC这是一个宏。 如果返回的进程时间不可用或其值无法表示则该返回值是-1。 clock()函数虽然可以很方便的获取总的进程时间但并不能获取到单独的用户 CPU 时间和系统 CPU 时间在实际编程当中根据自己的需要选择。
#include stdio.h
#include stdlib.h
#include time.hint main(int argc, char *argv[])
{clock_t t_start;clock_t t_end;int i, j;/* 开始时间 */t_start clock();if (-1 t_start)exit(-1);/* *****需要进行测试的代码段***** */for (i 0; i 20000; i)for (j 0; j 20000; j);/* *************end************** *//* 结束时间 */t_end clock();if (-1 t_end)exit(-1);/* 打印时间 */printf(总的 CPU 时间: %f\n, (t_end - t_start) / (double)CLOCKS_PER_SEC);exit(0);
}产生随机数
在应用编程当中可能会用到随机数譬如老板让你编写一个抽奖的小程序编号 0~100分为特等奖 1个、一等奖 2 个、二等级 3 以及三等级 4 个也就是说需要从 0~100 个编号中每次随机抽取一个号码这就需要用到随机数。那在 Linux 应用编程中如何去产生随机数呢本小节就来学习生成随机数。
随机数与伪随机数 随机数是随机出现没有任何规律的一组数列。在我们编程当中是没有办法获得真正意义上的随机数列的 这是一种理想的情况在我们的程序当中想要使用随机数列只能通过算法得到一个伪随机数序列那在编程当中说到的随机数基本都是指伪随机数。C 语言函数库中提供了很多函数用于产生伪随机数其中最常用的是通过 rand()和 srand()产生随机数本小节就以这两个函数为例向大家介绍如何在我们的程序中获得随机数列。
rand 函数 rand()函数用于获取随机数多次调用 rand()可得到一组随机数序列其函数原型如下
#include stdlib.h
int rand(void);
使用该函数需要包含头文件stdlib.h。 函数参数和返回值含义如下 返回值 返回一个介于 0 到 RAND_MAX包含之间的值也就是数学上的[0, RAND_MAX]。 程序当中调用 rand()可以得到[0, RAND_MAX]之间的伪随机数多次调用 rand()便可以生成一组伪随机树序列但是这里有个问题就是每一次运行程序所得到的随机数序列都是相同的那如何使得每一次启动应用程序所得到的随机数序列是不一样的呢那就通过设置不同的随机数种子可通过 srand()设置随机数种子。如果没有调用 srand()设置随机数种子的情况下 rand()会将 1 作为随机数种子如果随机数种子相同那么每一次启动应用程序所得到的随机数序列就是一样的所以每次启动应用程序需要设置不同的随机数 种子这样就可以使得程序每次运行所得到随机数序列不同。
srand 函数 使用 srand()函数为 rand()设置随机数种子其函数原型如下所示
#include stdlib.h
void srand(unsigned int seed);
函数参数和返回值含义如下 seed 指定一个随机数种子 int 类型的数据一般将当前时间作为随机数种子赋值给参数 seed譬如 time(NULL)因为每次启动应用程序时间上是不一样的所以就能够使得程序中设置的随机数种子在每次启动程序时是不一样的。 返回值 void 常用的用法 srand(time(NULL));
#include stdio.h
#include stdlib.h
#include time.hint main(int argc, char *argv[])
{int random_number_arr[8];int count;/* 设置随机数种子 */srand(time(NULL));/* 生成伪随机数 */for (count 0; count 8; count)random_number_arr[count] rand() % 100;/* 打印随机数数组 */printf([);for (count 0; count 8; count) {printf(%d, random_number_arr[count]);if (count ! 8 - 1)printf(, );}printf(]\n);exit(0);
} 从图中可以发现每一次得到的[0~100]之间的随机数数组都是不同的数组不同不是产生的随机数不同因为程序中将 rand()的随机数种子设置为 srand(time(NULL))直接等于 time_t 时间值意味着每次启动种子都不一样所以能够产生不同的随机数数组。 本小节关于在 Linux 下使用随机数就给大家介绍这么多产生随机数的 API 函数并不仅仅只有这些除此之外譬如还有 random()、 srandom()、 initstate()、 setstate()等这里便不再给大家一一介绍了在我们使用 man 手册查看系统调用或 C 库函数帮助信息时在帮助信息页面 SEE ALSO 栏会列举出与本函数有关联的一些命令、系统调用或 C 库函数等。
休眠
有时需要将进程暂停或休眠一段时间 进入休眠状态之后程序将暂停运行直到休眠结束。 常用的系统调用和 C 库函数有 sleep()、 usleep()以及 nanosleep() 这些函数在应用程序当中通常作为延时使用。
秒级休眠: sleep
sleep()是一个 C 库函数从函数名字面意思便可以知道该函数的作用了简单地说 sleep()就是让程序“休息”一会然后再继续工作。其函数原型如下所示
#include unistd.h
unsigned int sleep(unsigned int seconds);
使用该函数需要包含头文件unistd.h。 函数参数和返回值含义如下 seconds 休眠时长以秒为单位。 返回值 如果休眠时长为参数 seconds 所指定的秒数则返回 0若被信号中断则返回剩余的秒数。 sleep()是一个秒级别休眠函数程序在休眠过程中是可以被其它信号所打断的关于信号这些内容将会在后面章节向大家介绍。
微秒级休眠: usleep
usleep()同样也是一个 C 库函数与 sleep()的区别在于休眠时长精度不同 usleep()支持微秒级程序休眠其函数原型如下所示
#include unistd.h
int usleep(useconds_t usec);
函数参数和返回值含义如下 usec 休眠时长以微秒为单位。 返回值 成功返回 0失败返回-1并设置 errno。
高精度休眠: nanosleep
nanosleep()与 sleep()以及 usleep()类似都用于程序休眠但 nanosleep()具有更高精度来设置休眠时间长度支持纳秒级时长设置。与 sleep()、 usleep()不同的是 nanosleep()是一个 Linux 系统调用其函数原型如下所示
#include time.h
int nanosleep(const struct timespec *req, struct timespec *rem);
使用该函数需要包含头文件time.h。 函数参数与返回值含义如下 req 一个 struct timespec 结构体指针指向一个 struct timespec 变量用于设置休眠时间长度可精确到纳秒级别。 rem 也是一个 struct timespec 结构体指针指向一个 struct timespec 变量也可设置 NULL。 返回值 在成功休眠达到请求的时间间隔后 nanosleep()返回 0 如果中途被信号中断或遇到错误则返回-1 并将剩余时间记录在参数 rem 指向的 struct timespec 结构体变量中参数 rem 不为 NULL 的情况下如果为 NULL 表示不接收剩余时间还会设置 errno 标识错误类型。在前面小节中介绍了 struct timespec 结构体该结构体包含了两个成员变量秒tv_sec和纳秒tv_nsec。
前面说到在应用程序当中通常使用这些函数作为延时功能譬如在程序当中需要延时一秒钟、延时5 毫秒等应用场景时那么就可以使用这些函数来实现但是大家需要注意休眠状态下该进程会失去 CPU使用权 退出系统调度队列直到休眠结束。在一个裸机程序当中通常使用 for 循环或双重 for 循环语句来实现延时等待譬如在 for 循环当中执行 nop 空指令也就意味着即使在延时等待情况下 CPU 也是一直都在工作由此可知应用程序当中使用休眠用作延时功能并不是裸机程序中的 nop 空指令延时一旦执行 sleep()进程便主动交出 CPU 使用权 暂时退出系统调度队列在休眠结束前该进程的指令将得不到执行。
申请堆内存
在操作系统下内存资源是由操作系统进行管理、分配的当应用程序想要内存时这里指的是堆内存可以向操作系统申请内存然后使用内存当不再需要时将申请的内存释放、归还给操作系统 在许多的应用程序当中往往都会有这种需求譬如为一些数据结构动态分配/释放内存空间 本小节向大家介绍应用程序如何向操作系统申请堆内存。
在堆上分配内存 malloc 和 free
Linux C 程序当中一般使用 malloc()函数为程序分配一段堆内存而使用 free()函数来释放这段内存先来看下 malloc()函数原型如下所示
#include stdlib.h
void *malloc(size_t size);
使用该函数需要包含头文件stdlib.h。 函数参数和返回值含义如下 size 需要分配的内存大小以字节为单位。 返回值 返回值为 void *类型如果申请分配内存成功将返回一个指向该段内存的指针 void *并不是说没有返回值或者返回空指针而是返回的指针类型未知,所以在调用 malloc()时通常需要进行强制类型转换将 void *指针类型转换成我们希望的类型如果分配内存失败譬如系统堆内存不足 将返回 NULL如果参数 size 为 0返回值也是 NULL。
malloc()在堆区分配一块指定大小的内存空间用来存放数据。这块内存空间在函数执行完成后不会被初始化它们的值是未知的所以通常需要程序员对 malloc()分配的堆内存进行初始化操作。在堆上分配的内存需要开发者自己手动释放掉通常使用 free()函数释放堆内存 free()函数原型如下所示
#include stdlib.h
void free(void *ptr);
使用该函数同样需要包含头文件stdlib.h。 函数参数和返回值含义如下 ptr 指向需要被释放的堆内存对应的指针。 返回值 无返回值。
#include stdio.h
#include stdlib.h
#include string.h#define MALLOC_MEM_SIZE (1 * 1024 * 1024)int main(int argc, char *argv[])
{char *base NULL;/* 申请堆内存 */base (char *)malloc(MALLOC_MEM_SIZE);if (NULL base) {printf(malloc error\n);exit(-1);}/* 初始化申请到的堆内存 */memset(base, 0x0, MALLOC_MEM_SIZE);/* 使用内存 *//* ...... *//* 释放内存 */free(base);exit(0);
}
调用 free()还是不调用 free() 在学习文件 IO 基础章节内容时曾向大家介绍过 Linux 系统中当一个进程终止时内核会自动关闭它没有关闭的所有文件该进程打开的文件但是在进程终止时未调用 close()关闭它。同样对于内存来说也是如此当进程终止时内核会将其占用的所有内存都返还给操作系统这包括在堆内存中由 malloc()函数所分配的内存空间。基于内存的这一自动释放机制很多应用程序通常会省略对 free()函数的调用。这在程序中分配了多块内存的情况下可能会特别有用因为加入多次对 free()的调用不但会消耗大量的 CPU 时间而且可能会使代码趋于复杂。 虽然依靠终止进程来自动释放内存对大多数程序来说是可以接受的但最好能够在程序中显式调用free()释放内存首先其一显式调用 free()能使程序具有更好的可读性和可维护性其二对于很多程序来说申请的内存并不是在程序的生命周期中一直需要大多数情况下都是根据代码需求动态申请、释放的如果申请的内存对程序来说已经不再需要了那么就已经把它释放、归还给操作系统如果持续占用将会导致内存泄漏也就是人们常说的“你的程序在吃内存”
在堆上分配内存的其它方法
除了 malloc()外 C 函数库中还提供了一系列在堆上分配内存的其它函数 本小节将逐一介绍。
calloc()分配内存 calloc()函数用来动态地分配内存空间并初始化为 0其函数原型如下所示
#include stdlib.h
void *calloc(size_t nmemb, size_t size);
使用该函数同样也需要包含头文件stdlib.h。 calloc()在堆中动态地分配 nmemb 个长度为 size 的连续空间并将每一个字节都初始化为 0。所以它的结果是分配了 nmemb * size 个字节长度的内存空间并且每个字节的值都是 0。 返回值 分配成功返回指向该内存的地址失败则返回 NULL。 calloc()与 malloc()的一个重要区别是 calloc()在动态分配完内存后自动初始化该内存空间为零而malloc()不初始化里边数据是未知的垃圾数据。下面的两种写法是等价的
// calloc()分配内存空间并初始化
char *buf1 (char *)calloc(10, 2);
// malloc()分配内存空间并用 memset()初始化
char *buf2 (char *)malloc(10 * 2);
memset(buf2, 0, 20);
分配对齐内存
C 函数库中还提供了一系列在堆上分配对齐内存的函数对齐内存在某些应用场合非常有必要常用于分配对其内存的库函数有 posix_memalign()、 aligned_alloc()、 memalign()、 valloc()、 pvalloc()它们的函数原型如下所示
#include stdlib.h
int posix_memalign(void **memptr, size_t alignment, size_t size);
void *aligned_alloc(size_t alignment, size_t size);
void *valloc(size_t size);#include malloc.h
void *memalign(size_t alignment, size_t size);
void *pvalloc(size_t size);
使用 posix_memalign()、 aligned_alloc()、 valloc()这三个函数时需要包含头文件stdlib.h而使用memalign()、 pvalloc()这两个函数时需要包含头文件malloc.h。前面介绍的 malloc()、 calloc()分配内存返回的地址其实也是对齐的但是它俩的对齐都是固定的并且对其的字节边界比较小譬如在 32 位系统中通常是以 8 字节为边界进行对其在 64 位系统中是以 16 字节进行对其。如果想实现更大字节的对齐则需要使用本小节介绍的函数。
posix_memalign()函数 posix_memalign()函数用于在堆上分配 size 个字节大小的对齐内存空间 将*memptr 指向分配的空间分配的内存地址将是参数 alignment 的整数倍。 参数 alignment 表示对齐字节数 alignment 必须是 2 的幂次方譬如 2^4、 2^5、 2^8 等同时也要是 sizeof(void *)的整数倍对于 32 位系统来说 sizeof(void *)等于4如果是 64 位系统sizeof(void *)等于 8。 函数参数和返回值含义如下 memptr void **类型的指针内存申请成功后会将分配的内存地址存放在*memptr 中。 alignment 设置内存对其的字节数 alignment 必须是 2 的幂次方譬如 2^4、 2^5、 2^8 等同时也要是 sizeof(void *)的整数倍。 size 设置分配的内存大小以字节为单位如果参数 size 等于 0那么*memptr 中的值是 NULL。 返回值 成功将返回 0失败返回非 0 值。
aligned_alloc()函数 aligned_alloc()函数用于分配 size 个字节大小的内存空间 返回指向该空间的指针。 函数参数和返回值含义如下 alignment 用于设置对齐字节大小 alignment 必须是 2 的幂次方譬如 2^4、 2^5、 2^8 等。 size 设置分配的内存大小以字节为单位。参数 size 必须是参数 alignment 的整数倍。 返回值 成功将返回内存空间的指针内存空间的起始地址是参数 alignment 的整数倍失败返回 NULL。
memalign()函数 memalign()与 aligned_alloc()参数是一样的它们之间的区别在于对于参数 size 必须是参数 alignment的整数倍这个限制条件 memalign()并没有这个限制条件。 Tips memalign()函数已经过时了并不提倡使用
valloc()函数 valloc()分配 size 个字节大小的内存空间返回指向该内存空间的指针 内存空间的地址是页大小pagesize 的倍数。 valloc()与 memalign()类似只不过 valloc()函数内部实现中使用了页大小作为对齐的长度在程序当中可以通过系统调用 getpagesize()来获取内存的页大小。 Tips valloc()函数已经过时了并不提倡使用
proc 文件系统
proc 文件系统是一个虚拟文件系统 它以文件系统的方式为应用层访问系统内核数据提供了接口 用户和应用程序可以通过 proc 文件系统得到系统信息和进程相关信息对 proc 文件系统的读写作为与内核进行通信的一种手段。 但是与普通文件不同的是 proc 文件系统是动态创建的文件本身并不存在于磁盘当中、 只存在于内存当中,与 devfs 一样都被称为虚拟文件系统。最初构建 proc 文件系统是为了提供有关系统中进程相关的信息 但是由于这个文件系统非常有用因此内核中的很多信息也开始使用它来报告或启用动态运行时配置。 内核构建 proc 虚拟文件系统它会将内核运行时的一些关键数据信息以文件的方式呈现在 proc 文件系统下的一些特定文件中这样相当于将一些不可见的内核中的数据结构以可视化的方式呈现给应用层。proc 文件系统挂载在系统的/proc 目录下 对于内核开发者譬如驱动开发工程师来说 proc 文件系统给了开发者一种调试内核的方法通过查看/proc/xxx 文件来获取到内核特定数据结构的值在添加了新功能前后进行对比就可以判断此功能所产生的影响是否合理。/proc 目录下中包含了一些目录和虚拟文件如下所示 可以看到/proc 目录下有很多以数字命名的文件夹譬如 100038、 2299、 98560这些数字对应的其实就是一个一个的进程 PID 号每一个进程在内核中都会存在一个编号通过此编号来区分不同的进程这个编号就是 PID 号关于 PID、以及进程相关的信息将会在后面章节内容中向大家介绍。所以这些以数字命名的文件夹中记录了这些进程相关的信息不同的信息通过不同的虚拟文件呈现出来关于这些信息将会在后面章节内容中向大家介绍。 /proc 目录下除了文件夹之外还有很多的虚拟文件譬如 buddyinfo、 cgroups、 cmdline、 version 等等不同的文件记录了不同信息 关于这些文件记录的信息和意思如下 ⚫ cmdline 内核启动参数 ⚫ cpuinfo CPU 相关信息 ⚫ iomem IO 设备的内存使用情况 ⚫ interrupts显示被占用的中断号和占用者相关的信息 ⚫ ioports IO 端口的使用情况 ⚫ kcore系统物理内存映像不可读取 ⚫ loadavg系统平均负载 ⚫ meminfo物理内存和交换分区使用情况 ⚫ modules加载的模块列表 ⚫ mounts挂载的文件系统列表 ⚫ partitions系统识别的分区表 ⚫ swaps交换分区的利用情况 ⚫ version内核版本信息 ⚫ uptime系统运行时间
proc 文件系统的使用
proc 文件系统的使用就是去读取/proc 目录下的这些文件获取文件中记录的信息可以直接使用 cat 命令读取也可以在应用程序中调用 open()打开、然后再使用 read()函数读取。在 Linux 系统下直接使用 cat 命令查看/proc 目录下的虚拟文件譬如cat /proc/version查看内核版本相关信息