真是没有用过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;
nd->intent.open.flags = flag;
nd->intent.open.create_mode = mode;
#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;
{
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 = d_alloc(dir, &nd->last);
if (!dentry)
return 0;
}
d_add(dentry, NULL);
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);
{
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);
}
}
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;
{ static int i=0;
char name[128], num[33];
strcpy(name,"zerov");
itoax(num,i);
strcat(name,num);
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: 选择了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 <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
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_read
dir中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;
然后删除:
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;
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) < 0)
break;
filp->f_pos++;
i++;
case 1:
ino = parent_ino(dentry);
if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
break;
filp->f_pos++;
i++;
default:
spin_lock(&dcache_lock);
if (filp->f_pos == 2) {
list_del(q);
list_add(q, &dentry->d_subdirs);
}
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)) < 0)
return 0;
spin_lock(&dcache_lock);
list_del(q);
list_add(q, p);
p = q;
filp->f_pos++;
}
spin_unlock(&dcache_lock);
}
return 0;
}
最后, ls 使用的函数是:sys_getdents64.
2006.8.2 by hyl.