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

 时钟与信号

  获取时钟

  UNIX的时间系统存在一个基点, 就是格林威治时间197011日凌晨000, 也是传说中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) 忽略信号. 信号收到后, 立即丢弃. 注意信号SIGSTOPSIGKILL不能忽略.

  (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+CDelete结束程序.

[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进程中, 不能同时使用alarmITIMER_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+CDelete结束程序.

[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]$

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值