首页
社区
课程
招聘
《精通UNIX下C语言编程及项目实践》学习笔记03 并发程序设计
发表于: 2009-4-19 08:03 8814

《精通UNIX下C语言编程及项目实践》学习笔记03 并发程序设计

2009-4-19 08:03
8814
[bill@billstone Unix_study]$ XYZ=/home/bill
[bill@billstone Unix_study]$ env | grep XYZ
[bill@billstone Unix_study]$ export XYZ
[bill@billstone Unix_study]$ env | grep XYZ
XYZ=/home/bill
[bill@billstone Unix_study]$
Extern char **environ;
[bill@billstone Unix_study]$ cat env1.c
#include <stdio.h>
extern char **environ;
 
int main()
{
        char **p = environ;
 
        while(*p){
                fprintf(stderr, "%s\n", *p);
                p++;
        }
 
        return 0;
}
[bill@billstone Unix_study]$ make env1
cc     env1.c   -o env1
[bill@billstone Unix_study]$ ./env1
SSH_AGENT_PID=1392
HOSTNAME=billstone
DESKTOP_STARTUP_ID=
SHELL=/bin/bash
TERM=xterm
... ... ... ...
[bill@billstone Unix_study]
#include <stdlib.h>
char *getenv(char *name);
int putenv(const char *string);
[bill@billstone Unix_study]$ cat env2.c
#include <stdio.h>
#include <stdlib.h>
 
int main(int argc, char **argv)
{
        int i;
 
        for(i=1;i<argc;i++)
                fprintf(stderr, "%s=%s\n", argv[i], getenv(argv[i]));
 
        return 0;
}
[bill@billstone Unix_study]$ make env2
cc     env2.c   -o env2
[bill@billstone Unix_study]$ ./env2 HOME LOGNAME rp
HOME=/home/bill
LOGNAME=bill
rp=(null)
[bill@billstone Unix_study]$
#include <unistd.h>
int execl(const char *path, const char *arg0, ..., (char *)0);
int execle(const char *path, const char *arg0, ..., (char *)0, char *const envp[]);
int execlp(const char *file, const char *arg0, ..., (char *)0);
int execv(const char *path, const char *argv[]);
int execve(const char *path, const char *argv[], const char *envp[]);
int execvp(const char *file, const char *argv[]);
extern char **environ;
[bill@billstone Unix_study]$ cat exec1.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
 
int main()
{
        pid_t pid;
 
        if((pid = fork()) == 0){
                fprintf(stderr, "---- begin ----\n");
                // sleep(3);               // 睡眠3秒会导致子进程成为僵死进程
                execl("/bin/uname", "uname", "-a", 0);
                fprintf(stderr, "----  end  ----\n");
        }
        else if(pid > 0)
                fprintf(stderr, "fork child pid = [%d]\n", pid);
        else
                fprintf(stderr, "Fork failed.\n");
 
        return 0;
}
[bill@billstone Unix_study]$ make exec1
cc     exec1.c   -o exec1
[bill@billstone Unix_study]$ ./exec1
---- begin ----
Linux billstone 2.4.20-8 #1 Thu Mar 13 17:18:24 EST 2003 i686 athlon i386 GNU/Linux
fork child pid = [13276]               
[bill@billstone Unix_study]$ ./exec1
---- begin ----
fork child pid = [13278]                      
[bill@billstone Unix_study]$ Linux billstone 2.4.20-8 #1 Thu Mar 13 17:18:24 EST 2003 i686 athlon i386 GNU/Linux
[bill@billstone Unix_study]$ cat exec2.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
 
int main()
{
        pid_t pid;
 
        if((pid = vfork()) == 0){
                fprintf(stderr, "---- begin ----\n");
                sleep(3);
                execl("/bin/uname", "uname", "-a", 0);
                fprintf(stderr, "----  end  ----\n");
        }
        else if(pid > 0)
                fprintf(stderr, "fork child pid = [%d]\n", pid);
        else
                fprintf(stderr, "Fork failed.\n");
 
        return 0;
}
[bill@billstone Unix_study]$ make exec2
make: `exec2' is up to date.
[bill@billstone Unix_study]$ ./exec2
---- begin ----
fork child pid = [13293]               
[bill@billstone Unix_study]$ Linux billstone 2.4.20-8 #1 Thu Mar 13 17:18:24 EST 2003 i686 athlon i386 GNU/Linux

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (6)
雪    币: 339
活跃值: (10)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
2
十、时钟与信号
  获取时钟

  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]$
2009-4-19 17:52
0
雪    币: 339
活跃值: (10)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
3
欢迎转载,请保留作者信息
bill@华中科技大学
http://billstone.cublog.cn  

全局跳转

  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秒后定时返回.

  一般情况下, 处理UNIX中I/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

  参数nfds是select监控的文件描述符的时间, 一般为监控的最大描述符编号加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]$
2009-4-19 17:55
0
雪    币: 103
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
楼主用的什么系统啊.
2009-4-19 18:41
0
雪    币: 339
活跃值: (10)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
5
现在使用的是Red Hat 9,不过这个学习笔记里的实例不同于《Linux下溢出漏洞利用学习》,可以在任意Unix环境下实现
2009-4-19 19:07
0
雪    币: 339
活跃值: (10)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
6
回答“全局跳转”要点中的问题<红色标记>:
  我重新阅读了APUE中关于setjmp的章节,截出关于该问题的解答:
  "what are the states of the automatic variables and register variables in the main function?" When main is returned to by the longjmp, do these variables have values corresponding to when the setjmp was previously called, or are their values left alone so that their values left alone so that their values are whatever they were when do_line was called? Unfortunately, the answer is "it depends."

  下面以一个实例看下具体情况:
#include <setjmp.h>
#include <stdio.h>

static void f1(int, int, int, int);
static void f2(void);

static jmp_buf jmpbuffer;
static int globval;

int main(void)
{
    int autoval;
    register int regival;
    volatile int volaval;
    static int statval;

    globval = 1;
    autoval = 2;
    regival = 3;
    volaval = 4;
    statval = 5;
    if(setjmp(jmpbuffer) != 0){
        printf("after longjmp: \n");
        printf("globval = %d, autoval = %d, regival = %d, volaval = %d, statval = %d\n", globval, autoval, regival, volaval, statval);
        exit(0);
    }
         globval = 91;
        autoval = 92;
        regival = 93;
        volaval = 94;
        statval = 95;

    f1(autoval, regival, volaval, statval);
    exit(0);
}

static void f1(int i, int j, int k, int l){
    printf("in f1():\n");
    printf("globval = %d, autoval = %d, regival = %d, volaval = %d, statval = %d\n", globval, i, j, k, l);
    f2();
}

static void f2(void){
    longjmp(jmpbuffer, 1);
}

  下面是在Red Hat9上的运行结果:
[bill@billstone Unix_study]$ uname -a
Linux billstone 2.4.20-8 #1 Thu Mar 13 17:18:24 EST 2003 i686 athlon i386 GNU/Linux
[bill@billstone Unix_study]$ ./testjmp
in f1():
globval = 91, autoval = 92, regival = 93, volaval = 94, statval = 95
after longjmp:
globval = 91, autoval = 92, regival = 93, volaval = 94, statval = 95
[bill@billstone Unix_study]$


  在Ubuntu上的测试结果:
billstone@ubuntu:~/Unix_C$ uname -a
Linux ubuntu 2.6.24-22-generic #1 SMP Mon Nov 24 19:35:06 UTC 2008 x86_64 GNU/Linux
billstone@ubuntu:~/Unix_C$ ./testjmp 
in f1():
globval = 91, autoval = 92, regival = 93, volaval = 94, statval = 95
after longjmp: 
globval = 91, autoval = 92, regival = 93, volaval = 94, statval = 95
billstone@ubuntu:~/Unix_C$ 


  可以看到在这两个系统上setjmp并没有将自动变量和寄存器变量回复到之前的状态。大家如果使用了其他系统,请测试一下并将结果发上来一起探讨。
2009-4-23 10:16
0
雪    币: 212
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
对于setjmp,man 里这样说的
A call to setjmp() shall save the calling environment in its env argument for later use
       by longjmp().
我觉得这里只是保存了调用setjmp时的堆栈位置,程序eip等流程控制相关的东西,并没有保存变量值
还有jmp_buf 并没有多大,放不了所有的临时变量的,看过定义应该是12个int 型用来保存这个calling environment 。
个人猜测,欢迎指正。
2009-5-8 18:20
0
游客
登录 | 注册 方可回帖
返回
//