⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 022_mm_swapfile_c.html

📁 重读linux 2.4.2o所写的笔记
💻 HTML
📖 第 1 页 / 共 2 页
字号:
      flow: static(header);    }    /* used to insert page numbers */    div.google_header::before, div.google_footer::before {      position: absolute;      top: 0;    }    div.google_footer {      flow: static(footer);    }    /* always consider this element at the start of the doc */    div#google_footer {      flow: static(footer, start);    }    span.google_pagenumber {      content: counter(page);    }    span.google_pagecount {      content: counter(pages);    }  }  @page {    @top {      content: flow(header);    }    @bottom {      content: flow(footer);    }  }  /* end default print css */ /* custom css *//* end custom css */  /* ui edited css */    body {    font-family: Verdana;        font-size: 10.0pt;    line-height: normal;    background-color: #ffffff;  }    .documentBG {    background-color: #ffffff;  }  /* end ui edited css */</style>   </head>  <body  revision="dcbsxfpf_63cqjcn5t9:4">      <table align=center cellpadding=0 cellspacing=0 height=5716 width=768>
  <tbody>
  <tr>
    <td height=5716 valign=top width=100%>
      <pre>2006-8-9   <br>mm/swapfile.c<br><br>  linux可以指定交换分区也可以指定交换文件(专门的分区效率高). 交换设备分为两种:块<br>设备和交换文件. <br>  这里交换设备是交换用的设备文件和普通文件的通称,希望不要引起混淆.每个交换设备在<br>内核中都有一个swap_info_struct与之对应:<br><br>struct swap_info_struct {<br>	unsigned int flags;<br>	kdev_t swap_device;<br>	spinlock_t sdev_lock;<br>	struct dentry * swap_file;<br>	struct vfsmount *swap_vfsmnt;<br>	unsigned short * swap_map; /*记录交换设备上page 的引用计数(SWAP_MAP_MAX)*/<br>	                                           /* 数组大小为this-&gt;max */<br><br>  /*按簇分配算法变量,一个簇包含SWAPFILE_CLUSTER 个页面 */<br>	unsigned int lowest_bit;  /* 和highest_bit一起构成有可能空闲的页面的索引范围*/<br>	unsigned int highest_bit;<br>	unsigned int cluster_next; /*swap cluster 中下一个可分配页面*/<br>	unsigned int cluster_nr; /*本簇内剩余页面数量*/<br><br>	int prio;			/* swap priority ,数值小的在swap list中靠前 */<br>	int pages;  /*nr_good_pages*/<br>	unsigned long max; /*来自swap_header 的last_page(和),见sys_swapon*/<br>	int next;			/* next entry on swap list */<br>};<br>  <br>  这个结构保存存在一个数组:<br>struct swap_info_struct swap_info[MAX_SWAPFILES];<br><br>  所有可用的swap设备按照优先级从高到低组成一个链表,其表头是:<br>  struct swap_list_t swap_list = {-1, -1};<br>  <br>  这是一个存储在数组中的链表,next域是整数,-1代表空指针.<br>struct swap_list_t {<br>	int head;	/* head of priority-ordered swapfile list */<br>	int next;	/* swapfile to be used next */<br>};<br>  head就是表头,优先级最高. swap_list.next指向下次分配swap page页面时应优先考虑的<br>swap设备.<br><br>  系统中可能存在几个swap设备,优先级可能不同,也可能相同.分配swap page的时候在最高<br>优先级的swap设备中轮换分配.只有当所有的高优先级的交换设备上的空间都耗尽了,才启用<br>低优先级的交换设备.<br>  <br>  交换设备有一个固定的头,其大小是一个PAGE_SIZE. 2.4支持两种格式的swap设备:<br>union swap_header {<br>	struct <br>	{<br>		char reserved[PAGE_SIZE - 10];<br>		char magic[10]; /*version 1:"SWAP-SPACE" version 2:"SWAPSPACE2"*/<br>	} magic;<br>	struct <br>	{ /*version 2 使用的结构*/<br>		char	     bootbits[1024];	/* Space for disklabel etc. */<br>		unsigned int version;  /*subversion, olny 1 now*/<br>		unsigned int last_page; /* 交换设备上最后一个页面的nr*/<br>		unsigned int nr_badpages;<br>		unsigned int padding[125];<br>		unsigned int badpages[1];<br>	} info;<br>};<br>1)version 1<br>  结构很简单,swap header的末尾是10字节的version magic 字符,剩下的部分是一个位图,置<br>1的位对应的此交换换设备上的page可用.置1则不可用.<br><br>2)version 2<br>  version 2中2.4只支持subversion(swap_hearder.info.version)为1的交换分区.相对于ver1<br>增加了几个有用的数据,比如最大页面号,等.其结构如下:<br>  预留1024字节用于disk label(引导扇区,赫赫,要从swap引导么!),然后跟着,subversion,最大<br>页面号,预留页面个数,pading,预留(bad page)数组.<br><br>  在一个交换设备内部,采用簇来管理分配,见swap_info_struct的相关数据.一个簇含有SWAPFILE_<br>CLUSTER(256)个页面. 每次分配的时候,尽量在当前簇中分配,如果失败,寻找下一个完整的空簇,如<br>还是失败,就放弃簇分配方式.<br><br>  簇分配算法的核心函数是:(注意一下簇分配所用到的swap info中的变量)<br>/*<br>  * 从指定swap设备中分配swap page, 引用计数置为count<br>  * 使用簇来减少碎片<br>  */<br>static inline int scan_swap_map(struct swap_info_struct *si, unsigned short count)<br>{<br>	unsigned long offset;<br>	/*<br>	 * Cluster ---- 连续的SWAPFILE_CLUSTER 个空页面<br>	 */<br><br>	/*<br>	 * [lowest_bit, highest_bit] 内也许可用<br>	 *<br>	 * 但此范围之外绝对不可用<br>	 */<br>	/* <br>	 * We try to cluster swap pages by allocating them<br>	 * sequentially in swap.  Once we've allocated<br>	 * SWAPFILE_CLUSTER pages this way, however, we resort to<br>	 * first-free allocation, starting a new cluster.  This<br>	 * prevents us from scattering swap pages all over the entire<br>	 * swap partition, so that we reduce overall disk seek times<br>	 * between swap pages.  -- sct */<br>	if (si-&gt;cluster_nr) { //这个群中还剩余页面可以一试<br>		while (si-&gt;cluster_next &lt;= si-&gt;highest_bit) {<br>			offset = si-&gt;cluster_next++;<br>			if (si-&gt;swap_map[offset])<br>				continue;<br>			si-&gt;cluster_nr--;<br>			goto got_page;<br>		}<br>	}<br>	si-&gt;cluster_nr = SWAPFILE_CLUSTER;<br><br>	/* try to find an empty (even not aligned) cluster. */<br>	offset = si-&gt;lowest_bit; //从可分配处寻找一个群<br> check_next_cluster:<br>	if (offset+SWAPFILE_CLUSTER-1 &lt;= si-&gt;highest_bit)<br>	{<br>		int nr; //群的始索引<br>		for (nr = offset; nr &lt; offset+SWAPFILE_CLUSTER; nr++)<br>			if (si-&gt;swap_map[nr])<br>			{<br>				offset = nr+1;<br>				goto check_next_cluster;<br>			}<br>		/* We found a completly empty cluster, so start<br>		 * using it.<br>		 */<br>		goto got_page;<br>	}<br>	/* 不幸, 没有连续的空闲页, <br>	 * 只能缩小搜索粒度, 一页一页的看看有没有空闲页<br>     * finegrined-----有细致的纹理的, 像篦子一样密<br>	 */<br>	for (offset = si-&gt;lowest_bit; offset &lt;= si-&gt;highest_bit ; offset++) {<br>		if (si-&gt;swap_map[offset])<br>			continue;<br>	got_page:<br>		if (offset == si-&gt;lowest_bit)<br>			si-&gt;lowest_bit++; //可分配范围调整<br>		if (offset == si-&gt;highest_bit)<br>			si-&gt;highest_bit--;<br>		si-&gt;swap_map[offset] = count;<br>		nr_swap_pages--;<br>		si-&gt;cluster_next = offset+1; //页群中下一个准备分配的页面在<br>		                          // swap_map 中的索引<br>		return offset;<br>	}<br>	return 0;<br>}<br>  <br><br>  <br>  swap设备轮换分配算法:<br>/*<br>  *  尽量在高优先级的设备中分配swap 页面<br>  *  并且在这些高级别的swap设备中轮换分配<br>  *  分担io压力. 只有在高于当前级别的所有<br>  *  设备都耗尽时才启用的优先级设备.<br>  *  <br>  */<br>swp_entry_t __get_swap_page(unsigned short count)<br>{<br>	struct swap_info_struct * p;<br>	unsigned long offset;<br>	swp_entry_t entry;<br>	int type, wrapped = 0; /*wrapped:是否找遍所有的swap设备*/<br><br>	entry.val = 0;	/* Out of memory */<br>	if (count &gt;= SWAP_MAP_MAX)<br>		goto bad_count;<br>	swap_list_lock();<br>	type = swap_list.next;<br>	if (type &lt; 0)<br>		goto out;<br>	if (nr_swap_pages == 0)<br>		goto out;<br><br>	while (1) {<br>		p = &amp;swap_info[type];<br>		if ((p-&gt;flags &amp; SWP_WRITEOK) == SWP_WRITEOK) {<br>			swap_device_lock(p);<br>			offset = scan_swap_map(p, count);<br>			swap_device_unlock(p);<br>			if (offset) { <br>				/* 分配成功,下一次在同(or high)优先级的下<br>				  *一个设备中分配swap entry*/<br>				entry = SWP_ENTRY(type,offset);<br><br>				type = swap_info[type].next;/*next*/<br>				if (type &lt; 0 || /*NULL*/ <br>					p-&gt;prio != swap_info[type].prio ){/*优先级下降*/<br>						swap_list.next = swap_list.head; /*从头开始*/<br>				} else {<br>					swap_list.next = type;<br>				}<br>				goto out;<br>			}<br>		}<br><br>		/*当前设备, swap_list.head,空间耗尽*/<br>		type = p-&gt;next; /*考察下一个设备*/<br><br>		if (!wrapped) {<br>			if (type &lt; 0 || p-&gt;prio != swap_info[type].prio) {<br>				type = swap_list.head; /*尽量使用高优先级设备*/<br>				wrapped = 1;<br>			}<br>		} else/*高优先级的设备都耗尽才用低优先级设备*/<br>			if (type &lt; 0)/*全部耗尽*/<br>				goto out;	/* out of swap space */<br>	}<br>out:<br>	swap_list_unlock();<br>	return entry;<br><br>bad_count:<br>	printk(KERN_ERR "get_swap_page: bad count %hd from %p\n",<br>	       count, __builtin_return_address(0));<br>	goto out;<br>}<br><br><br><br>  交换页面的释放算法就极为的简单了:__swap_free,代码略.<br>  get_swaparea_info为proc文件系统提供数据,略.is_swap_partition,si_swapinfo,<br>swap_duplicate,swap_count,get_swaphandle_info等接口提供一些简单功能,不详细<br>介绍了.<br><br>   int valid_swaphandles 在介绍sapin_readahead,的时候有提及.其功能是从cluster<br>边界开始,寻找一个连续可读取范围(无坏页,预留页,在使用中),并将引用计数增1.<br>/*<br> * Kernel_lock protects against swap device deletion. Grab an extra<br> * reference on the swaphandle so that it dos not become unused.<br> */<br> /*call from swapin_readahead*/<br>int valid_swaphandles(swp_entry_t entry, unsigned long *offset)<br>{<br>	int ret = 0, i = 1 &lt;&lt; page_cluster; /*一次预读一个cluster*/<br>	unsigned long toff;<br>	struct swap_info_struct *swapdev = SWP_TYPE(entry) + swap_info;<br><br>	*offset = SWP_OFFSET(entry);<br>	/*将预读设置到一个cluster的边界*/<br>	toff = *offset = (*offset &gt;&gt; page_cluster) &lt;&lt; page_cluster;<br><br>	swap_device_lock(swapdev);<br>	/*从cluster边界开始,寻找一个连续可读取范围*/<br>	do {<br>		<br>		/* Don't read-ahead past the end of the swap area */<br>		if (toff &gt;= swapdev-&gt;max)<br>			break;<br>		/* Don't read in bad or busy pages */<br>		if (!swapdev-&gt;swap_map[toff])<br>			break;<br>		if (swapdev-&gt;swap_map[toff] == SWAP_MAP_BAD)<br>			break;<br>		swapdev-&gt;swap_map[toff]++;<br>		toff++;<br>		ret++;<br>	} while (--i);<br>	swap_device_unlock(swapdev);<br>	return ret;<br>}<br><br><br>   剩下的部分是系统调用,sys_swapon和sys_swapoff. swapon就是打开交换设备,建立swap<br>info结构,读取swap header以设置swap info.<br>/*<br> * Written 01/25/92 by Simmule Turner, heavily changed by Linus.<br> *<br> * The swapon system call<br> */<br>/*<br>  *  加载swap 分区,或者启用swap 文件<br>  */<br>asmlinkage long sys_swapon(const char * specialfile, int swap_flags)<br>{<br>	struct swap_info_struct * p;<br>	struct nameidata nd;<br>	struct inode * swap_inode;<br>	unsigned int type;<br>	int i, j, prev;<br>	int error;<br>	static int least_priority = 0;<br>	union swap_header *swap_header = 0;<br>	int swap_header_version;<br>	int nr_good_pages = 0;<br>	unsigned long maxpages;<br>	int swapfilesize;<br>	struct block_device *bdev = NULL;<br>	<br>	if (!capable(CAP_SYS_ADMIN))<br>		return -EPERM;<br>	lock_kernel();<br><br>	/*寻找空闲swap info,初始化swap info*/<br>   .... //略<br>   <br>	if (swap_flags &amp; SWAP_FLAG_PREFER) {/*指定了优先级*/<br>		p-&gt;prio =<br>		  (swap_flags &amp; SWAP_FLAG_PRIO_MASK)&gt;&gt;SWAP_FLAG_PRIO_SHIFT;<br>	} else {<br>		p-&gt;prio = --least_priority;<br>	}<br><br>	/*找到文件或者设备*/<br>	error = user_path_walk(specialfile, &amp;nd);<br>	if (error)<br>		goto bad_swap_2;<br><br>	p-&gt;swap_file = nd.dentry;<br>	p-&gt;swap_vfsmnt = nd.mnt;<br>	swap_inode = nd.dentry-&gt;d_inode;<br>	error = -EINVAL;<br><br>	if (S_ISBLK(swap_inode-&gt;i_mode)) {/*真正的交换设备*/<br>		kdev_t dev = swap_inode-&gt;i_rdev;<br>		struct block_device_operations *bdops;<br><br>    /*强制交换设备的block为PAGE_SIZE*/<br>		p-&gt;swap_device = dev;<br>		set_blocksize(dev, PAGE_SIZE);<br><br>		/*打开交换设备*/<br>		bdev = swap_inode-&gt;i_bdev;<br>		bdops = devfs_get_ops(devfs_get_handle_from_inode(swap_inode));<br>		if (bdops) bdev-&gt;bd_op = bdops;<br><br>		error = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_SWAP);<br>		if (error)<br>			goto bad_swap_2;<br>		set_blocksize(dev, PAGE_SIZE);<br>		<br>		error = -ENODEV;<br>		if (!dev || (blk_size[MAJOR(dev)] &amp;&amp;<br>		     !blk_size[MAJOR(dev)][MINOR(dev)]))<br>			goto bad_swap;<br>		error = -EBUSY;<br>		/*是否已经是交换设备*/<br>		for (i = 0 ; i &lt; nr_swapfiles ; i++) {<br>			if (i == type)<br>				continue;<br>			if (dev == swap_info[i].swap_device)<br>				goto bad_swap;<br>		}<br>		swapfilesize = 0;<br>		if (blk_size[MAJOR(dev)])<br>			swapfilesize = blk_size[MAJOR(dev)][MINOR(dev)]<br>				&gt;&gt; (PAGE_SHIFT - 10);/*blk_size 记录的大小是以1024为单位的*/<br>	} else if (S_ISREG(swap_inode-&gt;i_mode)) {/*交换文件*/<br>		error = -EBUSY;<br>		for (i = 0 ; i &lt; nr_swapfiles ; i++) {<br>			if (i == type || !swap_info[i].swap_file)<br>				continue;<br>			if (swap_inode == swap_info[i].swap_file-&gt;d_inode)<br>				goto bad_swap;<br>		}<br>		swapfilesize = swap_inode-&gt;i_size &gt;&gt; PAGE_SHIFT;<br>	} else<br>		goto bad_swap;<br><br>  /*读入交换设备/文件的交换信息*/<br>	swap_header = (void *) __get_free_page(GFP_USER);<br>	if (!swap_header) {<br>		printk("Unable to start swapping: out of memory :-)\n");<br>		error = -ENOMEM;<br>		goto bad_swap;<br>	}<br><br>	lock_page(virt_to_page(swap_header));<br>	rw_swap_page_nolock(READ, SWP_ENTRY(type,0), (char *) swap_header, 1);<br><br>  /*有两个版本的swap*/<br>	if (!memcmp("SWAP-SPACE",swap_header-&gt;magic.magic,10))<br>		swap_header_version = 1;<br>	else if (!memcmp("SWAPSPACE2",swap_header-&gt;magic.magic,10))<br>		swap_header_version = 2;<br>	else {<br>		printk("Unable to find swap-space signature\n");<br>		error = -EINVAL;<br>		goto bad_swap;<br>	}<br>	<br>	switch (swap_header_version) {<br>	case 1:/*swap  version  1*/<br>		memset(((char *) swap_header)+PAGE_SIZE-10,0,10);/*clear magic*/<br>		/*查找可使用swap 页面的范围和数量*/<br>		j = 0;<br>		p-&gt;lowest_bit = 0;<br>		p-&gt;highest_bit = 0;<br>		for (i = 1 ; i &lt; 8*PAGE_SIZE ; i++) {<br>			if (test_bit(i,(char *) swap_header)) {<br>				if (!p-&gt;lowest_bit)<br>					p-&gt;lowest_bit = i;<br>				p-&gt;highest_bit = i;<br>				p-&gt;max = i+1;<br>				j++;<br>			}<br>		}<br>		/*预留交换页面,设置mem map(引用技术)*/<br>		nr_good_pages = j;<br>		p-&gt;swap_map = vmalloc(p-&gt;max * sizeof(short));<br>		if (!p-&gt;swap_map) {<br>			error = -ENOMEM;		<br>			goto bad_swap;<br>		}<br>		for (i = 1 ; i &lt; p-&gt;max ; i++) {<br>			if (test_bit(i,(char *) swap_header))<br>				p-&gt;swap_map[i] = 0;<br>			else<br>				p-&gt;swap_map[i] = SWAP_MAP_BAD;<br>		}<br>		break;<br><br>	case 2: /*swap version 2*/<br>		/* Check the swap header's sub-version and the size of<br>                   the swap file and bad block lists */<br>		if (swap_header-&gt;info.version != 1) {<br>			printk(KERN_WARNING<br>			       "Unable to handle swap header version %d\n",<br>			       swap_header-&gt;info.version);<br>			error = -EINVAL;<br>			goto bad_swap;<br>		}<br>    /*version 2的范围和数量有记录*/<br>		p-&gt;lowest_bit  = 1;<br>		p-&gt;highest_bit = swap_header-&gt;info.last_page - 1;<br>		p-&gt;max	       = swap_header-&gt;info.last_page;<br><br>		maxpages = SWP_OFFSET(SWP_ENTRY(0,~0UL));<br>		if (p-&gt;max &gt;= maxpages)<br>			p-&gt;max = maxpages-1;<br><br>		error = -EINVAL;<br>		if (swap_header-&gt;info.nr_badpages &gt; MAX_SWAP_BADPAGES)<br>			goto bad_swap;<br>		<br>		/* OK, set up the swap map and apply the bad block list */<br>		if (!(p-&gt;swap_map = vmalloc (p-&gt;max * sizeof(short)))) {<br>			error = -ENOMEM;<br>			goto bad_swap;<br>		}<br>    /*查找预留页面即可*/<br>		error = 0;<br>		memset(p-&gt;swap_map, 0, p-&gt;max * sizeof(short));<br>		for (i=0; i&lt;swap_header-&gt;info.nr_badpages; i++) {<br>			int page = swap_header-&gt;info.badpages[i];<br>			if (page &lt;= 0 || page &gt;= swap_header-&gt;info.last_page)<br>				error = -EINVAL;<br>			else<br>				p-&gt;swap_map[page] = SWAP_MAP_BAD;<br>		}<br>		nr_good_pages = swap_header-&gt;info.last_page -<br>				swap_header-&gt;info.nr_badpages -<br>				1 /* header page */;<br>		if (error) <br>			goto bad_swap;<br>	}<br>	<br>  ...... //检查,忽略<br>	p-&gt;swap_map[0] = SWAP_MAP_BAD; /*第一个swap page是交换设备信息*/<br>	p-&gt;flags = SWP_WRITEOK;<br>	p-&gt;pages = nr_good_pages;<br>	swap_list_lock();<br>	nr_swap_pages += nr_good_pages;<br>	printk(KERN_INFO "Adding Swap: %dk swap-space (priority %d)\n",<br>	       nr_good_pages&lt;&lt;(PAGE_SHIFT-10), p-&gt;prio);<br><br>	/* insert swap space into swap_list: */<br>	prev = -1;<br>	for (i = swap_list.head; i &gt;= 0; i = swap_info[i].next) {<br>		if (p-&gt;prio &gt;= swap_info[i].prio) {<br>			break;<br>		}<br>		prev = i;<br>	}<br>	p-&gt;next = i;<br>	if (prev &lt; 0) {<br>		swap_list.head = swap_list.next = p - swap_info;<br>	} else {<br>		swap_info[prev].next = p - swap_info;<br>	}<br>	swap_list_unlock();<br>	error = 0;<br>	goto out;<br>  ..........//失败时的clear工作,略<br>	return error;<br>}<br><br><br>  相对swap on,swap off就复杂些. 关闭swap设备的时候需要将已经交换到swap设备上的页<br>面重新读入内存. 然后才能释放交换设备. <br>  思路是这样的: 只有user使用的页面才能够被swap out,内核自己使用的页面是不可以swap<br>out的,所以遍历用户的页目录,页表,逐项检查pte,如果是not in mem,并且是一个swap entry<br>则释放他.不过要遍历所有进程的页表.所以速度是不快的.<br>  <br>  不过,内核使用的页面真的不能交换吗? maybe吧,不过特殊的是tmpfs(shmmem.c),从内核引<br>用了swap entry. 介绍shmem.c的时候已经提及过那个函数了:shmem_unuse.参考 shmem.c的分<br>析.<br><br>/*<br>  *   关闭指定交换设备(读入所有交换设备上的page)<br>  */<br>asmlinkage long sys_swapoff(const char * specialfile)<br>{<br>	struct swap_info_struct * p = NULL;<br>	struct nameidata nd;<br>	int i, type, prev;<br>	int err;<br>	<br>	........//capable check<br>		<br>  /*找到要关闭的文件*/<br>	err = user_path_walk(specialfile, &amp;nd);<br>	if (err)<br>		goto out;<br><br>	lock_kernel();<br>	prev = -1;<br>	swap_list_lock();<br>	/*swap_list遍历在看看要关闭那个设备,略*/<br>	...............<br><br>  /*关闭设备前调整一下分配策略*/<br>	if (prev &lt; 0) {<br>		swap_list.head = p-&gt;next;<br>	} else {<br>		swap_info[prev].next = p-&gt;next;<br>	}<br>	if (type == swap_list.next) {<br>		/* just pick something that's safe... */<br>		swap_list.next = swap_list.head;<br>	}<br>	nr_swap_pages -= p-&gt;pages;<br>	swap_list_unlock();<br>	p-&gt;flags = SWP_USED;<br>	<br>	err = try_to_unuse(type); //释放所有swap entry,将在交换设备上的page读入内存<br>	<br>	if (err) {/*由于内存不足关闭失败,重新启用此swap设备*/<br>		/* re-insert swap space back into swap_list */<br>		swap_list_lock();<br>		for (prev = -1, i = swap_list.head; i &gt;= 0; prev = i, i = swap_info[i].next)<br>			if (p-&gt;prio &gt;= swap_info[i].prio)<br>				break;<br>		p-&gt;next = i;<br>		if (prev &lt; 0)<br>			swap_list.head = swap_list.next = p - swap_info;<br>		else<br>			swap_info[prev].next = p - swap_info;<br>		nr_swap_pages += p-&gt;pages;<br>		swap_list_unlock();<br>		p-&gt;flags = SWP_WRITEOK;<br>		goto out_dput;<br>	}<br><br>	/*释放交换设备*/<br>	............<br>}<br><br>核心函数:<br><br>/*<br> * We completely avoid races by reading each swap page in advance,<br> * and then search for the process using it.  All the necessary<br> * page table adjustments can then be made atomically.<br> */<br> /*除非内存耗尽,否则肯定可以释放swap entry*/<br>static int try_to_unuse(unsigned int type)<br>{<br>	struct swap_info_struct * si = &amp;swap_info[type];<br>	struct task_struct *p;<br>	struct page *page;<br>	swp_entry_t entry;<br>	int i;<br><br>	while (1) {<br>		/*<br>		 * Find a swap page in use and read it in.<br>		 */<br>		swap_device_lock(si);<br>		for (i = 1; i &lt; si-&gt;max ; i++) {<br>			if (si-&gt;swap_map[i] &gt; 0 &amp;&amp; si-&gt;swap_map[i] != SWAP_MAP_BAD) {<br>				/*<br>				 * Prevent swaphandle from being completely<br>				 * unused by swap_free while we are trying<br>				 * to read in the page - this prevents warning<br>				 * messages from rw_swap_page_base.<br>				 */<br>				if (si-&gt;swap_map[i] != SWAP_MAP_MAX)<br>					si-&gt;swap_map[i]++; /*我们要从此swap page读入*/<br>				swap_device_unlock(si);<br>				goto found_entry;<br>			}<br>		}<br>		swap_device_unlock(si);<br>		break;<br><br>	found_entry:<br>		entry = SWP_ENTRY(type, i);<br><br>		/* Get a page for the entry, using the existing swap<br>                   cache page if there is one.  Otherwise, get a clean<br>                   page and read the swap into it. */<br>		page = read_swap_cache(entry);<br>		if (!page) {/*swap entry  不再有人用,或者内存分配失败见read_swap_cache*/<br>			swap_free(entry); /*不可能无人用,swap off还用呢*/<br>  			return -ENOMEM;   /*所以就是内存分配失败*/<br>		}<br><br>		/*有必要,可能进程已经读入另外一个page*/<br>		if (PageSwapCache(page))  <br>			delete_from_swap_cache(page);/*那时必须彻底释放此page*/<br><br><br>		read_lock(&amp;tasklist_lock);<br>		for_each_task(p)<br>			unuse_process(p-&gt;mm, entry, page);/*已经读入page,让各个进程释放swap entry*/<br>		read_unlock(&amp;tasklist_lock);<br>		shmem_unuse(entry, page); /*让shmem 也释放对swap entry的引用*/<br>		/* Now get rid of the extra reference to the temporary<br>                   page we've been using. */<br>		page_cache_release(page);/* read_swap_cache -&gt; alloc */<br>		/*<br>		 * Check for and clear any overflowed swap map counts.<br>		 */<br>		swap_free(entry); /* over*/<br>		swap_list_lock();<br>		swap_device_lock(si);<br>		if (si-&gt;swap_map[i] &gt; 0) {<br>			if (si-&gt;swap_map[i] != SWAP_MAP_MAX)<br>				printk("VM: Undead swap entry %08lx\n", <br>								entry.val);<br>			nr_swap_pages++;<br>			si-&gt;swap_map[i] = 0;<br>		}<br>		swap_device_unlock(si);<br>		swap_list_unlock();<br>	}<br>	return 0;<br>}<br><br>  shmem_unuse我们就不提了.看unuse_process,这是一个遍进程vma,遍历每个vma涉及到的<br>pmd,page table, pte.这种遍历我们见过很多了.只看看unuse_pte,<br>/*<br> * The swap entry has been read in advance, and we return 1 to indicate<br> * that the page has been used or is no longer needed.<br> *<br> * Always set the resulting pte to be nowrite (the same as COW pages<br> * after one process has exited).  We don't know just how many PTEs will<br> * share this swap entry, so be cautious and let do_wp_page work out<br> * what to do if a write is requested later.<br> */<br>static inline void unuse_pte(struct vm_area_struct * vma, unsigned long address,<br>	pte_t *dir, swp_entry_t entry, struct page* page)<br>{<br>	pte_t pte = *dir;<br><br>	if (pte_none(pte))<br>		return;<br>	if (pte_present(pte)) {<br>		/* If this entry is swap-cached, then page must already<br>                   hold the right address for any copies in physical<br>                   memory */<br>		if (pte_page(pte) != page)<br>			return;<br>		/* We will be removing the swap cache in a moment, so... */<br>		ptep_mkdirty(dir);<br>		return;<br>	}<br><br>	/*swap out的page 进程页表 设置成swap entry,这里是一个理由*/<br>	if (pte_to_swp_entry(pte).val != entry.val) /*赫赫,动作真快,已经换了一个swap设备*/<br>		return;<br>	set_pte(dir, pte_mkdirty(mk_pte(page, vma-&gt;vm_page_prot)));<br>	swap_free(entry);<br>	get_page(page); /*此进程使用此page*/<br>	++vma-&gt;vm_mm-&gt;rss;<br>}<br><br><br><br>       over. 2006.8.9<br>    <br><br></pre>
    </td>
  </tr>
  </tbody>
</table></body></html>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -