日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

Linux系统信息与系统资源

發(fā)布時(shí)間:2023/12/10 linux 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux系统信息与系统资源 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

  • 系統(tǒng)信息
    • 系統(tǒng)標(biāo)識(shí)uname
    • sysinfo 函數(shù)
    • gethostname 函數(shù)
    • sysconf()函數(shù)
  • 時(shí)間、日期
    • GMT 時(shí)間
    • UTC 時(shí)間
    • UTC 時(shí)間格式
    • 時(shí)區(qū)
    • 實(shí)時(shí)時(shí)鐘RTC
    • 獲取時(shí)間time/gettimeofday
    • 時(shí)間轉(zhuǎn)換函數(shù)
    • 設(shè)置時(shí)間settimeofday
    • 總結(jié)
  • 進(jìn)程時(shí)間
    • times 函數(shù)
    • clock 函數(shù)
  • 產(chǎn)生隨機(jī)數(shù)
  • 休眠(延時(shí))
    • 秒級(jí)休眠: sleep
    • 微秒級(jí)休眠: usleep
    • 高精度休眠: nanosleep
  • 申請(qǐng)堆內(nèi)存
    • 在堆上分配內(nèi)存:malloc 和free
    • 調(diào)用free()還是不調(diào)用free()
    • 在堆上分配內(nèi)存的其它方法
    • 分配對(duì)齊內(nèi)存
  • proc 虛擬文件系統(tǒng)
    • proc 文件系統(tǒng)的使用

在應(yīng)用程序當(dāng)中,有時(shí)往往需要去獲取到一些系統(tǒng)相關(guān)的信息。

  • 時(shí)間、日期、以及其它一些系統(tǒng)相關(guān)信息,如何通過(guò)Linux 系統(tǒng)調(diào)用或C 庫(kù)函數(shù)獲取系統(tǒng)信息;
  • Linux 系統(tǒng)下的/proc 虛擬文件系統(tǒng),包括/proc 文件系統(tǒng)是什么以及如何從/proc 文件系統(tǒng)中讀取系統(tǒng)、進(jìn)程有關(guān)信息;
  • 系統(tǒng)資源的使用,譬如系統(tǒng)內(nèi)存資源的申請(qǐng)與使用等。

系統(tǒng)信息

系統(tǒng)標(biāo)識(shí)uname

系統(tǒng)調(diào)用uname()用于獲取有關(guān)當(dāng)前操作系統(tǒng)內(nèi)核的名稱和信息,函數(shù)原型如下所示(可通過(guò)"man 2 uname"命令查看):

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

buf:struct utsname 結(jié)構(gòu)體類型指針,指向一個(gè)struct utsname 結(jié)構(gòu)體類型對(duì)象。
返回值:成功返回0;失敗將返回-1,并設(shè)置errno。

struct utsname 結(jié)構(gòu)體如下所示:

struct utsname {char sysname[]; /* 當(dāng)前操作系統(tǒng)的名稱*/char nodename[]; /* 網(wǎng)絡(luò)上的名稱(主機(jī)名)*/char release[]; /* 操作系統(tǒng)內(nèi)核版本*/char version[]; /* 操作系統(tǒng)發(fā)行版本*/char machine[]; /* 硬件架構(gòu)類型*/#ifdef _GNU_SOURCEchar domainname[];/* 當(dāng)前域名*/#endif };

可以看到,struct utsname 結(jié)構(gòu)體中的所有成員變量都是字符數(shù)組,所以獲取到的信息都是字符串。

測(cè)試

#include <stdio.h> #include <stdlib.h> #include <sys/utsname.h>int main(void) {struct utsname os_info;int ret;/* 獲取信息*/ret = uname(&os_info);if (-1 == ret) {perror("uname error");exit(-1);}/* 打印信息*/printf("操作系統(tǒng)名稱: %s\n", os_info.sysname);printf("主機(jī)名: %s\n", os_info.nodename);printf("內(nèi)核版本: %s\n", os_info.release);printf("發(fā)行版本: %s\n", os_info.version);printf("硬件架構(gòu): %s\n", os_info.machine);exit(0); }

運(yùn)行結(jié)果:

sysinfo 函數(shù)

sysinfo 系統(tǒng)調(diào)用可用于獲取一些系統(tǒng)統(tǒng)計(jì)信息,其函數(shù)原型如下所示:

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

info:struct sysinfo 結(jié)構(gòu)體類型指針,指向一個(gè)struct sysinfo 結(jié)構(gòu)體類型對(duì)象。
返回值:成功返回0;失敗將返回-1,并設(shè)置errno。

struct sysinfo 結(jié)構(gòu)體如下所示:

struct sysinfo {long uptime; /* 自系統(tǒng)啟動(dòng)之后所經(jīng)過(guò)的時(shí)間(以秒為單位)*/unsigned long loads[3]; /* 1, 5, and 15 minute load averages */unsigned long totalram; /* 總的可用內(nèi)存大小*/unsigned long freeram; /* 還未被使用的內(nèi)存大小*/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; /* 系統(tǒng)當(dāng)前進(jìn)程數(shù)量*/unsigned long totalhigh; /* Total high memory size */unsigned long freehigh; /* Available high memory size */unsigned int mem_unit; /* 內(nèi)存單元大小(以字節(jié)為單位)*/char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding to 64 bytes */ };

測(cè)試

#include <stdio.h> #include <stdlib.h> #include <sys/sysinfo.h> int main(void) {struct sysinfo sys_info;int ret;/* 獲取信息*/ret = sysinfo(&sys_info);if (-1 == ret) {perror("sysinfo error");exit(-1);}/* 打印信息*/printf("自系統(tǒng)啟動(dòng)之后所經(jīng)過(guò)的時(shí)間: %ld\n", sys_info.uptime);printf("總的可用內(nèi)存大小: %lu\n", sys_info.totalram);printf("還未被使用的內(nèi)存大小: %lu\n", sys_info.freeram);printf("系統(tǒng)當(dāng)前進(jìn)程數(shù)量: %u\n", sys_info.procs);exit(0); }

運(yùn)行結(jié)果:

gethostname 函數(shù)

用于單獨(dú)獲取Linux 系統(tǒng)主機(jī)名,與struct utsname 數(shù)據(jù)結(jié)構(gòu)體中的nodename 變量一樣:

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

name:指向用于存放主機(jī)名字符串的緩沖區(qū)。
len:緩沖區(qū)長(zhǎng)度。
返回值:成功返回0,;失敗將返回-1,并會(huì)設(shè)置errno。

測(cè)試

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main(void) {char hostname[20];int ret;memset(hostname, 0x0, sizeof(hostname));ret = gethostname(hostname, sizeof(hostname));if (-1 == ret) {perror("gethostname error");exit(ret);}puts(hostname);exit(0); }

運(yùn)行結(jié)果:

sysconf()函數(shù)

sysconf()函數(shù)是一個(gè)庫(kù)函數(shù),可在運(yùn)行時(shí)獲取系統(tǒng)的一些配置信息,譬如頁(yè)大小(page size)、主機(jī)名的最大長(zhǎng)度、進(jìn)程可以打開的最大文件數(shù)、每個(gè)用戶ID 的最大并發(fā)進(jìn)程數(shù)等。其函數(shù)原型如下所示:

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

參數(shù)name 指定了要獲取哪個(gè)配置信息,參數(shù)name 可取以下任何一個(gè)值(都是宏定義,可通過(guò)man 手冊(cè)查詢):

? _SC_ARG_MAX:exec 族函數(shù)的參數(shù)的最大長(zhǎng)度,exec 族函數(shù)后面會(huì)介紹,這里先不管!
? _SC_CHILD_MAX:每個(gè)用戶的最大并發(fā)進(jìn)程數(shù),也就是同一個(gè)用戶可以同時(shí)運(yùn)行的最大進(jìn)程數(shù)。
? _SC_HOST_NAME_MAX:主機(jī)名的最大長(zhǎng)度。
? _SC_LOGIN_NAME_MAX:登錄名的最大長(zhǎng)度。
? _SC_CLK_TCK:每秒時(shí)鐘滴答數(shù),也就是系統(tǒng)節(jié)拍率。
? _SC_OPEN_MAX:一個(gè)進(jìn)程可以打開的最大文件數(shù)。
? _SC_PAGESIZE:系統(tǒng)頁(yè)大小(page size)。
? _SC_TTY_NAME_MAX:終端設(shè)備名稱的最大長(zhǎng)度。
? ……

若指定的參數(shù)name 為無(wú)效值,則sysconf()函數(shù)返回-1,并會(huì)將errno 設(shè)置為EINVAL。否則返回的值便是對(duì)應(yīng)的配置值。

使用示例

#include <stdio.h> #include <stdlib.h> #include <unistd.h>int main(void) {printf("每個(gè)用戶的最大并發(fā)進(jìn)程數(shù): %ld\n", sysconf(_SC_CHILD_MAX));printf("系統(tǒng)節(jié)拍率: %ld\n", sysconf(_SC_CLK_TCK));printf("系統(tǒng)頁(yè)大小: %ld\n", sysconf(_SC_PAGESIZE));exit(0); }

運(yùn)行結(jié)果:

時(shí)間、日期

GMT 時(shí)間

GMT(Greenwich Mean Time)中文全稱是格林威治標(biāo)準(zhǔn)時(shí)間,1884 年被確立,GMT 時(shí)間就是英國(guó)格林威治當(dāng)?shù)貢r(shí)間,也就是零時(shí)區(qū)(中時(shí)區(qū))所在時(shí)間,與我國(guó)的標(biāo)準(zhǔn)時(shí)間北京時(shí)間(東八區(qū))相差8 個(gè)小時(shí),即早八個(gè)小時(shí),所以GMT 12:00 對(duì)應(yīng)的北京時(shí)間是20:00。

UTC 時(shí)間

UTC(Coordinated Universal Time)指的是世界協(xié)調(diào)時(shí)間(又稱世界標(biāo)準(zhǔn)時(shí)間、世界統(tǒng)一時(shí)間),是經(jīng)過(guò)平均太陽(yáng)時(shí)(以格林威治時(shí)間GMT 為準(zhǔn))、地軸運(yùn)動(dòng)修正后的新時(shí)標(biāo)以及以「秒」為單位的國(guó)際原子時(shí)所綜合精算而成的時(shí)間,計(jì)算過(guò)程相當(dāng)嚴(yán)謹(jǐn)精密,因此若以「世界標(biāo)準(zhǔn)時(shí)間」的角度來(lái)說(shuō),UTC 比GMT 來(lái)得更加精準(zhǔn)。

在Ubuntu 系統(tǒng)下,可以使用"date -u"命令查看到當(dāng)前的UTC 時(shí)間,如下所示:

UTC 時(shí)間格式

根據(jù) ISO 8601《數(shù)據(jù)存儲(chǔ)和交換形式·信息交換·日期和時(shí)間的表示方法》,UTC時(shí)間,也就是國(guó)際統(tǒng)一時(shí)間/國(guó)際協(xié)調(diào)時(shí),表示方法如下:
YYYYMMDD T HHMMSS Z(或者時(shí)區(qū)標(biāo)識(shí))。

例如,20100607T152000Z,表示2010年6月7號(hào)15點(diǎn)20分0秒,Z表示是標(biāo)準(zhǔn)時(shí)間

如果表示北京時(shí)間,那么就是:
20100607T152000+08,其中 “+08” 表示東八區(qū)。

時(shí)區(qū)

全球被劃分為24 個(gè)時(shí)區(qū),每一個(gè)時(shí)區(qū)橫跨經(jīng)度15 度,以英國(guó)格林威治的本初子午線作為零度經(jīng)線,將全球劃分為東西兩半球,分為東一區(qū)、東二區(qū)、東三區(qū)……東十二區(qū)以及西一區(qū)、西二區(qū)、西三區(qū)……西十二區(qū),而本初子午線所在時(shí)區(qū)被稱為中時(shí)區(qū)(或者叫零時(shí)區(qū)),劃分圖如下所示:

東十二區(qū)和西十二區(qū)其實(shí)是一個(gè)時(shí)區(qū),就是十二區(qū),東十二區(qū)與西十二區(qū)各橫跨經(jīng)度7.5 度,以180 度經(jīng)線作為分界線。每個(gè)時(shí)區(qū)的中央經(jīng)線上的時(shí)間就是這個(gè)時(shí)區(qū)內(nèi)統(tǒng)一采用的時(shí)間,稱為區(qū)時(shí)。相鄰兩個(gè)時(shí)區(qū)的時(shí)間相差1 小時(shí)。例如,我國(guó)東8 區(qū)的時(shí)間總比泰國(guó)東7 區(qū)的時(shí)間早1 小時(shí),而比日本東9 區(qū)的時(shí)間晚1小時(shí)。因此,出國(guó)旅行的人,必須隨時(shí)調(diào)整自己的手表,才能和當(dāng)?shù)貢r(shí)間相一致。凡向西走,每過(guò)一個(gè)時(shí)區(qū),就要把表向前撥1 小時(shí)(比如2 點(diǎn)撥到1 點(diǎn));凡向東走,每過(guò)一個(gè)時(shí)區(qū),就要把表向后撥1 小時(shí)(比如1 點(diǎn)撥到2 點(diǎn))。

實(shí)際上,世界上不少國(guó)家和地區(qū)都不嚴(yán)格按時(shí)區(qū)來(lái)計(jì)算時(shí)間。為了在全國(guó)范圍內(nèi)采用統(tǒng)一的時(shí)間,一般都把某一個(gè)時(shí)區(qū)的時(shí)間作為全國(guó)統(tǒng)一采用的時(shí)間。例如,我國(guó)把首都北京所在的東8 區(qū)的時(shí)間作為全國(guó)統(tǒng)一的時(shí)間,稱為北京時(shí)間,北京時(shí)間就作為我國(guó)使用的本地時(shí)間,譬如我們電腦上顯示的時(shí)間就是北京時(shí)間,我國(guó)國(guó)土面積廣大,由東到西橫跨了5 個(gè)時(shí)區(qū),也就意味著我國(guó)最東邊的地區(qū)與最西邊的地區(qū)實(shí)際上相差了4、5 個(gè)小時(shí)。又例如,英國(guó)、法國(guó)、荷蘭和比利時(shí)等國(guó),雖地處中時(shí)區(qū),但為了和歐洲大多數(shù)國(guó)家時(shí)間相一致,則采用東1 區(qū)的時(shí)間。

譬如在Ubuntu 系統(tǒng)下,可以使用date 命令查看系統(tǒng)當(dāng)前的本地時(shí)間,如下所示:

可以看到顯示出來(lái)的字符串后面有一個(gè)"CST"字樣,CST 在這里其實(shí)指的是China Standard Time(中國(guó)標(biāo)準(zhǔn)時(shí)間)的縮寫,表示當(dāng)前查看到的時(shí)間是中國(guó)標(biāo)準(zhǔn)時(shí)間,也就是我國(guó)所使用的標(biāo)準(zhǔn)時(shí)間–北京時(shí)間,一般在安裝Ubuntu 系統(tǒng)的時(shí)候會(huì)提示用戶設(shè)置所在城市,那么系統(tǒng)便會(huì)根據(jù)你所設(shè)置的城市來(lái)確定系統(tǒng)的本地時(shí)間對(duì)應(yīng)的時(shí)區(qū),譬如設(shè)置的城市為上海,那么系統(tǒng)的本地時(shí)間就是北京時(shí)間,因?yàn)槲覈?guó)統(tǒng)一使用北京時(shí)間作為本國(guó)的標(biāo)準(zhǔn)時(shí)間。

在Ubuntu 系統(tǒng)下,時(shí)區(qū)信息通常以標(biāo)準(zhǔn)格式保存在一些文件當(dāng)中,這些文件通常位于/usr/share/zoneinfo目錄下,該目錄下的每一個(gè)文件(包括子目錄下的文件)都包含了一個(gè)特定國(guó)家或地區(qū)內(nèi)時(shí)區(qū)制度的相關(guān)信息,且往往根據(jù)其所描述的城市或地區(qū)縮寫來(lái)加以命名,譬如EST(美國(guó)東部標(biāo)準(zhǔn)時(shí)間)、CET(歐洲中部時(shí)間)、UTC(世界標(biāo)準(zhǔn)時(shí)間)、Hongkong、Iran、Japan(日本標(biāo)準(zhǔn)時(shí)間)等,也把這些文件稱為時(shí)區(qū)配置文件,如下圖所示:

系統(tǒng)的本地時(shí)間由時(shí)區(qū)配置文件/etc/localtime 定義,通常鏈接到/usr/share/zoneinfo 目錄下的某一個(gè)文件(或其子目錄下的某一個(gè)文件):

如果我們要修改Ubuntu 系統(tǒng)本地時(shí)間的時(shí)區(qū)信息,可以直接將/etc/localtime 鏈接到/usr/share/zoneinfo目錄下的任意一個(gè)時(shí)區(qū)配置文件,譬如EST(美國(guó)東部標(biāo)準(zhǔn)時(shí)間),首先進(jìn)入到/etc 目錄下,執(zhí)行下面的命令:

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


接下來(lái)再使用date 命令查看下系統(tǒng)當(dāng)前的時(shí)間,如下所示:

可以發(fā)現(xiàn)后面的標(biāo)識(shí)變成了EST,也就意味著當(dāng)前系統(tǒng)的本地時(shí)間變成了EST 時(shí)間(美國(guó)東部標(biāo)準(zhǔn)時(shí)間)。

實(shí)時(shí)時(shí)鐘RTC

操作系統(tǒng)中一般會(huì)有兩個(gè)時(shí)鐘,一個(gè)系統(tǒng)時(shí)鐘(system clock),一個(gè)實(shí)時(shí)時(shí)鐘(Real time clock),也叫RTC;系統(tǒng)時(shí)鐘由系統(tǒng)啟動(dòng)之后由內(nèi)核來(lái)維護(hù),譬如使用date 命令查看到的就是系統(tǒng)時(shí)鐘,所以在系統(tǒng)關(guān)機(jī)情況下是不存在的;而實(shí)時(shí)時(shí)鐘一般由RTC 時(shí)鐘芯片提供,RTC 芯片有相應(yīng)的電池為其供電,以保證系統(tǒng)在關(guān)機(jī)情況下RTC 能夠繼續(xù)工作、繼續(xù)計(jì)時(shí)。

Linux 系統(tǒng)如何記錄時(shí)間
Linux 系統(tǒng)在開機(jī)啟動(dòng)之后首先會(huì)讀取RTC 硬件獲取實(shí)時(shí)時(shí)鐘作為系統(tǒng)時(shí)鐘的初始值,之后內(nèi)核便開始維護(hù)自己的系統(tǒng)時(shí)鐘。所以由此可知,RTC 硬件只有在系統(tǒng)開機(jī)啟動(dòng)時(shí)會(huì)讀取一次,目的是用于對(duì)系統(tǒng)時(shí)鐘進(jìn)行初始化操作,之后的運(yùn)行過(guò)程中便不會(huì)再對(duì)其進(jìn)行讀取操作了。
而在系統(tǒng)關(guān)機(jī)時(shí),內(nèi)核會(huì)將系統(tǒng)時(shí)鐘寫入到RTC 硬件、以進(jìn)行同步操作。

jiffies 的引入
jiffies 是內(nèi)核中定義的一個(gè)全局變量,內(nèi)核使用jiffies 來(lái)記錄系統(tǒng)從啟動(dòng)以來(lái)的系統(tǒng)節(jié)拍數(shù),所以這個(gè)變量用來(lái)記錄以系統(tǒng)節(jié)拍時(shí)間為單位的時(shí)間長(zhǎng)度,Linux 內(nèi)核在編譯配置時(shí)定義了一個(gè)節(jié)拍時(shí)間,使用節(jié)拍率(一秒鐘多少個(gè)節(jié)拍數(shù))來(lái)表示,譬如常用的節(jié)拍率為100Hz(一秒鐘100 個(gè)節(jié)拍數(shù),節(jié)拍時(shí)間為1s /100)、200Hz(一秒鐘200 個(gè)節(jié)拍,節(jié)拍時(shí)間為1s / 200)、250Hz(一秒鐘250 個(gè)節(jié)拍,節(jié)拍時(shí)間為1s /250)、300Hz(一秒鐘300 個(gè)節(jié)拍,節(jié)拍時(shí)間為1s / 300)、500Hz(一秒鐘500 個(gè)節(jié)拍,節(jié)拍時(shí)間為1s /500)等。由此可以發(fā)現(xiàn)配置的節(jié)拍率越低,每一個(gè)系統(tǒng)節(jié)拍的時(shí)間就越短,也就意味著jiffies 記錄的時(shí)間精度越高,當(dāng)然,高節(jié)拍率會(huì)導(dǎo)致系統(tǒng)中斷的產(chǎn)生更加頻繁,頻繁的中斷會(huì)加劇系統(tǒng)的負(fù)擔(dān),一般默認(rèn)情況下都是采用100Hz 作為系統(tǒng)節(jié)拍率。

內(nèi)核其實(shí)通過(guò)jiffies 來(lái)維護(hù)系統(tǒng)時(shí)鐘,全局變量jiffies 在系統(tǒng)開機(jī)啟動(dòng)時(shí)會(huì)設(shè)置一個(gè)初始值,RTC 實(shí)時(shí)時(shí)鐘會(huì)在系統(tǒng)開機(jī)啟動(dòng)時(shí)讀取一次,目的是用于對(duì)系統(tǒng)時(shí)鐘進(jìn)行初始化,這里說(shuō)的初始化其實(shí)指的就是對(duì)內(nèi)核的jiffies 變量進(jìn)行初始化操作,具體如何將讀取到的實(shí)時(shí)時(shí)鐘換算成jiffies 數(shù)值。

所以由此可知,操作系統(tǒng)使用jiffies 這個(gè)全局變量來(lái)記錄當(dāng)前時(shí)間,當(dāng)我們需要獲取到系統(tǒng)當(dāng)前時(shí)間點(diǎn)時(shí),就可以使用jiffies 變量去計(jì)算,當(dāng)然并不需要我們手動(dòng)去計(jì)算,Linux 系統(tǒng)提供了相應(yīng)的系統(tǒng)調(diào)用或C庫(kù)函數(shù)用于獲取當(dāng)前時(shí)間,譬如系統(tǒng)調(diào)用time()、gettimeofday(),其實(shí)質(zhì)上就是通過(guò)jiffies 變量換算得到。

獲取時(shí)間time/gettimeofday

(1)time 函數(shù)
系統(tǒng)調(diào)用time()用于獲取當(dāng)前時(shí)間,以秒為單位,返回得到的值是自1970-01-01 00:00:00 +0000 (UTC)以來(lái)的秒數(shù),函數(shù)原型如下所示:

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

tloc:如果tloc 參數(shù)不是NULL,則返回值也存儲(chǔ)在tloc 指向的內(nèi)存中。
返回值:成功則返回自1970-01-01 00:00:00 +0000 (UTC)以來(lái)的時(shí)間值(以秒為單位);失敗則返回-1,并會(huì)設(shè)置errno。

time 函數(shù)獲取得到的是一個(gè)時(shí)間段,也就是從1970-01-01 00:00:00 +0000 (UTC)到現(xiàn)在這段時(shí)間所經(jīng)過(guò)的秒數(shù),所以你要計(jì)算現(xiàn)在這個(gè)時(shí)間點(diǎn),只需要使用time()得到的秒數(shù)加1970-01-01 00:00:00即可!當(dāng)然,這并不需要我們手動(dòng)去計(jì)算,可以直接使用相關(guān)系統(tǒng)調(diào)用或C 庫(kù)函數(shù)來(lái)得到當(dāng)前時(shí)間,后面介紹。

自1970-01-01 00:00:00 +0000 (UTC)以來(lái)經(jīng)過(guò)的總秒數(shù),我們把這個(gè)稱之為日歷時(shí)間或time_t 時(shí)間。

測(cè)試
使用系統(tǒng)調(diào)用time()獲取自1970-01-01 00:00:00 +0000 (UTC)以來(lái)的時(shí)間值:

#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) {time_t t;t = time(NULL);if (-1 == t) {perror("time error");exit(-1);}printf("時(shí)間值: %ld\n", t);exit(0); }

運(yùn)行結(jié)果:

(2)gettimeofday 函數(shù)
time()獲取到的時(shí)間只能精確到秒,gettimeofday()函數(shù)提供微秒級(jí)時(shí)間精度,函數(shù)原型如下:

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

tv:參數(shù)tv 是一個(gè)struct timeval 結(jié)構(gòu)體指針變量,struct timeval 結(jié)構(gòu)體在前面章節(jié)內(nèi)容中已經(jīng)給大家介紹過(guò),具體參考示例代碼5.6.3。

tz:參數(shù)tz 是個(gè)歷史產(chǎn)物,早期實(shí)現(xiàn)用其來(lái)獲取系統(tǒng)的時(shí)區(qū)信息,目前已遭廢棄,在調(diào)用gettimeofday()函數(shù)時(shí)應(yīng)將參數(shù)tz 設(shè)置為NULL。

返回值:成功返回0;失敗將返回-1,并設(shè)置errno。

獲取得到的時(shí)間值存儲(chǔ)在參數(shù)tv 所指向的struct timeval 結(jié)構(gòu)體變量中,該結(jié)構(gòu)體包含了兩個(gè)成員變量tv_sec 和tv_usec,分別用于表示秒和微秒,所以獲取得到的時(shí)間值就是tv_sec(秒)+tv_usec(微秒),同樣獲取得到的秒數(shù)與time()函數(shù)一樣,也是自1970-01-01 00:00:00 +0000 (UTC)到現(xiàn)在這段時(shí)間所經(jīng)過(guò)的秒數(shù),也就是日歷時(shí)間,所以由此可知time()返回得到的值和函數(shù)gettimeofday()所返回的tv 參數(shù)中tv_sec 字段的數(shù)值相同。

測(cè)試

使用gettimeofday 獲取自1970-01-01 00:00:00 +0000 (UTC)以來(lái)的時(shí)間值:

#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("時(shí)間值: %ld 秒+%ld 微秒\n", tval.tv_sec, tval.tv_usec);exit(0); }

運(yùn)行結(jié)果:

時(shí)間轉(zhuǎn)換函數(shù)

通過(guò)time()或gettimeofday()函數(shù)可以獲取到當(dāng)前時(shí)間點(diǎn)相對(duì)于1970-01-01 00:00:00 +0000 (UTC)這個(gè)時(shí)間點(diǎn)所經(jīng)過(guò)時(shí)間(日歷時(shí)間),所以獲取得到的是一個(gè)時(shí)間段的長(zhǎng)度,但是這并不利于我們查看當(dāng)前時(shí)間,這個(gè)結(jié)果對(duì)于我們來(lái)說(shuō)非常不友好,那么本小節(jié)將向大家介紹一些系統(tǒng)調(diào)用或C 庫(kù)函數(shù),通過(guò)這些API 可以將time()或gettimeofday()函數(shù)獲取到的秒數(shù)轉(zhuǎn)換為利于查看和理解的形式。

(1)ctime 函數(shù)
ctime()是一個(gè)C 庫(kù)函數(shù),可以將日歷時(shí)間轉(zhuǎn)換為可打印輸出的字符串形式,ctime()函數(shù)原型如下所示:

#include <time.h>char *ctime(const time_t *timep); char *ctime_r(const time_t *timep, char *buf);

timep:time_t 時(shí)間變量指針。
返回值:成功將返回一個(gè)char *類型指針,指向轉(zhuǎn)換后得到的字符串;失敗將返回NULL。

所以由此可知,使用ctime 函數(shù)非常簡(jiǎn)單,只需將time_t 時(shí)間變量的指針傳入即可,調(diào)用成功便可返回字符串指針,拿到字符串指針之后,可以使用printf 將其打印輸出。但是ctime()是一個(gè)不可重入函數(shù),存在一些安全上面的隱患,ctime_r()是ctime()的可重入版本,一般推薦大家使用可重入函數(shù)ctime_r(),可重入函數(shù)ctime_r()多了一個(gè)參數(shù)buf,也就是緩沖區(qū)首地址,所以ctime_r()函數(shù)需要調(diào)用者提供用于存放字符串的緩沖區(qū)。

Tips:關(guān)于可重入函數(shù)與不可重入函數(shù)將會(huì)在后面章節(jié)內(nèi)容中進(jìn)行介紹,這里暫時(shí)先不去管這個(gè)問(wèn)題,在Linux 系統(tǒng)中,有一些系統(tǒng)調(diào)用或C庫(kù)函數(shù)提供了可重入版本與不可重入版本的函數(shù)接口,可重入版本函數(shù)所對(duì)應(yīng)的函數(shù)名一般都會(huì)有一個(gè)" _r "后綴來(lái)表明它是一個(gè)可重入函數(shù)。
ctime (或ctime_r)轉(zhuǎn)換得到的時(shí)間是計(jì)算機(jī)所在地對(duì)應(yīng)的本地時(shí)間(譬如在中國(guó)對(duì)應(yīng)的便是北京時(shí)間),并不是UTC時(shí)間,接下來(lái)編寫一段簡(jiǎn)單地代碼進(jìn)行測(cè)試。

測(cè)試

#include <stdio.h> #include <stdlib.h> #include <time.h>int main(void) {char tm_str[100] = {0};time_t tm;/* 獲取時(shí)間*/tm = time(NULL);if (-1 == tm) {perror("time error");exit(-1);}/* 將時(shí)間轉(zhuǎn)換為字符串形式*/ctime_r(&tm, tm_str);/* 打印輸出*/printf("當(dāng)前時(shí)間: %s", tm_str);exit(0); }

運(yùn)行結(jié)果:

從圖中可知,打印出來(lái)的時(shí)間為"Mon Feb 22 17:10:46 2021",Mon 表示星期一,這是一個(gè)英文單詞的縮寫,Feb 表示二月份,這也是一個(gè)英文單詞的縮寫,22 表示22 日,所以整個(gè)打印信息顯示的時(shí)間就是2021年2 月22 日星期一17 點(diǎn)10 分46 秒。

(2)localtime 函數(shù)
localtime()函數(shù)可以把time()或gettimeofday()得到的秒數(shù)(time_t 時(shí)間或日歷時(shí)間)變成一個(gè)struct tm結(jié)構(gòu)體所表示的時(shí)間,該時(shí)間對(duì)應(yīng)的是本地時(shí)間。localtime 函數(shù)原型如下:

#include <time.h>struct tm *localtime(const time_t *timep); struct tm *localtime_r(const time_t *timep, struct tm *result);

函數(shù)參數(shù)和返回值含義如下:
timep:需要進(jìn)行轉(zhuǎn)換的time_t 時(shí)間變量對(duì)應(yīng)的指針,可通過(guò)time()或gettimeofday()獲取得到。
result:是一個(gè)struct tm 結(jié)構(gòu)體類型指針,稍后給大家介紹struct tm 結(jié)構(gòu)體,參數(shù)result 是可重入函數(shù)
localtime_r()需要額外提供的參數(shù)。
返回值:對(duì)于不可重入版本localtime()來(lái)說(shuō),成功則返回一個(gè)有效的struct tm 結(jié)構(gòu)體指針,而對(duì)于可重入版本localtime_r()來(lái)說(shuō),成功執(zhí)行情況下,返回值將會(huì)等于參數(shù)result;失敗則返回NULL。

使用不可重入函數(shù)localtime()并不需要調(diào)用者提供struct tm 變量,而是它會(huì)直接返回出來(lái)一個(gè)struct tm結(jié)構(gòu)體指針,然后直接通過(guò)該指針訪問(wèn)里邊的成員變量即可!雖然很方便,但是存在一些安全隱患,所以一般不推薦使用不可重入版本。

使用可重入版本localtime_r()調(diào)用者需要自己定義struct tm 結(jié)構(gòu)體變量、并將該變量指針賦值給參數(shù)result,在函數(shù)內(nèi)部會(huì)對(duì)該結(jié)構(gòu)體變量進(jìn)行賦值操作。
struct tm 結(jié)構(gòu)體如下所示:

struct tm {int tm_sec; /* 秒(0-60) */int tm_min; /* 分(0-59) */int tm_hour; /* 時(shí)(0-23) */int tm_mday; /* 日(1-31) */int tm_mon; /* 月(0-11) */int tm_year; /* 年(這個(gè)值表示的是自1900 年到現(xiàn)在經(jīng)過(guò)的年數(shù)) */int tm_wday; /* 星期(0-6, 星期日Sunday = 0、星期一=1…) */int tm_yday; /* 一年里的第幾天(0-365, 1 Jan = 0) */int tm_isdst; /* 夏令時(shí)*/ };

從struct tm 結(jié)構(gòu)體內(nèi)容可知,該結(jié)構(gòu)體中包含了年月日時(shí)分秒星期等信息,使用localtime/localtime_r()便可以將time_t 時(shí)間總秒數(shù)分解成了各個(gè)獨(dú)立的時(shí)間信息,易于我們查看和理解。

測(cè)試

#include <stdio.h> #include <stdlib.h> #include <time.h>int main(void) {struct tm t;time_t sec;/* 獲取時(shí)間*/sec = time(NULL);if (-1 == sec) {perror("time error");exit(-1);}/* 轉(zhuǎn)換得到本地時(shí)間*/localtime_r(&sec, &t);/* 打印輸出*/printf("當(dāng)前時(shí)間: %d 年%d 月%d 日%d:%d:%d\n",t.tm_year + 1900, t.tm_mon, t.tm_mday,t.tm_hour, t.tm_min, t.tm_sec);exit(0); }

運(yùn)行結(jié)果:

(3)gmtime 函數(shù)
gmtime()函數(shù)也可以把time_t 時(shí)間變成一個(gè)struct tm 結(jié)構(gòu)體所表示的時(shí)間,與localtime()所不同的是,gmtime()函數(shù)所得到的是UTC 國(guó)際標(biāo)準(zhǔn)時(shí)間,并不是計(jì)算機(jī)的本地時(shí)間,這是它們之間的唯一區(qū)別。gmtime()函數(shù)原型如下所示:

#include <time.h>struct tm *gmtime(const time_t *timep); struct tm *gmtime_r(const time_t *timep, struct tm *result);

gmtime_r()是gmtime()的可重入版本,同樣也是推薦大家使用可重入版本函數(shù)gmtime_r。關(guān)于該函數(shù)的參數(shù)和返回值,這里便不再介紹,與localtime()是一樣的。

測(cè)試
使用localtime 獲取本地時(shí)間、使用gmtime 獲取UTC 國(guó)際標(biāo)準(zhǔn)時(shí)間,并進(jìn)行對(duì)比:

#include <stdio.h> #include <stdlib.h> #include <time.h>int main(void) {struct tm local_t;struct tm utc_t;time_t sec;/* 獲取時(shí)間*/sec = time(NULL);if (-1 == sec) {perror("time error");exit(-1);}/* 轉(zhuǎn)換得到本地時(shí)間*/localtime_r(&sec, &local_t);/* 轉(zhuǎn)換得到國(guó)際標(biāo)準(zhǔn)時(shí)間*/gmtime_r(&sec, &utc_t);/* 打印輸出*/printf("本地時(shí)間: %d 年%d 月%d 日%d:%d:%d\n",local_t.tm_year + 1900, local_t.tm_mon, local_t.tm_mday,local_t.tm_hour, local_t.tm_min, local_t.tm_sec);printf("UTC 時(shí)間: %d 年%d 月%d 日%d:%d:%d\n",utc_t.tm_year + 1900, utc_t.tm_mon, utc_t.tm_mday,utc_t.tm_hour, utc_t.tm_min, utc_t.tm_sec);exit(0); }

運(yùn)行結(jié)果:

從打印結(jié)果可知,本地時(shí)間與UTC 時(shí)間(國(guó)際標(biāo)準(zhǔn)時(shí)間)相差8 個(gè)小時(shí),因?yàn)楣P者使用的計(jì)算機(jī)其對(duì)應(yīng)的本地時(shí)間指的便是北京時(shí)間,而北京時(shí)間要早于國(guó)際標(biāo)準(zhǔn)時(shí)間8 個(gè)小時(shí)(東八區(qū))。
(4)mktime 函數(shù)
mktime()函數(shù)與localtime()函數(shù)相反,mktime()可以將使用struct tm 結(jié)構(gòu)體表示的分解時(shí)間轉(zhuǎn)換為time_t時(shí)間(日歷時(shí)間),同樣這也是一個(gè)C 庫(kù)函數(shù),其函數(shù)原型如下所示:

#include <time.h>time_t mktime(struct tm *tm);

tm:需要進(jìn)行轉(zhuǎn)換的struct tm 結(jié)構(gòu)體變量對(duì)應(yīng)的指針。
返回值:成功返回轉(zhuǎn)換得到time_t 時(shí)間值;失敗返回-1。

測(cè)試

#include <stdio.h> #include <stdlib.h> #include <time.h>int main(void) {struct tm local_t;time_t sec;/* 獲取時(shí)間*/sec = time(NULL);if (-1 == sec) {perror("time error");exit(-1);}printf("獲取得到的秒數(shù): %ld\n", sec);localtime_r(&sec, &local_t);printf("轉(zhuǎn)換得到的秒數(shù): %ld\n", mktime(&local_t));exit(0); }

運(yùn)行結(jié)果:

(5)asctime 函數(shù)
asctime()函數(shù)與ctime()函數(shù)的作用一樣,也可將時(shí)間轉(zhuǎn)換為可打印輸出的字符串形式,與ctime()函數(shù)的區(qū)別在于,ctime()是將time_t 時(shí)間轉(zhuǎn)換為固定格式字符串、而asctime()則是將struct tm 表示的分解時(shí)間轉(zhuǎn)換為固定格式的字符串。asctime()函數(shù)原型如下所示:

#include <time.h>char *asctime(const struct tm *tm); char *asctime_r(const struct tm *tm, char *buf);

tm:需要進(jìn)行轉(zhuǎn)換的struct tm 表示的時(shí)間。
buf:可重入版本函數(shù)asctime_r 需要額外提供的參數(shù)buf,指向一個(gè)緩沖區(qū),用于存放轉(zhuǎn)換得到的字符串。
返回值:轉(zhuǎn)換失敗將返回NULL;成功將返回一個(gè)char *類型指針,指向轉(zhuǎn)換后得到的時(shí)間字符串,對(duì)于asctime_r 函數(shù)來(lái)說(shuō),返回值就等于參數(shù)buf。

測(cè)試

#include <stdio.h> #include <stdlib.h> #include <time.h>int main(void) {struct tm local_t;char tm_str[100] = {0};time_t sec;/* 獲取時(shí)間*/sec = time(NULL);if (-1 == sec) {perror("time error");exit(-1);}localtime_r(&sec, &local_t);asctime_r(&local_t, tm_str);printf("本地時(shí)間: %s", tm_str);exit(0); }

運(yùn)行結(jié)果:

(6)strftime 函數(shù)

除了asctime()函數(shù)之外,這里再給大家介紹一個(gè)C 庫(kù)函數(shù)strftime(),此函數(shù)也可以將一個(gè)struct tm 變量表示的分解時(shí)間轉(zhuǎn)換為為格式化字符串,并且在功能上比asctime()和ctime()更加強(qiáng)大,它可以根據(jù)自己的喜好自定義時(shí)間的顯示格式,而asctime()和ctime()轉(zhuǎn)換得到的字符串時(shí)間格式的固定的。

strftime()函數(shù)原型如下所示:

#include <time.h>size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);

s:指向一個(gè)緩存區(qū)的指針,該緩沖區(qū)用于存放生成的字符串。
max:字符串的最大字節(jié)數(shù)。
format:這是一個(gè)用字符串表示的字段,包含了普通字符和特殊格式說(shuō)明符,可以是這兩種字符的任意組合。特殊格式說(shuō)明符將會(huì)被替換為struct tm 結(jié)構(gòu)體對(duì)象所指時(shí)間的相應(yīng)值,這些特殊格式說(shuō)明符如下:



strftime 函數(shù)的特殊格式說(shuō)明符還是比較多的,不用去記它,需要用的時(shí)候再去查即可!
通過(guò)上表可知,譬如我要想輸出"2021-01-14 16:30:25 January Thursday"這樣一種形式表示的時(shí)間日期,那么就可以這樣來(lái)設(shè)置format 參數(shù):

"%Y-%m-%d %H:%M:%S<%p> %B %A"

tm:指向struct tm 結(jié)構(gòu)體對(duì)象的指針。
返回值:如果轉(zhuǎn)換得到的目標(biāo)字符串不超過(guò)最大字節(jié)數(shù)(也就是max),則返回放置到s 數(shù)組中的字節(jié)數(shù);如果超過(guò)了最大字節(jié)數(shù),則返回0。
測(cè)試

#include <stdio.h> #include <stdlib.h> #include <time.h>int main(void) {struct tm local_t;char tm_str[100] = {0};time_t sec;/* 獲取時(shí)間*/sec = time(NULL);if (-1 == sec) {perror("time error");exit(-1);}localtime_r(&sec, &local_t);strftime(tm_str, sizeof(tm_str), "%Y-%m-%d %A %H:%M:%S", &local_t);printf("本地時(shí)間: %s\n", tm_str);exit(0); }

運(yùn)行結(jié)果:

設(shè)置時(shí)間settimeofday

使用settimeofday()函數(shù)可以設(shè)置時(shí)間,也就是設(shè)置系統(tǒng)的本地時(shí)間,函數(shù)原型如下所示:

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

tv:參數(shù)tv 是一個(gè)struct timeval 結(jié)構(gòu)體指針變量,struct timeval 結(jié)構(gòu)體在前面章節(jié)內(nèi)容中已經(jīng)給大家介紹了,需要設(shè)置的時(shí)間便通過(guò)參數(shù)tv 指向的struct timeval 結(jié)構(gòu)體變量傳遞進(jìn)去。
tz:參數(shù)tz 是個(gè)歷史產(chǎn)物,早期實(shí)現(xiàn)用其來(lái)設(shè)置系統(tǒng)的時(shí)區(qū)信息,目前已遭廢棄,在調(diào)用settimeofday()函數(shù)時(shí)應(yīng)將參數(shù)tz 設(shè)置為NULL。
返回值:成功返回0;失敗將返回-1,并設(shè)置errno。

使用settimeofday 設(shè)置系統(tǒng)時(shí)間時(shí)內(nèi)核會(huì)進(jìn)行權(quán)限檢查,只有超級(jí)用戶(root)才可以設(shè)置系統(tǒng)時(shí)間,普通用戶將無(wú)操作權(quán)限。

總結(jié)

本小節(jié)給大家介紹了時(shí)間相關(guān)的基本概念,譬如GMT 時(shí)間、UTC 時(shí)間以及全球24 個(gè)時(shí)區(qū)的劃分等,并且給大家介紹了Linux 系統(tǒng)下常用的時(shí)間相關(guān)的系統(tǒng)調(diào)用和庫(kù)函數(shù),主要有9 個(gè):time/ctime/localtime/gmtime/mktime/asctime/strftime/gettimeofday/settimeofday,對(duì)這些函數(shù)的功能、作用總結(jié)如下:

進(jìn)程時(shí)間

進(jìn)程時(shí)間指的是進(jìn)程從創(chuàng)建后(也就是程序運(yùn)行后)到目前為止這段時(shí)間內(nèi)使用CPU 資源的時(shí)間總數(shù),出于記錄的目的,內(nèi)核把CPU 時(shí)間(進(jìn)程時(shí)間)分為以下兩個(gè)部分:
? 用戶CPU 時(shí)間:進(jìn)程在用戶空間(用戶態(tài))下運(yùn)行所花費(fèi)的CPU 時(shí)間。有時(shí)也成為虛擬時(shí)間(virtualtime)。
? 系統(tǒng)CPU 時(shí)間:進(jìn)程在內(nèi)核空間(內(nèi)核態(tài))下運(yùn)行所花費(fèi)的CPU 時(shí)間。這是內(nèi)核執(zhí)行系統(tǒng)調(diào)用或代表進(jìn)程執(zhí)行的其它任務(wù)(譬如,服務(wù)頁(yè)錯(cuò)誤)所花費(fèi)的時(shí)間。
一般來(lái)說(shuō),進(jìn)程時(shí)間指的是用戶CPU 時(shí)間和系統(tǒng)CPU 時(shí)間的總和,也就是總的CPU 時(shí)間。
Tips:進(jìn)程時(shí)間不等于程序的整個(gè)生命周期所消耗的時(shí)間,如果進(jìn)程一直處于休眠狀態(tài)(進(jìn)程被掛起、不會(huì)得到系統(tǒng)調(diào)度),那么它并不會(huì)使用CPU 資源,所以休眠的這段時(shí)間并不計(jì)算在進(jìn)程時(shí)間中。

times 函數(shù)

times()函數(shù)用于獲取當(dāng)前進(jìn)程時(shí)間,其函數(shù)原型如下所示:

#include <sys/times.h>clock_t times(struct tms *buf);

buf:times()會(huì)將當(dāng)前進(jìn)程時(shí)間信息存在一個(gè)struct tms 結(jié)構(gòu)體數(shù)據(jù)中,所以我們需要提供struct tms 變量,使用參數(shù)buf 指向該變量,關(guān)于struct tms 結(jié)構(gòu)體稍后給大家介紹。

返回值:返回值類型為clock_t(實(shí)質(zhì)是long 類型),調(diào)用成功情況下,將返回從過(guò)去任意的一個(gè)時(shí)間點(diǎn)(譬如系統(tǒng)啟動(dòng)時(shí)間)所經(jīng)過(guò)的時(shí)鐘滴答數(shù)(其實(shí)就是系統(tǒng)節(jié)拍數(shù)),將(節(jié)拍數(shù)/ 節(jié)拍率)便可得到秒數(shù),返回值可能會(huì)超過(guò)clock_t 所能表示的范圍(溢出);調(diào)用失敗返回-1,并設(shè)置errno。

如果我們想查看程序運(yùn)行到某一個(gè)位置時(shí)的進(jìn)程時(shí)間,或者計(jì)算出程序中的某一段代碼執(zhí)行過(guò)程所花費(fèi)的進(jìn)程時(shí)間,都可以使用times()函數(shù)來(lái)實(shí)現(xiàn)。

struct tms 結(jié)構(gòu)體內(nèi)容如下所示:

struct tms {clock_t tms_utime; /* user time, 進(jìn)程的用戶CPU 時(shí)間, tms_utime 個(gè)系統(tǒng)節(jié)拍數(shù)*/clock_t tms_stime; /* system time, 進(jìn)程的系統(tǒng)CPU 時(shí)間, tms_stime 個(gè)系統(tǒng)節(jié)拍數(shù)*/clock_t tms_cutime; /* user time of children, 已死掉子進(jìn)程的tms_utime + tms_cutime 時(shí)間總和*/clock_t tms_cstime; /* system time of children, 已死掉子進(jìn)程的tms_stime + tms_cstime 時(shí)間總和*/ };

測(cè)試
以下我們演示了通過(guò)times()來(lái)計(jì)算程序中某一段代碼執(zhí)行所耗費(fèi)的進(jìn)程時(shí)間和總的時(shí)間,測(cè)試程序如下所示:

#include <stdio.h> #include <stdlib.h> #include <sys/times.h> #include <unistd.h>int 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;/* 獲取系統(tǒng)的節(jié)拍率*/tck = sysconf(_SC_CLK_TCK);/* 開始時(shí)間*/t_start = times(&t_buf_start);if (-1 == t_start) {perror("times error");exit(-1);}/* *****需要進(jìn)行測(cè)試的代碼段***** */for (i = 0; i < 20000; i++)for (j = 0; j < 20000; j++);sleep(1); //休眠掛起/* *************end************** *//* 結(jié)束時(shí)間*/t_end = times(&t_buf_end);if (-1 == t_end) {perror("times error");exit(-1);}/* 打印時(shí)間*/printf("時(shí)間總和: %f 秒\n", (t_end - t_start) / (double)tck);printf("用戶CPU 時(shí)間: %f 秒\n", (t_buf_end.tms_utime - t_buf_start.tms_utime) / (double)tck);printf("系統(tǒng)CPU 時(shí)間: %f 秒\n", (t_buf_end.tms_stime - t_buf_start.tms_stime) / (double)tck);exit(0); }

首先,筆者先對(duì)測(cè)試程序做一個(gè)簡(jiǎn)單地介紹,程序中使用sysconf(_SC_CLK_TCK)獲取到系統(tǒng)節(jié)拍率,程序還使用了一個(gè)庫(kù)函數(shù)sleep(),該函數(shù)也是本章將要向大家介紹的函數(shù),具體參考7.5.1 小節(jié)中的介紹。

示例代碼7.3.2 中對(duì)如下代碼段進(jìn)行了測(cè)試:

for (i = 0; i < 20000; i++)for (j = 0; j < 20000; j++); sleep(1); //休眠掛起

接下來(lái)編譯運(yùn)行,測(cè)試結(jié)果如下:

可以看到用戶CPU 時(shí)間為1.9 秒,系統(tǒng)CPU 時(shí)間為0 秒,也就是說(shuō)測(cè)試的這段代碼并沒(méi)有進(jìn)入內(nèi)核態(tài)運(yùn)行,所以總的進(jìn)程時(shí)間= 用戶CPU 時(shí)間+ 系統(tǒng)CPU 時(shí)間= 1.9 秒。

圖7.3.1 中顯示的時(shí)間總和并不是總的進(jìn)程時(shí)間,前面也給大家解釋過(guò),這個(gè)時(shí)間總和指的是從起點(diǎn)到終點(diǎn)鎖經(jīng)過(guò)的時(shí)間,并不是進(jìn)程時(shí)間,這里大家要理解。時(shí)間總和包括了進(jìn)程處于休眠狀態(tài)時(shí)消耗的時(shí)間(sleep 等會(huì)讓進(jìn)程掛起、進(jìn)入休眠狀態(tài)),可以發(fā)現(xiàn)時(shí)間總和比進(jìn)程時(shí)間多1 秒,其實(shí)這一秒就是進(jìn)程處于休眠狀態(tài)的時(shí)間。

clock 函數(shù)

庫(kù)函數(shù)clock()提供了一個(gè)更為簡(jiǎn)單的方式用于進(jìn)程時(shí)間,它的返回值描述了進(jìn)程使用的總的CPU 時(shí)間(也就是進(jìn)程時(shí)間,包括用戶CPU 時(shí)間和系統(tǒng)CPU 時(shí)間),其函數(shù)原型如下所示:

#include <time.h>clock_t clock(void);

無(wú)參數(shù)。
返回值:返回值是到目前為止程序的進(jìn)程時(shí)間,為clock_t 類型,注意clock()的返回值并不是系統(tǒng)節(jié)拍數(shù),如果想要獲得秒數(shù),請(qǐng)除以CLOCKS_PER_SEC(這是一個(gè)宏)。如果返回的進(jìn)程時(shí)間不可用或其值無(wú)法表示,則該返回值是-1。
clock()函數(shù)雖然可以很方便的獲取總的進(jìn)程時(shí)間,但并不能獲取到單獨(dú)的用戶CPU 時(shí)間和系統(tǒng)CPU 時(shí)間,在實(shí)際編程當(dāng)中,根據(jù)自己的需要選擇。

測(cè)試
對(duì)示例代碼7.3.2 進(jìn)行簡(jiǎn)單地修改,使用clock()獲取到待測(cè)試代碼段所消耗的進(jìn)程時(shí)間,如下:

#include <stdio.h> #include <stdlib.h> #include <time.h>int main(int argc, char *argv[]) {clock_t t_start;clock_t t_end;int i, j;/* 開始時(shí)間*/t_start = clock();if (-1 == t_start)exit(-1);/* *****需要進(jìn)行測(cè)試的代碼段***** */for (i = 0; i < 20000; i++)for (j = 0; j < 20000; j++);/* *************end************** *//* 結(jié)束時(shí)間*/t_end = clock();if (-1 == t_end)exit(-1);/* 打印時(shí)間*/printf("總的CPU 時(shí)間: %f\n", (t_end - t_start) / (double)CLOCKS_PER_SEC);exit(0); }

運(yùn)行結(jié)果:

產(chǎn)生隨機(jī)數(shù)

隨機(jī)數(shù)與偽隨機(jī)數(shù)
隨機(jī)數(shù)是隨機(jī)出現(xiàn),沒(méi)有任何規(guī)律的一組數(shù)列。在我們編程當(dāng)中,是沒(méi)有辦法獲得真正意義上的隨機(jī)數(shù)列的,這是一種理想的情況,在我們的程序當(dāng)中想要使用隨機(jī)數(shù)列,只能通過(guò)算法得到一個(gè)偽隨機(jī)數(shù)序列,那在編程當(dāng)中說(shuō)到的隨機(jī)數(shù),基本都是指?jìng)坞S機(jī)數(shù)。

C 語(yǔ)言函數(shù)庫(kù)中提供了很多函數(shù)用于產(chǎn)生偽隨機(jī)數(shù),其中最常用的是通過(guò)rand()和srand()產(chǎn)生隨機(jī)數(shù),本小節(jié)就以這兩個(gè)函數(shù)為例向大家介紹如何在我們的程序中獲得隨機(jī)數(shù)列。

rand 函數(shù)
rand()函數(shù)用于獲取隨機(jī)數(shù),多次調(diào)用rand()可得到一組隨機(jī)數(shù)序列,其函數(shù)原型如下:

#include <stdlib.h>int rand(void);

返回值:返回一個(gè)介于0 到RAND_MAX(包含)之間的值,也就是數(shù)學(xué)上的[0, RAND_MAX]。
程度當(dāng)中調(diào)用rand()可以得到[0, RAND_MAX]之間的偽隨機(jī)數(shù),多次調(diào)用rand()便可以生成一組偽隨機(jī)樹序列,但是這里有個(gè)問(wèn)題,就是每一次運(yùn)行程序所得到的隨機(jī)數(shù)序列都是相同的,那如何使得每一次啟動(dòng)應(yīng)用程序所得到的隨機(jī)數(shù)序列是不一樣的呢?那就通過(guò)設(shè)置不同的隨機(jī)數(shù)種子,可通過(guò)srand()設(shè)置隨機(jī)數(shù)種子。
如果沒(méi)有調(diào)用srand()設(shè)置隨機(jī)數(shù)種子的情況下,rand()會(huì)將1 作為隨機(jī)數(shù)種子,如果隨機(jī)數(shù)種子相同,那么每一次啟動(dòng)應(yīng)用程序所得到的隨機(jī)數(shù)序列就是一樣的,所以每次啟動(dòng)應(yīng)用程序需要設(shè)置不同的隨機(jī)數(shù)種子,這樣就可以使得程序每次運(yùn)行所得到隨機(jī)數(shù)序列不同。

srand 函數(shù)
使用srand()函數(shù)為rand()設(shè)置隨機(jī)數(shù)種子,其函數(shù)原型如下所示:

#include <stdlib.h>void srand(unsigned int seed);

seed:指定一個(gè)隨機(jī)數(shù)中,int 類型的數(shù)據(jù),一般嘗嘗將當(dāng)前時(shí)間作為隨機(jī)數(shù)種子賦值給參數(shù)seed,譬如time(NULL),因?yàn)槊看螁?dòng)應(yīng)用程序時(shí)間上是一樣的,所以就能夠使得程序中設(shè)置的隨機(jī)數(shù)種子在每次啟動(dòng)程序時(shí)是不一樣的。
返回值:void
常用的用法srand(time(NULL));

測(cè)試
使用rand()和srand()產(chǎn)生一組偽隨機(jī)數(shù),數(shù)值范圍為[0~100],將其打印出來(lái):

#include <stdio.h> #include <stdlib.h> #include <time.h>int main(int argc, char *argv[]) {int random_number_arr[8];int count;/* 設(shè)置隨機(jī)數(shù)種子*/srand(time(NULL));/* 生成偽隨機(jī)數(shù)*/for (count = 0; count < 8; count++)random_number_arr[count] = rand() % 100;/* 打印隨機(jī)數(shù)數(shù)組*/printf("[");for (count = 0; count < 8; count++) {printf("%d", random_number_arr[count]);if (count != 8 - 1)printf(", ");}printf("]\n");exit(0); }

運(yùn)行結(jié)果:

從圖中可以發(fā)現(xiàn),每一次得到的[0~100]之間的隨機(jī)數(shù)數(shù)組都是不同的(數(shù)組不同,不是產(chǎn)生的隨機(jī)數(shù)不同),因?yàn)槌绦蛑袑and()的隨機(jī)數(shù)種子設(shè)置為srand(time(NULL)),直接等于time_t 時(shí)間值,意味著每次啟動(dòng)種子都不一樣,所以能夠產(chǎn)生不同的隨機(jī)數(shù)數(shù)組。

本小節(jié)關(guān)于在Linux 下使用隨機(jī)數(shù)就給大家介紹這么多,產(chǎn)生隨機(jī)數(shù)的API 函數(shù)并不僅僅只有這些,除此之外,譬如還有random()、srandom()、initstate()、setstate()等,這里便不再給大家一一介紹了,在我們使用man 手冊(cè)查看系統(tǒng)調(diào)用或C 庫(kù)函數(shù)幫助信息時(shí),在幫助信息頁(yè)面SEE ALSO 欄會(huì)列舉出與本函數(shù)有關(guān)聯(lián)的一些命令、系統(tǒng)調(diào)用或C 庫(kù)函數(shù)等,如下所示(譬如執(zhí)行man 3 srand 查看):

休眠(延時(shí))

有時(shí)需要將進(jìn)程暫停或休眠一段時(shí)間,進(jìn)入休眠狀態(tài)之后,程序?qū)和_\(yùn)行,直到休眠結(jié)束。常用的系統(tǒng)調(diào)用和C 庫(kù)函數(shù)有sleep()、usleep()以及nanosleep(),這些函數(shù)在應(yīng)用程序當(dāng)中通常作為延時(shí)使用,譬如延時(shí)1 秒鐘,本小節(jié)將一一介紹。

秒級(jí)休眠: sleep

sleep()是一個(gè)C 庫(kù)函數(shù),從函數(shù)名字面意思便可以知道該函數(shù)的作用了,簡(jiǎn)單地說(shuō),sleep()就是讓程序“休息”一會(huì),然后再繼續(xù)工作。其函數(shù)原型如下所示:

#include <unistd.h>unsigned int sleep(unsigned int seconds);

seconds:休眠時(shí)長(zhǎng),以秒為單位。
返回值:如果休眠時(shí)長(zhǎng)為參數(shù)seconds 所指定的秒數(shù),則返回0;若被信號(hào)中斷則返回剩余的秒數(shù)。
sleep()是一個(gè)秒級(jí)別休眠函數(shù),程序在休眠過(guò)程中,是可以被其它信號(hào)所打斷的,關(guān)于信號(hào)這些內(nèi)容,將會(huì)在后面章節(jié)向大家介紹。

測(cè)試
編寫一個(gè)簡(jiǎn)單地程序,調(diào)用sleep()函數(shù)讓程序暫停運(yùn)行(休眠)3 秒鐘。

#include <stdio.h> #include <stdlib.h> #include <unistd.h>int main(void) {puts("Sleep Start!");/* 讓程序休眠3 秒鐘*/sleep(3);puts("Sleep End!");exit(0); }

運(yùn)行結(jié)果:

微秒級(jí)休眠: usleep

usleep()同樣也是一個(gè)C 庫(kù)函數(shù),與sleep()的區(qū)別在于休眠時(shí)長(zhǎng)精度不同,usleep()支持微秒級(jí)程序休眠,其函數(shù)原型如下所示:

#include <unistd.h>int usleep(useconds_t usec);

函數(shù)參數(shù)和返回值含義如下:
usec:休眠時(shí)長(zhǎng),以微秒為單位。
返回值:成功返回0;失敗返回-1,并設(shè)置errno。

測(cè)試
使用usleep()函數(shù)讓程序休眠3 秒鐘。

#include <stdio.h> #include <stdlib.h> #include <unistd.h>int main(void) {puts("Sleep Start!");/* 讓程序休眠3 秒鐘(3*1000*1000 微秒) */usleep(3 * 1000 * 1000);puts("Sleep End!");exit(0); }

運(yùn)行結(jié)果:

高精度休眠: nanosleep

nanosleep()與sleep()以及usleep()類似,都用于程序休眠,但nanosleep()具有更高精度來(lái)設(shè)置休眠時(shí)間長(zhǎng)度,支持納秒級(jí)時(shí)長(zhǎng)設(shè)置。與sleep()、usleep()不同的是,nanosleep()是一個(gè)Linux 系統(tǒng)調(diào)用,其函數(shù)原型如下所示:

#include <time.h>int nanosleep(const struct timespec *req, struct timespec *rem);

req:一個(gè)struct timespec 結(jié)構(gòu)體指針,指向一個(gè)struct timespec 變量,用于設(shè)置休眠時(shí)間長(zhǎng)度,可精確到納秒級(jí)別。
rem:也是一個(gè)struct timespec 結(jié)構(gòu)體指針,指向一個(gè)struct timespec 變量,也可設(shè)置NULL。
返回值:在成功休眠達(dá)到請(qǐng)求的時(shí)間間隔后,nanosleep()返回0;如果中途被信號(hào)中斷或遇到錯(cuò)誤,則返回-1,并將剩余時(shí)間記錄在參數(shù)rem 指向的struct timespec 結(jié)構(gòu)體變量中(參數(shù)rem 不為NULL 的情況下,如果為NULL 表示不接收剩余時(shí)間),還會(huì)設(shè)置errno 標(biāo)識(shí)錯(cuò)誤類型。
在5.2.3 小節(jié)中介紹了struct timespec 結(jié)構(gòu)體,該結(jié)構(gòu)體包含了兩個(gè)成員變量,秒(tv_sec)和納秒(tv_nsec),具體定義可參考示例代碼5.2.2。

測(cè)試

#include <stdio.h> #include <stdlib.h> #include <time.h>int main(void) {struct timespec request_t;puts("Sleep Start!");/* 讓程序休眠3 秒鐘*/request_t.tv_sec = 3;request_t.tv_nsec = 0;nanosleep(&request_t, NULL);puts("Sleep End!");exit(0); }

運(yùn)行結(jié)果:

前面說(shuō)到,在應(yīng)用程序當(dāng)中,通常使用這些函數(shù)作為延時(shí)功能,譬如在程序當(dāng)中需要延時(shí)一秒鐘、延時(shí)5 毫秒等應(yīng)用場(chǎng)景時(shí),那么就可以使用這些函數(shù)來(lái)實(shí)現(xiàn);但是大家需要注意,休眠狀態(tài)下,該進(jìn)程會(huì)失去CPU使用權(quán),退出系統(tǒng)調(diào)度隊(duì)列,直到休眠結(jié)束。在一個(gè)裸機(jī)程序當(dāng)中,通常使用for 循環(huán)(或雙重for 循環(huán))語(yǔ)句來(lái)實(shí)現(xiàn)延時(shí)等待,譬如在for 循環(huán)當(dāng)中執(zhí)行nop 空指令,也就意味著即使在延時(shí)等待情況下,CPU 也是一直都在工作;由此可知,應(yīng)用程序當(dāng)中使用休眠用作延時(shí)功能,并不是裸機(jī)程序中的nop 空指令延時(shí),一旦執(zhí)行sleep(),進(jìn)程便主動(dòng)交出CPU 使用權(quán),暫時(shí)退出系統(tǒng)調(diào)度隊(duì)列,在休眠結(jié)束前,該進(jìn)程的指令將得不到執(zhí)行。

申請(qǐng)堆內(nèi)存

在操作系統(tǒng)下,內(nèi)存資源是由操作系統(tǒng)進(jìn)行管理、分配的,當(dāng)應(yīng)用程序想要內(nèi)存時(shí)(這里指的是堆內(nèi)存),可以向操作系統(tǒng)申請(qǐng)內(nèi)存,然后使用內(nèi)存;當(dāng)不再需要時(shí),將申請(qǐng)的內(nèi)存釋放、歸還給操作系統(tǒng);在許多的應(yīng)用程序當(dāng)中,往往都會(huì)有這種需求,譬如為一些數(shù)據(jù)結(jié)構(gòu)動(dòng)態(tài)分配/釋放內(nèi)存空間,本小節(jié)向大家介紹應(yīng)用程序如何向操作系統(tǒng)申請(qǐng)堆內(nèi)存。

在堆上分配內(nèi)存:malloc 和free

Linux C 程序當(dāng)中一般使用malloc()函數(shù)為程序分配一段堆內(nèi)存,而使用free()函數(shù)來(lái)釋放這段內(nèi)存,先來(lái)看下malloc()函數(shù)原型,如下所示:

#include <stdlib.h>void *malloc(size_t size);

size:需要分配的內(nèi)存大小,以字節(jié)為單位。
返回值:返回值為void *類型,如果申請(qǐng)分配內(nèi)存成功,將返回一個(gè)指向該段內(nèi)存的指針,void *并不是說(shuō)沒(méi)有返回值或者返回空指針,而是返回的指針類型未知,所以在調(diào)用malloc()時(shí)通常需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換,將void *指針類型轉(zhuǎn)換成我們希望的類型;如果分配內(nèi)存失敗(譬如系統(tǒng)堆內(nèi)存不足)將返回NULL,如果參數(shù)size 為0,返回值也是NULL。

malloc()在堆區(qū)分配一塊指定大小的內(nèi)存空間,用來(lái)存放數(shù)據(jù)。這塊內(nèi)存空間在函數(shù)執(zhí)行完成后不會(huì)被初始化,它們的值是未知的,所以通常需要程序員對(duì)malloc()分配的堆內(nèi)存進(jìn)行初始化操作。
在堆上分配的內(nèi)存,需要開發(fā)者自己手動(dòng)釋放掉,通常使用free()函數(shù)釋放堆內(nèi)存,free()函數(shù)原型如下所示:

#include <stdlib.h>void free(void *ptr);

ptr:指向需要被釋放的堆內(nèi)存對(duì)應(yīng)的指針。
返回值:無(wú)返回值。
測(cè)試

#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;/* 申請(qǐng)堆內(nèi)存*/base = (char *)malloc(MALLOC_MEM_SIZE);if (NULL == base) {printf("malloc error\n");exit(-1);}/* 初始化申請(qǐng)到的堆內(nèi)存*/memset(base, 0x0, MALLOC_MEM_SIZE);/* 使用內(nèi)存*//* ...... *//* 釋放內(nèi)存*/free(base);exit(0); }

調(diào)用free()還是不調(diào)用free()

在學(xué)習(xí)文件IO 基礎(chǔ)章節(jié)內(nèi)容時(shí)曾向大家介紹過(guò),Linux 系統(tǒng)中,當(dāng)一個(gè)進(jìn)程終止時(shí),內(nèi)核會(huì)自動(dòng)關(guān)閉它沒(méi)有關(guān)閉的所有文件(該進(jìn)程打開的文件,但是在進(jìn)程終止時(shí)未調(diào)用close()關(guān)閉它)。同樣,對(duì)于內(nèi)存來(lái)說(shuō),也是如此!當(dāng)進(jìn)程終止時(shí),內(nèi)核會(huì)將其占用的所有內(nèi)存都返還給操作系統(tǒng),這包括在堆內(nèi)存中由malloc()函數(shù)所分配的內(nèi)存空間。基于內(nèi)存的這一自動(dòng)釋放機(jī)制,很多應(yīng)用程序通常會(huì)省略對(duì)free()函數(shù)的調(diào)用。

這在程序中分配了多塊內(nèi)存的情況下可能會(huì)特別有用,因?yàn)榧尤攵啻螌?duì)free()的調(diào)用不但會(huì)消耗品大量的CPU 時(shí)間,而且可能會(huì)使代碼趨于復(fù)雜。

雖然依靠終止進(jìn)程來(lái)自動(dòng)釋放內(nèi)存對(duì)大多數(shù)程序來(lái)說(shuō)是可以接受的,但最好能夠在程序中顯式調(diào)用free()釋放內(nèi)存:

  • 首先其一,顯式調(diào)用free()能使程序具有更好的可讀性和可維護(hù)性;
  • 其二,對(duì)于很多程序來(lái)說(shuō),申請(qǐng)的內(nèi)存并不是在程序的生命周期中一直需要,大多數(shù)情況下,都是根據(jù)代碼需求動(dòng)態(tài)申請(qǐng)、釋放的,如果申請(qǐng)的內(nèi)存對(duì)程序來(lái)說(shuō)已經(jīng)不再需要了,那么就已經(jīng)把它釋放、歸還給操作系統(tǒng),如果持續(xù)占用,將會(huì)導(dǎo)致內(nèi)存泄漏,也就是人們常說(shuō)的“你的程序在吃內(nèi)存”

在堆上分配內(nèi)存的其它方法

除了malloc()外,C 函數(shù)庫(kù)中還提供了一系列在堆上分配內(nèi)存的其它函數(shù),本小節(jié)將逐一介紹。

用calloc()分配內(nèi)存
calloc()函數(shù)用來(lái)動(dòng)態(tài)地分配內(nèi)存空間并初始化為0,其函數(shù)原型如下所示:

#include <stdlib.h>void *calloc(size_t nmemb, size_t size);

calloc()在堆中動(dòng)態(tài)地分配nmemb 個(gè)長(zhǎng)度為size 的連續(xù)空間,并將每一個(gè)字節(jié)都初始化為0。所以它的結(jié)果是分配了nmemb * size 個(gè)字節(jié)長(zhǎng)度的內(nèi)存空間,并且每個(gè)字節(jié)的值都是0。
返回值:分配成功返回指向該內(nèi)存的地址,失敗則返回NULL。
calloc()與malloc()的一個(gè)重要區(qū)別是:calloc()在動(dòng)態(tài)分配完內(nèi)存后,自動(dòng)初始化該內(nèi)存空間為零,而malloc()不初始化,里邊數(shù)據(jù)是未知的垃圾數(shù)據(jù)。下面的兩種寫法是等價(jià)的:

// calloc()分配內(nèi)存空間并初始化 char *buf1 = (char *)calloc(10, 2); // malloc()分配內(nèi)存空間并用memset()初始化 char *buf2 = (char *)malloc(10 * 2); memset(buf2, 0, 20);

測(cè)試
編寫測(cè)試代碼,將用戶輸入的一組數(shù)字存放到堆內(nèi)存中,并打印出來(lái)。

#include <stdio.h> #include <stdlib.h>int main(int argc, char *argv[]) {int *base = NULL;int i;/* 校驗(yàn)傳參*/if (2 > argc)exit(-1);/* 使用calloc 申請(qǐng)內(nèi)存*/base = (int *)calloc(argc - 1, sizeof(int));if (NULL == base) {printf("calloc error\n");exit(-1);}/* 將字符串轉(zhuǎn)為int 型數(shù)據(jù)存放在base 指向的內(nèi)存中*/for (i = 0; i < argc - 1; i++)base[i] = atoi(argv[i+1]);/* 打印base 數(shù)組中的數(shù)據(jù)*/printf("你輸入的數(shù)據(jù)是: ");for (i = 0; i < argc - 1; i++)printf("%d ", base[i]);putchar('\n');/* 釋放內(nèi)存*/free(base);exit(0); }

運(yùn)行結(jié)果:

分配對(duì)齊內(nèi)存

C 函數(shù)庫(kù)中還提供了一系列在堆上分配對(duì)齊內(nèi)存的函數(shù),對(duì)齊內(nèi)存在某些應(yīng)用場(chǎng)合非常有必要,常用于分配對(duì)其內(nèi)存的庫(kù)函數(shù)有:posix_memalign()、aligned_alloc()、memalign()、valloc()、pvalloc(),它們的函數(shù)原型如下所示:

#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()這三個(gè)函數(shù)時(shí)需要包含頭文件<stdlib.h>,而使用memalign()、pvalloc()這兩個(gè)函數(shù)時(shí)需要包含頭文件<malloc.h>。前面介紹的malloc()、calloc()分配內(nèi)存返回的地址其實(shí)也是對(duì)齊的,但是它倆的對(duì)齊都是固定的,并且對(duì)其的字節(jié)邊界比較小,譬如在32 位系統(tǒng)中,通常是以8 字節(jié)為邊界進(jìn)行對(duì)其,在64 位系統(tǒng)中是以16 字節(jié)進(jìn)行對(duì)其。如果想實(shí)現(xiàn)更大字節(jié)的對(duì)齊,則需要使用本小節(jié)介紹的函數(shù)。

posix_memalign()函數(shù)

posix_memalign()函數(shù)用于在堆上分配size 個(gè)字節(jié)大小的對(duì)齊內(nèi)存空間,將 * memptr 指向分配的空間,分配的內(nèi)存地址將是參數(shù)alignment 的整數(shù)倍。參數(shù) alignment 表示對(duì)齊字節(jié)數(shù),alignment 必須是2 的冪次方(譬如2 ^ 4、2 ^ 5、2 ^ 8 等),同時(shí)也要是sizeof( void * )的整數(shù)倍,對(duì)于32 位系統(tǒng)來(lái)說(shuō),sizeof(void *)等于
4,如果是64 位系統(tǒng)sizeof(void *)等于8。

函數(shù)參數(shù)和返回值含義如下:
memptr:void ** 類型的指針,內(nèi)存申請(qǐng)成功后會(huì)將分配的內(nèi)存地址存放在* memptr 中。
alignment:設(shè)置內(nèi)存對(duì)其的字節(jié)數(shù),alignment 必須是2 的冪次方(譬如2^ 4、2 ^ 5、2 ^ 8 等),同時(shí)也要是 sizeof( void *)的整數(shù)倍。
size:設(shè)置分配的內(nèi)存大小,以字節(jié)為單位,如果參數(shù)size 等于0,那么 * memptr 中的值是NULL。
返回值:成功將返回0;失敗返回非0 值。
示例代碼

#include <stdio.h> #include <stdlib.h>int main(int argc, char *argv[]) {int *base = NULL;int ret;/* 申請(qǐng)內(nèi)存: 256 字節(jié)對(duì)齊*/ret = posix_memalign((void **)&base, 256, 1024);if (0 != ret) {printf("posix_memalign error\n");exit(-1);}/* 使用內(nèi)存*/// base[0] = 0;// base[1] = 1;// base[2] = 2;// base[3] = 3;/* 釋放內(nèi)存*/free(base);exit(0); }

aligned_alloc()函數(shù)
aligned_alloc()函數(shù)用于分配size 個(gè)字節(jié)大小的內(nèi)存空間,返回指向該空間的指針。
函數(shù)參數(shù)和返回值含義如下:
alignment:用于設(shè)置對(duì)齊字節(jié)大小,alignment 必須是2 的冪次方(譬如2 ^ 4、2 ^ 5、2 ^ 8 等)。
size:設(shè)置分配的內(nèi)存大小,以字節(jié)為單位。參數(shù)size 必須是參數(shù)alignment 的整數(shù)倍。
返回值:成功將返回內(nèi)存空間的指針,內(nèi)存空間的起始地址是參數(shù)alignment 的整數(shù)倍;失敗返回NULL。
使用示例

#include <stdio.h> #include <stdlib.h>int main(int argc, char *argv[]) {int *base = NULL;/* 申請(qǐng)內(nèi)存: 256 字節(jié)對(duì)齊*/base = (int *)aligned_alloc(256, 256 * 4);if (base == NULL) {printf("aligned_alloc error\n");exit(-1);}/* 使用內(nèi)存*/// base[0] = 0;// base[1] = 1;// base[2] = 2;// base[3] = 3;/* 釋放內(nèi)存*/free(base);exit(0); }

memalign()函數(shù)
memalign()與aligned_alloc()參數(shù)是一樣的,它們之間的區(qū)別在于:對(duì)于參數(shù)size 必須是參數(shù)alignment的整數(shù)倍這個(gè)限制條件,memalign()并沒(méi)有這個(gè)限制條件。
Tips:memalign()函數(shù)已經(jīng)過(guò)時(shí)了,并不提倡使用!
使用示例

#include <stdio.h> #include <stdlib.h> #include <malloc.h>int main(int argc, char *argv[]) {int *base = NULL;/* 申請(qǐng)內(nèi)存: 256 字節(jié)對(duì)齊*/base = (int *)memalign(256, 1024);if (base == NULL) {printf("memalign error\n");exit(-1);}/* 使用內(nèi)存*/// base[0] = 0;// base[1] = 1;// base[2] = 2;// base[3] = 3;/* 釋放內(nèi)存*/free(base);exit(0); }

valloc()函數(shù)

valloc()分配size 個(gè)字節(jié)大小的內(nèi)存空間,返回指向該內(nèi)存空間的指針,內(nèi)存空間的地址是頁(yè)大小(pagesize)的倍數(shù)。
valloc()與memalign()類似,只不過(guò)valloc()函數(shù)內(nèi)部實(shí)現(xiàn)中,使用了頁(yè)大小作為對(duì)齊的長(zhǎng)度,在程序當(dāng)中,可以通過(guò)系統(tǒng)調(diào)用getpagesize()來(lái)獲取內(nèi)存的頁(yè)大小。
Tips:valloc()函數(shù)已經(jīng)過(guò)時(shí)了,并不提倡使用!

使用示例

#include <stdio.h> #include <stdlib.h>int main(int argc, char *argv[]) {int *base = NULL;/* 申請(qǐng)內(nèi)存: 1024 個(gè)字節(jié)*/base = (int *)valloc(1024);if (base == NULL) {printf("valloc error\n");exit(-1);}/* 使用內(nèi)存*/// base[0] = 0;// base[1] = 1;// base[2] = 2;// base[3] = 3;/* 釋放內(nèi)存*/free(base);exit(0); }

proc 虛擬文件系統(tǒng)

proc文件系統(tǒng)是一個(gè)虛擬文件系統(tǒng),它以文件系統(tǒng)的方式為應(yīng)用層訪問(wèn)系統(tǒng)內(nèi)核數(shù)據(jù)提供了接口,用戶和應(yīng)用程序可以通過(guò)proc文件系統(tǒng)得到系統(tǒng)信息和進(jìn)程相關(guān)信息,對(duì)proc文件系統(tǒng)的讀寫作為與內(nèi)核進(jìn)行通信的一種手段。但是與普通文件不同的是,proc文件系統(tǒng)是動(dòng)態(tài)創(chuàng)建的,文件本身并不存在于磁盤當(dāng)中、只存在于內(nèi)存當(dāng)中,與devfs一樣,都被稱為虛擬文件系統(tǒng)

最初構(gòu)建proc文件系統(tǒng)是為了提供有關(guān)系統(tǒng)中進(jìn)程相關(guān)的信息,但是由于這個(gè)文件系統(tǒng)非常有用,因此內(nèi)核中的很多信息也開始使用它來(lái)報(bào)告,或啟用動(dòng)態(tài)運(yùn)行時(shí)配置。內(nèi)核構(gòu)建proc虛擬文件系統(tǒng),它會(huì)將內(nèi)核運(yùn)行時(shí)的一些關(guān)鍵數(shù)據(jù)信息以文件的方式呈現(xiàn)在proc文件系統(tǒng)下的一些特定文件中,這樣相當(dāng)于將一些不可見(jiàn)的內(nèi)核中的數(shù)據(jù)結(jié)構(gòu)以可視化的方式呈現(xiàn)給應(yīng)用層。

proc文件系統(tǒng)掛載在系統(tǒng)的/proc目錄下,對(duì)于內(nèi)核開發(fā)者(譬如驅(qū)動(dòng)開發(fā)工程師)來(lái)說(shuō),proc文件系統(tǒng)給了開發(fā)者一種調(diào)試內(nèi)核的方法:通過(guò)查看/proc/xxx文件來(lái)獲取到內(nèi)核特定數(shù)據(jù)結(jié)構(gòu)的值,在添加了新功能前后進(jìn)行對(duì)比,就可以判斷此功能所產(chǎn)生的影響是否合理。

/proc目錄下中包含了一些目錄和虛擬文件,如下所示:

可以看到/proc 目錄下有很多以數(shù)字命名的文件夾,譬如100038、2299、98560,這些數(shù)字對(duì)應(yīng)的其實(shí)就是一個(gè)一個(gè)的進(jìn)程PID 號(hào),每一個(gè)進(jìn)程在內(nèi)核中都會(huì)存在一個(gè)編號(hào)。

所以這些以數(shù)字命名的文件夾中記錄了這些進(jìn)程相關(guān)的信息,不同的信息通過(guò)不同的虛擬文件呈現(xiàn)出來(lái),關(guān)于這些信息將會(huì)在后面章節(jié)內(nèi)容中向大家介紹。

/proc 目錄下除了文件夾之外,還有很多的虛擬文件,譬如buddyinfo、cgroups、cmdline、version 等等,不同的文件記錄了不同信息,關(guān)于這些文件記錄的信息和意思如下:

? cmdline:內(nèi)核啟動(dòng)參數(shù);
? cpuinfo:CPU 相關(guān)信息;
? iomem:IO 設(shè)備的內(nèi)存使用情況;
? interrupts:顯示被占用的中斷號(hào)和占用者相關(guān)的信息;
? ioports:IO 端口的使用情況;
? kcore:系統(tǒng)物理內(nèi)存映像,不可讀取;
? loadavg:系統(tǒng)平均負(fù)載;
? meminfo:物理內(nèi)存和交換分區(qū)使用情況;
? modules:加載的模塊列表;
? mounts:掛載的文件系統(tǒng)列表;
? partitions:系統(tǒng)識(shí)別的分區(qū)表;
? swaps:交換分區(qū)的利用情況;
? version:內(nèi)核版本信息;
? uptime:系統(tǒng)運(yùn)行時(shí)間;

proc 文件系統(tǒng)的使用

proc 文件系統(tǒng)的使用就是去讀取/proc 目錄下的這些文件,獲取文件中記錄的信息,可以直接使用cat 命令讀取,也可以在應(yīng)用程序中調(diào)用open()打開、然后再使用read()函數(shù)讀取。

使用cat 命令讀取
在Linux 系統(tǒng)下直接使用cat 命令查看/proc 目錄下的虛擬文件,譬如"cat /proc/version"查看內(nèi)核版本相關(guān)信息:

使用read()函數(shù)讀取
編寫一個(gè)簡(jiǎn)單地程序,使用read()函數(shù)讀取/proc/version 文件。

#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h>int main(int argc, char *argv[]) {char buf[512] = {0};int fd;int ret;/* 打開文件*/fd = open("/proc/version", O_RDONLY);if (-1 == fd) {perror("open error");exit(-1);}/* 讀取文件*/ret = read(fd, buf, sizeof(buf));if (-1 == ret) {perror("read error");exit(-1);}/* 打印信息*/puts(buf);/* 關(guān)閉文件*/close(fd);exit(0); }

運(yùn)行結(jié)果:

總結(jié)

以上是生活随笔為你收集整理的Linux系统信息与系统资源的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。