三.linux应用&网络编程之获取系统信息
有道云笔记:https://note.youdao.com/ynoteshare1/index.html?id=a71be28974000520f480b5369a5236c3&type=note#/
目录
ctime/asctime/gmtime/localtime/mktime 函数
一、时间的概念
1.1、 GMT与UTC时间的区别
GMT时间是格林尼治时间,世界统一,但是现在基本不用
UTC时间,原子钟时间,非常精确,现在一般用这个
1.2、计算机中与时间有关的部件
(1)点时间和段时间。段时间=点时间-点时间
(2)定时器和实时时钟。定时器(timer)定的时间就是段时间,
实时时钟(RTC)就是和点时间有关的一个器件。
二、linux系统中的时间
2.1、话说jiffies
- 全局变量jiffies用来记录自系统启动以来产生的节拍的总数。
- 启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值。
- 一秒内时钟中断的次数等于Hz,所以jiffies一秒内增加的值也就是Hz。系统运行时间以秒为单位,等于jiffies/Hz。
- jiffies过1s则+1。记录的是1970年1月1日00:00距离现在的秒数,是一个段时间。(不是点时间)
2.2、linux系统如何记录时间
- 操作系统只在开机时读一次RTC,整个系统运行过程中RTC是无作用的。RTC的真正作用其实是在OS的2次开机之间进行时间的保存。
- 内核在开机启动的时候会读取RTC硬件获取一个时间作为初始基准时间,这个基准时间对应一个jiffies值。
- (这个基准时间换算成jiffies值的方法是:用这个时间减去1970-01-01 00:00:00 +0000(UTC),然后把这个时间段换算成jiffies数值),这个jiffies值作为我们开机时的基准jiffies值存在。
- 然后系统运行时每个时钟节拍的末尾都会给jiffies这个全局变量加1,因此操作系统就使用jiffies这个全局变量记录了下来当前的时间。
- 当我们需要当前时间点时,就用jiffies这个时间点去计算(计算方法就是先把这个jiffies值对应的时间段算出来,然后加上1970-01-01 00:00:00 +0000(UTC)即可得到这个时间点)
2.3、linux中时间相关的系统调用
-
常用的时间函数
time_t time(time_t *t); man 2 |
#include <time.h> man 3
char *asctime(const struct tm *tm); char *asctime_r(const struct tm *tm, char *buf);
char *ctime(const time_t *timep); char *ctime_r(const time_t *timep, char *buf);
struct tm *gmtime(const time_t *timep); struct tm *gmtime_r(const time_t *timep, struct tm *result);
struct tm *localtime(const time_t *timep); struct tm *localtime_r(const time_t *timep, struct tm *result);
time_t mktime(struct tm *tm); |
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm); |
#include <sys/time.h> man 3
int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv, const struct timezone *tz); |
- 常用时间格式之间的转换
2.4常用时间API函数功能测试
(相关API,自己man,有的传参是地址,有的是buf,有的是time_t,具体看api定义 )
-
time函数
返回值:为1970-01-01 00:00:00 +0000 (UTC). 到今天的秒数
int main(void)
{
time_t tNOW=-1;
tNOW= time(NULL);//返回值的方式
//time(&tNOW);//指针做输出型参数的方式
if(tNOW<0)
{
perror("time");
return -1;
}
printf("time:%ld\n\n",tNOW);
return 0;
}
-
ctime/asctime/gmtime/localtime/mktime 函数
E:\Linux\3.AppNet\3.osinfo\3.1
int main(void)
{
time_t tNOW=-1;
struct tm tmNow;
tNOW= time(NULL);//返回值的方式
//time(&tNOW);//指针做输出型参数的方式
if(tNOW<0)
{
perror("time");
return -1;
}
/1./time函数测试
printf("1.time函数测试\n");
printf("time:%ld\n\n",tNOW);
//2.ctime函数测试:ctime可以从time_t出发得到一个字符串格式的当前时间。
printf("2.ctime函数测试\n");
printf("ctime:%s\n",ctime(&tNOW));
/*3.gtime函数测试:获取的时间中:年份是以1970为基准的差值,月份是0表示1月,小时数是以UTC时间的0时区为标准的小时数(北京是东8区,因此北京时间比这个时间大8)*/
memset(&tmNow,0,sizeof(tmNow));//将tmNow清零
gmtime_r(&tNOW,&tmNow);
printf("3.gtime函数测试\n");
printf("年%d月%d日%d时%d\n\n",tmNow.tm_year,tmNow.tm_mon,tmNow.tm_mday,tmNow.tm_hour);
/*4..localtime函数测试:localtime和gmtime的唯一区别就是localtime以当前计算机中设置的时区为小时的时间基准 */
memset(&tmNow,0,sizeof(tmNow));//将tmNow清零
localtime_r(&tNOW,&tmNow);
printf("4.localtime函数测试\n");
printf("年%d月%d日%d时%d\n\n",tmNow.tm_year,tmNow.tm_mon,tmNow.tm_mday,tmNow.tm_hour);
//5.mktime asctime函数测试: mktime功能和time函数一样,asctime函数和ctime函数功能一样 参考上面的时间格式之间的转换图
memset(&tmNow,0,sizeof(tmNow));//将tmNow清零
localtime_r(&tNOW,&tmNow);
printf("5.mktime函数 asctime函数测试\n");
printf("mktime:%ld\n",mktime(&tmNow));
printf("asctime:%s\n",asctime(&tmNow));
return 0;
}
-
strftime函数 ()
E:\Linux\3.AppNet\3.osinfo\3.2
int main(void)
{
time_t tNOW=-1;
struct tm tmNow;
char buf[100];
struct timeval tv;
struct timezone tz;
int ret=-1;
tNOW= time(NULL);//返回值的方式
//time(&tNOW);//指针做输出型参数的方式
if(tNOW<0)
{
perror("time");
return -1;
}
//strftime函数测试
memset(&tmNow,0,sizeof(tmNow));//将tmNow清零
localtime_r(&tNOW,&tmNow);
printf("strftime函数测试\n");
memset(buf,0,sizeof(buf));//将buf清零
strftime(buf,sizeof(buf),"%Y--%m--%d, %H--%M--%S",&tmNow);
printf("标准时间自定义格式输出为:[%s]\n",buf);
}
//它和printf()一样类似的,可以格式化输出,来达到与众不同的格式输出
-
gettimeofday和settimeofday函数
E:\Linux\3.AppNet\3.osinfo\3.2
int main(void)
{
time_t tNOW=-1;
struct tm tmNow;
char buf[100];
struct timeval tv;
struct timezone tz;
int ret=-1;
tNOW= time(NULL);//返回值的方式
//time(&tNOW);//指针做输出型参数的方式
if(tNOW<0)
{
perror("time");
return -1;
}
//gettimeofday和settimeofday 函数测试
memset(&tv,0,sizeof(tv));//将tv清零
memset(&tz,0,sizeof(tz));//将tz清零
ret=gettimeofday(&tv,&tz);
if(ret<0)
{
perror("gettimeofday");
return -1;
}
printf("gettimeofday函数测试\n");
printf("seconde:%ld\n",tv.tv.sec);
printf("seconde:%ld\n",tv.tv_usec);
printf("timezone:%d\n",tz.tz_minuteswest);
printf("timezone:%d\n",tz.tz_dsttime);
return 0;
}
gettimeofday返回的时间是由struct timeval和struct timezone这两个结构体来共同表示的,
其中timeval表示时间,而timezone表示时区。
settimeofday是用来设置当前的时间和时区的
2.4总时间API程序
E:\Linux\3.AppNet\3.osinfo\3.3
int main(void)
{
time_t tNOW=-1;
struct tm tmNow;
char buf[100];
struct timeval tv;
struct timezone tz;
int ret=-1;
tNOW= time(NULL);//返回值的方式
//time(&tNOW);//指针做输出型参数的方式
if(tNOW<0)
{
perror("time");
return -1;
}
//1.time函数测试
printf("1.time函数测试\n");
printf("time:%ld\n\n",tNOW);
//2.ctime函数测试
printf("2.ctime函数测试\n");
printf("ctime:%s\n",ctime(&tNOW));
//3.ctime函数测试
memset(&tmNow,0,sizeof(tmNow));//将tmNow清零
gmtime_r(&tNOW,&tmNow);
printf("3.gtime函数测试\n");
printf("年%d月%d日%d时%d\n\n",tmNow.tm_year,tmNow.tm_mon,tmNow.tm_mday,tmNow.tm_hour);
//4.localtime函数测试
memset(&tmNow,0,sizeof(tmNow));//将tmNow清零
localtime_r(&tNOW,&tmNow);
printf("4.localtime函数测试\n");
printf("年%d月%d日%d时%d\n\n",tmNow.tm_year,tmNow.tm_mon,tmNow.tm_mday,tmNow.tm_hour);
//5.mktime函数测试
memset(&tmNow,0,sizeof(tmNow));//将tmNow清零
localtime_r(&tNOW,&tmNow);
printf("5.mktime函数 asctime函数测试\n");
printf("mktime:%ld\n",mktime(&tmNow));
printf("asctime:%s\n",asctime(&tmNow));
//6.strftime函数测试
memset(&tmNow,0,sizeof(tmNow));//将tmNow清零
localtime_r(&tNOW,&tmNow);
printf("6.strftime函数测试\n");
memset(buf,0,sizeof(buf));//将buf清零
strftime(buf,sizeof(buf),"%Y--%m--%d, %H--%M--%S",&tmNow);
printf("标准时间自定义格式输出为:[%s]\n\n",buf);
//7.gettimeofday函数测试
memset(&tv,0,sizeof(tv));//将tv清零
memset(&tz,0,sizeof(tz));//将tz清零
ret=gettimeofday(&tv,&tz);
if(ret<0)
{
perror("gettimeofday");
return -1;
}
printf("7.gettimeofday函数测试\n");
printf("seconde:%ld\n",tv.tv_sec);
printf("seconde:%ld\n",tv.tv_usec);
printf("timezone:%d\n",tz.tz_minuteswest);
printf("timezone:%d\n",tz.tz_dsttime);
return 0;
}
三、linux中的随机数
E:\Linux\3.AppNet\3.osinfo\3.4
-
获取伪随机数的函数:
#include <stdlib.h>
int rand(void); int rand_r(unsigned int *seedp); void srand(unsigned int seed); |
Linux系统一般通过使用系统时间作为种子获取伪随机数,只要每次使用相同的seed值,就能得到相同的伪随机数列。
我们一般用的是随机数是伪随机数,是由一定的算法得到的。它实际是有迹可循的,真正理想的完全随机数是不存在的。
-
linux中随机数相关API
(1)连续多次调用rand函数可以返回一个伪随机数序列
rand(void); //多次调用返回野怪1-27亿之间的随机数序列。若想设定1-100,那么输出取余100即可。
rand的一个缺点是:我们多次重复使用rand得到的数是相同的,是因为种子一直不变。
int main(int argc,char **argv)
{
int i=0,val=0;
if(argc != 2)
{
printf("usage:%s num\n",argv[0]);
return -1;
} printf("%d\n",RAND_MAX);
srand(atoi(argv[1]));//atoi函数将字符串转化为常数
for(i=0;i<6;i++)
{
val=rand();
printf("%d\n",(val%6));
}
printf("\n");
return 0;
}
(2)srand函数用来设置rand获取的伪随机序列的种子
srand(); //设置随机数的种子。
想要rand值发生变化,有效的方法就是换种子,用srand函数来设置种子。
int main(int argc,char **argv)
{
int i=0,val=0;
printf("%d\n",RAND_MAX);
srand(time(NULL));
for(i=0;i<6;i++)
{
val=rand();
printf("%d\n",(val%6));
}
printf("\n");
return 0;
}
种子可以设置为time_t time()的返回值,这样就按时间计算随机数。这几乎是随机度非常高的了
唯一的缺陷是1s才能变化一次。1s内调用了两次,printf输出的还是相同结果
-
真正的随机数
Linux中其实是有获取真实随机数的方法的,这些方法与“中断”有关。我们知道中断是随机的,是无法预测的,Linux可以根据输入设备的操作,如点鼠标键盘触摸屏事件的触发,设置不同的种子和随机数。
四、proc文件系统
1、操作系统级别的调试:
这里说的不是基于OS调试,而是调试操作系统本身。
-
单步调试
next走一步,非常直观而简单
但需要一个环境,必须有gdd、eclipse、jlink等调试环境。
不适合熟练的技术工人
-
复杂程序printf打印信息调试
打印输出信息,不使用断点,观察一些中间值
-
框架体系日志记录调试
一个产品两天之内没问题,第三天开始吃内存,七八天就停了
将日志信息用重定向,打印到log文件中
-
操作系统内核调试
我们使用的内核都是别人写好的
但是调试linux内核的那些人是面临非常大困境的。经常会加一个功能导致影响其他已有的。
内核开发者自己也驾驭不住庞大的内核了,新手也难以融入。linux内核面临断代的考验。为了降低内核调试和学习的难度,内核开发者们在内核中添加了一些属性专门用于调试内核,proc文件系统就是一个尝试。
2.proc应运而生
-
proc文件系统的思路是:
在内核中构建一个虚拟文件系统/proc,内核运行时将内核中一些关键的数据结构以文件的方式呈现在/proc目录中的一些特定文件中,这样相当于将不可见的内核中的数据结构以可视化的方式呈现给内核的开发者。
-
proc文件系统给了开发者一种调试内核的方法:
我们通过实时的观察/proc/xxx文件,来观看内核中特定数据结构的值。在我们添加一个新功能的前后来对比,就可以知道这个新功能产生的影响对还是不对。
-
proc文件系统是一个接口
在/proc目录下,有一些虚拟文件是ls无法看到,却可以cat得到的
比如你在那个目录下是找不到cmdline这个文件的,实际上它是一个虚拟文件。
“没有大小,没有block,但是它有内容,也可以编辑”
他并非一个真实存在于硬盘的文件,他只是一个接口。当我们读取这个文件时,内核不去硬盘上找而是直接去映射内核的数据结构,转化为一个字符串呈现给我们。所以我们看起来和普通文件是一样的。
proc文件系统不是来自硬盘,而是映射于kernel!!
-
常用proc中的文件介绍
proc文件系统可以被用于收集有用的关于系统和运行中的内核的信息。常见的重要的文件:
/proc/cpuinfo - CPU 的信息 (型号, 家族, 缓存大小等) |
/proc/meminfo - 物理内存、交换空间等的信息 |
/proc/mounts - 已加载的文件系统的列表 |
/proc/devices - 可用设备的列表 |
/proc/filesystems - 被支持的文件系统 |
/proc/modules - 已加载的模块 |
/proc/version - 内核版本 |
/proc/cmdline - 系统启动时输入的内核命令行参数 |
/proc/interrupts- 每个IRQ相关的中断号列表 |
/proc/apm- 高级电源管理(APM)版本信息及电池相关状态信息,通常由apm命令使用 |
/proc/crypto - 系统上已安装的内核使用的密码算法及每个算法的详细信息列表 |
/proc/dma -每个正在使用且注册的ISA DMA通道的信息列表 |
/proc/ioports - 当前正在使用且已经注册过的与物理设备进行通讯的输入-输出端口范围信息列表 |
/proc/kallsyms 模块管理工具用来动态链接或绑定可装载模块的符号定义,由内核输出 |
/proc/kmsg 用来保存由内核输出的信息,通常由/sbin/klogd或/bin/dmsg等程序使用 |
/proc/mdstat 保存RAID相关的多块磁盘的当前状态信息 |
-
proc文件系统的使用方法
(1)、cat以手工查看
at /proc/cmdline
cat /proc/xxxx 比如:cat /proc/cmdline |
(2)、程序中可以文件IO访问 E:\Linux\3.AppNet\3.osinfo\3.5
int main(int argc,char **argv)
{
int i=0,val=0;
printf("%d\n",RAND_MAX);
srand(time(NULL));
for(i=0;i<6;i++)
{
val=rand();
printf("%d\n",(val%6));
}
printf("\n");
return 0;
}
(3)、在shell程序中用cat命令结合正则表达式来获取并处理内核信息
-
扩展:sys文件系统
(1)sys文件系统本质上和proc文件系统是一样的,都是虚拟文件系统,都在根目录下有个目录(一个是/proc目录,另一个是/sys目录),因此都不是硬盘中的文件,都是内核中的数据结构的可视化接口。
(2)不同的是/proc中的文件只能读,但是/sys中的文件可以读写。读/sys中的文件就是获取内核中数据结构的值,而写入/sys中的文件就是设置内核中的数据结构的元素的值。