- 论坛徽章:
- 0
|
2006-8-2 真是没有用过linux,很多事情搞不清楚呢还。读mm/shmem.c 的时候看到mmap 的私有共享影射使用了tmpfs的文件,但是ls /dev/shm并没有看到那个dev/zero名字的文件。于是想搞清楚。(mm/shmem.c shmem_zero_setup) 先是考虑ls使用什么系统调用读取文件夹内的文件名列表,开始没有经验,就是搜索到了sysv_readdir这个系统调用.顺着看下去,觉得vfs_readdir是个关键的函数。并通过查看tmpfs的相关代码确认了这一点。sys_open->dentry_open->fops_get(inode->i_fop)然后看tmpfs的inode,shmem_get_inode 使用的是simple_dir_operations。 然后看dcache_readdir,开始没有仔细看,只是注意到有如下的一句: (本文实际上都是采用kernel2.6.14) if (d_unhashed(next) || !next->d_inode) continue; 直接注释掉,编译内核,测试。不幸,什么都没有。 如此反复了好几次,后来又改shemem_setup_zero,初看觉得inode->i_link被置位0了,去掉(注意,这里已经走入歧途了),还是没有结果。这样改来改去没有任何结果编译了多次内核。倒是体会到,这样的小改动只需要 make 即可,无须make clean等操作。 过了几天,感觉需要先找个什么方法看看是不是这样行的通。于是直接将shmem_zero_setup的shmem_file_setup换成相应的filp_open(注意需要创建,并且名字不能相同)。居然可以了。(后来意识到这样不过是饮鸩止渴)于是认定只要走一个完整的filp_open就可以将问题找出来(没想到这样是在错误的方向上越走越远)。于是将filp_open拆分,改动到shmem.c.经历了‘成功’后,这次痛苦的时间更长了。下面是相关的改动(注意这不能工作,老是死机):int shmem_do_open_namei(const char * pathname, int flag, int mode, struct nameidata *nd){ struct dentry *dentry; struct dentry *dir; struct qstr *this;/*prepare ND */ /* Fill in the open() intent data */ nd->intent.open.flags = flag; nd->intent.open.create_mode = mode; /* * Create - we need to know the parent. */ /*Get parent nd*/#if 0 error = path_lookup(pathname, LOOKUP_PARENT|LOOKUP_OPEN|LOOKUP_CREATE, nd); if (error) return error;#endif nd->dentry = shm_mnt->mnt_root; nd->mnt = mntget(shm_mnt); nd->last_type= LAST_NORM;{ /*prepare last compnet hash*/ this= &nd->last; unsigned long hash; unsigned char c; this->name = pathname; c = *(const unsigned char *)pathname; hash = init_name_hash(); do { pathname++; hash = partial_name_hash(c, hash); c = *(const unsigned char *)pathname; } while (c && (c != '/')); this->len = pathname - (const char *) this->name; this->hash = end_name_hash(hash);} dir = shm_mnt->mnt_root; nd->flags &= ~LOOKUP_PARENT; { /*dentry = shmem__lookup_hash(&nd->last, nd->dentry, nd);*/ dentry = d_alloc(dir, &nd->last); if (!dentry) return 0;} /* dentry = inode->i_op->lookup(inode, new, nd); */ d_add(dentry, NULL); /* Negative dentry, just create the file */ /*error = shmem_create(dir->d_inode, dentry, mode, nd);*/ struct inode *inode = shmem_get_inode(dir->d_sb, S_IFREG | S_IRWXUGO, 0);; if (inode) { dir->d_inode->i_size += BOGO_DIRENT_SIZE; dir->d_inode->i_ctime = dir->d_inode->i_mtime = CURRENT_TIME; d_instantiate(dentry, inode); }else return -ENOSPC; nd->dentry = dentry; return 0;}struct file *shmem_filp_open(const char * filename, int flags, int mode){ int error; struct nameidata nd; error = shmem_do_open_namei(filename, flags, mode, &nd); { /* if (!error) return dentry_open(nd.dentry, nd.mnt, flags);*/ struct file * f; struct inode *inode; int error; error = -ENFILE; f = get_empty_filp(); if (!f) goto cleanup_dentry; f->f_flags = flags; f->f_mode = FMODE_WRITE | FMODE_READ;; inode = nd.dentry->d_inode; file_ra_state_init(&f->f_ra, inode->i_mapping); f->f_dentry = nd.dentry; f->f_vfsmnt =nd.mnt; f->f_pos = 0; f->f_op = &shmem_file_operations; file_move(f, &inode->i_sb->s_files); return f; cleanup_dentry: dput(nd.dentry); mntput(nd.mnt); return ERR_PTR(error); }}/* * shmem_zero_setup - setup a shared anonymous mapping * * @vma: the vma to be mmapped is prepared by do_mmap_pgoff */void itoax(char *str, int num){ int i=32; while(i) { if(num&0x1) *str++ = '1'; else *str++='0'; num>>=1; i--; if(num==0) break; } *str=0;}int shmem_zero_setup(struct vm_area_struct *vma){ struct file *file; loff_t size = vma->vm_end - vma->vm_start; /*file = shmem_file_setup("zero", size, vma->vm_flags);*/{ static int i=0; char name[128], num[33]; strcpy(name,"zerov"); itoax(num,i); strcat(name,num); /*file = shmem_filp_open(name,FMODE_READ|FMODE_WRITE,0700);*/ filp_open(name,O_WRONLY|O_CREAT,0700); file = shmem_file_setup(name, size, vma->vm_flags); file->f_dentry->d_inode->i_size=size; i++;} if (IS_ERR(file)) return PTR_ERR(file); if (vma->vm_file) fput(vma->vm_file); vma->vm_file = file; vma->vm_ops = &shmem_vm_ops; return 0;} 痛苦的久了就想到了‘万能’的调试,实在是增很printk(何况还不知道如何查看输出信息,ft)。只是知道printk,kgdb,qmenu(?)可以工作,但是我感觉User mode linux对付这个问题才是王道。于是查找uml的使用指南,并逐步实施。 I) 首先是找一个合适的内核版本来编译ARCH=um的内核。我选择了2.6.14,后来证明这个内核比较稳定,编译没有问题。 解压缩到/usr/src/linux2.6.14-uml。 II)到上述目录 make clean make mrproper (这两个操作对于以前编译过其他arch的情况比较重要) make xconfig ARCH=um make all ARCH=um 现在目录下有个linux的可执行文件。 III)不幸,运行uml还需要一个rootfs,就是uml的‘硬盘’啦。当然下载一个 比较快,http://user-mode-linux.sourceforge.net/dl-sf.html 选择了Debian-3.0r0.ext2.bz2,解压缩后改名为root_fs IV)./linux ubd0=root_fs 这样就搞定了。 V)其实还有要注意的事情:make xconfig ARCH=um的时候注意 1.如果有如下提示: VFS: Cannot open root device "ubd0" or unknown-block(0,0) 则大概问题如下 1)需要支持rootfs的文件类型,选上ext2,ext3支持 2)选上Block devices/Virtual block device(ubd支持) 2.如果提示: Cannot open init console 则你的rootfs 的/dev/console文件不存在,需要 mount -o loop root_fs /mnt #cp -dpR /dev/tty[0-9] /mnt/dev #cp -dpR /dev/ram* /mnt/dev #cp -dpR /dev/console /mnt/dev 3.如果提示:cannot setup thead-local storage: cannot setup LDT for thread-local storage, 那么证明你的rootfs使用了NPTL,不幸现在uml 还不能很好支持这个特性。换个简单点的rootfs吧。 VI)具体到我推荐的这个rootfs,还是有些问题的: 1。竟然没有启动bash, 在相应的runlevel中加上启动bash的命令即可 2。没有安装shm,编辑fstab加上相应条目即可 3。fstab文件中 / 安装在 /dev/ubd/0,改为/dev/ubd0 4。编译uml的时候当然要选上proc支持 VII)我使用的host linux也是linux2.6.14,挺好用,如果你么有发现更好的 用这个就可以了。并且我使用的是FC4,不推荐FC5。 这样,搞定了uml,可以测试了。我写了一个小程序,只有一行:#include #include #include #include main(int argc, char** argv){ int i; char *p_map; char temp; p_map=(char*)mmap(NULL,10*10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0); if(p_map==NULL) printf("mmap error!\n");} 在host linux下编译,拷贝到uml rootfs即可。测试的时候执行 shm&,让他后台运行。 至于调试,就是:(直接在/usr/src/linux2.6.14-uml下执行) #gdb # (gdb) set args ubd0=root_fs # (gdb) file linux # (gdb) run # (gdb) c (不断回车直到uml启动完成) # (gdb) break dcache_readdir # (gdb) c 进入到uml #(none):shm& #(none):ls /dev/shm 好,会在一个奇怪的地方断住, c,然后就到了dcache_readdir.不断的用n,s,finish等命令调试你的uml内核即可. 可以用p命令打印相应变量.info break 可以看断点,clear 可以清楚断点.好像我就用了这几个命令.bt都没有用过. 调试的结果很令人不快.问题依然.要注意的是内核编译有-O2优化,代码执行起来怪怪的,如果你想加一段程序,gcc看起来没有用就会不执行到(甚至都没有,优化掉了),解决的方法是: var=....; pirntk("",var); 这样可以骗过gcc,保留你的代码. 如果你改动了几个文件,只需要 make ARCH=um即可,编译很快的. 不再详述调试过程了,最后的转机是在2006.8.1号,昨天,突然注意到,dcache_readdir中filp->f_dentry(/dev/shm->root of tmpfs)的地址并不是shm_mnt->root的地址,终于明白了,原来不是一棵树,赫赫.剩下的就简单了,只要修改dcache_readdir将 struct dentry *dentry = filp->f_dentry; 换成 struct dentry *dentry = shm_mnt->root;/*注意,需要将shm_mnt声明为非static*/ 然后删除: if (d_unhashed(next) || !next->d_inode) continue; 就可以在ls /dev/shm的时候看到一堆 dev/zero文件了.int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir){ struct dentry *dentry = filp->f_dentry; struct dentry *cursor = filp->private_data; /*dcache_dir_open 将cursor 设置为dentry ''.''*/ /* cursor d_child 挂入了filp->f_dentry->d_subdirs */ struct list_head *p, *q = &cursor->d_child; ino_t ino; int i = filp->f_pos; switch (i) { case 0: ino = dentry->d_inode->i_ino; if (filldir(dirent, ".", 1, i, ino, DT_DIR) break; filp->f_pos++; i++; /* fallthrough */ case 1: ino = parent_ino(dentry); if (filldir(dirent, "..", 2, i, ino, DT_DIR) break; filp->f_pos++; i++; /* fallthrough */ default: spin_lock(&dcache_lock); if (filp->f_pos == 2) { /* pos=2 意味从头开始 */ list_del(q);/*将cursor从f_dentry->d_subdirs 删除*/ list_add(q, &dentry->d_subdirs);/*又加回来,从而位于subdir之首*/ } /*从cursor 的下一个subdir 开始*/ for (p=q->next; p != &dentry->d_subdirs; p=p->next) { struct dentry *next; next = list_entry(p, struct dentry, d_child); if (d_unhashed(next) || !next->d_inode) continue; spin_unlock(&dcache_lock); if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, dt_type(next->d_inode)) return 0; spin_lock(&dcache_lock); /* next is still alive */ list_del(q); /*q 是cursor, 后移一个位置 */ list_add(q, p); p = q; /*从cursor 取下一个subdir*/ filp->f_pos++; } spin_unlock(&dcache_lock); } return 0;} 最后, ls 使用的函数是:sys_getdents64. 2006.8.2 by hyl.
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/79526/showart_1200080.html |
|