为什么/dev/shm中看不到shmget创建的内存文件及其它(下)
一、如何看到sysV IPC shm文件名
1、file_system_type.get_sb修改
正如上篇所說,在用戶態無法看到可shm文件的名稱,不同的掛載點使用不同的dentry,而對于tmpfs文件,它的readdir的系統實現就是通過dcache_readdir函數來實現的,但是這個dentry的不同本質上是由于
static int shmem_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
return get_sb_nodev(fs_type, flags, data, shmem_fill_super, mnt);
}
函數實現的,它會為每次掛載分配一個新的super_block結構,這個實現在get_sb_nodev中。作為對比我們,proc文件無論掛接在哪里,它看到的內容都是一致的,所以可以以proc文件系統為例進行修改,將這里的shmem_get_sub修改為如下形式
static int shmem_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
returnget_sb_single(fs_type, flags, data, shmem_fill_super, mnt);
}
這里注意到一個細節,那就是sys_mount--->>>do_new_mount--->>do_kern_mount---->>>vfs_kern_mount--->>alloc_vfsmnt這個調用連中,vfsmount結構是始終分配的,每次掛載都將會分配一個獨立的vfsmount結構,而所謂的single_sb,它只是說它的super_block是唯一的,進一步說,這些vfsmount結構中的mnt_root指針指向的位置是相同的。然后在path_lookup--->>>do_path_lookup--->>>link_path_walk---->>__link_path_walk--->>>do_lookup--->>>__follow_mount--->>lookup_mnt
這條路徑中將會返回這個vfsmount結構的mnt_root作為新的文件夾(這一點在__follow_mount函數中的path->dentry = dget(mounted->mnt_root)完成)。
2、修改內核掛接屬性
進行上面修改之后,在用戶態通過
mount -t tmpfs none myshm
掛載,會提示參數錯誤,這個地方時內核在掛載內核使用的shm_mnt的時候加了選項限制不能從用戶態掛載,所以此處也要做相應修改。
linux-2.6.21mmshmem.c
static int __init init_tmpfs(void)
shm_mnt = vfs_kern_mount(&tmpfs_fs_type,MS_NOUSER,刪除該選項,從而可以在用戶態掛載文件。
tmpfs_fs_type.name, NULL);
3、修改dentry DCACHE_UNHASHED 屬性
做上面修改之后,用戶態可以掛載文件,在不同掛接點中看到的內容一致,也就是說不同掛接點均可以看到,但是遺憾的時候通過shmget隱形創建的文件依然不可見,此時再次跟蹤,發現問題所在。在
linux-2.6.21mmshmem.c
struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags)
{
……
dentry = d_alloc(root, &this);
……
}
在d_alloc中創建的dentry項剛開始是出于刪除狀態(也就是DCACHE_UNHASED狀態),這個狀態也就是內存文件的特征,它可以在內存中的dentry結構引用計數降低為零的時候從內存中直接釋放,這也正是tmpfs中"tmp"屬性的體現。偏偏在執行文件夾內容顯示的dcache_readdir中:
for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
struct dentry *next;
next = list_entry(p, struct dentry, d_u.d_child);
if (d_unhashed(next)|| !next->d_inode)這里對于出于DCACHE_UNHASHED狀態的目錄項不予顯示,所以內核中掛接點依然無法看到。
continue;
對于通常的實體文件系統(例如ext2,),它們在調用d_alloc之后一般要通過d_splice_alias--->>>d_rehash--->>_d_rehash
static void __d_rehash(struct dentry * entry, struct hlist_head *list)
{
entry->d_flags &= ~DCACHE_UNHASHED;
hlist_add_head_rcu(&entry->d_hash, list);
}
在這里通過該操作來清除對于文件的刪除狀態,從而讓它在內存中生效。由于shmem_file_setup并沒有代勞執行這個操作,所以我們就不按套路出牌,強制在shmem_setup_file函數中,新分配的dentry結構中清除這個標志。
經過這么一番折騰,用戶態終于可以看到內核創建的文件了,由于我是通過IPC_PRIVATE創建了多個共享內存,所以在該文件夾下可以看到很多個同名的文件,它們都是“SYSV00000000”,這也說明一個問題,那就是并非所有的文件系統都不能在同一個文件夾下有重名文件,文件系統的限制只是為了避免二義性。
二、為什么do_shmat中每次會分配一個struct file結構
可以看到,在newseg函數中,已經通過shmem_setup_file分配了一個struct file結構,但是在每次執行shmat的時候還會再次分配一個結構,這個結構是不是冗余的呢?后來我看了一下2.4.37的內核版本,在sys_shmat調用中的確是沒有分配這個結構的,使用的就是newseg中創建的那個file結構。所以為什么在新的版本中必須要再次分配還真是不太容易理解。但是看一下兩個文件的差別,可以看新的版本的shm_file_operations定義為
static const struct file_operations shm_file_operations = {
.mmap = shm_mmap,
.fsync = shm_fsync,
.release = shm_release,
.get_unmapped_area = shm_get_unmapped_area,
};
而2.4版本中對應內容為
static struct file_operations shm_file_operations = {
mmap: shm_mmap
};
可見新的版本中定義了一些新的file_operations接口。并且其中還有一個判斷使用到了這個結構
int is_file_shm_hugepages(struct file *file)
{
int ret = 0;
if (file->f_op == &shm_file_operations) {
struct shm_file_data *sfd;
sfd = shm_file_data(file);
ret = is_file_hugepages(sfd->file);
}
return ret;
}
所以猜測這里是為了支持hugepages功能而添加的一個功能?不管如何,這個都是一個猜測了,沒有實作,也算是一個疑案吧,知道的同學請指教。
三、為什么要定義shm_vm_ops結構中的open/close接口
這個是一個比較特殊的例子,因為它在mmap的時候定義了vm_operations_struct結構中的open和close接口,這兩個接口的調用位置分別為
copy_process-->>copy_mm--->>>dup_mm--->>>dup_mmap
if (tmp->vm_ops && tmp->vm_ops->open)
tmp->vm_ops->open(tmp);
和
do_exit---->>>exit_mm---->>>mmput--->>exit_mmap--->>remove_vma
if (vma->vm_ops && vma->vm_ops->close)
vma->vm_ops->close(vma);
在通過shmget創建一個tmpfs文件之后,這個文件不論是否執行shmat,這個文件是始終存在的,刪除這個文件的方法就是通過shmctl RMID的方法來刪除這個文件。但是這里有一個比較奇特但是又比較典型的策略,就是當一個資源被多個實例共享時,如果該資源被刪除,那么刪除并不馬上執行,而只是減少引用計數,只有當計數值降低為零的時候才真正執行刪除操作。這一點在文件的刪除上比較常見和典型。
現在假設說一個shm被通過shmctl刪除,內核只是對這個文件做了一個標記SHM_DEST屬性,而真正的觸發則只有框架來支持,也就是說這里只能以“回調”的形式注冊給系統,從而在引用附加和移除的時候得到通知并進行計算,進而根據計算結果做出可能的刪除操作。
四、shm/shmem文件何時釋放
1、shm文件
這里所說的shm文件是在shmat中通過file = get_empty_filp();分配的struct file結構。它的示范同樣是由mmap框架來完成。在前一節說vm_operations_struct的open/close函數的附近,各自都有對vma區間是否有文件映射的判斷,如果是文件映射,會分別遞增和遞減這個文件的引用次數。
dup_mmap函數中為
if (file) {
struct inode *inode = file->f_path.dentry->d_inode;
get_file(file);
remove_vma函數中
if (vma->vm_file)
fput(vma->vm_file);
然后在
fput--->>__fput
if (file->f_op && file->f_op->release)
file->f_op->release(inode, file);
在其中執行自己私有數據的釋放。
2、shmem文件的釋放
這里所說內容為newseg中創建的文件,它的釋放是通過
shm_destroy--->>>fput--->>>__fput--->>>dput--->>>dentry_iput--->>>iput--->>>generic_delete_inode--->>>shmem_delete_inode
釋放,由于該文件是內存文件,所以inode dentry這些內存結構被刪除之后就無法恢復,也意味著它們從系統中消失了。
總結
以上是生活随笔為你收集整理的为什么/dev/shm中看不到shmget创建的内存文件及其它(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php正则匹配sg-nc-wap_php
- 下一篇: python程序详细描述_如何逐行描述P