7.3 进程时间
进程时间指的是进程从创建后(也就是程序运行后)到目前为止这段时间内使用 CPU 资源的时间总数,出于记录的目的,内核把 CPU 时间(进程时间)分为以下两个部分:
⚫ 用户 CPU 时间:进程在用户空间(用户态)下运行所花费的 CPU 时间。有时也成为虚拟时间(virtual time)。
⚫ 系统 CPU 时间:进程在内核空间(内核态)下运行所花费的 CPU 时间。这是内核执行系统调用或代表进程执行的其它任务(譬如,服务页错误)所花费的时间。
一般来说,进程时间指的是用户 CPU 时间和系统 CPU 时间的总和,也就是总的 CPU 时间。
进程时间不等于程序的整个生命周期所消耗的时间,如果进程一直处于休眠状态(进程被挂起、不会得到系统调度),那么它并不会使用 CPU 资源,所以休眠的这段时间并不计算在进程时间中。
7.3.1 times 函数
times()函数用于获取当前进程时间。
#include <sys/times.h>
clock_t times(struct tms *buf);
/*函数参数/:
buf:times()会将当前进程时间信息存在一个 struct tms 结构体数据中,
返回值:
返回值类型为 clock_t(实质是 long 类型)。
调用成功情况下,将返回从过去任意的一个时间点(譬如系统启动时间)所经过的时钟滴答数(其实就是系统节拍数),将(节拍数 / 节拍率)便可得到秒数,返回值可能会超过 clock_t 所能表示的范围(溢出);
调用失败返回-1,并设置 errno。*/
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 时间总和 */
};
总的进程时间 = 用户 CPU 时间 + 系统 CPU 时间
以下我们演示了通过 times()来计算程序中某一段代码执行所耗费的进程时间和总的时间,测试程序如下所示:
#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;
/* 获取系统的节拍率 */
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);
}
7.3.2 clock 函数
库函数 clock()提供了一个更为简单的方式用于进程时间,它的返回值描述了进程使用的总的 CPU 时间(也就是进程时间,包括用户 CPU 时间和系统 CPU 时间)。
#include <time.h>
clock_t clock(void);
/*函数参数:
无参数。
返回值:
返回值是到目前为止程序的进程时间,为 clock_t 类型,
注意 clock()的返回值并不是系统节拍数,如果想要获得秒数,请除以 CLOCKS_PER_SEC(这是一个宏)。
如果返回的进程时间不可用或其值无法表示,则该返回值是-1。*/
clock()函数虽然可以很方便的获取总的进程时间,但并不能获取到单独的用户 CPU 时间和系统 CPU 时间,在实际编程当中,根据自己的需要选择。
7.4 产生随机数
在应用编程当中可能会用到随机数,譬如老板让你编写一个抽奖的小程序,编号 0~100,分为特等奖 1个、一等奖 2 个、二等级 3 以及三等级 4 个,也就是说需要从 0~100 个编号中每次随机抽取一个号码,这就需要用到随机数。
随机数与伪随机数
随机数是随机出现,没有任何规律的一组数列。在我们编程当中,是没有办法获得真正意义上的随机数列的,这是一种理想的情况,在我们的程序当中想要使用随机数列,只能通过算法得到一个伪随机数序列,那在编程当中说到的随机数,基本都是指伪随机数。
rand 函数
rand()函数用于获取随机数,多次调用 rand()可得到一组随机数序列。
其函数原型如下:
#include <stdlib.h>
int rand(void);
/*返回值:
返回一个介于 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),因为每次启动应用程序时间上是一样的,所以就能够使得程序中设置的随机数种子在每次
启动程序时是不一样的。*/
常用的用法 srand(time(NULL));
每一次得到的[0~100]之间的随机数数组都是不同的(数组不同,不是产生的随机数不同),
因为程序中将 rand()的随机数种子设置为 srand(time(NULL)),直接等于 time_t 时间值,意味着每次启动种子都不一样,所以能够产生不同的随机数数组。
7.5 休眠
系统有时需要将进程暂停或休眠一段时间,进入休眠状态之后,程序将暂停运行,直到休眠结束。
7.5.1 秒级休眠: sleep
/* 让程序休眠 3 秒钟 */
sleep(3);
7.5.2 微秒级休眠: usleep
/* 让程序休眠 3 秒钟(3*1000*1000 微秒) */
usleep(3 * 1000 * 1000);
7.5.3 高精度休眠: nanosleep
nanosleep()与 sleep()以及 usleep()类似,都用于程序休眠,但 nanosleep()具有更高精度来设置休眠时间长度,支持纳秒级时长设置。与 sleep()、usleep()不同的是,nanosleep()是一个 Linux 系统调用。
#include <time.h>
int nanosleep(const struct timespec *req, struct timespec *rem);
/*函数参数与返回值含义如下:
req:一个 struct timespec 结构体指针,指向一个 struct timespec 变量,
用于设置休眠时间长度,可精确到纳秒级别。
rem:也是一个 struct timespec 结构体指针,指向一个 struct timespec 变量,
也可设置 NULL。
返回值:
在成功休眠达到请求的时间间隔后,nanosleep()返回 0;
如果中途被信号中断或遇到错误,则返回-1,
并将剩余时间记录在参数 rem 指向的 struct timespec 结构体变量中
(参数 rem 不为 NULL 的情况下,如果为 NULL 表示不接收剩余时间),
还会设置 errno 标识错误类型。*/