6.2.3 The inode

ファイルシステムがマウントされると,スーパブロックが生成され,システムのルートinodeはinode構造体のi_mountから参照される.

struct inode {
    dev_t        i_dev;
    unsigned long    i_ino;
    umode_t        i_mode;
    nlink_t        i_nlink;
    uid_t        i_uid;
    gid_t        i_gid;
    dev_t        i_rdev;
    off_t        i_size;
    time_t        i_atime;
    time_t        i_mtime;
    time_t        i_ctime;
    unsigned long    i_blksize;
    unsigned long    i_blocks;
    unsigned long    i_version;
    struct semaphore i_sem;
    struct inode_operations * i_op;
    struct super_block * i_sb;
    struct wait_queue * i_wait;
    struct file_lock * i_flock;
    struct vm_area_struct * i_mmap;
    struct inode * i_next, * i_prev;
    struct inode * i_hash_next, * i_hash_prev;
    struct inode * i_bound_to, * i_bound_by;
    struct inode * i_mount;
    unsigned short i_count;
    unsigned short i_wcount;
    unsigned short i_flags;
    unsigned char i_lock;
    unsigned char i_dirt;
    unsigned char i_pipe;
    unsigned char i_sock;
    unsigned char i_seek;
    unsigned char i_update;
    union {
        struct pipe_inode_info pipe_i;
        struct minix_inode_info minix_i;
        struct ext_inode_info ext_i;
        struct ext2_inode_info ext2_i;
        struct hpfs_inode_info hpfs_i;
        struct msdos_inode_info msdos_i;
        struct umsdos_inode_info umsdos_i;
        struct iso_inode_info isofs_i;
        struct nfs_inode_info nfs_i;
        struct xiafs_inode_info xiafs_i;
        struct sysv_inode_info sysv_i;
        struct socket socket_i;
        void * generic_ip;
    } u;
};

メモリ内では,inodeは2つの方法で管理される.まず,first_inodeで始まる2重連結循環リストで管理され,エントリi_nextおよびi_prevを介してアクセスされる.

fs/inode.c

int fs_may_mount(dev_t dev)
{
    struct inode * inode, * next;
    int i;

    next = first_inode;
    for (i = nr_inodes ; i > 0 ; i--) {
        inode = next;
        next = inode->i_next;    /* clear_inode() はキューを変更する */
        if (inode->i_dev != dev)
            continue;
        if (inode->i_count || inode->i_dirt || inode->i_lock)
            return 0;
        clear_inode(inode);
    }
    return 1;
}

void clear_inode(struct inode * inode)
{
    struct wait_queue * wait;

    wait_on_inode(inode);
    remove_inode_hash(inode);
    remove_inode_free(inode);
    wait = ((volatile struct inode *) inode)->i_wait;
    if (inode->i_count)
        nr_free_inodes++;
    memset(inode,0,sizeof(*inode));
    ((volatile struct inode *) inode)->i_wait = wait;
    insert_inode_free(inode);
}

static inline void wait_on_inode(struct inode * inode)
{
    if (inode->i_lock)
        __wait_on_inode(inode);
}

/*
 * 「新しい」スケジューリングプリミティヴは,割り込みを無効にすることなく
 * 実行できる(実際のキューを更新すること以外は386命令だけで済む).
 * これは割り込みレイテンシのほうがはるかに良いはず.
 */
static void __wait_on_inode(struct inode * inode)
{
    struct wait_queue wait = { current, NULL };

    add_wait_queue(&inode->i_wait, &wait);
repeat:
    current->state = TASK_UNINTERRUPTIBLE;
    if (inode->i_lock) {
        schedule();
        goto repeat;
    }
    remove_wait_queue(&inode->i_wait, &wait);
    current->state = TASK_RUNNING;
}

static void remove_inode_hash(struct inode *inode)
{
    struct inode_hash_entry *h;
    h = hash(inode->i_dev, inode->i_ino);

    if (h->inode == inode)
        h->inode = inode->i_hash_next;
    if (inode->i_hash_next)
        inode->i_hash_next->i_hash_prev = inode->i_hash_prev;
    if (inode->i_hash_prev)
        inode->i_hash_prev->i_hash_next = inode->i_hash_next;
    inode->i_hash_prev = inode->i_hash_next = NULL;
}

static void remove_inode_free(struct inode *inode)
{
    if (first_inode == inode)
        first_inode = first_inode->i_next;
    if (inode->i_next)
        inode->i_next->i_prev = inode->i_prev;
    if (inode->i_prev)
        inode->i_prev->i_next = inode->i_next;
    inode->i_next = inode->i_prev = NULL;
}

static void insert_inode_free(struct inode *inode)
{
    inode->i_next = first_inode;
    inode->i_prev = first_inode->i_prev;
    inode->i_next->i_prev = inode;
    inode->i_prev->i_next = inode;
    first_inode = inode;
}

未使用のinodeはgrow_inode()関数によって生成される.すべてのinodeの1/4が開放され,NR_INODE(2048)以下であった場合,この関数は呼び出される.未使用のinodeの数と,利用可能なすべてのinodeの数は,それぞれ静的変数nr_freenr_inodeにある.

struct inode * get_empty_inode(void)
{
    struct inode * inode, * best;
    int i;

    if (nr_inodes < NR_INODE && nr_free_inodes < (nr_inodes >> 2))
        grow_inodes();

高速アクセスのために,inodeはハッシュテーブルhash_table[]にも格納されている.構成要素i_hash_nexti_hash_prevを使い,2重連結リストを介してコリジョンが処理される.任意のRN_IHASHエントリへのアクセスは,デバイス番号とinode番号を見ておこなわれる.

inodeを扱う関数はiget()namei()iput()である.

struct inode * __iget(struct super_block * sb, int nr, int crossmntp)
{
    static struct wait_queue * update_wait = NULL;
    struct inode_hash_entry * h;
    struct inode * inode;
    struct inode * empty = NULL;

    if (!sb)
        panic("VFS: iget with sb==NULL");
    h = hash(sb->s_dev, nr);
repeat:
    for (inode = h->inode; inode ; inode = inode->i_hash_next)
        if (inode->i_dev == sb->s_dev && inode->i_ino == nr)
            goto found_it;
    if (!empty) {
        h->updating++;
        empty = get_empty_inode();
        if (!--h->updating)
            wake_up(&update_wait);
        if (empty)
            goto repeat;
        return (NULL);
    }
    inode = empty;
    inode->i_sb = sb;
    inode->i_dev = sb->s_dev;
    inode->i_ino = nr;
    inode->i_flags = sb->s_flags;
    put_last_free(inode);
    insert_inode_hash(inode);
    read_inode(inode);
    goto return_it;

found_it:
    if (!inode->i_count)
        nr_free_inodes--;
    inode->i_count++;
    wait_on_inode(inode);
    if (inode->i_dev != sb->s_dev || inode->i_ino != nr) {
        printk("Whee.. inode changed from under us. Tell Linus\n");
        iput(inode);
        goto repeat;
    }
    if (crossmntp && inode->i_mount) {
        struct inode * tmp = inode->i_mount;
        tmp->i_count++;
        iput(inode);
        inode = tmp;
        wait_on_inode(inode);
    }
    if (empty)
        iput(empty);

return_it:
    while (h->updating)
        sleep_on(&update_wait);
    return inode;
}

void iput(struct inode * inode)
{
    if (!inode)
        return;
    wait_on_inode(inode);
    if (!inode->i_count) {
        printk("VFS: iput: trying to free free inode\n");
        printk("VFS: device %d/%d, inode %lu, mode=0%07o\n",
            MAJOR(inode->i_rdev), MINOR(inode->i_rdev),
                    inode->i_ino, inode->i_mode);
        return;
    }
    if (inode->i_pipe)
        wake_up_interruptible(&PIPE_WAIT(*inode));
repeat:
    if (inode->i_count>1) {
        inode->i_count--;
        return;
    }
    wake_up(&inode_wait);
    if (inode->i_pipe) {
        unsigned long page = (unsigned long) PIPE_BASE(*inode);
        PIPE_BASE(*inode) = NULL;
        free_page(page);
    }
    if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->put_inode) {
        inode->i_sb->s_op->put_inode(inode);
        if (!inode->i_nlink)
            return;
    }
    if (inode->i_dirt) {
        write_inode(inode);    /* we can sleep - so do again */
        wait_on_inode(inode);
        goto repeat;
    }
    inode->i_count--;
    if (inode->i_mmap) {
        printk("iput: inode %lu on device %d/%d still has mappings.\n",
            inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
        inode->i_mmap = NULL;
    }
    nr_free_inodes++;
    return;
}

fs/namei.c

/*
 * namei()はほとんどの単純なコマンドによって,指定されたファイル名の
 * inodeを取得するために使われる.openやlinkなどは別のルーチンを使うが,
 * chmodなどのためには十分である.
 */
int namei(const char * pathname, struct inode ** res_inode)
{
    int error;
    char * tmp;

    error = getname(pathname,&tmp);
    if (!error) {
        error = _namei(tmp,NULL,1,res_inode);
        putname(tmp);
    }
    return error;
}