linux 函数库之 times

本文介绍Linux下使用times函数获取进程时间的方法,包括用户CPU时间、系统CPU时间及子进程时间,并通过示例验证其准确性。

函数名: times
头文件: #include<sys/times>
函数声明: 
clock_t times(struct tms *buf);
man帮助查看: man 2 times
参数介绍:
1. clock_t 
typedef long int clock_t
2. tms
struct tms {
    clock_t tms_utime; /* user time */
    clock_t tms_stime; /* system time */
    clock_t tms_cutime; /* user time of children */
    clock_t tms_cstime; /* system time of children */
};
概念:
1.实际时间(real time):从命令行执行到运行终止的消逝时间

2.用户CPU时间(user CPU time):命令执行完成花费的系统CPU时间,即命令在用户态中执行时的总和

3.系统CPU时间(system CPU time):命令执行完成花费的系统CPU时间,即命令在核心态中执行时间的总和。

4.cutime是用户CPU时间+子进程用户CPU时间。cstime同理。

    用户CPU时间和系统CPU时间之和为CPU时间,即命令占用CPU执行的时间总和。实际时间要大于CPU时间,因为Linux是多任务操作系统,往往在执行一条命令时,系统还要处理其他任务。另一个需要注意的问题是即使每次执行相同的命令,所花费的时间也不一定相同,因为其花费的时间与系统运行相关。 
    由于tms结构没有提供实际时间,所以times函数返回“实际时间”。事实上,如果想获得某段程序运行的时间,可以由以下代码获得

struct tms tmp;
clock_t begin = times(&tmp);
/* your code */
clock_t end = times(&tmp);
printf("%lf\n", (double) (end-begin)/CLOCKS_PER_SEC );

这个是获得程序的实际运行时间(秒)

其中,CLOCKS_PER_SEC是个宏,定义在bits/time.h,可以这么理解

const long long CLOCKS_PER_SEC = 1000000L;

# define CLOCKS_PER_SEC 1000000l


times() 函数 |  获取进程时间函数

函数原型 :
引用
#include <sys/times.h> 
clock_t times (struct tms * buf );


函数功能 :
获取进程时间。

说明 :
times() 函数返回从过去一个任意的时间点所经过的时钟数。返回值可能会超出 clock_t  (一般为 long 型) 的范围(溢出)。如果发生错误,则返回 (clock_t ) -1 类型,然后设置相应的 errno 值。

系统每秒的时钟可以通过 sysconf(_SC_CLK_TCK); 函数获得。关于 sysconf() 函数的详细信息见:http://www.groad.net/bbs/read.php?tid-1485.html 

上面 _SC_CLK_TCK 的值为 2,因为它在 /usr/include/bits/confname.h 头文件的一个枚举类型里定义。

struct tms 结构体定义在 <sys/times.h> 头文件里,具体定义如下:
引用
/* Structure describing CPU time used by a process and its children.  */ 
struct tms 
  { 
    clock_t tms_utime ;          /* User CPU time.  用户程序 CPU 时间*/ 
    clock_t tms_stime ;          /* System CPU time. 系统调用所耗费的 CPU 时间 */ 

    clock_t tms_cutime ;         /* User CPU time of dead children. 已死掉子进程的 CPU 时间*/ 
    clock_t tms_cstime ;         /* System CPU time of dead children.  已死掉子进程所耗费的系统调用 CPU 时间*/ 
  };

实例验证 times() 函数
测试代码-1 :
引用
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <sys/times.h> 

int main ()
{ 
    struct tms time_buf_head , time_buf_end ;
    long   tck = 0 ;
    clock_t time_head , time_end ;
    
    tck = sysconf (_SC_CLK_TCK );    /*获取系统时钟(1秒里有多少个)*/ 

    time_head = times ( & time_buf_head );   /*进程运行到此时的系统时钟数(总的)*/ 
    printf ("head_time is : %f /n " , time_head / (double )tck );  /*此时进程所处的时间点(单位为秒)*/ 

    //system ("./time_test.exe"); 
    system ("sleep 2" );    /*睡眠2秒*/ 
    
    time_end = times ( & time_buf_end );   /*进程到此时的系统时钟数*/ 
    printf ("end_time is : %f /n " , time_end / (double )tck ); /*此时进程所处的时间点(单位为秒)*/ 

    printf ("user time is : %f /n " , ((time_buf_end . tms_utime - time_buf_head . tms_utime ) /double )tck ));   /*打印出用户进程到此所经历时间*/ 
    printf ("systime time is : %f /n " , ((time_buf_end . tms_stime - time_buf_head . tms_stime/ double )tck ));
    printf ("child user time is : %f /n " , ((time_buf_end . tms_cutime - time_buf_head .tms_cutime ) / (double )tck ));
    printf ("child sys time is : %f /n " , ((time_buf_end . tms_cstime - time_buf_head .tms_cstime ) / (double )tck ));
    return (0 );
} ( (

运行输出 :
引用
beyes@beyes-groad:~$ ./time.exe 
head_time is : 17236892.770000 
end_time is : 17236894.790000 
user time is : 0.000000
systime time is : 0.000000
child user time is : 0.000000
child sys time is : 0.000000

上面蓝色部分的时间间隔刚好是 2s 。从上面看到,下面的时间值都是 0。为了验证问题,现在修改一下源程序:
引用
#include <sys/types.h> 
#include <sys/stat.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <fcntl.h> 
#include <sys/times.h> 

int main ()
{ 
        struct tms time_buf_head , time_buf_end ;
        long   tck = 0 ;
        clock_t time_head , time_end ;
        int i ;
        int j ;

        tck = sysconf (_SC_CLK_TCK );

        time_head = times ( & time_buf_head );
        printf ("head_time is : %f /n " , time_head / (double )tck );

        /*延迟测试用*/ 
        for (i = 0 ; i < 20000 ; i ++ )
                for (j = 0 ; j < 20000 ; j ++ ) { 
                        ;
        } 


        time_end = times ( & time_buf_end );
        printf ("end_time is : %f /n " , time_end / (double )tck );

        printf ("user time is : %f /n " , ((time_buf_end . tms_utime - time_buf_head . tms_utime/ double )tck ));   /*用户进程所耗费的时间*/ 
        printf ("systime time is : %f /n " , ((time_buf_end . tms_stime - time_buf_head .tms_stime ) / (double )tck ));
        printf ("child user time is : %f /n " , ((time_buf_end . tms_cutime - time_buf_head .tms_cutime ) / (double )tck ));
        printf ("child sys time is : %f /n " , ((time_buf_end . tms_cstime - time_buf_head .tms_cstime ) / (double )tck ));
        return (0 );
} (

再次运行输出:
引用
beyes@beyes-groad:~$ ./time.exe 
head_time is : 17184643.070000
end_time is : 17184644.280000
user time is : 1.200000 
systime time is : 0.000000
child user time is : 0.000000
child sys time is : 0.000000

由于使用了大量的延迟,这时可以看到 user time 里耗费了 1.2s ,而 end_time 和 head_time 的时间间隔为 1.21s 约等于 1.2s 。

再来修改一下代码:
引用
#include <sys/types.h> 
#include <sys/stat.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <fcntl.h> 
#include <sys/times.h> 

int main ()
{ 
        struct tms time_buf_head , time_buf_end ;
        long   tck = 0 ;
        clock_t time_head , time_end ;
        int i ;
        int j ;

        tck = sysconf (_SC_CLK_TCK );

        time_head = times ( & time_buf_head );
        printf ("head_time is : %f /n " , time_head / (double )tck );

        for (i = 0 ; i < 1000 ; i ++ )
                for (j = 0 ; j < 1000 ; j ++ ) { 
                        open ("Cannon-1.txt" , O_RDONLY );
        } 


        time_end = times ( & time_buf_end );
        printf ("end_time is : %f /n " , time_end / (double )tck );

        printf ("user time is : %f /n " , ((time_buf_end . tms_utime - time_buf_head . tms_utime/ double )tck ));
        printf ("systime time is : %f /n " , ((time_buf_end . tms_stime - time_buf_head .tms_stime ) / (double )tck ));
        printf ("child user time is : %f /n " , ((time_buf_end . tms_cutime - time_buf_head .tms_cutime ) / (double )tck ));
        printf ("child sys time is : %f /n " , ((time_buf_end . tms_cstime - time_buf_head .tms_cstime ) / (double )tck ));
        return (0 );
} 
(

运行输出:
引用
beyes@beyes-groad:~$ ./time.exe 
head_time is : 17189923.210000
end_time is : 17189923.650000
user time is : 0.160000
systime time is : 0.280000
child user time is : 0.000000
child sys time is : 0.000000

在 上面的输出中可以看到,systime time 这时不再为 0,这是因为程序中使用了 open() 这个系统调用的结果,它在两个 for 循环里一共被调用了 1000*1000次,总耗时为0.28s ,而执行这两个 for 的时间为 0.16s ,两个时间加起来的时间间隔正好为 end_time - head_time = 0.44s 。

下面测试子进程的时间,也就是 child user time 和 child sys time 两个。这里,需要另外一个程序,它的主要作用就是和上面一样的调用 open() 打开一个在本目录下的一文本文件,代码如下:
引用
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 

int main ()
{ 
        int i ;
        int j ;

        for (i = 0 ; i < 1000 ; i ++ )
                for (j = 0 ; j < 1000 ; j ++ ) { 
                        open ("Cannon-1.txt" , O_RDONLY );
        } 
        return (0 );
}

上面的程序命名为 time_test.exe ,它会在主程序里被 system() 函数调用到,修改主程序如下:
引用
#include <sys/types.h> 
#include <sys/stat.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <fcntl.h> 
#include <sys/times.h> 

int main ()
{ 
        struct tms time_buf_head , time_buf_end ;
        long   tck = 0 ;
        clock_t time_head , time_end ;
        int i ;
        int j ;

        tck = sysconf (_SC_CLK_TCK );

        time_head = times ( & time_buf_head );
        printf ("head_time is : %f /n " , time_head / (double )tck );

        system ("./time_test.exe" );

        time_end = times ( & time_buf_end );
        printf ("end_time is : %f /n " , time_end / (double )tck );

        printf ("user time is : %f /n " , ((time_buf_end . tms_utime - time_buf_head . tms_utime/ double )tck ));
        printf ("systime time is : %f /n " , ((time_buf_end . tms_stime - time_buf_head .tms_stime ) / (double )tck ));
        printf ("child user time is : %f /n " , ((time_buf_end . tms_cutime - time_buf_head .tms_cutime ) / (double )tck ));
        printf ("child sys time is : %f /n " , ((time_buf_end . tms_cstime - time_buf_head .tms_cstime ) / (double )tck ));
        return (0 );
} (

运行输出:
引用
beyes@beyes-groad:~$ ./time.exe
head_time is : 17190766.590000
end_time is : 17190767.060000
user time is : 0.000000
systime time is : 0.000000
child user time is : 0.140000 
child sys time is : 0.300000

由上可见,child user time 和 child sys time 两者的时间也是为 0.44s,这和上面是一样的,这是因为程序的内容相同。



时钟滴答(clock tick) 请问时间的嘀嗒数是根据什么来设定的
有必要明确一些Linux内核时钟驱动中的基本概念。 
(1)时钟周期(clock cycle)的频率:8253/8254 PIT的本质就是对由晶体振荡器产生的时钟周期进行计数,晶体振荡器在1秒时间内产生的时钟脉冲个数就是时钟周期的频率。Linux用宏 CLOCK_TICK_RATE来表示8254 PIT的输入时钟脉冲的频率(在PC机中这个值通常是1193180HZ),该宏定义在include/asm-i386/timex.h头文件中: 
#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */ 
(2)时钟滴答(clock tick):我们知道,当PIT通道0的计数器减到0值时,它就在IRQ0上产生一次时钟中断,也即一次时钟滴答。PIT通道0的计数器的初始值决定了要过多少时钟周期才产生一次时钟中断,因此也就决定了一次时钟滴答的时间间隔长度。 
(3)时钟滴答的频率(HZ):也即1秒时间内PIT所产生的时钟滴答次数。类似地,这个值也是由PIT通道0的计数器初值决定的(反过来说,确定了时钟滴答的频率值后也就可以确定8254 PIT通道0的计数器初值)。Linux内核用宏HZ来表示时钟滴答的频率,而且在不同的平台上HZ有不同的定义值。对于ALPHA和IA62平台HZ的值是1024,对于SPARC、MIPS、ARM和i386等平台HZ的值都是100。该宏在i386平台上的定义如下(include/asm- i386/param.h): 
#ifndef HZ 
#define HZ 100 
#endif 
根据HZ的值,我们也可以知道一次时钟滴答的具体时间间隔应该是(1000ms/HZ)=10ms。

times函数使用不适用于线程上取执行时间间隔。一般的时间精度为10ms,比gettimeofday低的。

 

==times tms_utime HandControlProcess=== 0.000000
==times tms_stime HandControlProcess=== 0.000000
==times tms_cutime HandControlProcess=== 0.000000
==times tms_cstime HandControlProcess=== 0.000000
==times tms_cstime HandControlProcess times_2-times_1=== 2000.000000
HZ=100
20.000000

 

#include <stdio.h>

#include <linux/time.h>
#include<sys/times.h>
#include <unistd.h>
#define CLOCKS_PER_SEC 1l

//#define _SC_CLK_TCK 100l


int main()
{
  struct tms tmp;
        struct tms tmp1, tmp2;
     clock_t times_1, times_2;
  clock_t begin = times(&tmp);
  times_1 = times(&tmp1);
  /* your code */
  sleep(20);
  times_2 = times(&tmp2);
  printf("==times tms_utime HandControlProcess=== %lf\n", (double)(tmp2.tms_utime-tmp1.tms_utime)/1);
  printf("==times tms_stime HandControlProcess=== %lf\n", (double)(tmp2.tms_stime-tmp1.tms_stime)/1);
  printf("==times tms_cutime HandControlProcess=== %lf\n", (double)(tmp2.tms_cutime-tmp1.tms_cutime)/1);
  printf("==times tms_cstime HandControlProcess=== %lf\n", (double)(tmp2.tms_cstime-tmp1.tms_cstime)/1);
  printf("==times tms_cstime HandControlProcess times_2-times_1=== %lf\n", (double)(times_2-times_1)/1);
  clock_t end = times(&tmp);
  unsigned long HZ = sysconf( _SC_CLK_TCK );


  printf("HZ=%ld\n", HZ);
  printf("%lf\n", (double)(end-begin)/HZ );


}



<think>首先,用户的问题是关于在Linux环境下使用atoi函数的示例和说明。atoi函数是C标准库中的一个函数,用于将字符串转换为整数。它定义在stdlib.h头文件中。 从系统级指令中,我需要: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 用户可见层要求: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 参考的站内引用: - 引用[1]: 提到atoi函数没有错误检测,建议使用strtol函数。 - 引用[2]: 关于epoll函数,但似乎不直接相关,所以可能不需要引用。 用户的问题是:"我想在Linux环境下了解或使用atoi函数 请问Linux中使用atoi函数的示例和说明"。所以,我需要提供: 1. atoi函数的说明。 2. 使用示例。 3. 注意事项,如错误处理。 4. 生成相关问题。 结构我的回答: - 先介绍atoi函数。 - 给出一个简单的C代码示例。 - 解释其工作原理和限制。 - 讨论错误处理,并提到strtol作为更好的选择。 - 最后,生成相关问题。 在回答中,由于用户提到Linux环境,但atoi是标准C函数,所以在Linux中使用它与其他系统类似。 确保使用中文回答。 生成相关问题:基于atoi函数,生成2-3个相关问题。 添加引用:在相关部分自然地添加引用标识。例如,当提到错误处理时,引用[^1]来自引用[1]。 现在,构建回答大纲: 1. **介绍atoi函数**:解释atoi是什么,用于什么。 2. **示例代码**:提供一个简单的C程序示例,展示如何使用atoi。 3. **注意事项**:讨论其局限性,如没有错误检测。 4. **建议**:推荐strtol作为更安全的替代。 5. **相关问题**:生成一些相关问题。 在代码块中,使用Markdown格式,如示例中的Python代码。 最终,生成相关问题部分。</think>### atoi函数在Linux环境下的使用说明和示例 `atoi`函数是C标准库中的一个常用函数,用于将字符串转换为整数。它定义在`stdlib.h`头文件中,在Linux环境下与其他Unix-like系统兼容。该函数会扫描字符串,跳过前导空白字符(如空格或制表符),然后解析连续的十进制数字序列,并将其转换为整数值。如果字符串无法有效解析(例如包含非数字字符),函数会返回0或未定义的值,因为它**不提供错误检测功能**[^1]。这使得`atoi`在简单场景下高效,但在需要严格输入验证时存在风险。下面我将逐步解释其用法、提供示例,并讨论注意事项。 #### 1. **atoi函数的基本语法** - 函数原型:`int atoi(const char *str);` - 参数`str`:指向要转换的字符串的指针。 - 返回值:转换后的整数值。如果转换失败(如字符串不以数字开头),返回0。 - 数学原理:该函数本质上是解析字符串中的数字序列,将其转换为整数形式。例如,字符串"123"被转换为整数$123$,其中转换过程可表示为: $$ \text{result} = \sum_{i=0}^{n-1} d_i \times 10^{n-i-1} $$ 这里,$d_i$是字符串中第$i$个字符的数字值,$n$是数字序列的长度。 #### 2. **Linux环境下使用atoi的示例** 以下是一个简单的C程序示例,演示在Linux中如何编译和运行`atoi`函数。代码包括输入处理和错误提示,以突出其局限性。 ```c #include <stdio.h> #include <stdlib.h> // 包含atoi函数声明 int main() { char input[100]; printf("请输入一个整数字符串: "); fgets(input, sizeof(input), stdin); // 从标准输入读取字符串 // 使用atoi转换字符串 int num = atoi(input); printf("转换后的整数: %d\n", num); // 简单错误检查:如果输入无效,输出警告 if (num == 0 && input[0] != '0') { printf("警告: 输入可能无效,atoi无法检测错误!\n"); } return 0; } ``` - **编译和运行步骤**(在Linux终端中): 1. 将代码保存为`atoi_example.c`。 2. 编译:`gcc atoi_example.c -o atoi_example` 3. 运行:`./atoi_example` - **示例输出**: - 输入`"123"`:输出`转换后的整数: 123` - 输入`"abc"`:输出`转换后的整数: 0` 和警告信息。 - 输入`"12a3"`:输出`转换后的整数: 12`(只解析到第一个非数字字符)。 这个示例展示了`atoi`的简单性,但也暴露了问题:它无法区分"0"和无效输入(如"abc"),因为两者都返回0[^1]。 #### 3. **注意事项和局限性** - **无错误检测**:`atoi`不会设置错误标志或返回错误码。如果字符串包含非数字字符(如"123abc"),函数只解析有效部分并忽略后续内容,这可能导致逻辑错误。 - **溢出风险**:如果字符串表示的整数超出`int`类型的范围(例如超过$2^{31}-1$),函数行为未定义,可能返回溢出值。 - **推荐替代方案**:在需要健壮错误处理的场景,建议使用`strtol`函数(也定义在`stdlib.h`中)。`strtol`支持指定进制、错误检测和溢出检查。例如: ```c char *endptr; long num = strtol(input, &endptr, 10); if (input == endptr) { printf("错误: 无效输入\n"); // 无数字被解析 } else if (*endptr != '\0') { printf("警告: 部分无效输入\n"); // 有额外字符 } ``` 这比`atoi`更安全,尤其适合处理用户输入或文件数据[^1]。 #### 4. **适用场景** - **优点**:简单快速,适合已知输入格式可靠的场景,如解析配置文件中的固定数字。 - **缺点**:不适合安全关键应用。在Linux编程中,优先考虑`strtol`以避免未定义行为。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值