这些.tmp文件是从哪里来的

Where did these .tmp files come from?

本文关键字:tmp 文件 这些      更新时间:2023-10-16

首先,一些细节:

  • 我使用的是C++(Armadillo库)和R的组合
  • 我使用Ubuntu作为我的操作系统
  • 我不是使用Rcpp

假设我有一些名为cpp_code的C++代码,它是:

  • 从R读取一个整数作为输入
  • 执行一些计算
  • 将电子表格"out.csv"保存为R的输出。(我使用.save(name,file_type=csv))

一些简化的R代码是:

for(i in 1:10000)
{
 system(paste0("echo ", toString(i), " | ./cpp_code")) ## produces out.csv
 output[i,,] <- read.csv("out.csv") ## reads out.csv
}

我的问题:

99%的时候,一切都很好。然而,我时不时地会收到一些不寻常的.tmp文件,比如:"out.csv.tmp_a0ac9806ff7f0000703a"。这些.tmp文件只出现一秒钟左右,然后突然消失了。

问题:

  • 是什么原因造成的
  • 有没有办法阻止这种情况的发生

请对我宽容一点,因为计算机不是我的主要学科。

非常感谢您抽出时间。

许多程序将输出写入临时文件,然后将其重命名为目标文件。这样做通常是为了避免在写入过程中进程被终止时留下半写的输出文件。通过使用临时文件,可以将文件原子地重命名为输出文件名,从而确保:

  • 整个输出文件已正确写入或
  • 输出文件没有更改

请注意,通常仍然存在一些竞争条件,例如,输出文件被删除,但临时文件没有被重命名,但以上两种结果之一是总体目标。

我相信您在armadillo中使用.save函数。

http://arma.sourceforge.net/docs.html#save_load_field

您应该在中看到两个函数include/armadillo_bits/diskio_meat.hpp。在save_raw_ascii中,它首先将数据存储到diskio::gen_tmp_name的文件名中,如果是save_okay,则按safe_rename重命名。如果safe_okaysafe_rename失败,您将看到临时文件。临时文件名选择为filename + .tmp_ + some hex value from file name

//! Save a matrix as raw text (no header, human readable).
//! Matrices can be loaded in Matlab and Octave, as long as they don't have complex elements.
template<typename eT>
inline
bool
diskio::save_raw_ascii(const Mat<eT>& x, const std::string& final_name)
  {
  arma_extra_debug_sigprint();
  const std::string tmp_name = diskio::gen_tmp_name(final_name);
  std::fstream f(tmp_name.c_str(), std::fstream::out);
  bool save_okay = f.is_open();
  if(save_okay == true)
    {
    save_okay = diskio::save_raw_ascii(x, f);
    f.flush();
    f.close();
    if(save_okay == true)
      {
      save_okay = diskio::safe_rename(tmp_name, final_name);
      }
    }
  return save_okay;
  }
//! Append a quasi-random string to the given filename.
//! The rand() function is deliberately not used,
//! as rand() has an internal state that changes
//! from call to call. Such states should not be
//! modified in scientific applications, where the
//! results should be reproducable and not affected 
//! by saving data.
inline
std::string
diskio::gen_tmp_name(const std::string& x)
  {
  const std::string* ptr_x     = &x;
  const u8*          ptr_ptr_x = reinterpret_cast<const u8*>(&ptr_x);
  const char* extra      = ".tmp_";
  const uword extra_size = 5;
  const uword tmp_size   = 2*sizeof(u8*) + 2*2;
        char  tmp[tmp_size];
  uword char_count = 0;
  for(uword i=0; i<sizeof(u8*); ++i)
    {
    conv_to_hex(&tmp[char_count], ptr_ptr_x[i]);
    char_count += 2;
    }
  const uword x_size = static_cast<uword>(x.size());
  u8 sum = 0;
  for(uword i=0; i<x_size; ++i)
    {
    sum += u8(x[i]);
    }
  conv_to_hex(&tmp[char_count], sum);
  char_count += 2;
  conv_to_hex(&tmp[char_count], u8(x_size));

  std::string out;
  out.resize(x_size + extra_size + tmp_size);

  for(uword i=0; i<x_size; ++i)
    {
    out[i] = x[i];
    }
  for(uword i=0; i<extra_size; ++i)
    {
    out[x_size + i] = extra[i];
    }
  for(uword i=0; i<tmp_size; ++i)
    {
    out[x_size + extra_size + i] = tmp[i];
    }
  return out;
  }

"Dark Falcon"的假设完全正确:当调用save时,Armadillo会创建一个临时文件来保存数据,然后将文件重命名为最终名称。

这可以在源代码中看到(这是save_raw_ascii,但其他save*函数的工作方式类似):

const std::string tmp_name = diskio::gen_tmp_name(final_name);
std::fstream f(tmp_name.c_str(), std::fstream::out);
  
bool save_okay = f.is_open();
  
if(save_okay == true)
  {
  save_okay = diskio::save_raw_ascii(x, f);
   
  f.flush();
  f.close();
    
  if(save_okay == true)
    {
    save_okay = diskio::safe_rename(tmp_name, final_name);
    }
  }

关于safe_rename的评论是这样说的:

安全地重命名文件。在重命名之前,测试我们是否可以写入最终文件。这应该可以防止:

  1. 覆盖写保护的文件
  2. 覆盖目录

值得注意的是,这不会阻止比赛条件。