Linux應用編程和網絡編程(4)------- 系統信息的獲取


一,關於時間的概念

1、GMT時間
(1)GMT是世界時也就是格林尼治時間,也就是格林尼治地區的當地時間。
(2)GMT時間的意義?用格林尼治的當地時間作爲全球國際時間,用以描述全球性的事件的時間,方便大家記憶。
(3)一般爲了方便,一個國家都統一使用一個當地時間。

2、UTC時間
(1)GMT時間是以前使用的,近些年來越來越多的使用UTC時間。
(2)整個地球分爲二十四時區,每個時區都有自己的本地時間。在國際無線電通信場合,爲了統一起見,使用一個統一的時間,稱爲通用協調時(UTC, Universal Time Coordinated)。北京時區是東八區領先UTC八個小時UTC + 時區差 = 本地時間
時區差東爲正,西爲負。在此,把東八區時區差記爲 +0800, UTC + (+0800) = 本地(北京)時間 。那麼,UTC = 本地時間(北京時間))- 0800 。例如0942 - 0800 = 0142 ,即UTC是當天凌晨一點四十二分二十二秒。如果結果是負數就意味着是UTC前一天,把這個負數加上2400就是UTC在前一天的時間。例如,本地 (北京)時間是 0432 (凌晨四點三十二分),那麼,UTC就是 0432 - 0800 = -0368,負號意味着是前一天, -0368 + 2400 = 2032,既前一天的晚上八點三十二分。

可以參考:跟林尼治時間UTC

3、計算機中與時間有關的
(1)點時間和段時間。段時間=點時間-點時間
(2)定時器和實時時鐘。定時器(timer)定的時間就是段時間,實時時鐘(RTC)就是和點時間有關的一個器件。


二,linux系統中的時間

1、jiffies的引入

(1)jiffies是linux內核中的一個全局變量,jiffies記錄了系統啓動以來,經過了多少tick(系統時間片)。一個tick代表多長時間,在內核的CONFIG_HZ中定義。比如CONFIG_HZ=1000,則一個tick對應1ms時間。所以內核基於jiffies的定時器精度也是1ms,實際上linux內核的調度系統工作時就是以這個節拍時間爲時間片的。
(2)jiffies變量開機時有一個基準值,然後內核每過一個節拍時間(tick)jiffies就會加1,然後到了系統的任意一個時間我們當前時間就被jiffies這個變量所標註。

2、linux系統如何記錄時間
(1)內核在開機啓動的時候會讀取RTC硬件獲取一個時間作爲初始基準時間,這個基準時間對應一個jiffies值(這個基準時間換算成jiffies值的方法是:用這個時間減去1970-01-01 00:00:00 +0000(UTC),然後把這個時間段除以tick換算成jiffies數值),這個jiffies值作爲我們開機時的基準jiffies值存在。然後系統運行時每個時鐘節拍的末尾都會給jiffies這個全局變量加1,因此操作系統就使用jiffies這個全局變量記錄了下來當前的時間。當我們需要當前時間點時,就用jiffies這個時間點去計算(計算方法就是先把這個jiffies值對應的時間段算出來,然後加上1970-01-01 00:00:00 +0000(UTC)即可得到這個時間點)。

(2)其實操作系統只在開機時讀一次RTC,整個系統運行過程中RTC是無作用的。RTC的真正作用其實是在OS的2次開機之間進行時間的保存。
(3)jiffies這個變量記錄的其實是段時間(其實就是當前時間和1970-01-01 00:00:00 +0000(UTC)這個時間的差值)。

3、linux中時間相關的系統調用
常用的時間相關的API和C庫函數有9個:time/ctime/localtime/gmtime/mktime/asctime/strftime/gettimeofday/settimeofday

  • time系統調用返回當前時間以秒爲單位的距離1970-01-01 00:00:00+0000(UTC)過去的秒數。這個time內部就是用jiffies換算得到的秒數。其他函數基本都是圍繞着time來工作的。
  • gmtime和localtime把time得到的秒數變成一個struct tm結構體表示的時間。區別是gmtime得到的是國際時間,而localtime得到的是本地(指的是你運行localtime函數的程序所在的計算機所設置的時區對應的本地時間)時間。mktime用來完成相反方向的轉(struct tm到time_t)。
  • 如果從struct tm出發想得到字符串格式的時間,可以用asctime或者strftime都可以。(如果從time_t出發想得到字符串格式的時間用ctime即可
  • gettimeofday返回的時間是由struct timeval和struct timezone這兩個結構體來共同表示的,其中timeval表示時間,而timezone表示時區。settimeofday是用來設置當前的時間和時區的

在這裏插入圖片描述

總結:不管用哪個系統調用,最終得到的時間本質上都是一個時間(這個時間最終都是從kernel中記錄的jiffies中計算得來的),只不過不同的函數返回的時間的格式不同,精度不同。


三,時間相關API實戰

在這裏插入圖片描述

1、time
(1)time能得到一個當前時間距離標準起點時間1970-01-01 00:00:00 +0000(UTC)過去了多少秒

2、ctime
(1)ctime可以從time_t出發得到一個容易觀察的字符串格式的當前時間。
(2)ctime好處是很簡單好用,可以直接得到當前時間的字符串格式,直接打印來看。壞處是ctime的打印時間格式是固定的,沒法按照我們的想法去變。
(3)實驗結果可以看出ctime函數得到的時間是考慮了計算機中的本地時間的(計算機中的時區設置)。

3、gmtime和localtime
(1)gmtime獲取的時間中:年份是以1970爲基準的差值,月份是0表示1月,小時數是以UTC時間的0時區爲標準的小時數(北京是東8區,因此北京時間比這個時間大8)
(2)猜測localtime和gmtime的唯一區別就是localtime以當前計算機中設置的時區爲小時的時間基準,其餘一樣。實踐證明我們的猜測是正確的。

1-3實驗代碼:

#include <stdio.h>
#include <time.h>
#include <string.h>



int main(void)
{
	time_t tNow = -1;
	struct tm tmNow;
	
	// time
	//tNow = time(NULL);		// 返回值
	time(&tNow);				// 指針做輸出型參數
	if (tNow < 0)
	{
		perror("time");
		return -1;
	}
	printf("time: %ld.\n", tNow);
	
	// ctime
	printf("ctime: %s\n", ctime(&tNow));
	
	// gmtime 和localtime
	memset(&tmNow, 0, sizeof(tmNow));
	gmtime_r(&tNow, &tmNow);
	printf("gmtime:%d年%d月%d日%d時.\n", tmNow.tm_year, tmNow.tm_mon, tmNow.tm_mday, tmNow.tm_hour);
	
	memset(&tmNow, 0, sizeof(tmNow));
	localtime_r(&tNow, &tmNow);
	printf("localtime:%d年%d月%d日%d時.\n", tmNow.tm_year, tmNow.tm_mon, tmNow.tm_mday, tmNow.tm_hour);
	
	
	return 0;
}

實驗結果:

在這裏插入圖片描述

4、mktime
(1)從OS中讀取時間時用不到mktime的,這個mktime是用來向操作系統設置時間時用的。

5、asctime
(1)asctime得到一個固定格式的字符串格式的當前時間,效果上和ctime一樣的。區別是ctime從time_t出發,而asctime從struct tm出發。

6、strftime
(1)asctime和ctime得到的時間字符串都是固定格式的,沒法用戶自定義格式
(2)如果需要用戶自定義時間的格式,則需要用strftime。

7、gettimeofday和settimeofday
(1)前面講到的基於time函數的那個系列都是以秒爲單位來獲取時間的,沒有比秒更精確的時間。
(2)有時候我們程序希望得到非常精確的時間(譬如以us爲單位),這時候就只能通過gettimeofday來實現了。

示例代碼:

#include <stdio.h>
#include <time.h>
#include <string.h>
#include <sys/time.h>


int main(void)
{
	time_t tNow = -1;
	struct tm tmNow;
	char buf[100];
	struct timeval tv = {0};
	struct timezone tz = {0};
	int ret = -1;
	
	// time
	//tNow = time(NULL);		// 返回值
	time(&tNow);				// 指針做輸出型參數
	if (tNow < 0)
	{
		perror("time");
		return -1;
	}
	printf("time: %ld.\n", tNow);
	
	// ctime
	printf("ctime: %s.\n", ctime(&tNow));
	// strftime
	memset(&tmNow, 0, sizeof(tmNow));
	localtime_r(&tNow, &tmNow);
	printf("年%d月%d日%d時%d.\n", tmNow.tm_year, tmNow.tm_mon, tmNow.tm_mday, tmNow.tm_hour);
	
	memset(buf, 0, sizeof(buf));
	strftime(buf, sizeof(buf), "%Y * %m * %d, %H-%M-%S.", &tmNow);
	printf("時間爲:[%s].\n", buf);


	// gettimeofday
	ret = gettimeofday(&tv, &tz);
	if (ret < 0)
	{
		perror("gettimeofday");
		return -1;
	}
	printf("seconde: %ld.\n", tv.tv_sec);
	printf("timezone:%d.\n", tz.tz_minuteswest);
	
	
	return 0;
}

四,linux中使用隨機數

1、隨機數和僞隨機數
(1)隨機數是隨機出現,沒有任何規律的一組數列。
(2)真正的完全隨機的數列是不存在的,只是一種理想情況。我們平時要用到隨機數時一般只能通過一些算法得到一個僞隨機數序列。
(3)我們平時說到隨機數,基本都指的是僞隨機數。

2、linux中隨機數相關API
(1)連續多次調用rand函數可以返回一個僞隨機數序列
(2)srand函數用來設置rand獲取的僞隨機序列的種子

在這裏插入圖片描述

3、實戰演示
(1)單純使用rand重複調用n次,就會得到一個0-RAND_MAX之間的僞隨機數,如果需要調整範圍,可以得到隨機數序列後再進行計算。
(2)單純使用rand來得到僞隨機數序列有缺陷,每次執行程序得到的僞隨機序列是同一個序列,沒法得到其他序列
(3)原因是因爲rand內部的算法其實是通過一個種子(seed,其實就是一個原始參數,int類型),rand內部默認是使用1作爲seed的,種子一定的算法也是一定的,那麼每次得到的僞隨機序列肯定是同一個。
(4)所以要想每次執行這個程序獲取的僞隨機序列不同,則每次都要給不同的種子。用srand函數來設置種子
示例代碼:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
	int i = 0, val = 0;
		
	printf("RAND_MAX = %d.\n", RAND_MAX);		// 2147483647
	
	srand(time(NULL));                       //用時間去作爲隨機數的種子
	for (i=0; i<6; i++)
	{
		val = rand();
		printf("%d ", (val % 6));
	}
	printf("\n");
	
	return 0;
}

實驗結果:
在這裏插入圖片描述

4、總結和說明
(1)在每次執行程序時,先用srand設置一個不同的種子,然後再多次調用rand獲取一個僞隨機序列,這樣就可以每次都得到一個不同的僞隨機序列。
(2)一般常規做法是用time函數的返回值來做srand的參數。

5、在linux系統中獲取真正的隨機數
(1)linux系統收集系統中的一些隨機發生的事件的時間(譬如有人動鼠標,譬如觸摸屏的操作和座標等)作爲隨機種子去生成隨機數序列。


五,proc文件系統介紹與使用

1.proc文件系統介紹

1、從簡單到複雜的調試
(1)簡單程序單步調試
(2)複雜程序printf打印信息調試
(3)框架體系日誌記錄信息調試
(4)操作系統內核用虛擬文件系統調試

2、proc虛擬文件系統的工作原理
(1)linux內核是一個非常龐大、非常複雜的一個單獨的程序,對於這樣的一個程序來說調試是非常複雜的。
(2)像kernel這樣龐大的項目,給裏面添加/更改一個功能是非常麻煩的,因爲你這添加的一個功能可能會影響其他已經有的。
(3)早期內核版本中儘管調試很麻煩,但是高手們還可以憑藉個人超凡脫俗的能力去駕馭。但是到了2.4左右的版本的時候,這個難度已經非常大了。
(4)爲了降低內核調試和學習的難度,內核開發者們在內核中添加了一些屬性專門用於調試內核,proc文件系統就是一個嘗試。

(5)proc文件系統的思路是:在內核中構建一個虛擬文件系統/proc,內核運行時將內核中一些關鍵的數據結構以文件的方式呈現在/proc目錄中的一些特定文件中,這樣相當於將不可見的內核中的數據結構以可視化的方式呈現給內核的開發者。

(6)proc文件系統給了開發者一種調試內核的方法:我們通過實時的觀察/proc/xxx文件,來觀看內核中特定數據結構的值。在我們添加一個新功能的前後來對比,就可以知道這個新功能產生的影響對還是不對。

(7)proc目錄下的文件大小都是0,因爲這些文件本身並不存在於硬盤中,也不是一個真實文件,他只是一個接口,當我們去讀取這個文件時,其實內核並不是去硬盤上找這個文件,而是映射爲內核內部一個數據結構被讀取並且格式化成字符串返回給我們。所以儘管我們看到的還是一個文件內容字符串,和普通文件一樣的;但是實際上我們知道這個內容是實時的從內核中數據結構來的,而不是硬盤中來的。
在這裏插入圖片描述

2.proc文件系統的使用

1、cat以手工查看常用proc中的文件
(1)/proc/cmdline
(2)/proc/cpuinfo
(3)/proc/devices
(4)/proc/interrupts等…
在這裏插入圖片描述

2、程序中可以文件IO訪問
示例代碼:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
	int fd = -1;
	char buf[512] = {0};
	
	if (argc != 2)
	{
		printf("usage: %s -v|-d\n", argv[0]);
		return -1;
	}
	
	if (!strcmp(argv[1], "-v"))
	{
		fd = open("/proc/version", O_RDONLY);
		if (fd < 0)
		{
			perror("open /proc/version");
			return -1;
		}
		read(fd, buf, sizeof(buf));
		printf("結果是:%s.\n", buf);
	}
	else if (!strcmp(argv[1], "-d"))
	{
		fd = open("/proc/devices", O_RDONLY);
		if (fd < 0)
		{
			perror("open /proc/devices");
			return -1;
		}
		read(fd, buf, sizeof(buf));
		printf("結果是:%s.\n", buf);
	}
	
	return 0;
}

實驗結果:

在這裏插入圖片描述

3、在shell程序中用cat命令結合正則表達式來獲取並處理內核信息

4、擴展:sys文件系統
(1)sys文件系統本質上和proc文件系統是一樣的,都是虛擬文件系統,都在根目錄下有個目錄(一個是/proc目錄,另一個是/sys目錄),因此都不是硬盤中的文件,都是內核中的數據結構的可視化接口。
(2)不同的是**/proc中的文件只能讀,但是/sys中的文件可以讀寫**。讀/sys中的文件就是獲取內核中數據結構的值,而寫入/sys中的文件就是設置內核中的數據結構的元素的值。
(3)歷史上剛開始先有/proc文件系統,人們希望通過這種技術來調試內核。實際做出來後確實很有用,所以很多內核開發者都去內核調價代碼向/proc目錄中寫文件,而且剛開始的時候內核管理者對proc目錄的使用也沒有什麼經驗也沒什麼統一規劃,後來的結果就是proc裏面的東西又多又雜亂。
(4)後來覺得proc中的內容太多太亂缺乏統一規劃,於是乎又添加了sys目錄。sys文件系統一開始就做了很好的規劃和約定,所以後來使用sys目錄時有了規矩。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章