What is an anonymous inode in Linux?

前端 未结 2 385
挽巷
挽巷 2020-12-05 01:09

I made a google search about \"anonymous inode\" and it seems it\'s related to epoll ... but what actually is it?

相关标签:
2条回答
  • 2020-12-05 01:29

    At least in some contexts, an anonymous inode is an inode without an attached directory entry. The easiest way to create such an inode is as such:

    int fd = open( "/tmp/file", O_CREAT | O_RDWR, 0666 );
    unlink( "/tmp/file" );
    // Note that the descriptor fd now points to an inode that has no filesystem entry; you
    // can still write to it, fstat() it, etc. but you can't find it in the filesystem.
    
    0 讨论(0)
  • 2020-12-05 01:35

    open with O_TMPFILE

    This would be a good definition of anonymous inode: it creates an inode inside a given directory without any name, which does not appear at all with ls.

    Then when you close the descriptor the file is deleted.

    It was added in Linux 3.11.

    main.c

    #define _GNU_SOURCE
    #include <assert.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int main(void) {
        char buf[]  = { 'a', 'b', 'c', 'd' };
        char buf2[]  = { 'e', 'f', 'g', 'h' };
        int f, ret;
        size_t off;
    
        /* write */
        f = open(".", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
        ret = write(f, buf, sizeof(buf));
    
        /* Interactivelly check if anything changed on directory. It hasn't. */
        /*puts("hit enter to continue");*/
        /*getchar();*/
    
        /* read */
        lseek(f, 0, SEEK_SET);
        off = 0;
        while ((ret = read(f, buf2 + off, sizeof(buf) - off))) {
            off += ret;
        }
        close(f);
        assert(!memcmp(buf, buf2, sizeof(buf)));
    
        return EXIT_SUCCESS;
    }
    

    GitHub upstream.

    Compile and run:

    gcc -o main.out -std=c99 -Wall -Wextra -pedantic  main.c
    ./main.out
    

    This is therefore essentially the optimal way of implementing temporary files: How to create a temporary text file in C++? as it can:

    • always immediately find a free name without us having to loop over filenames (no name is needed!)
    • and has automatic deletion

    Compare that to a more manual method for directories as the one shown at: How to create a temporary directory in C++?

    Tested in Ubuntu 17.04, Linux 4.10, glibc 2.24.

    How O_TMPFILE looks like in /proc/PID/fd

    We can examine it quickly with:

    #define _GNU_SOURCE
    #include <fcntl.h>
    #include <inttypes.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int main(void) {
        int f = open(".", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
        struct stat s;
        fstat(f, &s);
        printf("pid %jd\n", (intmax_t)getpid());
        printf("inode %jd\n", (intmax_t)s.st_ino);
        getchar();
    }
    

    then for a sample output:

    pid 15952     
    inode 44698689
    

    we do:

    ls -l /proc/15952/fd
    

    and that contains:

    3 -> '/home/ciro/test/#44698689 (deleted)'
    

    which shows us how to find temporary files for a given process and see their inode and inode parent directory.

    anon_inode_getfd Linux kernel function

    If you are dealing with kernel modules, this is a likely definition.

    You call it like:

    fd = anon_inode_getfd("random", &fops_anon, NULL, O_RDONLY | O_CLOEXEC);
    

    and return fd to user, e.g. from an ioctl.

    Now the user has an fd with associated arbitrary file_operations and inode, and when that fd is closed, everything is freed.

    This method is useful e.g. if you want to have multiple read syscalls, but don't want to create multiple device files, which further pollutes /dev: you just create extra ioctls instead.

    Minimal runnable example with QEMU Buildroot:

    • https://github.com/cirosantilli/linux-kernel-module-cheat/blob/de6c179fc0cf122789f0fe85cc69b847a1f4fe9c/kernel_module/anonymous_inode.c
    • https://github.com/cirosantilli/linux-kernel-module-cheat/blob/de6c179fc0cf122789f0fe85cc69b847a1f4fe9c/rootfs_overlay/anonymous_inode.sh
    #include <asm/uaccess.h> /* copy_from_user, copy_to_user */
    #include <linux/anon_inodes.h>
    #include <linux/debugfs.h>
    #include <linux/errno.h> /* EFAULT */
    #include <linux/fs.h>
    #include <linux/jiffies.h>
    #include <linux/kernel.h> /* min */
    #include <linux/module.h>
    #include <linux/printk.h> /* printk */
    
    #include "anonymous_inode.h"
    
    MODULE_LICENSE("GPL");
    
    static struct dentry *dir;
    
    static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
    {
        char kbuf[1024];
        size_t ret;
    
        ret = snprintf(kbuf, sizeof(kbuf), "%llu", (unsigned long long)jiffies);
        if (copy_to_user(buf, kbuf, ret)) {
            ret = -EFAULT;
        }
        return ret;
    }
    
    static const struct file_operations fops_anon = {
        .read = read,
    };
    
    static long unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long argp)
    {
        int fd;
    
        switch (cmd) {
            case LKMC_ANONYMOUS_INODE_GET_FD:
                fd = anon_inode_getfd(
                    "random",
                    &fops_anon,
                    NULL,
                    O_RDONLY | O_CLOEXEC
                );
                if (copy_to_user((void __user *)argp, &fd, sizeof(fd))) {
                    return -EFAULT;
                }
            break;
            default:
                return -EINVAL;
            break;
        }
        return 0;
    }
    
    static const struct file_operations fops_ioctl = {
        .owner = THIS_MODULE,
        .unlocked_ioctl = unlocked_ioctl
    };
    
    static int myinit(void)
    {
        dir = debugfs_create_dir("lkmc_anonymous_inode", 0);
        debugfs_create_file("f", 0, dir, NULL, &fops_ioctl);
        return 0;
    }
    
    static void myexit(void)
    {
        debugfs_remove_recursive(dir);
    }
    
    module_init(myinit)
    module_exit(myexit)
    

    anon_inode in /proc/PID/fd

    These are pipes and sockets, see: https://unix.stackexchange.com/questions/463548/what-is-anon-inode-in-the-output-of-ls-l

    0 讨论(0)
提交回复
热议问题