• 软件测试技术
  • 软件测试博客
  • 软件测试视频
  • 开源软件测试技术
  • 软件测试论坛
  • 软件测试沙龙
  • 软件测试资料下载
  • 软件测试杂志
  • 软件测试人才招聘
    暂时没有公告

字号: | 推荐给好友 上一篇 | 下一篇

事务控制范例程序

发布: 2007-6-08 22:43 | 作者: seanhe | 来源: Blog.ChinaUnix.net

软件测试论坛讨论

领测软件测试网

如何在你的程序中使用事务控制.....

事务控制范例程序

     /* 生成前台/后台事务的函数 */

    

     #include <stdio.h>

     #include <unistd.h>

     #include <stdlib.h>

     #include <fcntl.h>

     #include <signal.h>

     #include <sys/types.h>

     #include <sys/wait.h>

     #include <errno.h>    

    

     /* 一些下面的函数会因为无法定位控制tty和调用方不在前台而失败。

      * 第一种情况时,我们假设一个前台程序会有为标准输入,标准输出或标准错误输出打开的ctty,

      * 而如果没有则返回ENOTTY。

      * 第二种情况时,除foreground_self()函数的特殊情况以外,

      * 若一个非前台程序打算输出一些东西到前台,我们返回EPERM。

      * (也许想得太多了)

      */

    

    

     /* 为给定的pgrp安排一个终端 (打开一个ctty) .

      * 这个tcsetpgrp()外壳程序只是因为POSIX中特别错误(bogusity)的地方而需要;

      * 遵照标准的系统在一个非前台进程调用tcsetpgrp函数时传递SIGTTOU

      * 信号(差不多总是这样)。这是虚假的一致性之于一般想法的胜利。

      */

    

     int assign_terminal(int ctty, pid_t pgrp)

     {

         sigset_t sigs;

         sigset_t oldsigs;

         int rc;

    

         sigemptyset(&sigs);

         sigaddset(&sigs,SIGTTOU);

         sigprocmask(SIG_BLOCK, &sigs, &oldsigs);

    

         rc = tcsetpgrp(ctty, pgrp);

    

         sigprocmask(SIG_SETMASK, &oldsigs, NULL);

    

         return rc;

     }

    

    

     /* 类似fork函数,但做事务控制。如果新建立的进程放在前台则设fg为真。

      * (这样隐式地将调用方进程放置到后台,所以做完这个后要当心tty的输入/输出)

      * 设定pgrp为-1以创建一个新事务,在此情况下返回的进程号即是新事务的进程组号,

      * 或者设定一个同一会话中存在的事务(一般只用来启动管道操作的第二个或第二个以后

      * 的进程)。

      */

    

     pid_t spawn_job(int fg, pid_t pgrp)

     {

         int ctty = -1;

         pid_t pid;

    

         /* 如果生成一个*新*的前台事务,起码要求标准输入,标准输出或

          * 标准错误输出的其中一个指向的是控制tty,并且当前进程在前台。

          * 只有当在存在事务中开始一个新前台进程时才检查控制中的tty。

          * 一个没有控制tty的会话只能有后台事务。

          */

    

         if (fg)

         {

             pid_t curpgrp;

    

             if ((curpgrp = tcgetpgrp(ctty = 2)) < 0

                 && (curpgrp = tcgetpgrp(ctty = 0)) < 0

                 && (curpgrp = tcgetpgrp(ctty = 1)) < 0)

                 return errno = ENOTTY, (pid_t)-1;

    

             if (pgrp < 0 && curpgrp != getpgrp())

                 return errno = EPERM, (pid_t)-1;

         }

    

         switch (pid = fork())

         {

             case -1: /* fork失败 */

                 return pid;

    

             case 0: /* 子进程 */

    

                 /* 建立新进程组, 如果需要则将我们放到前台

                  * 不知道如果setpgid函数调用失败该怎么办(“不会发生”)

                  */

    

                 if (pgrp < 0)

                     pgrp = getpid();

    

                 if (setpgid(0,pgrp) == 0 && fg)

                     assign_terminal(ctty, pgrp);

    

                 return 0;

    

             default: /* 父进程 */

    

                 /* 这里也建立自进程组. */

    

                 if (pgrp < 0)

                     pgrp = pid;

    

                 setpgid(pid, pgrp);

    

                 return pid;

         }

    

         /*不会执行到这里*/

     }

    

    

     /* 用SIGNO表示的信号杀死PGRP表示的事务 */

    

     int kill_job(pid_t pgrp, int signo)

     {

         return kill(-pgrp, signo);

     }

    

    

     /* 中断PGRP表示的事务 */

    

     int suspend_job(pid_t pgrp)

     {

         return kill_job(pgrp, SIGSTOP);

     }

    

    

     /* 继续在后台执行PGRP表示的事务 */

    

     int resume_job_bg(pid_t pgrp)

     {

         return kill_job(pgrp, SIGCONT);

     }

    

    

     /* 继续在前台执行PGRP表示的事务 */

    

     int resume_job_fg(pid_t pgrp)

     {

         pid_t curpgrp;

         int ctty;

    

         if ((curpgrp = tcgetpgrp(ctty = 2)) < 0

             && (curpgrp = tcgetpgrp(ctty = 0)) < 0

             && (curpgrp = tcgetpgrp(ctty = 1)) < 0)

             return errno = ENOTTY, (pid_t)-1;

    

         if (curpgrp != getpgrp())

             return errno = EPERM, (pid_t)-1;

    

         if (assign_terminal(ctty, pgrp) < 0)

             return -1;

    

         return kill_job(pgrp, SIGCONT);

     }

    

    

     /* 将我们自己放置到前台,比如在中断一个前台事务之后调用

      */

    

     int foreground_self()

     {

         pid_t curpgrp;

         int ctty;

    

         if ((curpgrp = tcgetpgrp(ctty = 2)) < 0

             && (curpgrp = tcgetpgrp(ctty = 0)) < 0

             && (curpgrp = tcgetpgrp(ctty = 1)) < 0)

             return errno = ENOTTY, (pid_t)-1;

    

         return assign_terminal(ctty, getpgrp());

     }

    

    

     /* closeall() - 关闭所有>=给定FD的文件描述符 */

    

     void closeall(int fd)

     {

         int fdlimit = sysconf(_SC_OPEN_MAX);

    

         while (fd < fdlimit)

             close(fd++);

     }

    

    

     /* 类似system()函数,但将给定的命令作为后台事务执行,返回shell进程

      * 的进程号(并且也是这个事务的进程组号,适用于kill_job等等)。

      * 如果参数INFD,OUTFD或ERRFD为非NULL,则打开一个管道和一个文件描述

      * 符保存与该管道有关的父进程端,然后在子进程中将被从定向到/dev/null。

      * 并且在子进程中关闭所有>2的文件描述符(一个经常过份估计的工作)

      */

    

     pid_t spawn_background_command(const char *cmd,

                                    int *infd, int *outfd, int *errfd)

     {

         int nullfd = -1;

         int pipefds[3][2];

         int error = 0;

    

         if (!cmd)

             return errno = EINVAL, -1;

    

         pipefds[0][0] = pipefds[0][1] = -1;

         pipefds[1][0] = pipefds[1][1] = -1;

         pipefds[2][0] = pipefds[2][1] = -1;

    

         if (infd && pipe(pipefds[0]) < 0)

             error = errno;

         else if (outfd && pipe(pipefds[1]) < 0)

             error = errno;

         else if (errfd && pipe(pipefds[2]) < 0)

             error = errno;

    

         if (!error && !(infd && outfd && errfd))

         {

             nullfd = open("/dev/null",O_RDWR);

             if (nullfd < 0)

                 error = errno;

         }

    

         if (!error)

         {

             pid_t pid = spawn_job(0, -1);

             switch (pid)

             {

                 case -1: /* fork失败 */

                     error = errno;

                     break;

    

                 case 0: /* 子进程 */

    

                     dup2(infd ? pipefds[0][0] : nullfd, 0);

                     dup2(outfd ? pipefds[1][1] : nullfd, 1);

                     dup2(errfd ? pipefds[2][1] : nullfd, 2);

                     closeall(3);

    

                     execl("/bin/sh","sh","-c",cmd,(char*)NULL);

    

                     _exit(127);

    

                 default: /* 父进程 */

    

                     close(nullfd);

                     if (infd)

                         close(pipefds[0][0]), *infd = pipefds[0][1];

                     if (outfd)

                         close(pipefds[1][1]), *outfd = pipefds[1][0];

                     if (errfd)

                         close(pipefds[2][1]), *errfd = pipefds[2][0];

    

                     return pid;

             }

         }

    

         /* 只在错误时执行到这里 */

    

         {

             int i,j;

             for (i = 0; i < 3; ++i)

                 for (j = 0; j < 2; ++j)

                     if (pipefds[i][j] >= 0)

                         close(pipefds[i][j]);

         }

    

         if (nullfd >= 0)

             close(nullfd);

    

         return errno = error, (pid_t) -1;

     }

    

    

     /*---------------------------------------*/

     /* 这里是使用上述函数一个小例子.         */

    

     pid_t bgjob = -1;

     volatile int signo = 0;

    

     #ifndef WCOREDUMP

      /* 如果没有 WCOREDUMP, 你也许会希望在你的平台上为它设置一个准确的定义

       * (这通常是(status & 0x80) 但也不总是这样),或者就赌没有core dumps(

       * 就象这个程序所做)

       */

     # define WCOREDUMP(status) (0)

     #endif

    

     int check_children()

     {

         pid_t pid;

         int status;

         int count = 0;

    

         while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)

         {

             if (pid == bgjob && !WIFSTOPPED(status))

                 bgjob = -1;

    

             ++count;

    

             if (WIFEXITED(status))

                 fprintf(stderr,"Process %ld exited with return code %d\n",

                         (long)pid, WEXITSTATUS(status));

             else if (WIFSIGNALED(status))

                 fprintf(stderr,"Process %ld killed by signal %d%s\n",

                         (long)pid, WTERMSIG(status),

                         WCOREDUMP(status) ? " (core dumped)" : "");

             else if (WIFSTOPPED(status))

                 fprintf(stderr,"Process %ld stopped by signal %d\n",

                         (long)pid, WSTOPSIG(status));

             else

                 fprintf(stderr,"Unexpected status - pid=%ld, status=0x%x\n",

                         (long)pid, status);

         }

    

         return count;

     }

    

    

     void sighandler(int sig)

     {

         if (sig != SIGCHLD)

             signo = sig;

     }

    

    

     int main()

     {

         struct sigaction act;

         int sigcount = 0;

    

         act.sa_handler = sighandler;

         act.sa_flags = 0;

         sigemptyset(&act.sa_mask);

         sigaction(SIGINT,&act,NULL);

         sigaction(SIGQUIT,&act,NULL);

         sigaction(SIGTERM,&act,NULL);

         sigaction(SIGTSTP,&act,NULL);

         sigaction(SIGCHLD,&act,NULL);



    

         fprintf(stderr,"Starting background job 'sleep 60'\n");

         bgjob = spawn_background_command("sleep 60", NULL, NULL, NULL);

         if (bgjob < 0)

         {

             perror("spawn_background_command");

             exit(1);

         }

         fprintf(stderr,"Background job started with id %ld\n", (long)bgjob);

         while (bgjob >= 0)

         {

             if (signo)

             {

                 fprintf(stderr,"Signal %d caught\n", signo);

                 if (sigcount++)

                     kill_job(bgjob, SIGKILL);

                 else

                 {

                     kill_job(bgjob, SIGTERM);

                     kill_job(bgjob, SIGCONT);

                 }

             }

    

             if (!check_children())

                 pause();

         }

    

         fprintf(stderr,"Done - exiting\n");

         return 0;

     }

延伸阅读

文章来源于领测软件测试网 https://www.ltesting.net/


关于领测软件测试网 | 领测软件测试网合作伙伴 | 广告服务 | 投稿指南 | 联系我们 | 网站地图 | 友情链接
版权所有(C) 2003-2010 TestAge(领测软件测试网)|领测国际科技(北京)有限公司|软件测试工程师培训网 All Rights Reserved
北京市海淀区中关村南大街9号北京理工科技大厦1402室 京ICP备10010545号-5
技术支持和业务联系:info@testage.com.cn 电话:010-51297073

软件测试 | 领测国际ISTQBISTQB官网TMMiTMMi认证国际软件测试工程师认证领测软件测试网