tmpfs
Rev.1を表示中。最新版はこちら。
CONFIG_SHMEM(共有メモリ)カーネルオプションによって、tmpfsの実装は異なります。CONFIG_SHMEMが設定されていれば、固有の実装となりますが、そうでなければramfsのそれとなります。共有メモリは共通のファイルをプロセス間で、メモリマップすることで実装されていて、そのファイルをtmpfsに作成するものです。従ってtmpfsは本来、共有メモリを実現するためのファイルシステムです(たぶん)。CONFIG_TMPFSオプションがない場合、tmpfsがサポートされないのでなく、tmpfsのファイルオペレーションコールバック関数は設定されないということです。この時その容量に上限はありません。ただし、ユーザからtmpfsを参照することはできません。で、CONFIG_TMPFSオプションの場合は、ユーザから参照は可能となり、デフォルトで全メモリの半分となります。
#ifdef CONFIG_SHMEM static struct file_system_type shmem_fs_type = { .owner = THIS_MODULE, .name = "tmpfs", .mount = shmem_mount, .kill_sb = kill_litter_super, }; #else static struct file_system_type shmem_fs_type = { .name = "tmpfs", .mount = ramfs_mount, .kill_sb = kill_litter_super, }; #endif int shmem_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; struct dentry *root; struct shmem_sb_info *sbinfo; int err = -ENOMEM; sbinfo = kzalloc(max((int)sizeof(struct shmem_sb_info), L1_CACHE_BYTES), GFP_KERNEL); if (!sbinfo) return -ENOMEM; sbinfo->mode = S_IRWXUGO | S_ISVTX; sbinfo->uid = current_fsuid(); sbinfo->gid = current_fsgid(); sb->s_fs_info = sbinfo; #ifdef CONFIG_TMPFS if (!(sb->s_flags & MS_NOUSER)) { sbinfo->max_blocks = shmem_default_max_blocks(); sbinfo->max_inodes = shmem_default_max_inodes(); if (shmem_parse_options(data, sbinfo, false)) { err = -EINVAL; goto failed; } } sb->s_export_op = &shmem_export_ops; #else sb->s_flags |= MS_NOUSER; #endif spin_lock_init(&sbinfo->stat_lock); if (percpu_counter_init(&sbinfo->used_blocks, 0)) goto failed; sbinfo->free_inodes = sbinfo->max_inodes; sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = TMPFS_MAGIC; sb->s_op = &shmem_ops; sb->s_time_gran = 1; #ifdef CONFIG_TMPFS_XATTR sb->s_xattr = shmem_xattr_handlers; #endif #ifdef CONFIG_TMPFS_POSIX_ACL sb->s_flags |= MS_POSIXACL; #endif inode = shmem_get_inode(sb, NULL, S_IFDIR | sbinfo->mode, 0, VM_NORESERVE); if (!inode) goto failed; inode->i_uid = sbinfo->uid; inode->i_gid = sbinfo->gid; root = d_alloc_root(inode); if (!root) goto failed_iput; sb->s_root = root; return 0; failed_iput: iput(inode); failed: shmem_put_super(sb); return err; } static unsigned long shmem_default_max_blocks(void) { return totalram_pages / 2; }shmem_getpage_gf()は、tmpfsの読み書きで、対象のpageを取得する時にコールされます。find_lock_page()はファイルにマップされているpageキャッシュを取得し、radix_tree_exceptional_entry()でそれが、スワップされているかチェックします。スワップされているかどうかは、pageの1ビット目で識別しています。
スワップされているなら、radix_to_swp_entry()でスワップスロットを取得し、if (swap.val)なら、スワップされています。lookup_swap_cache()で、まずスワップキャッシュにあるかチェックします。なければスワップに書き出されていますので、shmem_swapin()でスワップキャッシュに読み込み、スワップキャッシュからこのpageをアンリストとして、set_page_dirty(page)としてswap_free()でこのスロットを削除し、pageを確保します。
find_lock_page()で取得できず、スワップされてないなら、新規ページと言うことです。shmem_alloc_page()でpageを取得します。なおshmem_add_to_page_cache()でスワップアウトキャッシュに設定することで、このpageがスワップ対象pageとしています。
static int shmem_getpage_gfp(struct inode *inode, pgoff_t index, struct page **pagep, enum sgp_type sgp, gfp_t gfp, int *fault_type) { struct address_space *mapping = inode->i_mapping; struct shmem_inode_info *info; struct shmem_sb_info *sbinfo; struct page *page; swp_entry_t swap; int error; int once = 0; if (index > (MAX_LFS_FILESIZE >> PAGE_CACHE_SHIFT)) return -EFBIG; repeat: swap.val = 0; page = find_lock_page(mapping, index); if (radix_tree_exceptional_entry(page)) { swap = radix_to_swp_entry(page); page = NULL; } if (sgp != SGP_WRITE && ((loff_t)index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) { error = -EINVAL; goto failed; } if (page || (sgp == SGP_READ && !swap.val)) { BUG_ON(page && !PageUptodate(page)); *pagep = page; return 0; } info = SHMEM_I(inode); sbinfo = SHMEM_SB(inode->i_sb); if (swap.val) { page = lookup_swap_cache(swap); if (!page) { if (fault_type) *fault_type |= VM_FAULT_MAJOR; page = shmem_swapin(swap, gfp, info, index); if (!page) { error = -ENOMEM; goto failed; } } lock_page(page); if (!PageUptodate(page)) { error = -EIO; goto failed; } wait_on_page_writeback(page); if (page->mapping) { if (page->mapping == mapping && page->index == index) goto done; error = -EEXIST; goto failed; } error = mem_cgroup_cache_charge(page, current->mm, gfp & GFP_RECLAIM_MASK); if (!error) error = shmem_add_to_page_cache(page, mapping, index, gfp, swp_to_radix_entry(swap)); if (error) goto failed; spin_lock(&info->lock); info->swapped--; shmem_recalc_inode(inode); spin_unlock(&info->lock); delete_from_swap_cache(page); set_page_dirty(page); swap_free(swap); } else { if (shmem_acct_block(info->flags)) { error = -ENOSPC; goto failed; } if (sbinfo->max_blocks) { if (percpu_counter_compare(&sbinfo->used_blocks, sbinfo->max_blocks) >= 0) { error = -ENOSPC; goto unacct; } percpu_counter_inc(&sbinfo->used_blocks); } page = shmem_alloc_page(gfp, info, index); if (!page) { error = -ENOMEM; goto decused; } SetPageSwapBacked(page); __set_page_locked(page); error = mem_cgroup_cache_charge(page, current->mm, gfp & GFP_RECLAIM_MASK); if (!error) error = shmem_add_to_page_cache(page, mapping, index, gfp, NULL); if (error) goto decused; lru_cache_add_anon(page); spin_lock(&info->lock); info->alloced++; inode->i_blocks += BLOCKS_PER_PAGE; shmem_recalc_inode(inode); spin_unlock(&info->lock); clear_highpage(page); flush_dcache_page(page); SetPageUptodate(page); if (sgp == SGP_DIRTY) set_page_dirty(page); } done: if (sgp != SGP_WRITE && ((loff_t)index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) { error = -EINVAL; goto trunc; } *pagep = page; return 0; : }tmpfsはramfsのように実デバイスをキャッシュとする、サイズ設定可能なファイルシステムです。しかもスワップ領域を使うことで、キャッシュとする実デバイスの物理メモリを超えてそのサイズが設定可能ということです。