【问题标题】:Get a unique file descriptor not associated with anything in the kernel in particular获取与内核中的任何内容无关的唯一文件描述符
【发布时间】:2013-05-23 02:10:03
【问题描述】:

我正在寻找一种方法来获取与任何特定内容无关的进程的唯一文件描述符。它必须是一个唯一的整数,内核不会为其创建等效的文件描述符。

到目前为止,我只是为了获得唯一的文件描述符编号而打开 /dev/null。我想知道是否有人知道实现此目的的更好方法(例如,某种“create_fd()”系统调用)。

【问题讨论】:

  • 您在寻找无效的文件描述符?为什么?
  • @ElchononEdelson:为了“模拟”某些完全不涉及内核的 I/O 操作。
  • 创建一个套接字。不要绑定或连接它。管道也会做同样的事情,见man -a pipe
  • @EricdesCourtis:打开/dev/null 更简单,而且效果相同。我只是希望有一种方法可以简单地保留与任何字符设备或文件无关的唯一合法文件描述符,以便我不拦截的所有调用都失败。
  • 使用负数或大于当前打开文件限制的数字。两者都保证永远不是有效的文件描述符。

标签: c linux mocking


【解决方案1】:

您可以创建一个模块并通过 ioctl() 或使用 proc 文件添加模拟文件描述符创建。文件描述符表仅存在于主管模式(在内核中)。我不明白如果不创建新的内核模块或重用恰好做你想做的事情的内核模块,你怎么能保留它。

详情请参阅内核源代码中的fdtable.h 和task_struct(进程描述符)中的files_struct

这是一个 proc 文件系统模块,可以做你想做的事情。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>

#define procfs_name "always_fail"

struct proc_dir_entry *proc_file;

int procfile_read(char *buffer,
          char **buffer_location,
          off_t offset, int buffer_length, int *eof, void *data)
{   
    printk(KERN_INFO "procfile_read (/proc/%s) called\n", procfs_name);

    return -1;
}

int init_module()
{
    proc_file = create_proc_entry(procfs_name, 0644, NULL);

    if (proc_file == NULL) {
        remove_proc_entry(procfs_name, &proc_root);
        printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",
               procfs_name);
        return -ENOMEM;
    }

    proc_file->read_proc     = procfile_read;
    proc_file->owner         = THIS_MODULE;
    proc_file->mode          = S_IFREG | S_IRUGO;
    proc_file->uid           = 0;
    proc_file->gid           = 0;
    proc_file->size          = 0;

    printk(KERN_INFO "/proc/%s created\n", procfs_name);    
    return 0;
}

void cleanup_module()
{
    remove_proc_entry(procfs_name, &proc_root);
    printk(KERN_INFO "/proc/%s removed\n", procfs_name);
}

阅读时应该打印类似的内容:

cat /proc/always_fail
cat: /proc/always_fail: File descriptor in bad state

另一种 hackish 方法是简单地使用 /dev/net/tun 假设你有它。它会在读取和写入时失败,但在打开时不会失败。

证明:

$ sudo strace cat /dev/net/tun 
execve("/bin/cat", ["cat", "/dev/net/tun"], [/* 18 vars */]) = 0
brk(0)                                  = 0x24c5000
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) = 0x7f83fed22000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=128879, ...}) = 0
mmap(NULL, 128879, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f83fed02000
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|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\37\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1848024, ...}) = 0
mmap(NULL, 3961912, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f83fe73a000
mprotect(0x7f83fe8f8000, 2093056, PROT_NONE) = 0
mmap(0x7f83feaf7000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7f83feaf7000
mmap(0x7f83feafd000, 17464, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f83feafd000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83fed01000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83fecff000
arch_prctl(ARCH_SET_FS, 0x7f83fecff740) = 0
mprotect(0x7f83feaf7000, 16384, PROT_READ) = 0
mprotect(0x60a000, 4096, PROT_READ)     = 0
mprotect(0x7f83fed24000, 4096, PROT_READ) = 0
munmap(0x7f83fed02000, 128879)          = 0
brk(0)                                  = 0x24c5000
brk(0x24e6000)                          = 0x24e6000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=7220496, ...}) = 0
mmap(NULL, 7220496, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f83fe057000
close(3)                                = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
**open("/dev/net/tun", O_RDONLY)          = 3**
fstat(3, {st_mode=S_IFCHR|S_ISVTX|0666, st_rdev=makedev(10, 200), ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
**read(3, 0x24c7000, 65536)               = -1 EBADFD (File descriptor in bad state)**
write(2, "cat: ", 5cat: )                    = 5
write(2, "/dev/net/tun", 12/dev/net/tun)            = 12
open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=2570, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83fed21000
read(4, "# Locale name alias data base.\n#"..., 4096) = 2570
read(4, "", 4096)                       = 0
close(4)                                = 0
munmap(0x7f83fed21000, 4096)            = 0
open("/usr/share/locale/en_CA/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en_CA/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
write(2, ": File descriptor in bad state", 30: File descriptor in bad state) = 30
write(2, "\n", 1
)                       = 1
**close(3)                                = 0**
close(1)                                = 0
close(2)                                = 0
exit_group(1)                           = ?
eric@ideapad:~$ sudo strace cat /dev/net/tun 
execve("/bin/cat", ["cat", "/dev/net/tun"], [/* 18 vars */]) = 0
brk(0)                                  = 0x109f000
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) = 0x7fdc6d3db000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=128879, ...}) = 0
mmap(NULL, 128879, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fdc6d3bb000
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|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\37\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1848024, ...}) = 0
mmap(NULL, 3961912, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fdc6cdf3000
mprotect(0x7fdc6cfb1000, 2093056, PROT_NONE) = 0
mmap(0x7fdc6d1b0000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7fdc6d1b0000
mmap(0x7fdc6d1b6000, 17464, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fdc6d1b6000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc6d3ba000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc6d3b8000
arch_prctl(ARCH_SET_FS, 0x7fdc6d3b8740) = 0
mprotect(0x7fdc6d1b0000, 16384, PROT_READ) = 0
mprotect(0x60a000, 4096, PROT_READ)     = 0
mprotect(0x7fdc6d3dd000, 4096, PROT_READ) = 0
munmap(0x7fdc6d3bb000, 128879)          = 0
brk(0)                                  = 0x109f000
brk(0x10c0000)                          = 0x10c0000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=7220496, ...}) = 0
mmap(NULL, 7220496, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fdc6c710000
close(3)                                = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
open("/dev/net/tun", O_RDONLY)          = 3
fstat(3, {st_mode=S_IFCHR|S_ISVTX|0666, st_rdev=makedev(10, 200), ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, 0x10a1000, 65536)               = -1 EBADFD (File descriptor in bad state)
write(2, "cat: ", 5cat: )                    = 5
write(2, "/dev/net/tun", 12/dev/net/tun)            = 12
open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=2570, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc6d3da000
read(4, "# Locale name alias data base.\n#"..., 4096) = 2570
read(4, "", 4096)                       = 0
close(4)                                = 0
munmap(0x7fdc6d3da000, 4096)            = 0
open("/usr/share/locale/en_CA/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en_CA/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
write(2, ": File descriptor in bad state", 30: File descriptor in bad state) = 30
write(2, "\n", 1
)                       = 1
close(3)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(1)                           = ?

【讨论】:

  • 这比使用/dev/null 有什么好处?
  • @R..:因为如果出于某种原因读/写绕过“模拟”库并实际执行系统调用,它会失败。使用/dev/null,读取给出EOF,写入总是成功。
【解决方案2】:

在 POSIX 上:

unsigned maxfd = sysconf(_SC_OPEN_MAX);
unsigned nulfd = maxfd + 1;

注意unsigned的使用,这样当sysconf()返回INT_MAX时我们不会溢出。

【讨论】:

  • 很酷,但是在程序运行时是否可以更改最大打开文件限制?
  • @VladLazarenko 是的。 And you can't do anything about it.
  • 呃,哦。到目前为止,看起来/dev/null 仍然是我最好的朋友。我在考虑一个“负”文件编号,但它违反了 POSIX 标准,许多应用程序可以简单地退出并拒绝使用它。
猜你喜欢
  • 2012-01-05
  • 2019-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
相关资源
最近更新 更多