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函数的原型就是它。

  • 相关阅读:
    STM32 F4 DAC DMA Waveform Generator
    STM32 F4 General-purpose Timers for Periodic Interrupts
    Python第十四天 序列化 pickle模块 cPickle模块 JSON模块 API的两种格式
    Python第十三天 django 1.6 导入模板 定义数据模型 访问数据库 GET和POST方法 SimpleCMDB项目 urllib模块 urllib2模块 httplib模块 django和web服务器整合 wsgi模块 gunicorn模块
    查看SQL Server服务运行帐户和SQL Server的所有注册表项
    Pycharm使用技巧(转载)
    SQL Server 2014内存优化表的使用场景
    Python第十天 print >> f,和fd.write()的区别 stdout的buffer 标准输入 标准输出 从控制台重定向到文件 标准错误 重定向 输出流和输入流 捕获sys.exit()调用 optparse argparse
    Python第七天 函数 函数参数 函数里的变量 函数返回值 多类型传值 函数递归调用 匿名函数 内置函数
    Python第六天 类型转换
  • 原文地址:https://www.cnblogs.com/bonelee/p/12923701.html
Copyright © 2011-2022 走看看