Uptime 源码学习
命令介绍
uptime会在一行中显示下列信息:
- 当前时间
- 系统运行了多久时间
- 当前登录的用户有多少
- 以及过去1分钟,5分钟,15分钟的系统平均负载
时间和用户数不必多讲,很容易理解,那么什么是系统的负载呢。
系统平均负载(Load Average)指的是在一段时间内CPU正在处理以及等待CPU处理的进程数之和的统计信息,也就是CPU使用队列的长度的统计信息,换句话讲,是处于可运行runnable(R状态)或不可中断uninterruptable状态(D状态)的进程的平均数。可运行状态的进程要么正在使用 CPU 要么在等待使用 CPU。 不可中断状态的进程则正在等待某些 I/O 访问,例如等待磁盘 IO。
负载体现的是系统的繁忙度或饱和度,1核的cup, 如果load为1(虽然CPU的核数和个数表现略有差异,但说到负载,还是以核心数量计算,以下都以1核举例), 可以简单理解为系统已饱和,没有剩余资源了,通常能够被接受的值是0.7, 0.7和1之间是一个良好的状态, 小于0.7表示cpu空闲较多,大于1表示系统饱和。
但是否大于1一定出问题, 也要根据实际情景分析,大于1是什么情况呢,咱们拿地铁举例:
高峰时间,站台等待进地铁的人会很多,并且每次有新的地铁班次进站,在站台等待的人都不能全部上车(load > 1),但是随着时间的流逝,高峰期逐步过去后,每次等待的人都可以全部上车(load < 1).
在等待的人群中, 有些人很可能由于其他原因,比如在等未进站的朋友,并不是马上要上地铁(I/O,网络等其他因素),但这些人也统计在了load中。所以load不一定完全反应的是系统状态,需要结合其他指标一起来分析。
等待的人很多,系统并不一定会出问题,只有当等待的人群数量超过了一定数量,比如持续增高超过站台的容量了,才可能会真正会产生实际问题;通常情况下,如果负载长时间保持都是0.7以上,管理员就应当保持警惕,长时间1以上,就应当马上进行分析(例如:很可能要扩容了)。
进程状态
为了能更容易读懂代码,先介绍一下Linux上进程的五种状态:
1.R——Runnable(运行):正在运行或在运行队列中等待
2.S——sleeping(中断):休眠中,受阻,在等待某个条件的形成或接收到信号
3.D——uninterruptible sleep(不可中断):收到信号不唤醒和不可运行,进程必须等待直到有中断发生
4.Z——zombie(僵死):进程已终止,但进程描述还在,直到父进程调用wait4()系统调用后释放
5.T——traced or stoppd(停止):进程收到SiGSTOP,SIGSTP,SIGTOU信号后停止运行
状态后缀表示:
<:优先级高的进程
N:优先级低的进程
L:有些页被锁进内存
s:进程的领导者(在它之下有子进程)
l:ismulti-threaded (using CLONE_THREAD, like NPTL pthreads do)
+:位于后台的进程组
源码
预备知识讲完,直接看源码。
查找centos对应的源码包
下载该源码包,打开uptime.c, 主要入口在这里
1)系统运行时间
主要涉及的源文件: uptime.c sysinfo.c。
通过sysinfo.c 中的uptime函数获取当前时间。
通过宏FILE_TO_BUF来读取系统 /proc/uptime 的时间, 通过一系列的转换, 得到所需要的时间。
此处可以看出,获取时间是通过/proc/uptime, “一切皆文件”嘛。
介绍一下/proc下面的魔法,我们首先呼叫一下超人(man proc),看下详细介绍
在喵一下该文件
2)登陆用户
主要涉及的源文件:uptime.c whattime.c sysinfo.c
使用 getutent() 获取登陆用户信息并计算数量。
etutent 在 glibc 包中。
默认文件定义在glibc头文件中sysdeps/generic/paths.h
/var/run/utmp是一个二进制文件,记录当前登录系统的用户信息。可用who命令显示当中的内容。
相关函数:getutent, getutid, getutline, setutent, endutent, pututline, utmpname
头文件:#include <utmp.h>
定义函数:struct utmp *getutent(void);
函数说明:getutent()用来从utmp 文件(/var/run/utmp)中读取一项登录数据, 该数据以utmp 结构返回. 第一次调用时会取得第一位用户数据, 之后每调用一次就会返回下一项\
数据, 直到已无任何数据时返回NULL。
Utmp结构体
重点:以下是对登陆类型的定义,uptime计算时,使用的是USER_PROCESS
通过使用utmpdump读取文件,来看一下该文件(/var/run/utmp)所有的记录,第一列便是上文所讲到的登陆类型。
utmpdump /var/run/utmp
知识点:
- /var/run/utmp(用于记录当前打开的会话)被who和w工具用来记录当前有谁登录以及他们正在做什么,而uptime用来记录系统启动时间。
- /var/log/wtmp (用于存储系统连接历史记录)被last工具用来记录最后登录的用户的列表。
- /var/log/btmp(记录失败的登录尝试)被lastb工具用来记录最后失败的登录尝试的列表。
每个日志行格式化成了多列,说明如下。
第一个字段显示了会话识别符, 第二个字段则是PID。 第三个字段可以是以下值:--(表示运行等级改变或系统重启),bw(启动守候进程),数字(表示TTY编号),或者字符和数字(表示伪终端)。 第四个字段可以为空或用户名、重启或运行级别。 第五个字段是主TTY或PTY(伪终端),如果此信息可获得的话。 第六个字段是远程主机名(如果是本地登录,该字段为空,运行级别信息除外,它会返回内核版本)。 第七个字段是远程系统的IP地址(如果是本地登录,该字段为0.0.0.0)。如果没有提供DNS解析,第六和第七字段会显示相同的信息(远程系统的IP地址)。 最后一个(第八)字段指明了该记录创建的日期和时间。
在whattime.c中, 计算登陆的user数量时, 只计算普通的用户进程。
3) 负载
与时间相同的获取方式,通过读取 /proc/loadavg 来获取load。
一样的流程,先呼叫超人(man proc):
再喵一下:
回顾
以上便是uptime所包含的知识点,主要的重点有3项,时间,登陆用户和系统负载。
同时,也介绍了获取系统信息的通用方法,读取/proc下的文件,通常系统监控程序便是通过该方法来获取基本的监控指标。