tmpfs


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

static const struct file_operations shmem_file_operations = {
       .mmap           = shmem_mmap,
#ifdef CONFIG_TMPFS
       .llseek         = generic_file_llseek,
       .read           = do_sync_read,
       .write          = do_sync_write,
       .aio_read       = shmem_file_aio_read,
       .aio_write      = generic_file_aio_write,
       .fsync          = noop_fsync,
       .splice_read    = shmem_file_splice_read,
       .splice_write   = generic_file_splice_write,
#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のように実デバイスをキャッシュとする、サイズ設定可能なファイルシステムです。しかもスワップ領域を使うことで、キャッシュとする実デバイスの物理メモリを超えてそのサイズが設定可能ということです。

補足

スワップアウト/インする時、直接スワップスロットから読み書きいたしません。in/out競合のスラッジングを避けるため、いったんキャッシュとしてスワップアウト/インpageを掛かるリストに繋いだのち、実際のデバイスへ読み書きするような実装となっています。なお、スワップとするメモリ管理はOSの根幹をなすエレメントだけに、非常に込み入っていて、今2つも3っつも見えかねています・・・。

最終更新 2014/02/22 00:07:54 - north
(2014/02/21 23:14:18 作成)


検索

アクセス数
3585531
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。