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

本文介绍了UNIX下三种常见的I/O超时处理方法:通过ioctl设置终端属性、使用setjmp和alarm结合信号处理,以及利用select进行多路复用监控。

全局跳转

  UNIX下的C语言中,有一对特殊的调用:跳转函数原型如下:

#include <setjmp.h>

int setjmp(jmp_buf env);

void longjump(jmp_buf env, int val);

  函数setjmp存储当前的堆栈环境(包括程序的当前执行位置)到参数env,当函数正常调用成功时返回0. 函数longjmp恢复保存在env中堆栈信息并使程序转移到env中保存的位置处重新执行这两个函数联合使用可以实现程序的重复执行.

  函数longjmp调用成功后程序转移到函数setjmp处执行函数setjmp返回val. 如果参数val的取值为0, 为了与上次正常调用setjmp相区别,函数setjmp将自动返回1.

  下面是一个使用了跳转语句的例子它跳转两次后退出.

[bill@billstone Unix_study]$ cat jmp1.c

#include <setjmp.h>

 

int j = 0;

jmp_buf env;

 

int main()

{

        auto int i, k = 0;

 

        i = setjmp(env);

        printf("setjmp = [%d], j = [%d], k = [%d]/n", i, j++, k++);

        if(j > 2)

                exit(0);

        sleep(1);

        longjmp(env, 1);

 

        return 0;

}

[bill@billstone Unix_study]$ make jmp1

cc     jmp1.c   -o jmp1

[bill@billstone Unix_study]$ ./jmp1

setjmp = [0], j = [0], k = [0]

setjmp = [1], j = [1], k = [1]

setjmp = [1], j = [2], k = [2]

[bill@billstone Unix_study]$

  其中, j记录了程序的执行次数按理说, k的值应该保持不变因为当返回到setjmp重新执行时保存的堆栈中k应该保持0不变但实际上却变化了请高手指点是不是setjmp本身实现的问题(我用的环境是Red Hat 9)?

  单线程I/O超时处理

  UNIX下的I/O超时处理是一个很常见的问题它的通常做法是接收输入(或发送输出)后立刻返回如果无输入(或输出)n秒后定时返回.

  一般情况下处理UNIXI/O超时的方式有终端方式信号跳转方式和多路复用方式等三种本节设计一个定时I/O的例子它从文件描述符0中读取一个字符当有输入时继续或者3秒钟后超时退出,并打印超时信息.

  (1) 终端I/O超时方式

  利用ioctl函数设置文件描述符对应的标准输入文件属性为接收输入后立刻返回如无输入则3秒后定时返回.

[bill@billstone Unix_study]$ cat timeout1.c

#include <unistd.h>

#include <termio.h>

#include <fcntl.h>

 

int main()

{

        struct termio old, new;

        char c = 0;

 

        ioctl(0, TCGETA, &old);

        new = old;

        new.c_lflag &= ~ICANON;

        new.c_cc[VMIN] = 0;

        new.c_cc[VTIME] = 30;         // 设置文件的超时时间为3

        ioctl(0, TCSETA, &new);

        if((read(0, &c, 1)) != 1)

                printf("timeout/n");

        else

                printf("/n%d/n", c);

        ioctl(0, TCSETA, &old);

 

        return 0;

}

[bill@billstone Unix_study]$ make timeout1

cc     timeout1.c   -o timeout1

[bill@billstone Unix_study]$ ./timeout1

x

120

[bill@billstone Unix_study]$ ./timeout1

timeout

[bill@billstone Unix_study]$

  (2) 信号与跳转I/O超时方式

  read函数前调用setjmp保存堆栈数据并使用alarm设定3秒定时.

[bill@billstone Unix_study]$ cat timeout2.c

#include <setjmp.h>

#include <stdio.h>

#include <unistd.h>

#include <signal.h>

 

int timeout = 0;

jmp_buf env;

 

void timefunc(int sig){

        timeout = 1;

        longjmp(env, 1);

}

 

int main()

{

        char c;

 

        signal(SIGALRM, timefunc);

        setjmp(env);

        if(timeout == 0){

                alarm(3);

                read(0, &c, 1);

                alarm(0);

                printf("%d/n", c);

        }

        else

                printf("timeout/n");

 

        return 0;

}

[bill@billstone Unix_study]$ make timeout2

cc     timeout2.c   -o timeout2

[bill@billstone Unix_study]$ ./timeout2

v                   // 需要按Enter健激活输入

118

[bill@billstone Unix_study]$ ./timeout2

timeout

[bill@billstone Unix_study]$

  (3) 多路复用I/O超时方式

  一个进程可能同时打开多个文件, UNIX中函数select可以同时监控多个文件描述符的输入输出进程将一直阻塞直到超时或产生I/O为止此时函数返回通知进程读取或发送数据.

  函数select的原型如下:

#include <sys/types.h>

#include <sys/times.h>

#include <sys/select.h>

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

FD_CLR(int fd, fd_set *fdset);    // fdset中删去文件描述符fd

FD_ISSET(int fd, fd_set *fdset);   // 查询文件描述符是否在fdset

FD_SET(int fd, fd_set *fdset);     // fdset中插入文件描述符fd

FD_ZERO(fd_set *fdset);        // 清空fdset

  参数nfdsselect监控的文件描述符的时间一般为监控的最大描述符编号加1.

  类型fd_set是文件描述符集合其元素为监控的文件描述符.

  参数timeout是描述精确时间的timeval结构,它确定了函数的超时时间,有三种取值情况:

  a) NULL. 函数永远等待直到文件描述符就绪.

  b) 0. 函数不等待检查文件描述符状态后立即返回.

  c) 其他值函数等待文件描述符就绪或者定时完成时返回.

  函数select将返回文件描述符集合中已准备好的文件总个数函数select返回就绪文件描述符数量后必须执行read等函数否则函数继续返回就绪文件数.

[bill@billstone Unix_study]$ cat timeout3.c

#include <stdio.h>

#include <sys/types.h>

#include <sys/times.h>

#include <sys/select.h>

 

int main()

{

        struct timeval timeout;

        fd_set readfds;

        int i;

        char c;

 

        timeout.tv_sec = 3;

        timeout.tv_usec = 0;

        FD_ZERO(&readfds);

        FD_SET(0, &readfds);

        i = select (1, &readfds, NULL, NULL, &timeout);

        if(i > 0){

                read(0, &c, 1);

                printf("%d/n", c);

        }

        else if(i == 0)

                printf("timeout/n");

        else

                printf("error/n");

 

        return 0;

}

[bill@billstone Unix_study]$ make timeout3

cc     timeout3.c   -o timeout3

[bill@billstone Unix_study]$ ./timeout3

x

120

[bill@billstone Unix_study]$

[bill@billstone Unix_study]$ ./timeout3

timeout

[bill@billstone Unix_study]$

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值