zoukankan      html  css  js  c++  java
  • 内核中do while(0)的巧用 避免goto的方法 linux内核中代码有这样的代码

    1. do...while(0)消除goto语句。
    通常,如果在一个函数中开始要分配一些资源,然后在中途执行过程中如果遇到错误则退出函数,当然,退出前先释放资源,我们的代码可能是这样:
    version 1

    bool Execute()
    {
       // 分配资源
       int *p = new int;
       bool bOk(true);

       // 执行并进行错误处理
       bOk = func1();
       if(!bOk) 
       {
          delete p;   
          p = NULL;
          return false;
       }

       bOk = func2();
       if(!bOk) 
       {
          delete p;   
          p = NULL;
          return false;
       }

       bOk = func3();
       if(!bOk) 
       {
          delete p;   
          p = NULL;
          return false;
       }

       // ..........

       // 执行成功,释放资源并返回
        delete p;   
        p = NULL;
        return true;
       
    }


    这里一个最大的问题就是代码的冗余,而且我每增加一个操作,就需要做相应的错误处理,非常不灵活。于是我们想到了goto:
    version 2

    bool Execute()
    {
       // 分配资源
       int *p = new int;
       bool bOk(true);

       // 执行并进行错误处理
       bOk = func1();
       if(!bOk) goto errorhandle;

       bOk = func2();
       if(!bOk) goto errorhandle;

       bOk = func3();
       if(!bOk) goto errorhandle;

       // ..........

       // 执行成功,释放资源并返回
        delete p;   
        p = NULL;
        return true;

    errorhandle:
        delete p;   
        p = NULL;
        return false;
       
    }


    代码冗余是消除了,但是我们引入了C++中身份比较微妙的goto语句,虽然正确的使用goto可以大大提高程序的灵活性与简洁性,但太灵活的东西往往是很危险的,它会让我们的程序捉摸不定,那么怎么才能避免使用goto语句,又能消除代码冗余呢,请看do...while(0)循环:
    version3

    bool Execute()
    {
       // 分配资源
       int *p = new int;

       bool bOk(true);
       do
       {
          // 执行并进行错误处理
          bOk = func1();
          if(!bOk) break;

          bOk = func2();
          if(!bOk) break;

          bOk = func3();
          if(!bOk) break;

          // ..........

       }while(0);

        // 释放资源
        delete p;   
        p = NULL;
        return bOk;
       
    }


    “漂亮!”, 看代码就行了,啥都不用说了...

    背景:在内核的系统调用API实现里看到了while(0)的使用!

    如何使用系统调用?

    先来看一个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include<linux/unistd.h> /*定义宏_syscall1*/
    #include<time.h>     /*定义类型time_t*/
    _syscall1(time_t,time,time_t *,tloc)    /*宏,展开后得到time()函数的原型*/
    main()
    {
            time_t the_time;
            the_time=time((time_t *)0); /*调用time系统调用*/
            printf("The time is %ld ",the_time);
    }
    系统调用time返回从格林尼治时间1970年1月1日0:00开始到现在的秒数。
    这是最标准的系统调用的形式,宏_syscall1()展开来得到一个函数原型,稍后我会作详细解释。但事实上,如果把程序改成下面的样子,程序也可以运行得同样的结果。
    #include<time.h>
    main()
    {
            time_t the_time;
            the_time=time((time_t *)0); /*调用time系统调用*/
            printf("The time is %ld ",the_time);
    }

    这是因为在time.h中实际上已经用库函数的形式实现了time这个系统调用,替我们省掉了调用_syscall1宏展开得到函数原型这一步。

    大多数系统调用都在各种C语言函数库中有所实现,所以在一般情况下,我们都可以像调用普通的库函数那样调用系统调用,只在极个别的情况下,我们才有机会用到_syscall*()这几个宏。

    _syscall*()是什么?

    在unistd.h里定义了7个宏,分别是

    1
    2
    3
    4
    5
    6
    7
    _syscall0(type,name)
    _syscall1(type,name,type1,arg1)
    _syscall2(type,name,type1,arg1,type2,arg2)
    _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3)
    _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)
    _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5)
    _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5,type6,arg6)

    它们看起来似乎不太像宏,但其实质和
    #define MAXSIZE 100
    里面的MAXSIZE没有任何区别。

    它们的作用是形成相应的系统调用函数原型,供我们在程序中调用。我们很容易就能发现规律,_syscall后面的数字和typeN,argN的数目一样多。事实上,_syscall后面跟的数字指明了展开后形成函数的参数的个数,让我们看一个实例,就是刚刚用过的time系统调用:

    1
    _syscall1(time_t,time,time_t *,tloc)

    展开后的情形是这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    time_t   time(time_t *   tloc)
    {
        long __res;
        __asm__ volatile("int $0x80" : "=a" (__res) : "0" (13),"b" ((long)(tloc)));
        do {
            if ((unsigned long)(__res) >= (unsigned long)(-125)) {
                errno = -(__res);
                __res  = -1;
            }
            return (time_t) (__res);
        } while (0) ;
    }

    可以看出,_syscall1(time_t,time,time_t *,tloc)展开成一个名为time的函数,原参数time_t就是函数的返回类型,原参数time_t *和tloc分别构成新函数的参数。事实上,程序中用到的time函数的原型就是它。

  • 相关阅读:
    frame和iframe区别
    idea基本
    Spring中加载xml配置文件的六种方式
    java中Map,List与Set的区别
    java集合框架
    springmvc IDEA
    springmvc 精华
    eclipse 中 git 解决冲突(重点)
    启动Tomcat报错 java.util.zip.ZipException: invalid LOC header (bad signature)
    PowerDesigner 提示 Existence of index、key、reference错误
  • 原文地址:https://www.cnblogs.com/bonelee/p/12923701.html
Copyright © 2011-2022 走看看