【问题标题】:How avoid using global variable when using nftw使用 nftw 时如何避免使用全局变量
【发布时间】:2012-05-04 02:28:38
【问题描述】:

我想用nftw遍历C中的一个目录结构。

但是,鉴于我想做的事情,我看不出使用全局变量的方法。

使用 (n)ftw 的教科书示例都涉及打印文件名之类的操作。相反,我想获取路径名和文件校验和并将它们放在数据结构中。但考虑到可以传递给 nftw 的内容的限制,我看不到这样做的好方法。

我使用的解决方案涉及一个全局变量。然后 nftw 调用的函数可以访问该变量并添加所需的数据。

有什么合理的方法可以在不使用全局变量的情况下做到这一点?

Here's the exchange in previous post on stackoverflow in which someone suggested I post this as a follow-up.

【问题讨论】:

    标签: c global-variables nftw


    【解决方案1】:

    使用 ftw 可能真的非常非常糟糕。在内部它将保存您使用的函数指针,如果另一个线程执行其他操作,它将覆盖函数指针。

    恐怖场景:

    thread 1:  count billions of files
    thread 2:  delete some files
    thread 1:  ---oops, it is now deleting billions of 
                  files instead of counting them.
    

    简而言之。你最好使用 fts_open。

    如果您仍想使用 nftw,那么我的建议是将“全局”类型放入命名空间并将其标记为“thread_local”。您应该能够根据自己的需要进行调整。

    /* in some cpp file */
    namespace {
       thread_local size_t gTotalBytes{0};  // thread local makes this thread safe
    int GetSize(const char* path, const struct stat* statPtr, int currentFlag, struct FTW* internalFtwUsage) {
        gTotalBytes+=  statPtr->st_size;
        return 0;  //ntfw continues
     }
    } // namespace
    
    
    size_t RecursiveFolderDiskUsed(const std::string& startPath) {
       const int flags = FTW_DEPTH | FTW_MOUNT | FTW_PHYS;
       const int maxFileDescriptorsToUse = 1024; // or whatever
       const int result = nftw(startPath.c_str(), GetSize, maxFileDescriptorsToUse , flags);
    
      // log or something if result== -1
      return gTotalBytes;
    }
    

    【讨论】:

    • glibc 的 ftw 实现将函数指针保存在堆栈中,而不是全局变量中,因此您列出的恐怖场景是不可能的。也就是说,我认为您使用 thread_local 的建议是这个问题的最佳答案。
    【解决方案2】:

    没有。 nftw 不提供任何可以传递给函数的用户参数,因此您必须在 C 中使用全局(或静态)变量。

    GCC 提供了一个扩展“嵌套函数”,它应该捕获其封闭范围的变量,因此可以像这样使用它们:

    void f()
    {
      int i = 0;
      int fn(const char *,
        const struct stat *, int, struct FTW *) {
        i++;
        return 0;
      };
      nftw("path", fn, 10, 0);
    }
    

    【讨论】:

      【解决方案3】:

      数据最好在一个单独的模块中给出静态链接(即文件范围),该模块仅包含访问数据所需的函数,包括传递给nftw()的函数。这样,数据在全局范围内不可见,并且所有访问都受到控制。可能调用 ntfw() 的函数也是这个模块的一部分,使得传递给 nftw() 的函数也是静态的,因此在外部是不可见的。

      换句话说,您应该做您可能已经在做的事情,但要明智地使用单独的编译和静态链接,以使数据仅通过访问函数可见。具有静态链接的数据可由同一翻译单元中的任何函数访问,并且您可以通过仅在该翻译单元中包含作为该数据的创建者、维护者或访问者的函数来避免与全局变量相关的问题。

      一般模式是:

      datamodule.h

      #if defined DATAMODULE_INCLUDE
      <type> create_data( <args>) ;
      <type> get_data( <args> ) ;
      #endif
      

      datamodule.c

      #include "datamodule.h"
      
      static <type> my_data ;
      
      static int nftwfunc(const char *filename, const struct stat *statptr, int fileflags, struct FTW *pfwt)
      {
          // update/add to my_data
          ...
      }
      
      
      <type> create_data( const char* path, <other args>)
      {
          ...
      
          ret = nftw( path, nftwfunc, fd_limit, flags);
      
          ... 
      }
      
      <type> get_data( <args> )
      {
          // Get requested data from my_data and return it to caller
      }
      

      【讨论】:

      • 我意识到进行大量代码隔离的好处,因为 BSD 库“libarchive”(我也在使用)就是这样设置的。但是您的评论有助于强化这个问题,因此我会更多地使用 static 关键字。 (如果可以的话,我会支持你。)
      • 我在您之前的帖子中链接的Pox on globals 文章也讨论了这种技术。该文章指的是嵌入式系统,但它同样适用于通用计算。我还建议 C++ 通过使用类和名称空间为您提供更好的数据封装的更多机会。关于以这种方式使用静态的一些注意事项,如果您只需要一个数据实例就可以了,这会导致不可重入和非线程安全的代码。其他解决方案可能适用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 2020-09-02
      • 2020-10-02
      • 2012-12-19
      • 1970-01-01
      • 2022-01-07
      相关资源
      最近更新 更多