今日はshmat()とshmdt()を読んでみます。 shmat()はshmget()、shmdt()、shmctl()と違ってsyscall_defineは使われていなくて、ipc()からdo_shmat()を呼び出しています。
では、do_shmat()を見ていきます。
最初は変数定義が並んでいるだけなので飛ばします。
1050 long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr, 1051 unsigned long shmlba) 1052 { 1053 struct shmid_kernel *shp; 1054 unsigned long addr; 1055 unsigned long size; 1056 struct file *file; 1057 int err; 1058 unsigned long flags; 1059 unsigned long prot; 1060 int acc_mode; 1061 struct ipc_namespace *ns; 1062 struct shm_file_data *sfd; 1063 struct path path; 1064 fmode_t f_mode; 1065 unsigned long populate = 0;
最初はユーザーランドから渡された引数のチェックです。最初のifはshat(2)の最初の引数shmidのチェックです。
1067 err = -EINVAL; 1068 if (shmid < 0) 1069 goto out;
shmat(2)の第二引数shmaddrがNULLでなかった場合の処理です。中括弧内はshmflgの値に応じたエラーチェックとフラグの設定で、この辺はmanにあるとおりですね。
1070 else if ((addr = (ulong)shmaddr)) { 1071 if (addr & (shmlba - 1)) { 1072 if (shmflg & SHM_RND) 1073 addr &= ~(shmlba - 1); /* round down */ 1074 else 1075 #ifndef __ARCH_FORCE_SHMLBA 1076 if (addr & ~PAGE_MASK) 1077 #endif 1078 goto out; 1079 } 1080 flags = MAP_SHARED | MAP_FIXED;
上記の条件に当てはまらなかった場合はここです。最初のif文はSHM_REMAPを使う場合はshmaddrがNULLではダメというmanの通りのチェックです。
1081 } else { 1082 if ((shmflg & SHM_REMAP)) 1083 goto out; 1084 1085 flags = MAP_SHARED; 1086 }
ここまでの処理で設定したflags変数は後ほどmmpaするときに出てきます。 次に進んでこの部分ですが、ここもshmflgの内容に応じたフラグ類の設定をするだけです。
1088 if (shmflg & SHM_RDONLY) { 1089 prot = PROT_READ; 1090 acc_mode = S_IRUGO; 1091 f_mode = FMODE_READ; 1092 } else { 1093 prot = PROT_READ | PROT_WRITE; 1094 acc_mode = S_IRUGO | S_IWUGO; 1095 f_mode = FMODE_READ | FMODE_WRITE; 1096 } 1097 if (shmflg & SHM_EXEC) { 1098 prot |= PROT_EXEC; 1099 acc_mode |= S_IXUGO; 1100 }
まずカレントタスクが所属するipc namespaceの取得をします。
1102 /* 1103 * We cannot rely on the fs check since SYSV IPC does have an 1104 * additional creator id... 1105 */ 1106 ns = current->nsproxy->ipc_ns;
そして、このnamespaceの中にshmidに該当する共有メモリのオブジェクト(struct shmid_kernel)を取得します。
1107 rcu_read_lock(); 1108 shp = shm_obtain_object_check(ns, shmid); 1109 if (IS_ERR(shp)) { 1110 err = PTR_ERR(shp); 1111 goto out_unlock; 1112 }
ここはオブジェクトに設定されているパーミッションとshmat()でshmflgに設定した内容に応じて決定したパーミッションとの比較です。
1114 err = -EACCES; 1115 if (ipcperms(ns, &shp->shm_perm, acc_mode)) 1116 goto out_unlock;
security_XXXはselinux等のセキュリティ機構が使うので飛ばします。
1118 err = security_shm_shmat(shp, shmaddr, shmflg); 1119 if (err) 1120 goto out_unlock;
ロックを取得して次のipc_valid_object()ですが、これはコメントのとおりで他の誰かがこのオブジェクトの削除中の場合はattachできないのでエラーになります。
1122 ipc_lock_object(&shp->shm_perm); 1123 1124 /* check if shm_destroy() is tearing down shp */ 1125 if (!ipc_valid_object(&shp->shm_perm)) { 1126 ipc_unlock_object(&shp->shm_perm); 1127 err = -EIDRM; 1128 goto out_unlock; 1129 }
ここからは擬似ファイルに対する操作が入ってきます。まずpathですがこれはstruct pathです。ここでshmid_kernelより擬似ファイルのパスを取得してpath_get()でマウントポイントへの参照カウントとdentryの参照カウントを増やします。そして、shm_nattchのインクリメントでこの共有メモリの参照が増えます。sizeはファイルのサイズです。
1131 path = shp->shm_file->f_path; 1132 path_get(&path); 1133 shp->shm_nattch++; 1134 size = i_size_read(path.dentry->d_inode);
今まで取ったロックを解除します。
1135 ipc_unlock_object(&shp->shm_perm); 1136 rcu_read_unlock();
ここは見たままですね。sfdはstruct shm_file_dataという構造体です。
1138 err = -ENOMEM; 1139 sfd = kzalloc(sizeof(*sfd), GFP_KERNEL); 1140 if (!sfd) { 1141 path_put(&path); 1142 goto out_nattch; 1143 }
この構造体はipc/shm.cにて定義されているローカルな構造体で、このような中身になってます。
50 struct shm_file_data { 51 int id; 52 struct ipc_namespace *ns; 53 struct file *file; 54 const struct vm_operations_struct *vm_ops; 55 };
do_shmat()に戻って、次はfile(struct file)をアローケートします。alloc_file()はfile構造体の領域を確保して初期化して返してくれる関数です。
1145 file = alloc_file(&path, f_mode, 1146 is_file_hugepages(shp->shm_file) ? 1147 &shm_file_operations_huge : 1148 &shm_file_operations); 1149 err = PTR_ERR(file); 1150 if (IS_ERR(file)) { 1151 kfree(sfd); 1152 path_put(&path); 1153 goto out_nattch; 1154 }
ここは先ほどkzallocしたsfdの設定です。sfdはstruct fileのprivate_dataに代入されます。そして、sfdのメンバ変数を設定してきます。
1156 file->private_data = sfd; 1157 file->f_mapping = shp->shm_file->f_mapping; 1158 sfd->id = shp->shm_perm.id; 1159 sfd->ns = get_ipc_ns(ns); 1160 sfd->file = shp->shm_file; 1161 sfd->vm_ops = NULL;
ここは飛ばします。
1163 err = security_mmap_file(file, prot, flags); 1164 if (err) 1165 goto out_fput;
ここからmmapを行うための処理になります。まずはロックを取ります。
1167 down_write(¤t->mm->mmap_sem);
ここはshmat()でshmaddrにNULL以外を渡し、フラグにSHM_REMAPが付いていなかった場合ですね。find_vma_intersection()を呼んでいますがここでは適切な領域があるかだけのチェックをしていて戻り値は使いません。
1168 if (addr && !(shmflg & SHM_REMAP)) { 1169 err = -EINVAL; 1170 if (addr + size < addr) 1171 goto invalid; 1172 1173 if (find_vma_intersection(current->mm, addr, addr + size)) 1174 goto invalid; 1175 /* 1176 * If shm segment goes below stack, make sure there is some 1177 * space left for the stack to grow (at least 4 pages). 1178 */ 1179 if (addr < current->mm->start_stack && 1180 addr > current->mm->start_stack - size - PAGE_SIZE * 5) 1181 goto invalid; 1182 }
do_mmap_pgoff()でmmapします。*raddrにaddrを代入しておきます。
1184 addr = do_mmap_pgoff(file, addr, size, prot, flags, 0, &populate); 1185 *raddr = addr; 1186 err = 0; 1187 if (IS_ERR_VALUE(addr)) 1188 err = (long)addr;
ここからはdo_shmat()終了処理に入っていきます。mmpaするのに取ったロックを解除
1189 invalid: 1190 up_write(¤t->mm->mmap_sem);
do_mmap_pgoff()に渡した変数のpopulateが0じゃなければmm_populate()を呼ぶ必要があるようです。
1191 if (populate) 1192 mm_populate(addr, populate); 1193
struct fileが不要になったので解放。
1194 out_fput: 1195 fput(file); 1196
namespaceのロックを解除と別のロックの取得。
1197 out_nattch: 1198 down_write(&shm_ids(ns).rwsem); 1199 shp = shm_lock(ns, shmid); 1200 BUG_ON(IS_ERR(shp));
shm_nattchの値をデクリメントして元に戻す。
1201 shp->shm_nattch--;
shm_may_destroy()を呼び、結果として共有メモリをdestroyする必要があればdestroyする。
1202 if (shm_may_destroy(ns, shp)) 1203 shm_destroy(ns, shp); 1204 else 1205 shm_unlock(shp);
さっき取ったロックを解除して終了。ユーザーランドにはアドレスが返りますがこれはdo_mmap_pgoff()の返り値になります。do_mmap_pgoff()の呼び出し後にraddr = addr;としてraddrに値を代入していたところがそれです。
1206 up_write(&shm_ids(ns).rwsem); 1207 return err; 1208
ここからは途中でエラーが発生時の処理。
1209 out_unlock: 1210 rcu_read_unlock(); 1211 out: 1212 return err; 1213 }
ここまでがshmat()の実装で、ここからはshmdt()を見てみましょう。 SYSCALL_DEFINE1が付いていますがipc()から呼ばれます。
1231 SYSCALL_DEFINE1(shmdt, char __user *, shmaddr) 1232 { 1233 struct mm_struct *mm = current->mm; 1234 struct vm_area_struct *vma; 1235 unsigned long addr = (unsigned long)shmaddr; 1236 int retval = -EINVAL; 1237 #ifdef CONFIG_MMU 1238 loff_t size = 0; 1239 struct vm_area_struct *next; 1240 #endif 1241
最初にdetachするアドレスのチェックをして、問題なければmmap用のロックを取ります。
1242 if (addr & ~PAGE_MASK) 1243 return retval; 1244 1245 down_write(&mm->mmap_sem);
ここは結構長めのコメントが書いてあるのですが、それを飛ばしてコードだけ見ると該当アドレスに対するvmaを取得します。
1267 vma = find_vma(mm, addr);
そうしたら後はdo_munmap()でmmapした領域を解放してきます。ここではアドレスだけでなく、vmaに設定されているオペレーションが共有メモリのオペレーションかというのもチェック対象になってますね。
1270 while (vma) { 1271 next = vma->vm_next; 1272 1273 /* 1274 * Check if the starting address would match, i.e. it's 1275 * a fragment created by mprotect() and/or munmap(), or it 1276 * otherwise it starts at this address with no hassles. 1277 */ 1278 if ((vma->vm_ops == &shm_vm_ops) && 1279 (vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) { 1280 1281 1282 size = file_inode(vma->vm_file)->i_size; 1283 do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start); 1284 /* 1285 * We discovered the size of the shm segment, so 1286 * break out of here and fall through to the next 1287 * loop that uses the size information to stop 1288 * searching for matching vma's. 1289 */ 1290 retval = 0; 1291 vma = next; 1292 break; 1293 } 1294 vma = next; 1295 }
引き続きdo_munmapの処理が入ります。vma周りはよくわかっていないので今はこれが限度です\(^o^)/
1297 /* 1298 * We need look no further than the maximum address a fragment 1299 * could possibly have landed at. Also cast things to loff_t to 1300 * prevent overflows and make comparisons vs. equal-width types. 1301 */ 1302 size = PAGE_ALIGN(size); 1303 while (vma && (loff_t)(vma->vm_end - addr) <= size) { 1304 next = vma->vm_next; 1305 1306 /* finding a matching vma now does not alter retval */ 1307 if ((vma->vm_ops == &shm_vm_ops) && 1308 (vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) 1309 1310 do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start); 1311 vma = next; 1312 }
最後はロックを解放して終わりです。
1324 up_write(&mm->mmap_sem); 1325 return retval; 1326 }
えー、shmatはmmapするだけだったんですが、shmdtは単にmmpaしたアドレスをunmmapするだけではないので難しいですね(´・ω・`)
- 作者: Bruce Molay,長尾高弘
- 出版社/メーカー: アスキー・メディアワークス
- 発売日: 2008/04/21
- メディア: 大型本
- 購入: 9人 クリック: 280回
- この商品を含むブログ (47件) を見る