真是没有用过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 <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;/*注意,需要将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) < 0)
				break;
			filp->f_pos++;
			i++;
			/* fallthrough */
		case 1:
			ino = parent_ino(dentry);
			if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
				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)) < 0)
					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.