学习笔记05-学习《精通UNIX下C语言编程及项目实践》


  十 时钟与信号

  获取时钟

  UNIX 的时间系统存在一个基点 , 就是格林威治时间 1970 1 1 日凌晨 0 0 0 , 也是传说中 UNIX 的生日 .

  UNIX 中存在三种格式的时间 :

  (1) 系统时间 . UNIX 从出生到现在的秒数 , 表现为一个 time_t 类型的变量

  (2) 高分辨率时间 . 精确到微秒的时间 , 表现为一个 timeval 结构的变量

  (3) 日历时间 . ' ' 结构表示的时间 , 表现为 tm 结构 .

  系统时间 . 它是 UNIX 中最基本的时间形式 . 用于系统时间的函数如下 :

#include <time.h>

time_t time(time_t *tloc);

double difftime(time_t time2, time_t time1);

  函数 difftime 获取两次 time 调用返回的系统时间差 .

  秒数往往很难读懂 , UNIX 中更改系统时间为日历时间的函数如下 :

#include <time.h>

struct tm *localtime(const time_t *clock);

time_t mktime(struct tm*timeptr);

  函数 localtime 转换系统时间 , clock 为当地时间 , 并以 tm 结构返回 .

  函数 mktime 实现函数 localtime 的反功能 .

  下面给出一个打印本地时间的例子

[bill@billstone Unix_study]$ cat time1.c

#include <time.h>

#include <stdio.h>

 

int main()

{

        struct tm when;

        time_t now;

 

        time(&now);

        when = *localtime(&now);

        printf("now=[%d] [%04d %02d %02d %02d:%02d:%02d]/n", now, /

                when.tm_year+1900, when.tm_mon+1, when.tm_mday, /

                when.tm_hour, when.tm_min, when.tm_sec);

 

        return 0;

}

[bill@billstone Unix_study]$ make time1

cc     time1.c   -o time1

[bill@billstone Unix_study]$ ./time1

now=[1239927129] [2009 04 17 08:12:09]

[bill@billstone Unix_study]$

  信号的概念

  信号是传送给进程的事件通知 , 它可以完成进程间异步事件的通信 .

  导致信号产生的原因很多 , 但总体说来有三种可能 :

  (1) 程序错误 . 当硬件出现异常 , 除数为 0 或者软件非法访问等情况时发生 .

  (2) 外部事件 . 当定时器到达 , 用户按健中断或者进程调用 abort 等信号发送函数时方生 .

  (3) 显式请求 . 当进程调用 kill, raise 等信号发送函数或者用户执行 shell 命令 kill 传递信号时发生 .

  同样的 , 当进程收到信号时有三种处理方式 :

  (1) 系统默认 . 系统针对不同的信号有不同的默认处理方式 .

  (2) 忽略信号 . 信号收到后 , 立即丢弃 . 注意信号 SIGSTOP SIGKILL 不能忽略 .

  (3) 捕获信号 . 进程接收信号 , 并调用自定义的代码响应之 .

  信号操作

  函数 signal 设置对信号的操作动作 , 原型如下 :

#include <signal.h>

void (*signal (int sig, void (*f) (int()) (int);

  这是个复杂的函数原型 , 不果可以分开看 :

typedef void (*func)(int);

func signal(int sig, func f);

  其中 , func 参数有三种选择 :SIG_DFL( 恢复信号默认处理机制 ), SIG_IGN( 忽略信号处理 ) 和函数地址 ( 调用信号捕获函数执行处理 ).

  首先看一个忽略终止信号 SIGINT 的例子 .

[bill@billstone Unix_study]$ cat sig1.c

#include <signal.h>

#include <stdio.h>

 

int main()

{

        signal(SIGINT, SIG_IGN);

        sleep(10);                 // 睡眠 10

 

        return 0;

}

[bill@billstone Unix_study]$ make sig1

cc     sig1.c   -o sig1

[bill@billstone Unix_study]$ ./sig1

[bill@billstone Unix_study]$

  在程序运行的 10 秒内 , 即使你键入 Ctrl+C 中断命令 , 进程也不退出 .

  再看一个捕获自定义信号的例子 .

[bill@billstone Unix_study]$ cat sig2.c

#include <signal.h>

#include <stdio.h>

 

int usr1 = 0, usr2 = 0;

void func(int);

 

int main()

{

        signal(SIGUSR1, func);

        signal(SIGUSR2, func);

        for(;;)

                sleep(1);              //  死循环 , 方便运行观察

 

        return 0;

}

 

void func(int sig){

        if(sig == SIGUSR1)

                usr1++;

        if(sig == SIGUSR2)

                usr2++;

        fprintf(stderr, "SIGUSR1[%d], SIGUSR2[%d]/n", usr1, usr2);

        signal(SIGUSR1, func);

        signal(SIGUSR2, func);

}

  在后台运行 , 结果如下 :

[bill@billstone Unix_study]$ make sig2

cc     sig2.c   -o sig2

[bill@billstone Unix_study]$ ./sig2&       // 后台运行

[2] 13822

[bill@billstone Unix_study]$ kill -USR1 13822    // 发送信号 SIGUSR1

SIGUSR1[1], SIGUSR2[0]

[bill@billstone Unix_study]$ kill -USR2 13822     // 发送信号 SIGUSR2

SIGUSR1[1], SIGUSR2[1]

[bill@billstone Unix_study]$ kill -USR2 13822     // 发送信号 SIGUSR2

SIGUSR1[1], SIGUSR2[2]

[bill@billstone Unix_study]$ kill -9 13822        //  发送信号 SIGSTOP, 杀死进程

[bill@billstone Unix_study]$

[2]+  已杀死                   ./sig2

[bill@billstone Unix_study]$

  UNIX 应用程序可以向进程显式发送任意信号 , 原型如下 :

#include <sys/types.h>

#include <signal.h>

int kill(pid_t pid, int signo);

int raise(int signo);

  看一个发送和捕获 SIGTERM 终止信号的例子

[bill@billstone Unix_study]$ cat sig3.c

#include <signal.h>

#include <stdio.h>

#include <assert.h>

#include <unistd.h>

#include <sys/types.h>

 

void childfunc(int sig){

        fprintf(stderr, "Get Sig/n");

}

 

int main()

{

        pid_t pid;

        int status;

 

        assert((pid = fork()) >= 0);

        if(pid == 0){

                signal(SIGTERM, childfunc);

                sleep(30);

                exit(0);

        }

        fprintf(stderr, "Parent [%d] Fork child pid=[%d]/n", getpid(), pid);

         sleep(1);

        kill(pid, SIGTERM);

        wait(&status);

        fprintf(stderr, "Kill child pid=[%d], exit status[%d]/n", pid, status>>8);

 

        return 0;

}

[bill@billstone Unix_study]$ make sig3

cc     sig3.c   -o sig3

[bill@billstone Unix_study]$ ./sig3

Parent [13898] Fork child pid=[13899]

Get Sig

Kill child pid=[13899], exit status[0]

[bill@billstone Unix_study]$

  定时器

  UNIX 下定时器可以分为普通的定时器和精确的定时器 .

  普通定时器通过 alarm 函数实现 , 它的精度是秒 , 而且每调用一次 alarm 函数只能产生一次定时操作 , 如果需要反复定时 , 就要多次调用 alarm. 调用 fork , 子进程中的定时器将被取消 , 但调用 exec , 定时器仍然有效 .

  UNIX 中使用普通定时器需要三个步骤 :

  (1) 调用 signal 函数设置捕获定时信号

  (2) 调用函数 alarm 定时 .

  (3) 编写响应定时信号函数 .

  下面是一个定时器的例子 , 每隔 1 秒向进程发送定时信号 , 用户可键入 Ctrl+C Delete 结束程序 .

[bill@billstone Unix_study]$ cat time2.c

#include <stdio.h>

#include <unistd.h>

#include <signal.h>

 

int n = 0;

 

void timefunc(int sig){

        fprintf(stderr, "Alarm %d/n", n++);

        signal(SIGALRM, timefunc);

        alarm(1);

}

 

int main()

{

        int status;

 

        signal(SIGALRM, timefunc);

        alarm(1);

        while(1);

 

        return 0;

}

[bill@billstone Unix_study]$ make time2

cc     time2.c   -o time2

[bill@billstone Unix_study]$ ./time2

Alarm 0

Alarm 1

Alarm 2

                        // Ctrl+C 结束

[bill@billstone Unix_study]$

  函数 alarm 设置的定时器只能精确到秒 , 而下面函数理论上可以精确到毫秒 :

#include <sys/select.h>

#include <sys/time.h>

int getitimer(int which, struct itimerval *value);

int setitimer(int which, const struct itimerval value, struct itimerval *ovalue);

  函数 setitimer 可以提供三种定时器 , 它们相互独立 , 任意一个定时完成都将发送定时信号到进程 , 并且重新计时 . 参数 which 确定了定时器的类型 :

  (1) ITIMER_REAL. 定时真实时间 , alarm 类型相同 . 对应信号为 SIGALRM.

  (2) ITIMER_VIRT. 定时进程在用户态下的实际执行时间 . 对应信号为 SIGVTALRM.

  (3) ITIMER_PROF. 定时进程在用户态和核心态下的实际执行时间 . 对应信号为 SIGPROF.

  在一个 UNIX 进程中 , 不能同时使用 alarm ITIMER_REAL 类定时器 .

  结构 itimerval 描述了定时器的组成 :

struct itimerval{

      struct timeval it_interval;

      struct timeval it_value;

}

  结构成员 it_value 指定首次定时的时间 , 结构成员 it_interval 指定下次定时的时间 . 定时器工作时 , 先将 it_value 的时间值减到 0, 发送一个信号 , 再将 it_vale 赋值为 it_interval 的值 , 重新开始定时 , 如此反复 . 如果 it_value 值被设置为 0, 则定时器停止定时 .

  结构 timeval 秒数了一个精确到微秒的时间 :

struct timeval{

      long tv_sec;

      long tv_usec;

}

  下面设计了一个精确定时器的例子 , 进程每 1.5 秒发送定时信号 SIGPROF, 用户可键入 Ctrl+C Delete 结束程序 .

[bill@billstone Unix_study]$ cat time3.c

#include <sys/select.h>

#include <sys/time.h>

#include <stdio.h>

#include <unistd.h>

#include <signal.h>

 

int n = 0;

 

void timefunc(int sig){

        fprintf(stderr, "ITIMER_PROF[%d]/n", n++);

        signal(SIGPROF, timefunc);

}

 

int main()

{

        struct itimerval value;

 

        value.it_value.tv_sec = 1;

        value.it_value.tv_usec = 500000;

         value.it_interval.tv_sec = 1;

        value.it_interval.tv_usec = 500000;

        signal(SIGPROF, timefunc);

        setitimer(ITIMER_PROF, &value, NULL);

        while(1);

 

        return 0;

}

[bill@billstone Unix_study]$ make time3

cc     time3.c   -o time3

[bill@billstone Unix_study]$ ./time3

ITIMER_PROF[0]

ITIMER_PROF[1]

ITIMER_PROF[2]

 

[bill@billstone Unix_study]$

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值