【问题标题】:temporary file location when using tmpfile() in C在 C 中使用 tmpfile() 时的临时文件位置
【发布时间】:2015-04-20 21:37:00
【问题描述】:

$ man tmpfile

标准没有指定 tmpfile() 将使用的目录。 Glibc 将尝试<stdio.h> 中定义的路径前缀 P_tmpdir,如果 目录 /tmp 失败。

我正在使用 Ubuntu 13.10 x86_64、gcc 和 libc BTW。

所以当我尝试使用 tmpfile() 创建临时文件时,我在 /tmp 中看不到任何临时文件。 (我可以在 stdio.h 中看到 # define P_tmpdir "/tmp")。这是我使用的代码 sn-p:

#include <stdio.h>

int main(int argc, char **argv)
{
    FILE *tmp;

    tmp = tmpfile();                            // Where's this file?
    scanf("%*d");

    return 0;
}

$ ./tmpfile
现在,当 scanf 等待下一个(冗余)输入时,我应该能够在 /tmp 中看到一个临时文件。但我不能。那么这个 tmpfile 到底是在哪里创建的呢?

【问题讨论】:

  • 查看当前工作目录了吗?
  • @alk - 之前我没有,但现在我做到了。不,没有临时文件。实际上,我很高兴 PWD 中没有 tmpfile,因为它与手册相矛盾。 ;)

标签: c linux posix glibc temporary-files


【解决方案1】:

可能是目录中的文件项被直接删除了。在 POSIX 系统上,只要您有一个打开的文件描述符,文件本身在删除后仍然有效。 (在您的情况下隐藏在 FILE* 返回值中。)

使用该技术,没有人可以潜入并打开该文件,它只能通过您的变量 tmp 访问。

【讨论】:

  • “使用该技术,没有人可以潜入并打开该文件,它只能通过您的变量 tmp 访问” - 这在一般情况下是错误的 - 至少在 Linux 上该文件仍然可以重新打开, 使用 /proc/$pid/fd.另见例如unix.stackexchange.com/a/98700/288001
【解决方案2】:

编译您的代码并通过strace 运行它会显示文件的位置和名称:

$ ./a.out
execve("./main", ["./main"], [/* 31 vars */]) = 0
brk(0)                                  = 0xe0c000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f038c51a000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=103425, ...}) = 0
mmap(NULL, 103425, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f038c500000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\357\1\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1603600, ...}) = 0
mmap(NULL, 3717176, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f038bf71000
mprotect(0x7f038c0f3000, 2097152, PROT_NONE) = 0
mmap(0x7f038c2f3000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x182000) = 0x7f038c2f3000
mmap(0x7f038c2f8000, 18488, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f038c2f8000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f038c4ff000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f038c4fe000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f038c4fd000
arch_prctl(ARCH_SET_FS, 0x7f038c4fe700) = 0
mprotect(0x7f038c2f3000, 16384, PROT_READ) = 0
mprotect(0x7f038c51c000, 4096, PROT_READ) = 0
munmap(0x7f038c500000, 103425)          = 0
stat("/tmp", {st_mode=S_IFDIR|0777, st_size=4096, ...}) = 0
getpid()                                = 25957
open("/tmp/tmpfflAlKG", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
unlink("/tmp/tmpfflAlKG")               = 0
fcntl(3, F_GETFL)                       = 0x8002 (flags O_RDWR|O_LARGEFILE)
brk(0)                                  = 0xe0c000
brk(0xe2d000)                           = 0xe2d000
fstat(3, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f038c519000
lseek(3, 0, SEEK_CUR)                   = 0
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f038c518000
read(0, alk
"alk\n", 1024)                  = 4
exit_group(0)                           = ?

"/tmp/tmpfflAlKG" 是通过 open() 创建的

open("/tmp/tmpfflAlKG", O_RDWR|O_CREAT|O_EXCL, 0600) = 3

然后立即得到unlink()

unlink("/tmp/tmpfflAlKG")               = 0

所以可见的目录条目消失了。

由于仍然对进程开放,文件本身对进程保持有效,直到进程close()s 它。后者通过调用 exit_group() 隐式发生:

exit_group(0)                           = ?

【讨论】:

  • 谢谢!现在我可以看到文件的名称,并且可以验证 Jens Gustedt 回答了什么,我也可以看到 unlink() 调用。感谢strace
  • 我宁愿建议strace -e open。临时文件无论如何都无法逃脱open() 系统调用。
  • 请注意,较新的 glibc 版本将首先使用 O_TMPFILE 标志,这会在目录的文件系统中创建一个未命名的 inode,没有指向它的链接,并且只会回退到常规的 open+@987654337 @ 如果这不起作用(例如,因为文件系统不支持它)。
【解决方案3】:

来自https://en.cppreference.com/w/c/io/tmpfile

在某些实现上(例如Linux),这个函数实际上是从文件系统中创建、打开并立即删除文件:只要程序持有一个已删除文件的打开文件描述符,该文件就存在,但是由于它被删除了,它的名字没有出现在任何目录中,因此没有其他进程可以打开它。一旦文件描述符关闭,或者程序一旦终止(正常或异常),文件占用的空间就会被文件系统回收。

在某些实现(例如 Windows)上,需要提升权限,因为该函数可能会在系统目录中创建临时文件。

【讨论】:

  • 不是一个真正的答案。适合作为评论。
猜你喜欢
  • 1970-01-01
  • 2010-12-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多