無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

共有メモリ(shmget)


共有メモリの実態はファイルでした。すなわち共有メモリを新規に取得する場合、そのファイルを作成する事になります。カーネルにとってファイルを作成するというのは、inodeを作成する事に他なりません。

shmgetシステムコールは第一引数のkeyがIPC_PRIVATEの時に、新規に共有メモリが割り当てられます。この割り当ての処理を行うのが、struct ipc_ops shm_opsでセットするnewseg()です。ipcget()でkey=IPC_PRIVATEなら、newseg()がコールされます。
SYSCALL_DEFINE3(shmget, key_t, key, size_t, size, int, shmflg)
{
       struct ipc_namespace *ns;
       struct ipc_ops shm_ops;
       struct ipc_params shm_params;

       ns = current->nsproxy->ipc_ns;

       shm_ops.getnew = newseg;
       shm_ops.associate = shm_security;
       shm_ops.more_checks = shm_more_checks;

       shm_params.key = key;
       shm_params.flg = shmflg;
       shm_params.u.size = size;

       return ipcget(ns, &shm_ids(ns), &shm_ops, &shm_params);
}
int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFTで、割り当てサイズのページ数を求め、サイズ/ページ数のチェックを行います。その時の条件は以下のようになっています。すなわち1つの割り当て可能の共有メモリのサイズは0より大きく、0x2000000小さくて、全ての共有メモリとして割り当てたページ数はSHMALLを超えてはならないと言うことです。
#define SHMALL (SHMMAX/PAGE_SIZE*(SHMMNI/16))について言えば、
0x2000000/4096*4096/16=0x222222ページ(ざっと8G以上となります。なお共有メモリはスワップ対象です。)
#define SHMMAX 0x2000000                 /* max shared seg size (bytes) */
#define SHMMIN 1                         /* min shared seg size (bytes) */
#define SHMMNI 4096                      /* max num of segs system wide */
#ifdef __KERNEL__
#define SHMALL (SHMMAX/PAGE_SIZE*(SHMMNI/16)) /* max shm system wide (pages)  */
#else
#define SHMALL (SHMMAX/getpagesize()*(SHMMNI/16))
#endif

void shm_init_ns(struct ipc_namespace *ns)
{
       ns->shm_ctlmax = SHMMAX;
       ns->shm_ctlall = SHMALL;
       ns->shm_ctlmni = SHMMNI;
       ns->shm_tot = 0;
       ipc_init_ids(&ns->ids[IPC_SHM_IDS]);
}
サイズに掛かるチェックがOKなら、共有メモリの実態と言うべきstruct shmid_kernel *shpに各種パラメタを設定し、hugetlb_file_setup()/shmem_file_setup()をコールします。この関数で共有メモリのファイルを作成し、そのファイル構造体をshp->shm_fileにセットします。

次にstruct shmid_kernel *shpをipc_addi()で、プロセスのネームスペース下のipcネームスペース下の共有メモリを管理している、struct ipc_idsに登録します。ここでの登録はradix-treeのような階層構造をなしていて、ちょっとごちゃごちゃしています。

ipcスロットに登録できたら、struct shmid_kernel *shpに他のもろもろの設定を施してお終いです。
static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
{
       key_t key = params->key;
       int shmflg = params->flg;
       size_t size = params->u.size;
       int error;
       struct shmid_kernel *shp;
       int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
       struct file * file;
       char name[13];
       int id;

       if (size < SHMMIN || size > ns->shm_ctlmax)
               return -EINVAL;

       if (ns->shm_tot + numpages > ns->shm_ctlall)
               return -ENOSPC;

       shp = ipc_rcu_alloc(sizeof(*shp));
       if (!shp)
               return -ENOMEM;

       shp->shm_perm.key = key;
       shp->shm_perm.mode = (shmflg & S_IRWXUGO);
       shp->mlock_user = NULL;

       shp->shm_perm.security = NULL;
       error = security_shm_alloc(shp);
       if (error) {
               ipc_rcu_putref(shp);
               return error;
       }

       sprintf (name, "SYSV%08x", key);
       if (shmflg & SHM_HUGETLB) {
               file = hugetlb_file_setup(name, size);
               shp->mlock_user = current->user;
       } else {
               int acctflag = VM_ACCOUNT;
               if  ((shmflg & SHM_NORESERVE) &&
                               sysctl_overcommit_memory != OVERCOMMIT_NEVER)
                       acctflag = 0;
               file = shmem_file_setup(name, size, acctflag);
       }
       error = PTR_ERR(file);
       if (IS_ERR(file))
               goto no_file;

       id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
       if (id < 0) {
               error = id;
               goto no_id;
       }

       shp->shm_cprid = task_tgid_vnr(current);
       shp->shm_lprid = 0;
       shp->shm_atim = shp->shm_dtim = 0;
       shp->shm_ctim = get_seconds();
       shp->shm_segsz = size;
       shp->shm_nattch = 0;
       shp->shm_file = file;
       file->f_dentry->d_inode->i_ino = shp->shm_perm.id;

       ns->shm_tot += numpages;
       error = shp->shm_perm.id;
       shm_unlock(shp);
       return error;

no_id:
       fput(file);
no_file:
       security_shm_free(shp);
       ipc_rcu_putref(shp);
       return error;
}

補足

return値がipcスロットに登録できた場合と、そうでない場合の値の取得が異なっています。ipc_addid()がエラーの場合、その返り値idをerror = idとして、return値としていますが、登録できた場合、error = shp->shm_perm.idをreturn値としています。ipc_addid()で返してくる値はipcのスロット位置なのですが、ipc_addid()内ではこのスロット位置にオフセットを加えたもの(SEQ_MULTIPLIER * seq + id)を、shp->shm_perm.idに設定しています。seqはipcスロットを割り当て毎に増加していきます。アプリケーションこの値を共有メモリ識別子として使うわけですが、近寄った値故、アプリケーションで間違って使わないようにとの配慮だそうです。

shmem_file_setu()では、共有メモリのファイルを作成することにありますが、共有メモリだから特別の処理はないようです。なお作成されるvfsmntはshm_mntで、これはinit_tmpfs(void)で、
shm_mnt = vfs_kern_mount(&tmpfs_fs_type, MS_NOUSER, tmpfs_fs_type.name, NULL);
としてtmpfsにマウントしています。
struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags)
{
       int error;
       struct file *file;
       struct inode *inode;
       struct dentry *dentry, *root;
       struct qstr this;

  :
       this.name = name;
       this.len = strlen(name);
       this.hash = 0; /* will go */
       root = shm_mnt->mnt_root;
       dentry = d_alloc(root, &this);
       if (!dentry)
               goto put_memory;

       error = -ENFILE;
       file = get_empty_filp();
       if (!file)
               goto put_dentry;

       error = -ENOSPC;
       inode = shmem_get_inode(root->d_sb, S_IFREG | S_IRWXUGO, 0);
       if (!inode)
               goto close_file;

       SHMEM_I(inode)->flags = flags & VM_ACCOUNT;
       d_instantiate(dentry, inode);
       inode->i_size = size;
       inode->i_nlink = 0;     /* It is unlinked */
       init_file(file, shm_mnt, dentry, FMODE_WRITE | FMODE_READ,
                       &shmem_file_operations);
       return file;
  :
}

最終更新 2011/09/01 18:32:11 - north
(2011/09/01 18:32:11 作成)