<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tek-life</title>
	<atom:link href="http://www.tek-life.org/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.tek-life.org</link>
	<description>Linux-kernel &#38; My life</description>
	<lastBuildDate>Wed, 23 Nov 2011 07:45:22 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1</generator>
		<item>
		<title>配置wsgi运行环境</title>
		<link>http://www.tek-life.org/2011/11/23/config-wsgi-envron/</link>
		<comments>http://www.tek-life.org/2011/11/23/config-wsgi-envron/#comments</comments>
		<pubDate>Wed, 23 Nov 2011 07:32:21 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Web Dev]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10669</guid>
		<description><![CDATA[我想用python写web. 之前最拿手的应该是用asp写，后来，学了php。总感觉，用这些脚本写网站，不太geek。所以，就摸索着用python写。 昨天，申请了支持python的SAE，其安装手册上的例子是用wsgi跑一个helloworld. 我对这些东西还不懂，纯菜鸟。SAE用的版本系统是svn，蛋疼的很。每次svn ci的时候，总感觉很不习惯，不如git那样舒服。 so.想在local上学习python web开发。python写web方式有很多，因为SAE上支持WSGI，所以，就先玩玩WSGI吧。可是在配置WSGI方面，网上有很多垃圾文章，明明很简单的东西，非要再加个Django，而我只是想运行个hello world而已，用不上这么厚重的东西。 下面是配置总结，整个过程比较简单： 依次安装apache2和libapache2-mod-wsgi后，我还安装了libapache2-mod-wsgi-py3. 然后，在/etc/apache2/mods-enabled/下面应该能够看到俩个文件： wsgi.conf  wsgi.load 然后在/etc/apache2/apache2.conf结尾加上： 231 &#60;Directory /var/www/&#62; 232 order deny,allow 233 Allow from all 234 &#60;/Directory&#62; 235 WSGIScriptAlias / /var/www/index.wsgi 于是，就OK了。这个时候，怎么测试是OK的呢？我们先重启apache2，然后写个简单的hello world程序。 重启apache2: $sudo /etc/init.d/apache2 restart Hello world程序： 在/var/www/下 &#160; &#8230; <a href="http://www.tek-life.org/2011/11/23/config-wsgi-envron/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>我想用python写web.<br />
之前最拿手的应该是用asp写，后来，学了php。总感觉，用这些脚本写网站，不太geek。所以，就摸索着用python写。</p>
<p>昨天，申请了支持python的SAE，其安装手册上的例子是用wsgi跑一个helloworld. 我对这些东西还不懂，纯菜鸟。SAE用的版本系统是svn，蛋疼的很。每次svn ci的时候，总感觉很不习惯，不如git那样舒服。</p>
<p>so.想在local上学习python web开发。python写web方式有很多，因为SAE上支持WSGI，所以，就先玩玩WSGI吧。可是在配置WSGI方面，网上有很多垃圾文章，明明很简单的东西，非要再加个<a class="reference internal" href="http://www.wsgi.org/en/latest/python3.html#presentation-at-djangocon-2010-by-armin-ronacher">Django</a>，而我只是想运行个hello world而已，用不上这么厚重的东西。</p>
<p>下面是配置总结，整个过程比较简单：</p>
<p>依次安装apache2和libapache2-mod-wsgi后，我还安装了libapache2-mod-wsgi-py3.</p>
<p>然后，在/etc/apache2/mods-enabled/下面应该能够看到俩个文件：<br />
wsgi.conf  wsgi.load</p>
<p>然后在/etc/apache2/apache2.conf结尾加上：</p>
<pre class="brush:plain;">231 &lt;Directory /var/www/&gt;
232     order deny,allow
233     Allow from all
234 &lt;/Directory&gt;
235 WSGIScriptAlias / /var/www/index.wsgi</pre>
<p>于是，就OK了。这个时候，怎么测试是OK的呢？我们先重启apache2，然后写个简单的hello world程序。<br />
重启apache2:</p>
<pre class="brush:shell;">	$sudo /etc/init.d/apache2 restart</pre>
<p>Hello world程序： 在/var/www/下</p>
<p>&nbsp;</p>
<pre class="brush:shell;">$touch index.wsgi
$vim index.wsgi</pre>
<pre class="brush:py">def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    yield 'Hello World\n'</pre>
<p>ps:该helloworld测试程序来自：http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface<br />
然后在浏览器中http://127.0.0.1就可以看到Hello world了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/11/23/config-wsgi-envron/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SD卡读写操作浅析</title>
		<link>http://www.tek-life.org/2011/10/08/sd-read-write-op/</link>
		<comments>http://www.tek-life.org/2011/10/08/sd-read-write-op/#comments</comments>
		<pubDate>Sat, 08 Oct 2011 03:22:52 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[File System]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10666</guid>
		<description><![CDATA[一个读写请求何时被读写，怎样读写，全看请求队列。以Goldfish平台上的MMC卡，我们来看看其请求队列都怎样设置的： mmc_blk_probe() 597 &#160; &#160; struct mmc_blk_data *md; 598 &#160; &#160; int err; 599&#160; 600 &#160; &#160; char cap_str[10]; 601&#160; 602 &#160; &#160; /* 603 &#160; &#160; &#160;* Check that the card supports the command class(es) we need. 604 &#160; &#8230; <a href="http://www.tek-life.org/2011/10/08/sd-read-write-op/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div>
	一个读写请求何时被读写，怎样读写，全看请求队列。以Goldfish平台上的MMC卡，我们来看看其请求队列都怎样设置的：</div>
<div>
	mmc_blk_probe()</div>
<div>
	597 &nbsp; &nbsp; struct mmc_blk_data *md;</div>
<div>
	598 &nbsp; &nbsp; int err;</div>
<div>
	599&nbsp;</div>
<div>
	600 &nbsp; &nbsp; char cap_str[10];</div>
<div>
	601&nbsp;</div>
<div>
	602 &nbsp; &nbsp; /*</div>
<div>
	603 &nbsp; &nbsp; &nbsp;* Check that the card supports the command class(es) we need.</div>
<div>
	604 &nbsp; &nbsp; &nbsp;*/</div>
<div>
	605 &nbsp; &nbsp; if (!(card-&gt;csd.cmdclass &amp; CCC_BLOCK_READ))</div>
<div>
	606 &nbsp; &nbsp; &nbsp; &nbsp; return -ENODEV;</div>
<div>
	607 &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div>
<div>
	608 &nbsp; &nbsp; md = mmc_blk_alloc(card);</div>
<div>
	&nbsp;</div>
<div>
	mmc_blk_probe-&gt;mmc_blk_alloc()</div>
<div>
	&nbsp;</div>
<div>
	510 &nbsp; &nbsp; struct mmc_blk_data *md;</div>
<div>
	511 &nbsp; &nbsp; int devidx, ret;</div>
<div>
	512&nbsp;</div>
<div>
	513 &nbsp; &nbsp; devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);//在dev_use中查找一个没有被用到的</div>
<div>
	514 &nbsp; &nbsp; if (devidx &gt;= MMC_NUM_MINORS)</div>
<div>
	515 &nbsp; &nbsp; &nbsp; &nbsp; return ERR_PTR(-ENOSPC);</div>
<div>
	516 &nbsp; &nbsp; __set_bit(devidx, dev_use);</div>
<div>
	517&nbsp;</div>
<div>
	518 &nbsp; &nbsp; md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);</div>
<div>
	519 &nbsp; &nbsp; if (!md) {</div>
<div>
	520 &nbsp; &nbsp; &nbsp; &nbsp; ret = -ENOMEM;</div>
<div>
	521 &nbsp; &nbsp; &nbsp; &nbsp; goto out;</div>
<div>
	522 &nbsp; &nbsp; }</div>
<div>
	523&nbsp;</div>
<div>
	524&nbsp;</div>
<div>
	525 &nbsp; &nbsp; /*</div>
<div>
	526 &nbsp; &nbsp; &nbsp;* Set the read-only status based on the supported commands</div>
<div>
	527 &nbsp; &nbsp; &nbsp;* and the write protect switch.</div>
<div>
	528 &nbsp; &nbsp; &nbsp;*/</div>
<div>
	529 &nbsp; &nbsp; md-&gt;read_only = mmc_blk_readonly(card);</div>
<div>
	530&nbsp;</div>
<div>
	531 &nbsp; &nbsp; md-&gt;disk = alloc_disk(1 &lt;&lt; MMC_SHIFT);//这个结构非常非常重要</div>
<div>
	532 &nbsp; &nbsp; if (md-&gt;disk == NULL) {</div>
<div>
	533 &nbsp; &nbsp; &nbsp; &nbsp; ret = -ENOMEM;</div>
<div>
	534 &nbsp; &nbsp; &nbsp; &nbsp; goto err_kfree;</div>
<div>
	535 &nbsp; &nbsp; }</div>
<div>
	536&nbsp;</div>
<div>
	537 &nbsp; &nbsp; spin_lock_init(&amp;md-&gt;lock);</div>
<div>
	538 &nbsp; &nbsp; md-&gt;usage = 1;//每get一次，会++</div>
<div>
	539&nbsp;</div>
<div>
	540 &nbsp; &nbsp; ret = mmc_init_queue(&amp;md-&gt;queue, card, &amp;md-&gt;lock);</div>
<div>
	541 &nbsp; &nbsp; if (ret)</div>
<div>
	542 &nbsp; &nbsp; &nbsp; &nbsp; goto err_putdisk;</div>
<div>
	543&nbsp;</div>
<div>
	544 &nbsp; &nbsp; md-&gt;queue.issue_fn = mmc_blk_issue_rq;</div>
<div>
	545 &nbsp; &nbsp; md-&gt;queue.data = md;</div>
<div>
	546&nbsp;</div>
<div>
	547 &nbsp; &nbsp; md-&gt;disk-&gt;major = MMC_BLOCK_MAJOR;//发送的request将根据它来寻找disk，然后挂载在disk-&gt;queue上</div>
<div>
	548 &nbsp; &nbsp; md-&gt;disk-&gt;first_minor = devidx &lt;&lt; MMC_SHIFT;</div>
<div>
	549 &nbsp; &nbsp; md-&gt;disk-&gt;fops = &amp;mmc_bdops;</div>
<div>
	550 &nbsp; &nbsp; md-&gt;disk-&gt;private_data = md;</div>
<div>
	551 &nbsp; &nbsp; md-&gt;disk-&gt;queue = md-&gt;queue.queue;//哦，原来如此哇~</div>
<div>
	552 &nbsp; &nbsp; md-&gt;disk-&gt;driverfs_dev = &amp;card-&gt;dev;</div>
<div>
	&nbsp;</div>
<div>
	第531行通过alloc_disk分配一个disk，这个结构就是通用块设备结构。所有的request，将查询其/dev/目录下的对应设备，通过major和minor找到对应的disk，然后挂载在disk-&gt;queue上。这个request被执行的时机，全部由这个请求队列决定。哪些方面呢？我们接着看，等所有流程跟踪结束后，会做一个总结。</div>
<div>
	&nbsp;</div>
<div>
	mmc_blk_probe-&gt;mmc_blk_alloc()-&gt;mmc_init_queue()</div>
<div>
	125 &nbsp; &nbsp; mq-&gt;card = card;</div>
<div>
	126 &nbsp; &nbsp; mq-&gt;queue = blk_init_queue(mmc_request, lock); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div>
<div>
	127 &nbsp; &nbsp; if (!mq-&gt;queue) &nbsp; &nbsp; &nbsp;&nbsp;</div>
<div>
	128 &nbsp; &nbsp; &nbsp; &nbsp; return -ENOMEM; &nbsp;&nbsp;</div>
<div>
	129&nbsp;</div>
<div>
	130 &nbsp; &nbsp; mq-&gt;queue-&gt;queuedata = mq;</div>
<div>
	131 &nbsp; &nbsp; mq-&gt;req = NULL; &nbsp; &nbsp; &nbsp;&nbsp;</div>
<div>
	132 &nbsp; &nbsp;</div>
<div>
	133 &nbsp; &nbsp; blk_queue_prep_rq(mq-&gt;queue, mmc_prep_request);</div>
<div>
	134 &nbsp; &nbsp; blk_queue_ordered(mq-&gt;queue, QUEUE_ORDERED_DRAIN, NULL);</div>
<div>
	135 &nbsp; &nbsp; queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq-&gt;queue);&nbsp;</div>
<div>
	这个mmc_init_queue，由调用通用的blk_init_queue创建一个请求队列,在mmc_init_queue中，单独设定了其特定的参数。</div>
<div>
	其特定的参数由：</div>
<div>
	queue-&gt;request_fn=mmc_request</div>
<div>
	queue-&gt;prep_rq_fn=mmc_prep_request</div>
<div>
	queue-&gt;odered=QUEUE_ORDERED_DRAIN</div>
<div>
	queue-&gt;next_ordered=QUEUE_ORDERED_DRAIN</div>
<div>
	queue-&gt;prepare_flush_fn=NULL</div>
<div>
	除了这些特定的参数外，还有一些参数是通用的，也是必不可少的。继续往下看：</div>
<div>
	mmc_blk_probe-&gt;mmc_blk_alloc()-&gt;mmc_init_queue()-&gt;blk_init_queue</div>
<div>
	&nbsp;540 /**</div>
<div>
	&nbsp;541 &nbsp;* blk_init_queue &nbsp;- prepare a request queue for use with a block device</div>
<div>
	&nbsp;542 &nbsp;* @rfn: &nbsp;The function to be called to process requests that have been</div>
<div>
	&nbsp;543 &nbsp;* &nbsp; &nbsp; &nbsp; &nbsp;placed on the queue.</div>
<div>
	&nbsp;544 &nbsp;* @lock: Request queue spin lock</div>
<div>
	&nbsp;545 &nbsp;*</div>
<div>
	&nbsp;546 &nbsp;* Description:</div>
<div>
	&nbsp;547 &nbsp;* &nbsp; &nbsp;If a block device wishes to use the standard request handling procedures,</div>
<div>
	&nbsp;548 &nbsp;* &nbsp; &nbsp;which sorts requests and coalesces adjacent requests, then it must</div>
<div>
	&nbsp;549 &nbsp;* &nbsp; &nbsp;call blk_init_queue(). &nbsp;The function @rfn will be called when there</div>
<div>
	&nbsp;550 &nbsp;* &nbsp; &nbsp;are requests on the queue that need to be processed. &nbsp;If the device</div>
<div>
	&nbsp;551 &nbsp;* &nbsp; &nbsp;supports plugging, then @rfn may not be called immediately when requests</div>
<div>
	&nbsp;552 &nbsp;* &nbsp; &nbsp;are available on the queue, but may be called at some time later instead.</div>
<div>
	&nbsp;553 &nbsp;* &nbsp; &nbsp;Plugged queues are generally unplugged when a buffer belonging to one</div>
<div>
	&nbsp;554 &nbsp;* &nbsp; &nbsp;of the requests on the queue is needed, or due to memory pressure.</div>
<div>
	&nbsp;555 &nbsp;*</div>
<div>
	&nbsp;556 &nbsp;* &nbsp; &nbsp;@rfn is not required, or even expected, to remove all requests off the</div>
<div>
	&nbsp;557 &nbsp;* &nbsp; &nbsp;queue, but only as many as it can handle at a time. &nbsp;If it does leave</div>
<div>
	&nbsp;558 &nbsp;* &nbsp; &nbsp;requests on the queue, it is responsible for arranging that the requests</div>
<div>
	&nbsp;559 &nbsp;* &nbsp; &nbsp;get dealt with eventually.</div>
<div>
	&nbsp;560 &nbsp;*&nbsp;</div>
<div>
	&nbsp;561 &nbsp;* &nbsp; &nbsp;The queue spin lock must be held while manipulating the requests on the</div>
<div>
	&nbsp;562 &nbsp;* &nbsp; &nbsp;request queue; this lock will be taken also from interrupt context, so irq</div>
<div>
	&nbsp;563 &nbsp;* &nbsp; &nbsp;disabling is needed for it.</div>
<div>
	&nbsp;564 &nbsp;*&nbsp;</div>
<div>
	&nbsp;565 &nbsp;* &nbsp; &nbsp;Function returns a pointer to the initialized request queue, or %NULL if</div>
<div>
	&nbsp;566 &nbsp;* &nbsp; &nbsp;it didn&#39;t succeed.</div>
<div>
	&nbsp;567 &nbsp;* &nbsp; &nbsp;&nbsp;</div>
<div>
	&nbsp;568 &nbsp;* Note:</div>
<div>
	&nbsp;569 &nbsp;* &nbsp; &nbsp;blk_init_queue() must be paired with a blk_cleanup_queue() call</div>
<div>
	&nbsp;570 &nbsp;* &nbsp; &nbsp;when the block device is deactivated (such as at module unload).</div>
<div>
	&nbsp;571 &nbsp;**/</div>
<div>
	&nbsp;572 &nbsp; &nbsp; &nbsp; &nbsp;</div>
<div>
	&nbsp;573 struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)</div>
<div>
	&nbsp;574 { &nbsp;</div>
<div>
	&nbsp;575 &nbsp; &nbsp; return blk_init_queue_node(rfn, lock, -1);</div>
<div>
	&nbsp;576 }</div>
<div>
	&nbsp;</div>
<div>
	mmc_blk_probe-&gt;mmc_blk_alloc()-&gt;mmc_init_queue()-&gt;blk_init_queue-&gt;blk_init_queue_node</div>
<div>
	&nbsp;579 struct request_queue *</div>
<div>
	&nbsp;580 blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)</div>
<div>
	&nbsp;581 {</div>
<div>
	&nbsp;582 &nbsp; &nbsp; struct request_queue *q = blk_alloc_queue_node(GFP_KERNEL, node_id);</div>
<div>
	&nbsp;583&nbsp;</div>
<div>
	&nbsp;584 &nbsp; &nbsp; if (!q)</div>
<div>
	&nbsp;585 &nbsp; &nbsp; &nbsp; &nbsp; return NULL;</div>
<div>
	&nbsp;586&nbsp;</div>
<div>
	&nbsp;587 &nbsp; &nbsp; q-&gt;node = node_id;</div>
<div>
	&nbsp;588 &nbsp; &nbsp; if (blk_init_free_list(q)) {</div>
<div>
	&nbsp;589 &nbsp; &nbsp; &nbsp; &nbsp; kmem_cache_free(blk_requestq_cachep, q);</div>
<div>
	&nbsp;590 &nbsp; &nbsp; &nbsp; &nbsp; return NULL;</div>
<div>
	&nbsp;591 &nbsp; &nbsp; }</div>
<div>
	&nbsp;592&nbsp;</div>
<div>
	&nbsp;593 &nbsp; &nbsp; /*</div>
<div>
	&nbsp;594 &nbsp; &nbsp; &nbsp;* if caller didn&#39;t supply a lock, they get per-queue locking with</div>
<div>
	&nbsp;595 &nbsp; &nbsp; &nbsp;* our embedded lock</div>
<div>
	&nbsp;596 &nbsp; &nbsp; &nbsp;*/</div>
<div>
	&nbsp;597 &nbsp; &nbsp; if (!lock)</div>
<div>
	&nbsp;598 &nbsp; &nbsp; &nbsp; &nbsp; lock = &amp;q-&gt;__queue_lock;</div>
<div>
	&nbsp;599&nbsp;</div>
<div>
	&nbsp;600 &nbsp; &nbsp; q-&gt;request_fn &nbsp; &nbsp; &nbsp; = rfn;</div>
<div>
	&nbsp;601 &nbsp; &nbsp; q-&gt;prep_rq_fn &nbsp; &nbsp; &nbsp; = NULL;</div>
<div>
	&nbsp;602 &nbsp; &nbsp; q-&gt;unplug_fn &nbsp; &nbsp; &nbsp; &nbsp;= generic_unplug_device;</div>
<div>
	&nbsp;603 &nbsp; &nbsp; q-&gt;queue_flags &nbsp; &nbsp; &nbsp;= QUEUE_FLAG_DEFAULT;</div>
<div>
	&nbsp;604 &nbsp; &nbsp; q-&gt;queue_lock &nbsp; &nbsp; &nbsp; = lock;</div>
<div>
	&nbsp;605&nbsp;</div>
<div>
	&nbsp;606 &nbsp; &nbsp; blk_queue_segment_boundary(q, BLK_SEG_BOUNDARY_MASK);</div>
<div>
	&nbsp;607&nbsp;</div>
<div>
	&nbsp;608 &nbsp; &nbsp; blk_queue_make_request(q, __make_request);</div>
<div>
	&nbsp;609 &nbsp; &nbsp; blk_queue_max_segment_size(q, MAX_SEGMENT_SIZE);</div>
<div>
	&nbsp;610&nbsp;</div>
<div>
	&nbsp;611 &nbsp; &nbsp; blk_queue_max_hw_segments(q, MAX_HW_SEGMENTS);</div>
<div>
	&nbsp;612 &nbsp; &nbsp; blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS);</div>
<div>
	&nbsp;613&nbsp;</div>
<div>
	&nbsp;614 &nbsp; &nbsp; q-&gt;sg_reserved_size = INT_MAX;</div>
<div>
	&nbsp;615&nbsp;</div>
<div>
	&nbsp;616 &nbsp; &nbsp; blk_set_cmd_filter_defaults(&amp;q-&gt;cmd_filter);</div>
<div>
	&nbsp;617&nbsp;</div>
<div>
	&nbsp;618 &nbsp; &nbsp; /*</div>
<div>
	&nbsp;619 &nbsp; &nbsp; &nbsp;* all done</div>
<div>
	&nbsp;620 &nbsp; &nbsp; &nbsp;*/</div>
<div>
	&nbsp;621 &nbsp; &nbsp; if (!elevator_init(q, NULL)) {</div>
<div>
	&nbsp;622 &nbsp; &nbsp; &nbsp; &nbsp; blk_queue_congestion_threshold(q);</div>
<div>
	&nbsp;623 &nbsp; &nbsp; &nbsp; &nbsp; return q;</div>
<div>
	&nbsp;624 &nbsp; &nbsp; }</div>
<div>
	在这里，又定义了几个通用的参数:</div>
<div>
	queue-&gt;unplug_fn=generic_unplug_device</div>
<div>
	queue-&gt;make_request_fn=__make_request,这个函数太通用了</div>
<div>
	queue-&gt;seg_boundary_mask,这个是合并的规则，默认是0xFFFFFFFF</div>
<div>
	queue-&gt;max_segment_size,最大的segment是2^16B(64KB)</div>
<div>
	queue-&gt;max_hw_segments，最多的segment数目(128)</div>
<div>
	queue-&gt;max_phys_segments，最多物理段数目(128)</div>
<div>
	第608行，通过调用blk_queue_make_request，设置了一个关键的数据结构，queue-&gt;unplug_timer，它决定了request执行的时机。</div>
<div>
	第616行，设置一些标志位，这些标志位起到filter的作用。在request的执行中起作用。</div>
<div>
	621行设置了I/O调度算法，默认采用&ldquo;anticipatory&rdquo;,不过将之改为none也可以。各个块设备的request与电梯调度中的queue是什么关系呢？</div>
<div>
	mmc_blk_probe-&gt;mmc_blk_alloc()-&gt;mmc_init_queue()-&gt;blk_init_queue-&gt;blk_init_queue_node-&gt;blk_queue_make_request</div>
<div>
	&nbsp;98 /**</div>
<div>
	&nbsp;99 &nbsp;* blk_queue_make_request &#8211; define an alternate make_request function for a device</div>
<div>
	100 &nbsp;* @q: &nbsp;the request queue for the device to be affected</div>
<div>
	101 &nbsp;* @mfn: the alternate make_request function</div>
<div>
	102 &nbsp;*&nbsp;</div>
<div>
	103 &nbsp;* Description:</div>
<div>
	104 &nbsp;* &nbsp; &nbsp;The normal way for &amp;struct bios to be passed to a device</div>
<div>
	105 &nbsp;* &nbsp; &nbsp;driver is for them to be collected into requests on a request</div>
<div>
	106 &nbsp;* &nbsp; &nbsp;queue, and then to allow the device driver to select requests</div>
<div>
	107 &nbsp;* &nbsp; &nbsp;off that queue when it is ready. &nbsp;This works well for many block</div>
<div>
	108 &nbsp;* &nbsp; &nbsp;devices. However some block devices (typically virtual devices</div>
<div>
	109 &nbsp;* &nbsp; &nbsp;such as md or lvm) do not benefit from the processing on the</div>
<div>
	110 &nbsp;* &nbsp; &nbsp;request queue, and are served best by having the requests passed</div>
<div>
	111 &nbsp;* &nbsp; &nbsp;directly to them. &nbsp;This can be achieved by providing a function</div>
<div>
	112 &nbsp;* &nbsp; &nbsp;to blk_queue_make_request().</div>
<div>
	113 &nbsp;*&nbsp;</div>
<div>
	114 &nbsp;* Caveat:</div>
<div>
	115 &nbsp;* &nbsp; &nbsp;The driver that does this *must* be able to deal appropriately</div>
<div>
	116 &nbsp;* &nbsp; &nbsp;with buffers in &quot;highmemory&quot;. This can be accomplished by either calling</div>
<div>
	117 &nbsp;* &nbsp; &nbsp;__bio_kmap_atomic() to get a temporary kernel mapping, or by calling</div>
<div>
	118 &nbsp;* &nbsp; &nbsp;blk_queue_bounce() to create a buffer in normal memory.</div>
<div>
	119 &nbsp;**/</div>
<div>
	120 void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)</div>
<div>
	121 { &nbsp;</div>
<div>
	122 &nbsp; &nbsp; /*</div>
<div>
	123 &nbsp; &nbsp; &nbsp;* set defaults</div>
<div>
	124 &nbsp; &nbsp; &nbsp;*/</div>
<div>
	125 &nbsp; &nbsp; q-&gt;nr_requests = BLKDEV_MAX_RQ;</div>
<div>
	126 &nbsp; &nbsp; blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS);</div>
<div>
	127 &nbsp; &nbsp; blk_queue_max_hw_segments(q, MAX_HW_SEGMENTS);</div>
<div>
	128 &nbsp; &nbsp; blk_queue_segment_boundary(q, BLK_SEG_BOUNDARY_MASK);</div>
<div>
	129 &nbsp; &nbsp; blk_queue_max_segment_size(q, MAX_SEGMENT_SIZE);</div>
<div>
	130 &nbsp; &nbsp;</div>
<div>
	131 &nbsp; &nbsp; q-&gt;make_request_fn = mfn;</div>
<div>
	132 &nbsp; &nbsp; q-&gt;backing_dev_info.ra_pages =&nbsp;</div>
<div>
	133 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;</div>
<div>
	134 &nbsp; &nbsp; q-&gt;backing_dev_info.state = 0;&nbsp;</div>
<div>
	135 &nbsp; &nbsp; q-&gt;backing_dev_info.capabilities = BDI_CAP_MAP_COPY;</div>
<div>
	136 &nbsp; &nbsp; blk_queue_max_sectors(q, SAFE_MAX_SECTORS);</div>
<div>
	137 &nbsp; &nbsp; blk_queue_hardsect_size(q, 512);</div>
<div>
	138 &nbsp; &nbsp; blk_queue_dma_alignment(q, 511);</div>
<div>
	139 &nbsp; &nbsp; blk_queue_congestion_threshold(q);</div>
<div>
	140 &nbsp; &nbsp; q-&gt;nr_batching = BLK_BATCH_REQ;</div>
<div>
	141 &nbsp; &nbsp;</div>
<div>
	142 &nbsp; &nbsp; q-&gt;unplug_thresh = 4; &nbsp; &nbsp; &nbsp; /* hmm */</div>
<div>
	143 &nbsp; &nbsp; q-&gt;unplug_delay = (3 * HZ) / 1000; &nbsp;/* 3 milliseconds */</div>
<div>
	144 &nbsp; &nbsp; if (q-&gt;unplug_delay == 0)</div>
<div>
	145 &nbsp; &nbsp; &nbsp; &nbsp; q-&gt;unplug_delay = 1;</div>
<div>
	146 &nbsp; &nbsp;</div>
<div>
	147 &nbsp; &nbsp; q-&gt;unplug_timer.function = blk_unplug_timeout;</div>
<div>
	148 &nbsp; &nbsp; q-&gt;unplug_timer.data = (unsigned long)q;</div>
<div>
	149 &nbsp; &nbsp;</div>
<div>
	150 &nbsp; &nbsp; /*</div>
<div>
	151 &nbsp; &nbsp; &nbsp;* by default assume old behaviour and bounce for any highmem page</div>
<div>
	152 &nbsp; &nbsp; &nbsp;*/</div>
<div>
	153 &nbsp; &nbsp; blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH);</div>
<div>
	154 } &nbsp;</div>
<div>
	每当插入一个request的时候，若请求队列为空，则会 blk_plug_device</div>
<div>
	__make_request</div>
<div>
	1248 &nbsp; &nbsp; if (!blk_queue_nonrot(q) &amp;&amp; elv_queue_empty(q))</div>
<div>
	1249 &nbsp; &nbsp; &nbsp; &nbsp; blk_plug_device(q);</div>
<div>
	1250 &nbsp; &nbsp; add_request(q, req);</div>
<div>
	__make_request-&gt;blk_plug_device</div>
<div>
	&nbsp;205 /*</div>
<div>
	&nbsp;206 &nbsp;* &quot;plug&quot; the device if there are no outstanding requests: this will</div>
<div>
	&nbsp;207 &nbsp;* force the transfer to start only after we have put all the requests</div>
<div>
	&nbsp;208 &nbsp;* on the list.</div>
<div>
	&nbsp;209 &nbsp;*</div>
<div>
	&nbsp;210 &nbsp;* This is called with interrupts off and no requests on the queue and</div>
<div>
	&nbsp;211 &nbsp;* with the queue lock held.</div>
<div>
	&nbsp;212 &nbsp;*/</div>
<div>
	&nbsp;213 void blk_plug_device(struct request_queue *q)</div>
<div>
	&nbsp;214 {</div>
<div>
	&nbsp;215 &nbsp; &nbsp; WARN_ON(!irqs_disabled());</div>
<div>
	&nbsp;216&nbsp;</div>
<div>
	&nbsp;217 &nbsp; &nbsp; /*</div>
<div>
	&nbsp;218 &nbsp; &nbsp; &nbsp;* don&#39;t plug a stopped queue, it must be paired with blk_start_queue()</div>
<div>
	&nbsp;219 &nbsp; &nbsp; &nbsp;* which will restart the queueing</div>
<div>
	&nbsp;220 &nbsp; &nbsp; &nbsp;*/</div>
<div>
	&nbsp;221 &nbsp; &nbsp; if (blk_queue_stopped(q))</div>
<div>
	&nbsp;222 &nbsp; &nbsp; &nbsp; &nbsp; return;</div>
<div>
	&nbsp;223&nbsp;</div>
<div>
	&nbsp;224 &nbsp; &nbsp; if (!queue_flag_test_and_set(QUEUE_FLAG_PLUGGED, q)) {</div>
<div>
	&nbsp;225 &nbsp; &nbsp; &nbsp; &nbsp; mod_timer(&amp;q-&gt;unplug_timer, jiffies + q-&gt;unplug_delay);</div>
<div>
	&nbsp;226 &nbsp; &nbsp; &nbsp; &nbsp; trace_block_plug(q);</div>
<div>
	&nbsp;227 &nbsp; &nbsp; }</div>
<div>
	&nbsp;228 }</div>
<div>
	通过225行的mod_timer来插入一个定时器。当定时器到期后，会执行timer.function，即blk_unplug_timeout。</div>
<div>
	&nbsp;316 void blk_unplug_timeout(unsigned long data)</div>
<div>
	&nbsp;317 { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
<div>
	&nbsp;318 &nbsp; &nbsp; struct request_queue *q = (struct request_queue *)data;</div>
<div>
	&nbsp;319 &nbsp; &nbsp;</div>
<div>
	&nbsp;320 &nbsp; &nbsp; trace_block_unplug_timer(q);</div>
<div>
	&nbsp;321 &nbsp; &nbsp; kblockd_schedule_work(q, &amp;q-&gt;unplug_work);</div>
<div>
	&nbsp;322 } &nbsp;</div>
<div>
	321行，定时器又调度了q-&gt;unplug_work。而q-&gt;unplug_work是什么时候定义的呢？</div>
<div>
	在前面的blk_init_queue_node就定义了的。</div>
<div>
	mmc_blk_probe-&gt;mmc_blk_alloc()-&gt;mmc_init_queue()-&gt;blk_init_queue-&gt;blk_init_queue_node</div>
<div>
	&nbsp;579 struct request_queue *</div>
<div>
	&nbsp;580 blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)</div>
<div>
	&nbsp;581 {</div>
<div>
	&nbsp;582 &nbsp; &nbsp; struct request_queue *q = blk_alloc_queue_node(GFP_KERNEL, node_id);</div>
<div>
	&nbsp;mmc_blk_probe-&gt;mmc_blk_alloc()-&gt;mmc_init_queue()-&gt;blk_init_queue-&gt;blk_init_queue_node-&gt;blk_alloc_queue_node</div>
<div>
	&nbsp;508 struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)</div>
<div>
	&nbsp;509 {</div>
<div>
	&nbsp;510 &nbsp; &nbsp; struct request_queue *q;</div>
<div>
	&nbsp;511 &nbsp; &nbsp; int err;</div>
<div>
	&nbsp;512&nbsp;</div>
<div>
	&nbsp;513 &nbsp; &nbsp; q = kmem_cache_alloc_node(blk_requestq_cachep,</div>
<div>
	&nbsp;514 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gfp_mask | __GFP_ZERO, node_id);</div>
<div>
	&nbsp;515 &nbsp; &nbsp; if (!q)</div>
<div>
	&nbsp;516 &nbsp; &nbsp; &nbsp; &nbsp; return NULL;</div>
<div>
	&nbsp;517&nbsp;</div>
<div>
	&nbsp;518 &nbsp; &nbsp; q-&gt;backing_dev_info.unplug_io_fn = blk_backing_dev_unplug;</div>
<div>
	&nbsp;519 &nbsp; &nbsp; q-&gt;backing_dev_info.unplug_io_data = q;</div>
<div>
	&nbsp;520 &nbsp; &nbsp; err = bdi_init(&amp;q-&gt;backing_dev_info);</div>
<div>
	&nbsp;521 &nbsp; &nbsp; if (err) {</div>
<div>
	&nbsp;522 &nbsp; &nbsp; &nbsp; &nbsp; kmem_cache_free(blk_requestq_cachep, q);</div>
<div>
	&nbsp;523 &nbsp; &nbsp; &nbsp; &nbsp; return NULL;</div>
<div>
	&nbsp;524 &nbsp; &nbsp; }</div>
<div>
	&nbsp;525&nbsp;</div>
<div>
	&nbsp;526 &nbsp; &nbsp; init_timer(&amp;q-&gt;unplug_timer);</div>
<div>
	&nbsp;527 &nbsp; &nbsp; setup_timer(&amp;q-&gt;timeout, blk_rq_timed_out_timer, (unsigned long) q);</div>
<div>
	&nbsp;528 &nbsp; &nbsp; INIT_LIST_HEAD(&amp;q-&gt;timeout_list);</div>
<div>
	&nbsp;529 &nbsp; &nbsp; INIT_WORK(&amp;q-&gt;unplug_work, blk_unplug_work);</div>
<div>
	&nbsp;530&nbsp;</div>
<div>
	&nbsp;531 &nbsp; &nbsp; kobject_init(&amp;q-&gt;kobj, &amp;blk_queue_ktype);</div>
<div>
	&nbsp;532&nbsp;</div>
<div>
	&nbsp;533 &nbsp; &nbsp; mutex_init(&amp;q-&gt;sysfs_lock);</div>
<div>
	&nbsp;534 &nbsp; &nbsp; spin_lock_init(&amp;q-&gt;__queue_lock);</div>
<div>
	&nbsp;535&nbsp;</div>
<div>
	&nbsp;536 &nbsp; &nbsp; return q;</div>
<div>
	&nbsp;537 }</div>
<div>
	在529行，定义了queue-&gt;unplug_work。(在第527行，竟然还有一个timer，这个q-&gt;timeout的timer什么时候用呢？)</div>
<div>
	queue-&gt;unplug_work的function是blk_unplug_work</div>
<div>
	blk_unplug_timeout-&gt;blk_unplug_work</div>
<div>
	&nbsp;307 void blk_unplug_work(struct work_struct *work)</div>
<div>
	&nbsp;308 {</div>
<div>
	&nbsp;309 &nbsp; &nbsp; struct request_queue *q =</div>
<div>
	&nbsp;310 &nbsp; &nbsp; &nbsp; &nbsp; container_of(work, struct request_queue, unplug_work);</div>
<div>
	&nbsp;311&nbsp;</div>
<div>
	&nbsp;312 &nbsp; &nbsp; trace_block_unplug_io(q);</div>
<div>
	&nbsp;313 &nbsp; &nbsp; q-&gt;unplug_fn(q);</div>
<div>
	&nbsp;314 }</div>
<div>
	第313行又调用了q-&gt;unplug_fn,即generic_unplug_device（在blk_init_queue_node中定义了）。</div>
<div>
	blk_unplug_timeout-&gt;blk_unplug_work-&gt;generic_unplug_device</div>
<div>
	&nbsp;278 /**</div>
<div>
	&nbsp;279 &nbsp;* generic_unplug_device &#8211; fire a request queue</div>
<div>
	&nbsp;280 &nbsp;* @q: &nbsp; &nbsp;The &amp;struct request_queue in question</div>
<div>
	&nbsp;281 &nbsp;*</div>
<div>
	&nbsp;282 &nbsp;* Description:</div>
<div>
	&nbsp;283 &nbsp;* &nbsp; Linux uses plugging to build bigger requests queues before letting</div>
<div>
	&nbsp;284 &nbsp;* &nbsp; the device have at them. If a queue is plugged, the I/O scheduler</div>
<div>
	&nbsp;285 &nbsp;* &nbsp; is still adding and merging requests on the queue. Once the queue</div>
<div>
	&nbsp;286 &nbsp;* &nbsp; gets unplugged, the request_fn defined for the queue is invoked and</div>
<div>
	&nbsp;287 &nbsp;* &nbsp; transfers started.</div>
<div>
	&nbsp;288 &nbsp;**/</div>
<div>
	&nbsp;289 void generic_unplug_device(struct request_queue *q)</div>
<div>
	&nbsp;290 {</div>
<div>
	&nbsp;291 &nbsp; &nbsp; if (blk_queue_plugged(q)) {</div>
<div>
	&nbsp;292 &nbsp; &nbsp; &nbsp; &nbsp; spin_lock_irq(q-&gt;queue_lock);</div>
<div>
	&nbsp;293 &nbsp; &nbsp; &nbsp; &nbsp; __generic_unplug_device(q);</div>
<div>
	&nbsp;294 &nbsp; &nbsp; &nbsp; &nbsp; spin_unlock_irq(q-&gt;queue_lock);</div>
<div>
	&nbsp;295 &nbsp; &nbsp; }</div>
<div>
	&nbsp;296 }</div>
<div>
	blk_unplug_timeout-&gt;blk_unplug_work-&gt;generic_unplug_device-&gt;__generic_unplug_device</div>
<div>
	&nbsp;268 void __generic_unplug_device(struct request_queue *q)</div>
<div>
	&nbsp;269 {</div>
<div>
	&nbsp;270 &nbsp; &nbsp; if (unlikely(blk_queue_stopped(q)))</div>
<div>
	&nbsp;271 &nbsp; &nbsp; &nbsp; &nbsp; return;</div>
<div>
	&nbsp;272 &nbsp; &nbsp; if (!blk_remove_plug(q) &amp;&amp; !blk_queue_nonrot(q))</div>
<div>
	&nbsp;273 &nbsp; &nbsp; &nbsp; &nbsp; return;</div>
<div>
	&nbsp;274&nbsp;</div>
<div>
	&nbsp;275 &nbsp; &nbsp; q-&gt;request_fn(q);</div>
<div>
	&nbsp;276 }</div>
<div>
	275行的request_fn对于mmc是mmc_request</div>
<div>
	blk_unplug_timeout-&gt;blk_unplug_work-&gt;generic_unplug_device-&gt;__generic_unplug_device-&gt;mmc_request</div>
<div>
	&nbsp;81 /*</div>
<div>
	&nbsp;82 &nbsp;* Generic MMC request handler. &nbsp;This is called for any queue on a</div>
<div>
	&nbsp;83 &nbsp;* particular host. &nbsp;When the host is not busy, we look for a request</div>
<div>
	&nbsp;84 &nbsp;* on any queue on this host, and attempt to issue it. &nbsp;This may</div>
<div>
	&nbsp;85 &nbsp;* not be the queue we were asked to process.</div>
<div>
	&nbsp;86 &nbsp;*/</div>
<div>
	&nbsp;87 static void mmc_request(struct request_queue *q)</div>
<div>
	&nbsp;88 {</div>
<div>
	&nbsp;89 &nbsp; &nbsp; struct mmc_queue *mq = q-&gt;queuedata;</div>
<div>
	&nbsp;90 &nbsp; &nbsp; struct request *req;</div>
<div>
	&nbsp;91 &nbsp; &nbsp; int ret;</div>
<div>
	&nbsp;92&nbsp;</div>
<div>
	&nbsp;93 &nbsp; &nbsp; if (!mq) {</div>
<div>
	&nbsp;94 &nbsp; &nbsp; &nbsp; &nbsp; printk(KERN_ERR &quot;MMC: killing requests for dead queue\n&quot;);</div>
<div>
	&nbsp;95 &nbsp; &nbsp; &nbsp; &nbsp; while ((req = elv_next_request(q)) != NULL) {</div>
<div>
	&nbsp;96 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; do {</div>
<div>
	&nbsp;97 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret = __blk_end_request(req, -EIO,</div>
<div>
	&nbsp;98 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; blk_rq_cur_bytes(req));</div>
<div>
	&nbsp;99 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } while (ret);</div>
<div>
	100 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	101 &nbsp; &nbsp; &nbsp; &nbsp; return;</div>
<div>
	102 &nbsp; &nbsp; }</div>
<div>
	103&nbsp;</div>
<div>
	104 &nbsp; &nbsp; if (!mq-&gt;req)</div>
<div>
	105 &nbsp; &nbsp; &nbsp; &nbsp; wake_up_process(mq-&gt;thread);//用线程来写</div>
<div>
	106 }</div>
<div>
	在105行,mmc又唤醒了mq-&gt;thread.即</div>
<div>
	mmc_init_queue</div>
<div>
	202 &nbsp; &nbsp; init_MUTEX(&amp;mq-&gt;thread_sem);</div>
<div>
	203&nbsp;</div>
<div>
	204 &nbsp; &nbsp; mq-&gt;thread = kthread_run(mmc_queue_thread, mq, &quot;mmcqd&quot;);</div>
<div>
	205 &nbsp; &nbsp; if (IS_ERR(mq-&gt;thread)) {</div>
<div>
	206 &nbsp; &nbsp; &nbsp; &nbsp; ret = PTR_ERR(mq-&gt;thread);</div>
<div>
	207 &nbsp; &nbsp; &nbsp; &nbsp; goto free_bounce_sg;</div>
<div>
	208 &nbsp; &nbsp; }</div>
<div>
	blk_unplug_timeout-&gt;blk_unplug_work-&gt;generic_unplug_device-&gt;__generic_unplug_device-&gt;mmc_request-&gt;mmc_queue_thread</div>
<div>
	&nbsp;44 static int mmc_queue_thread(void *d)</div>
<div>
	&nbsp;45 {</div>
<div>
	&nbsp;46 &nbsp; &nbsp; struct mmc_queue *mq = d;</div>
<div>
	&nbsp;47 &nbsp; &nbsp; struct request_queue *q = mq-&gt;queue;</div>
<div>
	&nbsp;48&nbsp;</div>
<div>
	&nbsp;49 &nbsp; &nbsp; current-&gt;flags |= PF_MEMALLOC;</div>
<div>
	&nbsp;50&nbsp;</div>
<div>
	&nbsp;51 &nbsp; &nbsp; down(&amp;mq-&gt;thread_sem);</div>
<div>
	&nbsp;52 &nbsp; &nbsp; do {</div>
<div>
	&nbsp;53 &nbsp; &nbsp; &nbsp; &nbsp; struct request *req = NULL;</div>
<div>
	&nbsp;54&nbsp;</div>
<div>
	&nbsp;55 &nbsp; &nbsp; &nbsp; &nbsp; spin_lock_irq(q-&gt;queue_lock);</div>
<div>
	&nbsp;56 &nbsp; &nbsp; &nbsp; &nbsp; set_current_state(TASK_INTERRUPTIBLE);</div>
<div>
	&nbsp;57 &nbsp; &nbsp; &nbsp; &nbsp; if (!blk_queue_plugged(q))</div>
<div>
	&nbsp;58 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; req = elv_next_request(q);</div>
<div>
	&nbsp;59 &nbsp; &nbsp; &nbsp; &nbsp; mq-&gt;req = req;</div>
<div>
	&nbsp;60 &nbsp; &nbsp; &nbsp; &nbsp; spin_unlock_irq(q-&gt;queue_lock);</div>
<div>
	&nbsp;61&nbsp;</div>
<div>
	&nbsp;62 &nbsp; &nbsp; &nbsp; &nbsp; if (!req) {</div>
<div>
	&nbsp;63 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (kthread_should_stop()) {</div>
<div>
	&nbsp;64 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; set_current_state(TASK_RUNNING);</div>
<div>
	&nbsp;65 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;</div>
<div>
	&nbsp;66 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	&nbsp;67 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; up(&amp;mq-&gt;thread_sem);</div>
<div>
	&nbsp;68 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; schedule();</div>
<div>
	&nbsp;69 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; down(&amp;mq-&gt;thread_sem);</div>
<div>
	&nbsp;70 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;</div>
<div>
	&nbsp;71 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	&nbsp;72 &nbsp; &nbsp; &nbsp; &nbsp; set_current_state(TASK_RUNNING);</div>
<div>
	&nbsp;73&nbsp;</div>
<div>
	&nbsp;74 &nbsp; &nbsp; &nbsp; &nbsp; mq-&gt;issue_fn(mq, req);</div>
<div>
	&nbsp;75 &nbsp; &nbsp; } while (1);</div>
<div>
	&nbsp;76 &nbsp; &nbsp; up(&amp;mq-&gt;thread_sem);</div>
<div>
	&nbsp;77&nbsp;</div>
<div>
	&nbsp;78 &nbsp; &nbsp; return 0;</div>
<div>
	&nbsp;79 }</div>
<div>
	&nbsp;80&nbsp;</div>
<div>
	而，该线程所做的事情是，从请求队列上取下一个request，然后用mq-&gt;issue_fn去执行。mq-&gt;issue_fn是在mmc_blk_probe-&gt;mmc_blk_alloc中定义的：mmc_blk_issue_rq</div>
<div>
	mmc_queue_thread-&gt;mmc_blk_issue_rq:</div>
<div>
	264 static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)</div>
<div>
	265 {</div>
<div>
	266 &nbsp; &nbsp; struct mmc_blk_data *md = mq-&gt;data;</div>
<div>
	267 &nbsp; &nbsp; struct mmc_card *card = md-&gt;queue.card;</div>
<div>
	268 &nbsp; &nbsp; struct mmc_blk_request brq;</div>
<div>
	269 &nbsp; &nbsp; int ret = 1, disable_multi = 0;</div>
<div>
	270&nbsp;</div>
<div>
	271 #ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME</div>
<div>
	272 &nbsp; &nbsp; if (mmc_bus_needs_resume(card-&gt;host)) {</div>
<div>
	273 &nbsp; &nbsp; &nbsp; &nbsp; mmc_resume_bus(card-&gt;host);</div>
<div>
	274 &nbsp; &nbsp; &nbsp; &nbsp; mmc_blk_set_blksize(md, card);</div>
<div>
	275 &nbsp; &nbsp; }</div>
<div>
	276 #endif</div>
<div>
	277&nbsp;</div>
<div>
	278 &nbsp; &nbsp; mmc_claim_host(card-&gt;host);</div>
<div>
	279&nbsp;</div>
<div>
	280 &nbsp; &nbsp; do {</div>
<div>
	281 &nbsp; &nbsp; &nbsp; &nbsp; struct mmc_command cmd;</div>
<div>
	282 &nbsp; &nbsp; &nbsp; &nbsp; u32 readcmd, writecmd, status = 0;</div>
<div>
	283&nbsp;</div>
<div>
	284 &nbsp; &nbsp; &nbsp; &nbsp; memset(&amp;brq, 0, sizeof(struct mmc_blk_request));</div>
<div>
	285 &nbsp; &nbsp; &nbsp; &nbsp; brq.mrq.cmd = &amp;brq.cmd;</div>
<div>
	286 &nbsp; &nbsp; &nbsp; &nbsp; brq.mrq.data = &amp;brq.data;</div>
<div>
	287&nbsp;</div>
<div>
	288 &nbsp; &nbsp; &nbsp; &nbsp; brq.cmd.arg = req-&gt;sector;</div>
<div>
	289 &nbsp; &nbsp; &nbsp; &nbsp; if (!mmc_card_blockaddr(card))</div>
<div>
	290 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; brq.cmd.arg &lt;&lt;= 9;</div>
<div>
	291 &nbsp; &nbsp; &nbsp; &nbsp; brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;</div>
<div>
	292 &nbsp; &nbsp; &nbsp; &nbsp; brq.data.blksz = 512;</div>
<div>
	293 &nbsp; &nbsp; &nbsp; &nbsp; brq.stop.opcode = MMC_STOP_TRANSMISSION;</div>
<div>
	294 &nbsp; &nbsp; &nbsp; &nbsp; brq.stop.arg = 0;</div>
<div>
	295 &nbsp; &nbsp; &nbsp; &nbsp; brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;</div>
<div>
	296 &nbsp; &nbsp; &nbsp; &nbsp; brq.data.blocks = req-&gt;nr_sectors;</div>
<div>
	297&nbsp;</div>
<div>
	298 &nbsp; &nbsp; &nbsp; &nbsp; /*</div>
<div>
	299 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* The block layer doesn&#39;t support all sector count</div>
<div>
	300 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* restrictions, so we need to be prepared for too big</div>
<div>
	301 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* requests.</div>
<div>
	302 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</div>
<div>
	303 &nbsp; &nbsp; &nbsp; &nbsp; if (brq.data.blocks &gt; card-&gt;host-&gt;max_blk_count)</div>
<div>
	304 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; brq.data.blocks = card-&gt;host-&gt;max_blk_count;</div>
<div>
	305&nbsp;</div>
<div>
	306 &nbsp; &nbsp; &nbsp; &nbsp; /*</div>
<div>
	307 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* After a read error, we redo the request one sector at a time</div>
<div>
	308 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* in order to accurately determine which sectors can be read</div>
<div>
	309 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* successfully.</div>
<div>
	310 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</div>
<div>
	311 &nbsp; &nbsp; &nbsp; &nbsp; if (disable_multi &amp;&amp; brq.data.blocks &gt; 1)</div>
<div>
	312 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; brq.data.blocks = 1;</div>
<div>
	313&nbsp;</div>
<div>
	314 &nbsp; &nbsp; &nbsp; &nbsp; if (brq.data.blocks &gt; 1) {</div>
<div>
	315 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* SPI multiblock writes terminate using a special</div>
<div>
	316 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* token, not a STOP_TRANSMISSION request.</div>
<div>
	317 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</div>
<div>
	318 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!mmc_host_is_spi(card-&gt;host)</div>
<div>
	319 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; || rq_data_dir(req) == READ)</div>
<div>
	320 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; brq.mrq.stop = &amp;brq.stop;</div>
<div>
	321 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; readcmd = MMC_READ_MULTIPLE_BLOCK;</div>
<div>
	322 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; writecmd = MMC_WRITE_MULTIPLE_BLOCK;</div>
<div>
	323 &nbsp; &nbsp; &nbsp; &nbsp; } else {</div>
<div>
	324 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; brq.mrq.stop = NULL;</div>
<div>
	325 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; readcmd = MMC_READ_SINGLE_BLOCK;</div>
<div>
	326 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; writecmd = MMC_WRITE_BLOCK;</div>
<div>
	327 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	328&nbsp;</div>
<div>
	329 &nbsp; &nbsp; &nbsp; &nbsp; if (rq_data_dir(req) == READ) {</div>
<div>
	330 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; brq.cmd.opcode = readcmd;</div>
<div>
	331 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; brq.data.flags |= MMC_DATA_READ;</div>
<div>
	332 &nbsp; &nbsp; &nbsp; &nbsp; } else {</div>
<div>
	333 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; brq.cmd.opcode = writecmd;</div>
<div>
	334 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; brq.data.flags |= MMC_DATA_WRITE;</div>
<div>
	335 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	336&nbsp;</div>
<div>
	337 &nbsp; &nbsp; &nbsp; &nbsp; mmc_set_data_timeout(&amp;brq.data, card);</div>
<div>
	338&nbsp;</div>
<div>
	339 &nbsp; &nbsp; &nbsp; &nbsp; brq.data.sg = mq-&gt;sg;</div>
<div>
	340 &nbsp; &nbsp; &nbsp; &nbsp; brq.data.sg_len = mmc_queue_map_sg(mq);</div>
<div>
	341&nbsp;</div>
<div>
	342 &nbsp; &nbsp; &nbsp; &nbsp; /*</div>
<div>
	343 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* Adjust the sg list so it is the same size as the</div>
<div>
	344 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* request.</div>
<div>
	345 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</div>
<div>
	346 &nbsp; &nbsp; &nbsp; &nbsp; if (brq.data.blocks != req-&gt;nr_sectors) {</div>
<div>
	347 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int i, data_size = brq.data.blocks &lt;&lt; 9;</div>
<div>
	348 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; struct scatterlist *sg;</div>
<div>
	349&nbsp;</div>
<div>
	350 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for_each_sg(brq.data.sg, sg, brq.data.sg_len, i) {</div>
<div>
	351 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data_size -= sg-&gt;length;</div>
<div>
	352 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (data_size &lt;= 0) {</div>
<div>
	353 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sg-&gt;length += data_size;</div>
<div>
	354 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; i++;</div>
<div>
	355 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;</div>
<div>
	356 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	357 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	358 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; brq.data.sg_len = i;</div>
<div>
	359 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	360&nbsp;</div>
<div>
	361 &nbsp; &nbsp; &nbsp; &nbsp; mmc_queue_bounce_pre(mq);</div>
<div>
	362&nbsp;</div>
<div>
	363 &nbsp; &nbsp; &nbsp; &nbsp; mmc_wait_for_req(card-&gt;host, &amp;brq.mrq);</div>
<div>
	364&nbsp;</div>
<div>
	365 &nbsp; &nbsp; &nbsp; &nbsp; mmc_queue_bounce_post(mq);</div>
<div>
	366&nbsp;</div>
<div>
	367 &nbsp; &nbsp; &nbsp; &nbsp; /*</div>
<div>
	368 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* Check for errors here, but don&#39;t jump to cmd_err</div>
<div>
	369 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* until later as we need to wait for the card to leave</div>
<div>
	370 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* programming mode even when things go wrong.</div>
<div>
	371 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</div>
<div>
	372 &nbsp; &nbsp; &nbsp; &nbsp; if (brq.cmd.error || brq.data.error || brq.stop.error) {</div>
<div>
	373 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (brq.data.blocks &gt; 1 &amp;&amp; rq_data_dir(req) == READ) {</div>
<div>
	374 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* Redo read one sector at a time */</div>
<div>
	375 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printk(KERN_WARNING &quot;%s: retrying using single &quot;</div>
<div>
	376 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&quot;block read\n&quot;, req-&gt;rq_disk-&gt;disk_name);</div>
<div>
	377 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; disable_multi = 1;</div>
<div>
	378 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;</div>
<div>
	379 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	380 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; status = get_card_status(card, req);</div>
<div>
	381 &nbsp; &nbsp; &nbsp; &nbsp; } else if (disable_multi == 1) {</div>
<div>
	382 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; disable_multi = 0;</div>
<div>
	383 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	384&nbsp;</div>
<div>
	385 &nbsp; &nbsp; &nbsp; &nbsp; if (brq.cmd.error) {</div>
<div>
	386 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printk(KERN_ERR &quot;%s: error %d sending read/write &quot;</div>
<div>
	387 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&quot;command, response %#x, card status %#x\n&quot;,</div>
<div>
	388 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;req-&gt;rq_disk-&gt;disk_name, brq.cmd.error,</div>
<div>
	389 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;brq.cmd.resp[0], status);</div>
<div>
	390 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	391&nbsp;</div>
<div>
	392 &nbsp; &nbsp; &nbsp; &nbsp; if (brq.data.error) {</div>
<div>
	393 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (brq.data.error == -ETIMEDOUT &amp;&amp; brq.mrq.stop)</div>
<div>
	394 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* &#39;Stop&#39; response contains card status */</div>
<div>
	395 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; status = brq.mrq.stop-&gt;resp[0];</div>
<div>
	396 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printk(KERN_ERR &quot;%s: error %d transferring data,&quot;</div>
<div>
	397 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&quot; sector %u, nr %u, card status %#x\n&quot;,</div>
<div>
	398 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;req-&gt;rq_disk-&gt;disk_name, brq.data.error,</div>
<div>
	399 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(unsigned)req-&gt;sector,</div>
<div>
	400 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(unsigned)req-&gt;nr_sectors, status);</div>
<div>
	401 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	402&nbsp;</div>
<div>
	403 &nbsp; &nbsp; &nbsp; &nbsp; if (brq.stop.error) {</div>
<div>
	404 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printk(KERN_ERR &quot;%s: error %d sending stop command, &quot;</div>
<div>
	405 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&quot;response %#x, card status %#x\n&quot;,</div>
<div>
	406 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;req-&gt;rq_disk-&gt;disk_name, brq.stop.error,</div>
<div>
	407 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;brq.stop.resp[0], status);</div>
<div>
	408 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	409&nbsp;</div>
<div>
	410 &nbsp; &nbsp; &nbsp; &nbsp; if (!mmc_host_is_spi(card-&gt;host) &amp;&amp; rq_data_dir(req) != READ) {</div>
<div>
	411 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; do {</div>
<div>
	412 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int err;</div>
<div>
	413&nbsp;</div>
<div>
	414 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cmd.opcode = MMC_SEND_STATUS;</div>
<div>
	415 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cmd.arg = card-&gt;rca &lt;&lt; 16;</div>
<div>
	416 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;</div>
<div>
	417 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err = mmc_wait_for_cmd(card-&gt;host, &amp;cmd, 5);</div>
<div>
	418 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (err) {</div>
<div>
	419 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printk(KERN_ERR &quot;%s: error %d requesting status\n&quot;,</div>
<div>
	420 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;req-&gt;rq_disk-&gt;disk_name, err);</div>
<div>
	421 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; goto cmd_err;</div>
<div>
	422 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	423 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /*</div>
<div>
	424 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* Some cards mishandle the status bits,</div>
<div>
	425 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* so make sure to check both the busy</div>
<div>
	426 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* indication and the card state.</div>
<div>
	427 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</div>
<div>
	428 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } while (!(cmd.resp[0] &amp; R1_READY_FOR_DATA) ||</div>
<div>
	429 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (R1_CURRENT_STATE(cmd.resp[0]) == 7));</div>
<div>
	430&nbsp;</div>
<div>
	431 #if 0</div>
<div>
	432 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (cmd.resp[0] &amp; ~0&#215;00000900)</div>
<div>
	433 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printk(KERN_ERR &quot;%s: status = %08x\n&quot;,</div>
<div>
	434 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;req-&gt;rq_disk-&gt;disk_name, cmd.resp[0]);</div>
<div>
	435 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (mmc_decode_status(cmd.resp))</div>
<div>
	436 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; goto cmd_err;</div>
<div>
	437 #endif</div>
<div>
	438 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	439&nbsp;</div>
<div>
	440 &nbsp; &nbsp; &nbsp; &nbsp; if (brq.cmd.error || brq.stop.error || brq.data.error) {</div>
<div>
	441 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (rq_data_dir(req) == READ) {</div>
<div>
	442 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /*</div>
<div>
	443 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* After an error, we redo I/O one sector at a</div>
<div>
	444 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* time, so we only reach here after trying to</div>
<div>
	445 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* read a single sector.</div>
<div>
	446 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</div>
<div>
	447 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; spin_lock_irq(&amp;md-&gt;lock);</div>
<div>
	448 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret = __blk_end_request(req, -EIO, brq.data.blksz);</div>
<div>
	449 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; spin_unlock_irq(&amp;md-&gt;lock);</div>
<div>
	450 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;</div>
<div>
	451 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	452 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; goto cmd_err;</div>
<div>
	453 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	454&nbsp;</div>
<div>
	455 &nbsp; &nbsp; &nbsp; &nbsp; /*</div>
<div>
	456 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* A block was successfully transferred.</div>
<div>
	457 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</div>
<div>
	458 &nbsp; &nbsp; &nbsp; &nbsp; spin_lock_irq(&amp;md-&gt;lock);</div>
<div>
	459 &nbsp; &nbsp; &nbsp; &nbsp; ret = __blk_end_request(req, 0, brq.data.bytes_xfered);</div>
<div>
	460 &nbsp; &nbsp; &nbsp; &nbsp; spin_unlock_irq(&amp;md-&gt;lock);</div>
<div>
	461 &nbsp; &nbsp; } while (ret);</div>
<div>
	462&nbsp;</div>
<div>
	463 &nbsp; &nbsp; mmc_release_host(card-&gt;host);</div>
<div>
	464&nbsp;</div>
<div>
	465 &nbsp; &nbsp; return 1;</div>
<div>
	466&nbsp;</div>
<div>
	467 &nbsp;cmd_err:</div>
<div>
	468 &nbsp; &nbsp; /*</div>
<div>
	469 &nbsp; &nbsp; &nbsp;* If this is an SD card and we&#39;re writing, we can first</div>
<div>
	470 &nbsp; &nbsp; &nbsp;* mark the known good sectors as ok.</div>
<div>
	471 &nbsp; &nbsp; &nbsp;*</div>
<div>
	472 &nbsp; &nbsp; &nbsp;* If the card is not SD, we can still ok written sectors</div>
<div>
	473 &nbsp; &nbsp; &nbsp;* as reported by the controller (which might be less than</div>
<div>
	474 &nbsp; &nbsp; &nbsp;* the real number of written sectors, but never more).</div>
<div>
	475 &nbsp; &nbsp; &nbsp;*/</div>
<div>
	476 &nbsp; &nbsp; if (mmc_card_sd(card)) {</div>
<div>
	477 &nbsp; &nbsp; &nbsp; &nbsp; u32 blocks;</div>
<div>
	478&nbsp;</div>
<div>
	479 &nbsp; &nbsp; &nbsp; &nbsp; blocks = mmc_sd_num_wr_blocks(card);</div>
<div>
	480 &nbsp; &nbsp; &nbsp; &nbsp; if (blocks != (u32)-1) {</div>
<div>
	481 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; spin_lock_irq(&amp;md-&gt;lock);</div>
<div>
	482 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret = __blk_end_request(req, 0, blocks &lt;&lt; 9);</div>
<div>
	483 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; spin_unlock_irq(&amp;md-&gt;lock);</div>
<div>
	484 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	485 &nbsp; &nbsp; } else {</div>
<div>
	486 &nbsp; &nbsp; &nbsp; &nbsp; spin_lock_irq(&amp;md-&gt;lock);</div>
<div>
	487 &nbsp; &nbsp; &nbsp; &nbsp; ret = __blk_end_request(req, 0, brq.data.bytes_xfered);</div>
<div>
	488 &nbsp; &nbsp; &nbsp; &nbsp; spin_unlock_irq(&amp;md-&gt;lock);</div>
<div>
	489 &nbsp; &nbsp; }</div>
<div>
	490&nbsp;</div>
<div>
	491 &nbsp; &nbsp; mmc_release_host(card-&gt;host);</div>
<div>
	492&nbsp;</div>
<div>
	493 &nbsp; &nbsp; spin_lock_irq(&amp;md-&gt;lock);</div>
<div>
	494 &nbsp; &nbsp; while (ret)</div>
<div>
	495 &nbsp; &nbsp; &nbsp; &nbsp; ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));</div>
<div>
	496 &nbsp; &nbsp; spin_unlock_irq(&amp;md-&gt;lock);</div>
<div>
	497&nbsp;</div>
<div>
	498 &nbsp; &nbsp; return 0;</div>
<div>
	499 }</div>
<div>
	500&nbsp;</div>
<div>
	该函数所做的事情就是将mmc-&gt;host锁住，然后让host进行操作。操作最重要的是363行：mmc_wait_for_req(card-&gt;host, &amp;brq.mrq)</div>
<div>
	mmc_queue_thread-&gt;mmc_blk_issue_rq-&gt;mmc_wait_for_req</div>
<div>
	&nbsp;186 /** &nbsp; &nbsp; &nbsp; &nbsp;</div>
<div>
	&nbsp;187 &nbsp;* &nbsp;mmc_wait_for_req &#8211; start a request and wait for completion</div>
<div>
	&nbsp;188 &nbsp;* &nbsp;@host: MMC host to start command</div>
<div>
	&nbsp;189 &nbsp;* &nbsp;@mrq: MMC request to start</div>
<div>
	&nbsp;190 &nbsp;* &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div>
<div>
	&nbsp;191 &nbsp;* &nbsp;Start a new MMC custom command request for a host, and wait</div>
<div>
	&nbsp;192 &nbsp;* &nbsp;for the command to complete. Does not attempt to parse the</div>
<div>
	&nbsp;193 &nbsp;* &nbsp;response. &nbsp; &nbsp; &nbsp;</div>
<div>
	&nbsp;194 &nbsp;*/ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
<div>
	&nbsp;195 void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)</div>
<div>
	&nbsp;196 { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
<div>
	&nbsp;197 &nbsp; &nbsp; DECLARE_COMPLETION_ONSTACK(complete);</div>
<div>
	&nbsp;198&nbsp;</div>
<div>
	&nbsp;199 &nbsp; &nbsp; mrq-&gt;done_data = &amp;complete;</div>
<div>
	&nbsp;200 &nbsp; &nbsp; mrq-&gt;done = mmc_wait_done;</div>
<div>
	&nbsp;201 &nbsp; &nbsp; &nbsp; &nbsp;</div>
<div>
	&nbsp;202 &nbsp; &nbsp; mmc_start_request(host, mrq);//maybe have a long time.</div>
<div>
	&nbsp;203 &nbsp; &nbsp; &nbsp; &nbsp;</div>
<div>
	&nbsp;204 &nbsp; &nbsp; wait_for_completion(&amp;complete);//wait until the data completed.the sem also anipulated by interrupt.</div>
<div>
	&nbsp;205 } &nbsp; &nbsp; &nbsp;</div>
<div>
	&nbsp;206&nbsp;</div>
<div>
	&nbsp;207 EXPORT_SYMBOL(mmc_wait_for_req);</div>
<div>
	mmc_start_request，就开始操作了。什么时候操作结束呢？要看complete了。complete中的done变量，在数据读取结束时，有中断来将其+1。然后本线程检测到后，就可以结束了。</div>
<div>
	&nbsp;</div>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/10/08/sd-read-write-op/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从Nand驱动到文件读写</title>
		<link>http://www.tek-life.org/2011/10/07/from-nand-driver-to-yaffs2/</link>
		<comments>http://www.tek-life.org/2011/10/07/from-nand-driver-to-yaffs2/#comments</comments>
		<pubDate>Fri, 07 Oct 2011 07:44:04 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[File System]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10663</guid>
		<description><![CDATA[Nand属于块设备。那么nand块设备是否像其他块设备那样，每次读写都经历一个&#8220;C/S&#8221;的过程呢？ 我们在Goldfish Platform上，从nand的驱动注册开始，看看nand之上的yaffs2文件读写到底是怎样的一个过程。 本文主要是对自己在学习过程中遇到疑问做一个记录，同以前的文章一样，基本上只有流程，那些原理之类的东西，请同学们google吧。在下文中，有些代码可能会有重复，主要目的是不想让各位看官看的太累，跳来跳去，眼镜受不了啊。 代码是Android Kernel 2.6.29.整个记录过程比较仓促，难免会由认识上的错误，欢迎大家指正。 下面是Android在Goldfish Platform上的执行流程： &#60;1&#62; 377 static int __init init_mtdblock(void)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 378 {&#160; 379&#160;&#160;&#160;&#160; return register_mtd_blktrans(&#38;mtdblock_tr); 380 }&#160; 在代码片段&#60;1&#62;中注册了一个struct mtd_blktrans_ops结构的mtdblock_tr,这个模块是系统在启动过程中加载的，从模块的init名字，可以看出，是针对mtd块设备的。由于在Linux中，Nand被归为MTD设备，MTD设备就是将nand设备封装了一下，让上层没有直接看到nand，而是看到的MTD。实际上，通过MTD来操作Nand，还是通过nand内部的驱动函数。不要把MTD看的太过神秘。如果还需要了解，请Google吧，我之前就是太较真了，一直没有弄明白，read the fucking code之后才算明白过来了。 这个mtd_blktrans_ops结构如下： &#60;2&#62; &#160;32 struct mtd_blktrans_ops { &#160;33&#160;&#160;&#160;&#160; char *name; &#160;34&#160;&#160;&#160;&#160; int major; &#160;35&#160;&#160;&#160;&#160; &#8230; <a href="http://www.tek-life.org/2011/10/07/from-nand-driver-to-yaffs2/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>
	Nand属于块设备。那么nand块设备是否像其他块设备那样，每次读写都经历一个&ldquo;C/S&rdquo;的过程呢？<br />
	我们在Goldfish Platform上，从nand的驱动注册开始，看看nand之上的yaffs2文件读写到底是怎样的一个过程。</p>
<p>
	本文主要是对自己在学习过程中遇到疑问做一个记录，同以前的文章一样，基本上只有流程，那些原理之类的东西，请同学们google吧。在下文中，有些代码可能会有重复，主要目的是不想让各位看官看的太累，跳来跳去，眼镜受不了啊。</p>
<p>
	代码是Android Kernel 2.6.29.整个记录过程比较仓促，难免会由认识上的错误，欢迎大家指正。</p>
<p>
	下面是Android在Goldfish Platform上的执行流程：<br />
	&lt;1&gt;<br />
	377 static int __init init_mtdblock(void)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
	378 {&nbsp;<br />
	379&nbsp;&nbsp;&nbsp;&nbsp; return register_mtd_blktrans(&amp;mtdblock_tr);<br />
	380 }&nbsp;<br />
	在代码片段&lt;1&gt;中注册了一个struct mtd_blktrans_ops结构的mtdblock_tr,这个模块是系统在启动过程中加载的，从模块的init名字，可以看出，是针对mtd块设备的。由于在Linux中，Nand被归为MTD设备，MTD设备就是将nand设备封装了一下，让上层没有直接看到nand，而是看到的MTD。实际上，通过MTD来操作Nand，还是通过nand内部的驱动函数。不要把MTD看的太过神秘。如果还需要了解，请Google吧，我之前就是太较真了，一直没有弄明白，read the fucking code之后才算明白过来了。</p>
<p>
	这个mtd_blktrans_ops结构如下：<br />
	&lt;2&gt;<br />
	&nbsp;32 struct mtd_blktrans_ops {<br />
	&nbsp;33&nbsp;&nbsp;&nbsp;&nbsp; char *name;<br />
	&nbsp;34&nbsp;&nbsp;&nbsp;&nbsp; int major;<br />
	&nbsp;35&nbsp;&nbsp;&nbsp;&nbsp; int part_bits;<br />
	&nbsp;36&nbsp;&nbsp;&nbsp;&nbsp; int blksize;<br />
	&nbsp;37&nbsp;&nbsp;&nbsp;&nbsp; int blkshift;<br />
	&nbsp;38&nbsp;&nbsp;&nbsp;&nbsp;<br />
	&nbsp;39&nbsp;&nbsp;&nbsp;&nbsp; /* Access functions */<br />
	&nbsp;40&nbsp;&nbsp;&nbsp;&nbsp; int (*readsect)(struct mtd_blktrans_dev *dev,<br />
	&nbsp;41&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long block, char *buffer);<br />
	&nbsp;42&nbsp;&nbsp;&nbsp;&nbsp; int (*writesect)(struct mtd_blktrans_dev *dev,<br />
	&nbsp;43&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long block, char *buffer);<br />
	&nbsp;44&nbsp;&nbsp;&nbsp;&nbsp; int (*discard)(struct mtd_blktrans_dev *dev,<br />
	&nbsp;45&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long block, unsigned nr_blocks);<br />
	&nbsp;46&nbsp;&nbsp;&nbsp;&nbsp;<br />
	&nbsp;47&nbsp;&nbsp;&nbsp;&nbsp; /* Block layer ioctls */<br />
	&nbsp;48&nbsp;&nbsp;&nbsp;&nbsp; int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo);<br />
	&nbsp;49&nbsp;&nbsp;&nbsp;&nbsp; int (*flush)(struct mtd_blktrans_dev *dev);<br />
	&nbsp;50<br />
	&nbsp;51&nbsp;&nbsp;&nbsp;&nbsp; /* Called with mtd_table_mutex held; no race with add/remove */<br />
	&nbsp;52&nbsp;&nbsp;&nbsp;&nbsp; int (*open)(struct mtd_blktrans_dev *dev);<br />
	&nbsp;53&nbsp;&nbsp;&nbsp;&nbsp; int (*release)(struct mtd_blktrans_dev *dev);<br />
	&nbsp;54&nbsp;&nbsp;&nbsp;<br />
	&nbsp;55&nbsp;&nbsp;&nbsp;&nbsp; /* Called on {de,}registration and on subsequent addition/removal<br />
	&nbsp;56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; of devices, with mtd_table_mutex held. */<br />
	&nbsp;57&nbsp;&nbsp;&nbsp;&nbsp; void (*add_mtd)(struct mtd_blktrans_ops *tr, struct mtd_info *mtd);<br />
	&nbsp;58&nbsp;&nbsp;&nbsp;&nbsp; void (*remove_dev)(struct mtd_blktrans_dev *dev);<br />
	&nbsp;59&nbsp;&nbsp;&nbsp;<br />
	&nbsp;60&nbsp;&nbsp;&nbsp;&nbsp; struct list_head devs;<br />
	&nbsp;61&nbsp;&nbsp;&nbsp;&nbsp; struct list_head list;<br />
	&nbsp;62&nbsp;&nbsp;&nbsp;&nbsp; struct module *owner;<br />
	&nbsp;63&nbsp;&nbsp;&nbsp;<br />
	&nbsp;64&nbsp;&nbsp;&nbsp;&nbsp; struct mtd_blkcore_priv *blkcore_priv;<br />
	&nbsp;65 };<br />
	其中第64行的 struct mtd_blkcore_priv，它包含了一个读写请求队列。所有的mtd设备的读写请求共用了一个请求队列。<br />
	init_mtdblock-&gt;register_mtd_blktrans<br />
	340 int register_mtd_blktrans(struct mtd_blktrans_ops *tr)<br />
	341 {<br />
	342&nbsp;&nbsp;&nbsp;&nbsp; int ret, i;<br />
	343<br />
	344&nbsp;&nbsp;&nbsp;&nbsp; /* Register the notifier if/when the first device type is<br />
	345&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; registered, to prevent the link/init ordering from fucking<br />
	346&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; us over. */<br />
	347&nbsp;&nbsp;&nbsp;&nbsp; if (!blktrans_notifier.list.next)<br />
	348&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; register_mtd_user(&amp;blktrans_notifier);<br />
	349<br />
	350&nbsp;&nbsp;&nbsp;&nbsp; tr-&gt;blkcore_priv = kzalloc(sizeof(*tr-&gt;blkcore_priv), GFP_KERNEL);//几乎算是一个队列了<br />
	351&nbsp;&nbsp;&nbsp;&nbsp; if (!tr-&gt;blkcore_priv)<br />
	352&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -ENOMEM;<br />
	353<br />
	354&nbsp;&nbsp;&nbsp;&nbsp; mutex_lock(&amp;mtd_table_mutex);<br />
	355<br />
	356&nbsp;&nbsp;&nbsp;&nbsp; ret = register_blkdev(tr-&gt;major, tr-&gt;name);//&quot;mtdblk&quot;注册一个通用块设备<br />
	357&nbsp;&nbsp;&nbsp;&nbsp; if (ret) {<br />
	358&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_WARNING &quot;Unable to register %s block device on major %d: %d\n&quot;,<br />
	359&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tr-&gt;name, tr-&gt;major, ret);<br />
	360&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kfree(tr-&gt;blkcore_priv);<br />
	361&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mutex_unlock(&amp;mtd_table_mutex);<br />
	362&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ret;<br />
	363&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	364&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_init(&amp;tr-&gt;blkcore_priv-&gt;queue_lock);<br />
	365<br />
	366&nbsp;&nbsp;&nbsp;&nbsp; tr-&gt;blkcore_priv-&gt;rq = blk_init_queue(mtd_blktrans_request, &amp;tr-&gt;blkcore_priv-&gt;queue_lock);<br />
	367&nbsp;&nbsp;&nbsp;&nbsp; if (!tr-&gt;blkcore_priv-&gt;rq) {<br />
	368&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unregister_blkdev(tr-&gt;major, tr-&gt;name);<br />
	369&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kfree(tr-&gt;blkcore_priv);<br />
	370&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mutex_unlock(&amp;mtd_table_mutex);<br />
	371&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -ENOMEM;<br />
	372&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	373<br />
	374&nbsp;&nbsp;&nbsp;&nbsp; tr-&gt;blkcore_priv-&gt;rq-&gt;queuedata = tr;<br />
	375&nbsp;&nbsp;&nbsp;&nbsp; blk_queue_hardsect_size(tr-&gt;blkcore_priv-&gt;rq, tr-&gt;blksize);<br />
	376&nbsp;&nbsp;&nbsp;&nbsp; if (tr-&gt;discard)<br />
	377&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blk_queue_set_discard(tr-&gt;blkcore_priv-&gt;rq,<br />
	378&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blktrans_discard_request);<br />
	379<br />
	380&nbsp;&nbsp;&nbsp;&nbsp; tr-&gt;blkshift = ffs(tr-&gt;blksize) &#8211; 1;<br />
	381<br />
	382&nbsp;&nbsp;&nbsp;&nbsp; tr-&gt;blkcore_priv-&gt;thread = kthread_run(mtd_blktrans_thread, tr,<br />
	383&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;%sd&quot;, tr-&gt;name);<br />
	384&nbsp;&nbsp;&nbsp;&nbsp; if (IS_ERR(tr-&gt;blkcore_priv-&gt;thread)) {<br />
	385&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blk_cleanup_queue(tr-&gt;blkcore_priv-&gt;rq);<br />
	386&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unregister_blkdev(tr-&gt;major, tr-&gt;name);<br />
	387&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kfree(tr-&gt;blkcore_priv);<br />
	388&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mutex_unlock(&amp;mtd_table_mutex);<br />
	389&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return PTR_ERR(tr-&gt;blkcore_priv-&gt;thread);<br />
	390&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	391<br />
	392&nbsp;&nbsp;&nbsp;&nbsp; INIT_LIST_HEAD(&amp;tr-&gt;devs);<br />
	393&nbsp;&nbsp;&nbsp;&nbsp; list_add(&amp;tr-&gt;list, &amp;blktrans_majors);<br />
	394<br />
	395&nbsp;&nbsp;&nbsp;&nbsp; for (i=0; i&lt;MAX_MTD_DEVICES; i++) {<br />
	396&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (mtd_table[i] &amp;&amp; mtd_table[i]-&gt;type != MTD_ABSENT)<br />
	397&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tr-&gt;add_mtd(tr, mtd_table[i]);//对于每一个mtd设备，都alloc_disk<br />
	398&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	399<br />
	400&nbsp;&nbsp;&nbsp;&nbsp; mutex_unlock(&amp;mtd_table_mutex);<br />
	401<br />
	402&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />
	403 }<br />
	356行，比较重要在/dev/目录下，将多一个mtdblk节点。为啥叫mtdblk呢，第二个参数决定的。^_^<br />
	366行，正如上述所言，声明了一个读写请求队列。<br />
	382行，声明了一个内核线程。当每一次请求发送的时候，会让这个线程run一次。(引入一个问题：线程自动终结后，如何释放所拥有的资源？)<br />
	在第395~398行，其实所有的nand并没有在这里添加，貌似是因为这个时候goldfish_nand设备驱动还没有被加进来出来。所以register_mtd_blktrans的工作至此已经结束了。它的贡献，仅仅是注册了一个mtd_blktrans_ops。<br />
	当执行到module_init(goldfish_nand_init)的时候，才开始添加mtd设备。<br />
	405 static int __init goldfish_nand_init(void)<br />
	406 {<br />
	407&nbsp;&nbsp;&nbsp;&nbsp; return platform_driver_register(&amp;goldfish_nand_driver);<br />
	408 }<br />
	在这之后，会遍历bus上的所有设备，直到和goldfish_nand相匹配。有同学可能会有疑问，为啥在设备注册的时候不主动去匹配驱动呢？确实，设备会主动去匹配驱动，但是当前驱动的代码还没有被加载进来的时候，去神马地方找驱动捏？<br />
	&nbsp;58 static void goldfish_pdev_worker(struct work_struct *work)<br />
	&nbsp;59 {<br />
	&nbsp;60&nbsp;&nbsp;&nbsp;&nbsp; int ret;<br />
	&nbsp;61&nbsp;&nbsp;&nbsp;&nbsp; struct pdev_bus_dev *pos, *n;<br />
	&nbsp;62<br />
	&nbsp;63&nbsp;&nbsp;&nbsp;&nbsp; list_for_each_entry_safe(pos, n, &amp;pdev_bus_removed_devices, list) {<br />
	&nbsp;64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list_del(&amp;pos-&gt;list);<br />
	&nbsp;65&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; platform_device_unregister(&amp;pos-&gt;pdev);<br />
	&nbsp;66&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kfree(pos);<br />
	&nbsp;67&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;68&nbsp;&nbsp;&nbsp;&nbsp; list_for_each_entry_safe(pos, n, &amp;pdev_bus_new_devices, list) {<br />
	&nbsp;69&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list_del(&amp;pos-&gt;list);<br />
	&nbsp;70&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = platform_device_register(&amp;pos-&gt;pdev);<br />
	&nbsp;71&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(ret) {<br />
	&nbsp;72&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(&quot;goldfish_pdev_worker failed to register device, %s\n&quot;, pos-&gt;pdev.name);<br />
	&nbsp;73&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
	&nbsp;74&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br />
	&nbsp;75&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(&quot;goldfish_pdev_worker registered %s\n&quot;, pos-&gt;pdev.name);<br />
	&nbsp;76&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;77&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list_add_tail(&amp;pos-&gt;list, &amp;pdev_bus_registered_devices);<br />
	&nbsp;78&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;79 }<br />
	看第70行，可以看到，确实注册了设备，并且在platform_device_register中，也确实去匹配驱动了，只不过没有找到驱动而饮恨&ldquo;铩羽而归&rdquo;。然后，当每一次注册一个驱动的时候，会去找对应的设备。当找到对应的设备后，就会调用对应驱动的probe函数了。对于goldfish_nand_driver，其probe函数是：<br />
	goldfish_nand_probe<br />
	315 static int goldfish_nand_probe(struct platform_device *pdev)<br />
	316 {<br />
	317&nbsp;&nbsp;&nbsp;&nbsp; uint32_t num_dev;<br />
	318&nbsp;&nbsp;&nbsp;&nbsp; int i;<br />
	319&nbsp;&nbsp;&nbsp;&nbsp; int err;<br />
	320&nbsp;&nbsp;&nbsp;&nbsp; uint32_t num_dev_working;<br />
	321&nbsp;&nbsp;&nbsp;&nbsp; uint32_t version;<br />
	322&nbsp;&nbsp;&nbsp;&nbsp; struct resource *r;<br />
	323&nbsp;&nbsp;&nbsp;&nbsp; struct goldfish_nand *nand;<br />
	324&nbsp;&nbsp;&nbsp;&nbsp; unsigned char __iomem&nbsp; *base;<br />
	325<br />
	326&nbsp;&nbsp;&nbsp;&nbsp; r = platform_get_resource(pdev, IORESOURCE_MEM, 0);<br />
	327&nbsp;&nbsp;&nbsp;&nbsp; if(r == NULL) {<br />
	328&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err = -ENODEV;<br />
	329&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto err_no_io_base;<br />
	330&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	331<br />
	332&nbsp;&nbsp;&nbsp;&nbsp; base = ioremap(r-&gt;start, PAGE_SIZE);<br />
	333&nbsp;&nbsp;&nbsp;&nbsp; if(base == NULL) {<br />
	334&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err = -ENOMEM;<br />
	335&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto err_ioremap;<br />
	336&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	337&nbsp;&nbsp;&nbsp;&nbsp; version = readl(base + NAND_VERSION);<br />
	338&nbsp;&nbsp;&nbsp;&nbsp; if(version != NAND_VERSION_CURRENT) {<br />
	339&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(&quot;goldfish_nand_init: version mismatch, got %d, expected %d\n&quot;,<br />
	340&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; version, NAND_VERSION_CURRENT);<br />
	341&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err = -ENODEV;<br />
	342&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto err_no_dev;<br />
	343&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	344&nbsp;&nbsp;&nbsp;&nbsp; num_dev = readl(base + NAND_NUM_DEV);<br />
	345&nbsp;&nbsp;&nbsp;&nbsp; if(num_dev == 0) {<br />
	346&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err = -ENODEV;<br />
	347&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto err_no_dev;<br />
	348&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	349<br />
	350&nbsp;&nbsp;&nbsp;&nbsp; nand = kzalloc(sizeof(*nand) + sizeof(struct mtd_info) * num_dev, GFP_KERNEL);<br />
	351&nbsp;&nbsp;&nbsp;&nbsp; if(nand == NULL) {<br />
	352&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err = -ENOMEM;<br />
	353&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto err_nand_alloc_failed;<br />
	354&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	355&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_init(&amp;nand-&gt;lock);<br />
	356&nbsp;&nbsp;&nbsp;&nbsp; nand-&gt;base = base;<br />
	357&nbsp;&nbsp;&nbsp;&nbsp; nand-&gt;mtd_count = num_dev;<br />
	358&nbsp;&nbsp;&nbsp;&nbsp; platform_set_drvdata(pdev, nand);<br />
	359<br />
	360&nbsp;&nbsp;&nbsp;&nbsp; num_dev_working = 0;<br />
	361&nbsp;&nbsp;&nbsp;&nbsp; for(i = 0; i &lt; num_dev; i++) {<br />
	362&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err = goldfish_nand_init_device(nand, i);<br />
	363&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(err == 0)<br />
	364&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; num_dev_working++;<br />
	365&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	366&nbsp;&nbsp;&nbsp;&nbsp; if(num_dev_working == 0) {<br />
	367&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err = -ENODEV;<br />
	368&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto err_no_working_dev;<br />
	369&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	370&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />
	371<br />
	372 err_no_working_dev:<br />
	373&nbsp;&nbsp;&nbsp;&nbsp; kfree(nand);<br />
	374 err_nand_alloc_failed:<br />
	375 err_no_dev:<br />
	376&nbsp;&nbsp;&nbsp;&nbsp; iounmap(base);<br />
	377 err_ioremap:<br />
	378 err_no_io_base:<br />
	379&nbsp;&nbsp;&nbsp;&nbsp; return err;<br />
	380 }<br />
	362行，根据枚举出来的nand，调用goldfish_nand_init_device去初始化。<br />
	goldfish_nand_probe-&gt;goldfish_nand_init_device<br />
	248 static int goldfish_nand_init_device(struct goldfish_nand *nand, int id)<br />
	249 {<br />
	250&nbsp;&nbsp;&nbsp;&nbsp; uint32_t name_len;<br />
	251&nbsp;&nbsp;&nbsp;&nbsp; uint32_t result;<br />
	252&nbsp;&nbsp;&nbsp;&nbsp; uint32_t flags;<br />
	253&nbsp;&nbsp;&nbsp;&nbsp; unsigned long irq_flags;<br />
	254&nbsp;&nbsp;&nbsp;&nbsp; unsigned char __iomem&nbsp; *base = nand-&gt;base;<br />
	255&nbsp;&nbsp;&nbsp;&nbsp; struct mtd_info *mtd = &amp;nand-&gt;mtd[id];<br />
	256&nbsp;&nbsp;&nbsp;&nbsp; char *name;<br />
	257<br />
	258&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_irqsave(&amp;nand-&gt;lock, irq_flags);<br />
	259&nbsp;&nbsp;&nbsp;&nbsp; writel(id, base + NAND_DEV);<br />
	260&nbsp;&nbsp;&nbsp;&nbsp; flags = readl(base + NAND_DEV_FLAGS);<br />
	261&nbsp;&nbsp;&nbsp;&nbsp; name_len = readl(base + NAND_DEV_NAME_LEN);<br />
	262&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;writesize = readl(base + NAND_DEV_PAGE_SIZE);<br />
	263&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;size = readl(base + NAND_DEV_SIZE_LOW);<br />
	264&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;size |= (uint64_t)readl(base + NAND_DEV_SIZE_HIGH) &lt;&lt; 32;<br />
	265&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;oobsize = readl(base + NAND_DEV_EXTRA_SIZE);<br />
	266&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;oobavail = mtd-&gt;oobsize;<br />
	267&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;erasesize = readl(base + NAND_DEV_ERASE_SIZE) /<br />
	268&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (mtd-&gt;writesize + mtd-&gt;oobsize) * mtd-&gt;writesize;<br />
	269&nbsp;&nbsp;&nbsp;&nbsp; do_div(mtd-&gt;size, mtd-&gt;writesize + mtd-&gt;oobsize);<br />
	270&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;size *= mtd-&gt;writesize;<br />
	271&nbsp;&nbsp;&nbsp;&nbsp; printk(&quot;goldfish nand dev%d: size %llx, page %d, extra %d, erase %d\n&quot;,<br />
	272&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id, mtd-&gt;size, mtd-&gt;writesize, mtd-&gt;oobsize, mtd-&gt;erasesize);<br />
	273&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock_irqrestore(&amp;nand-&gt;lock, irq_flags);<br />
	274<br />
	275&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;priv = nand;<br />
	276<br />
	277&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;name = name = kmalloc(name_len + 1, GFP_KERNEL);<br />
	278&nbsp;&nbsp;&nbsp;&nbsp; if(name == NULL)<br />
	279&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -ENOMEM;<br />
	280<br />
	281&nbsp;&nbsp;&nbsp;&nbsp; result = goldfish_nand_cmd(mtd, NAND_CMD_GET_DEV_NAME, 0, name_len, name);<br />
	282&nbsp;&nbsp;&nbsp;&nbsp; if(result != name_len) {<br />
	283&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kfree(mtd-&gt;name);<br />
	284&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;name = NULL;<br />
	285&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(&quot;goldfish_nand_init_device failed to get dev name %d != %d\n&quot;,<br />
	286&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result, name_len);<br />
	287&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -ENODEV;<br />
	288&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	289&nbsp;&nbsp;&nbsp;&nbsp; ((char *) mtd-&gt;name)[name_len] = &#39;\0&#39;;<br />
	290<br />
	291&nbsp;&nbsp;&nbsp;&nbsp; /* Setup the MTD structure */<br />
	292&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;type = MTD_NANDFLASH;<br />
	293&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;flags = MTD_CAP_NANDFLASH;<br />
	294&nbsp;&nbsp;&nbsp;&nbsp; if(flags &amp; NAND_DEV_FLAG_READ_ONLY)<br />
	295&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;flags &amp;= ~MTD_WRITEABLE;<br />
	296<br />
	297&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;owner = THIS_MODULE;<br />
	298&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;erase = goldfish_nand_erase;<br />
	299&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;read = goldfish_nand_read;<br />
	300&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;write = goldfish_nand_write;<br />
	301&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;read_oob = goldfish_nand_read_oob;<br />
	302&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;write_oob = goldfish_nand_write_oob;<br />
	303&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;block_isbad = goldfish_nand_block_isbad;<br />
	304&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;block_markbad = goldfish_nand_block_markbad;<br />
	305<br />
	306&nbsp;&nbsp;&nbsp;&nbsp; if (add_mtd_device(mtd)) {<br />
	307&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kfree(mtd-&gt;name);<br />
	308&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;name = NULL;<br />
	309&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -EIO;<br />
	310&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	311<br />
	312&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />
	313 }<br />
	306行，调用add_mtd_device<br />
	goldfish_nand_probe-&gt;goldfish_nand_init_device-&gt;add_mtd_device<br />
	&nbsp;35 /**<br />
	&nbsp;36&nbsp; *&nbsp; add_mtd_device &#8211; register an MTD device<br />
	&nbsp;37&nbsp; *&nbsp; @mtd: pointer to new MTD device info structure<br />
	&nbsp;38&nbsp; *<br />
	&nbsp;39&nbsp; *&nbsp; Add a device to the list of MTD devices present in the system, and<br />
	&nbsp;40&nbsp; *&nbsp; notify each currently active MTD &#39;user&#39; of its arrival. Returns<br />
	&nbsp;41&nbsp; *&nbsp; zero on success or 1 on failure, which currently will only happen<br />
	&nbsp;42&nbsp; *&nbsp; if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16)<br />
	&nbsp;43&nbsp; */<br />
	&nbsp;44<br />
	&nbsp;45 int add_mtd_device(struct mtd_info *mtd)<br />
	&nbsp;46 {<br />
	&nbsp;47&nbsp;&nbsp;&nbsp;&nbsp; int i;<br />
	&nbsp;48<br />
	&nbsp;49&nbsp;&nbsp;&nbsp;&nbsp; BUG_ON(mtd-&gt;writesize == 0);<br />
	&nbsp;50&nbsp;&nbsp;&nbsp;&nbsp; mutex_lock(&amp;mtd_table_mutex);<br />
	&nbsp;51<br />
	&nbsp;52&nbsp;&nbsp;&nbsp;&nbsp; for (i=0; i &lt; MAX_MTD_DEVICES; i++)<br />
	&nbsp;53&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!mtd_table[i]) {<br />
	&nbsp;54&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct mtd_notifier *not;<br />
	&nbsp;55<br />
	&nbsp;56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtd_table[i] = mtd;<br />
	&nbsp;57&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;index = i;<br />
	&nbsp;58&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;usecount = 0;<br />
	&nbsp;59<br />
	&nbsp;60&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (is_power_of_2(mtd-&gt;erasesize))<br />
	&nbsp;61&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;erasesize_shift = ffs(mtd-&gt;erasesize) &#8211; 1;<br />
	&nbsp;62&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />
	&nbsp;63&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;erasesize_shift = 0;<br />
	&nbsp;64<br />
	&nbsp;65&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (is_power_of_2(mtd-&gt;writesize))<br />
	&nbsp;66&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;writesize_shift = ffs(mtd-&gt;writesize) &#8211; 1;<br />
	&nbsp;67&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />
	&nbsp;68&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;writesize_shift = 0;<br />
	&nbsp;69<br />
	&nbsp;70&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;erasesize_mask = (1 &lt;&lt; mtd-&gt;erasesize_shift) &#8211; 1;<br />
	&nbsp;71&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;writesize_mask = (1 &lt;&lt; mtd-&gt;writesize_shift) &#8211; 1;<br />
	&nbsp;72<br />
	&nbsp;73&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Some chips always power up locked. Unlock them now */<br />
	&nbsp;74&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((mtd-&gt;flags &amp; MTD_WRITEABLE)<br />
	&nbsp;75&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; (mtd-&gt;flags &amp; MTD_POWERUP_LOCK) &amp;&amp; mtd-&gt;unlock) {<br />
	&nbsp;76&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (mtd-&gt;unlock(mtd, 0, mtd-&gt;size))<br />
	&nbsp;77&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_WARNING<br />
	&nbsp;78&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;%s: unlock failed, &quot;<br />
	&nbsp;79&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;writes may not work\n&quot;,<br />
	&nbsp;80&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtd-&gt;name);<br />
	&nbsp;81&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;82<br />
	&nbsp;83&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DEBUG(0, &quot;mtd: Giving out device %d to %s\n&quot;,i, mtd-&gt;name);<br />
	&nbsp;84&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* No need to get a refcount on the module containing<br />
	&nbsp;85&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; the notifier, since we hold the mtd_table_mutex */<br />
	&nbsp;86&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list_for_each_entry(not, &amp;mtd_notifiers, list)<br />
	&nbsp;87&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
	&nbsp;88&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; not-&gt;add(mtd);<br />
	&nbsp;89&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;<br />
	&nbsp;90&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
	&nbsp;91&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mutex_unlock(&amp;mtd_table_mutex);<br />
	&nbsp;92&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* We _know_ we aren&#39;t being removed, because<br />
	&nbsp;93&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; our caller is still holding us here. So none<br />
	&nbsp;94&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; of this try_ nonsense, and no bitching about it<br />
	&nbsp;95&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; either. <img src='http://www.tek-life.org/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  */<br />
	&nbsp;96&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __module_get(THIS_MODULE);<br />
	&nbsp;97&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />
	&nbsp;98&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;<br />
	&nbsp;99&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
	100&nbsp;&nbsp;&nbsp;&nbsp; mutex_unlock(&amp;mtd_table_mutex);<br />
	101&nbsp;&nbsp;&nbsp;&nbsp; return 1;<br />
	102 }&nbsp;&nbsp;<br />
	103<br />
	注意第88行，它调用的是：blktrans_notify_add.为啥是这个呢？在初始化的时候，有个模块是init_mtdblock，其调用了register_mtd_blktrans(&amp;mtdblock_tr),结构mtdblock_tr中定义了一些操作mtdblock的一些操作，比如add_mtd。<br />
	362 static struct mtd_blktrans_ops mtdblock_tr = {<br />
	363&nbsp;&nbsp;&nbsp;&nbsp; .name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = &quot;mtdblock&quot;,<br />
	364&nbsp;&nbsp;&nbsp;&nbsp; .major&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 31,<br />
	365&nbsp;&nbsp;&nbsp;&nbsp; .part_bits&nbsp; = 0,<br />
	366&nbsp;&nbsp;&nbsp;&nbsp; .blksize&nbsp;&nbsp;&nbsp; = 512,<br />
	367&nbsp;&nbsp;&nbsp;&nbsp; .open&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = mtdblock_open,<br />
	368&nbsp;&nbsp;&nbsp;&nbsp; .flush&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = mtdblock_flush,<br />
	369&nbsp;&nbsp;&nbsp;&nbsp; .release&nbsp;&nbsp;&nbsp; = mtdblock_release,<br />
	370&nbsp;&nbsp;&nbsp;&nbsp; .readsect&nbsp;&nbsp; = mtdblock_readsect,<br />
	371&nbsp;&nbsp;&nbsp;&nbsp; .writesect&nbsp; = mtdblock_writesect,<br />
	372&nbsp;&nbsp;&nbsp;&nbsp; .add_mtd&nbsp;&nbsp;&nbsp; = mtdblock_add_mtd,<br />
	373&nbsp;&nbsp;&nbsp;&nbsp; .remove_dev = mtdblock_remove_dev,<br />
	374&nbsp;&nbsp;&nbsp;&nbsp; .owner&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = THIS_MODULE,<br />
	375 };</p>
<p>
	那么它在初始化的时候，又定义了一个mtd_notifier结构的blktrans_notifier。具体，为什么要搞的这么复杂，Linux主要是为了可扩展性的考虑。<br />
	335 static struct mtd_notifier blktrans_notifier = {<br />
	336&nbsp;&nbsp;&nbsp;&nbsp; .add = blktrans_notify_add,<br />
	337&nbsp;&nbsp;&nbsp;&nbsp; .remove = blktrans_notify_remove,<br />
	338 };<br />
	324 static void blktrans_notify_add(struct mtd_info *mtd)<br />
	325 {<br />
	326&nbsp;&nbsp;&nbsp;&nbsp; struct mtd_blktrans_ops *tr;<br />
	327<br />
	328&nbsp;&nbsp;&nbsp;&nbsp; if (mtd-&gt;type == MTD_ABSENT)<br />
	329&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />
	330<br />
	331&nbsp;&nbsp;&nbsp;&nbsp; list_for_each_entry(tr, &amp;blktrans_majors, list)<br />
	332&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tr-&gt;add_mtd(tr, mtd);<br />
	333 }&nbsp;&nbsp;<br />
	回到上文，通过not-&gt;add(mtd)添加mtd设备，not-&gt;add又调用了tr-&gt;add_mtd,这个函数是上面mtdblock_tr定义的mtdblock_add_mtd。<br />
	blktrans_notify_add-&gt;mtdblock_add_mtd<br />
	337 static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)<br />
	338 {<br />
	339&nbsp;&nbsp;&nbsp;&nbsp; struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);<br />
	340<br />
	341&nbsp;&nbsp;&nbsp;&nbsp; if (!dev)<br />
	342&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />
	343<br />
	344&nbsp;&nbsp;&nbsp;&nbsp; dev-&gt;mtd = mtd;<br />
	345&nbsp;&nbsp;&nbsp;&nbsp; dev-&gt;devnum = mtd-&gt;index;<br />
	346<br />
	347&nbsp;&nbsp;&nbsp;&nbsp; dev-&gt;size = mtd-&gt;size &gt;&gt; 9;<br />
	348&nbsp;&nbsp;&nbsp;&nbsp; dev-&gt;tr = tr;<br />
	349<br />
	350&nbsp;&nbsp;&nbsp;&nbsp; if (!(mtd-&gt;flags &amp; MTD_WRITEABLE))<br />
	351&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dev-&gt;readonly = 1;<br />
	352<br />
	353&nbsp;&nbsp;&nbsp;&nbsp; add_mtd_blktrans_dev(dev);<br />
	354 }</p>
<p>
	blktrans_notify_add-&gt;mtdblock_add_mtd-&gt;add_mtd_blktrans_dev</p>
<p>
	216 int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)<br />
	217 {<br />
	218&nbsp;&nbsp;&nbsp;&nbsp; struct mtd_blktrans_ops *tr = new-&gt;tr;<br />
	219&nbsp;&nbsp;&nbsp;&nbsp; struct mtd_blktrans_dev *d;<br />
	220&nbsp;&nbsp;&nbsp;&nbsp; int last_devnum = -1;<br />
	221&nbsp;&nbsp;&nbsp;&nbsp; struct gendisk *gd;<br />
	222<br />
	223&nbsp;&nbsp;&nbsp;&nbsp; if (mutex_trylock(&amp;mtd_table_mutex)) {<br />
	224&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mutex_unlock(&amp;mtd_table_mutex);<br />
	225&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BUG();<br />
	226&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	227<br />
	228&nbsp;&nbsp;&nbsp;&nbsp; list_for_each_entry(d, &amp;tr-&gt;devs, list) {<br />
	229&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (new-&gt;devnum == -1) {<br />
	230&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Use first free number */<br />
	231&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (d-&gt;devnum != last_devnum+1) {<br />
	232&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Found a free devnum. Plug it in here */<br />
	233&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new-&gt;devnum = last_devnum+1;<br />
	234&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list_add_tail(&amp;new-&gt;list, &amp;d-&gt;list);<br />
	235&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto added;<br />
	236&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	237&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else if (d-&gt;devnum == new-&gt;devnum) {<br />
	238&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Required number taken */<br />
	239&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -EBUSY;<br />
	240&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else if (d-&gt;devnum &gt; new-&gt;devnum) {<br />
	241&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Required number was free */<br />
	242&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list_add_tail(&amp;new-&gt;list, &amp;d-&gt;list);<br />
	243&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto added;<br />
	244&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	245&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; last_devnum = d-&gt;devnum;<br />
	246&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	247&nbsp;&nbsp;&nbsp;&nbsp; if (new-&gt;devnum == -1)<br />
	248&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new-&gt;devnum = last_devnum+1;<br />
	249<br />
	250&nbsp;&nbsp;&nbsp;&nbsp; if ((new-&gt;devnum &lt;&lt; tr-&gt;part_bits) &gt; 256) {<br />
	251&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -EBUSY;<br />
	252&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	253<br />
	254&nbsp;&nbsp;&nbsp;&nbsp; list_add_tail(&amp;new-&gt;list, &amp;tr-&gt;devs);<br />
	255&nbsp; added:<br />
	256&nbsp;&nbsp;&nbsp;&nbsp; mutex_init(&amp;new-&gt;lock);<br />
	257&nbsp;&nbsp;&nbsp;&nbsp; if (!tr-&gt;writesect)<br />
	258&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new-&gt;readonly = 1;<br />
	259<br />
	260&nbsp;&nbsp;&nbsp;&nbsp; gd = alloc_disk(1 &lt;&lt; tr-&gt;part_bits);//哦，在这里分配alloc_disk<br />
	261&nbsp;&nbsp;&nbsp;&nbsp; if (!gd) {<br />
	262&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list_del(&amp;new-&gt;list);<br />
	263&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -ENOMEM;<br />
	264&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	265&nbsp;&nbsp;&nbsp;&nbsp; gd-&gt;major = tr-&gt;major;<br />
	266&nbsp;&nbsp;&nbsp;&nbsp; gd-&gt;first_minor = (new-&gt;devnum) &lt;&lt; tr-&gt;part_bits;<br />
	267&nbsp;&nbsp;&nbsp;&nbsp; gd-&gt;fops = &amp;mtd_blktrans_ops;<br />
	268<br />
	269&nbsp;&nbsp;&nbsp;&nbsp; if (tr-&gt;part_bits)<br />
	270&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (new-&gt;devnum &lt; 26)<br />
	271&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; snprintf(gd-&gt;disk_name, sizeof(gd-&gt;disk_name),<br />
	272&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;%s%c&quot;, tr-&gt;name, &#39;a&#39; + new-&gt;devnum);<br />
	273&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />
	274&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; snprintf(gd-&gt;disk_name, sizeof(gd-&gt;disk_name),<br />
	275&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;%s%c%c&quot;, tr-&gt;name,<br />
	276&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#39;a&#39; &#8211; 1 + new-&gt;devnum / 26,<br />
	277&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#39;a&#39; + new-&gt;devnum % 26);<br />
	278&nbsp;&nbsp;&nbsp;&nbsp; else<br />
	279&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; snprintf(gd-&gt;disk_name, sizeof(gd-&gt;disk_name),<br />
	280&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;%s%d&quot;, tr-&gt;name, new-&gt;devnum);<br />
	281<br />
	282&nbsp;&nbsp;&nbsp;&nbsp; /* 2.5 has capacity in units of 512 bytes while still<br />
	283&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */<br />
	284&nbsp;&nbsp;&nbsp;&nbsp; set_capacity(gd, (new-&gt;size * tr-&gt;blksize) &gt;&gt; 9);<br />
	285<br />
	286&nbsp;&nbsp;&nbsp;&nbsp; gd-&gt;private_data = new;<br />
	287&nbsp;&nbsp;&nbsp;&nbsp; new-&gt;blkcore_priv = gd;<br />
	288&nbsp;&nbsp;&nbsp;&nbsp; gd-&gt;queue = tr-&gt;blkcore_priv-&gt;rq;//使用的队列是tr的队列<br />
	289<br />
	290&nbsp;&nbsp;&nbsp;&nbsp; if (new-&gt;readonly)<br />
	291&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set_disk_ro(gd, 1);<br />
	292<br />
	293&nbsp;&nbsp;&nbsp;&nbsp; add_disk(gd);//加入<br />
	294<br />
	295&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />
	296 }<br />
	288行，使用了tr的队列，这个tr队列是在register_mtd_blktrans(&amp;mtdblock_tr)初始化时定义的。<br />
	340 int register_mtd_blktrans(struct mtd_blktrans_ops *tr)<br />
	341 {<br />
	342&nbsp;&nbsp;&nbsp;&nbsp; int ret, i;<br />
	343&nbsp;&nbsp;&nbsp;&nbsp;<br />
	344&nbsp;&nbsp;&nbsp;&nbsp; /* Register the notifier if/when the first device type is<br />
	345&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; registered, to prevent the link/init ordering from fucking<br />
	346&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; us over. */<br />
	347&nbsp;&nbsp;&nbsp;&nbsp; if (!blktrans_notifier.list.next)<br />
	348&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; register_mtd_user(&amp;blktrans_notifier);<br />
	349&nbsp;&nbsp;&nbsp;&nbsp;<br />
	350&nbsp;&nbsp;&nbsp;&nbsp; tr-&gt;blkcore_priv = kzalloc(sizeof(*tr-&gt;blkcore_priv), GFP_KERNEL);//几乎算是一个队列了<br />
	351&nbsp;&nbsp;&nbsp;&nbsp; if (!tr-&gt;blkcore_priv)<br />
	352&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -ENOMEM;<br />
	353<br />
	354&nbsp;&nbsp;&nbsp;&nbsp; mutex_lock(&amp;mtd_table_mutex);<br />
	355<br />
	356&nbsp;&nbsp;&nbsp;&nbsp; ret = register_blkdev(tr-&gt;major, tr-&gt;name);//&quot;mtdblk&quot;注册一个通用块设备<br />
	357&nbsp;&nbsp;&nbsp;&nbsp; if (ret) {<br />
	358&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_WARNING &quot;Unable to register %s block device on major %d: %d\n&quot;,<br />
	359&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tr-&gt;name, tr-&gt;major, ret);<br />
	360&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kfree(tr-&gt;blkcore_priv);<br />
	361&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mutex_unlock(&amp;mtd_table_mutex);<br />
	362&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ret;<br />
	363&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	364&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_init(&amp;tr-&gt;blkcore_priv-&gt;queue_lock);<br />
	365&nbsp;&nbsp;&nbsp;&nbsp; tr-&gt;blkcore_priv-&gt;rq = blk_init_queue(mtd_blktrans_request, &amp;tr-&gt;blkcore_priv-&gt;queue_lock);</p>
<p>
	tr队列比较通用，只有request_fn不同。设置的这么简单，让人始料不及啊。不过令人以外的是，mtd的读写并不经过request等策略。它们是通过什么策略呢？<br />
	我们直到在Linux中，文件的读写是经过几个层次，最上面是VFS，然后是具体的文件系统。具体的文件系统决定了，是否经过request策略。我们不妨直接去看看Yaffs2的file_operations对象，从那里入手，看看具体是否经过了request吧。<br />
	由于在Linux中的VFS是具有页缓存的，而页缓存相关联的数据结构是address_space，其host是inode。所以，对于文件的读写，基本上最终是通过调用address_space的operations结构。<br />
	这个是yaffs2的address_operations结构。它们是否最终request，看看其readpage即可<br />
	&nbsp;270 static struct address_space_operations yaffs_file_address_operations = {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
	&nbsp;271&nbsp;&nbsp;&nbsp;&nbsp; .readpage = yaffs_readpage,&nbsp;&nbsp;&nbsp;<br />
	&nbsp;272&nbsp;&nbsp;&nbsp;&nbsp; .writepage = yaffs_writepage,<br />
	&nbsp;273 #if (YAFFS_USE_WRITE_BEGIN_END &gt; 0)<br />
	&nbsp;274&nbsp;&nbsp;&nbsp;&nbsp; .write_begin = yaffs_write_begin,<br />
	&nbsp;275&nbsp;&nbsp;&nbsp;&nbsp; .write_end = yaffs_write_end,<br />
	&nbsp;276 #else<br />
	&nbsp;277&nbsp;&nbsp;&nbsp;&nbsp; .prepare_write = yaffs_prepare_write,<br />
	&nbsp;278&nbsp;&nbsp;&nbsp;&nbsp; .commit_write = yaffs_commit_write,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
	&nbsp;279 #endif<br />
	&nbsp;280 };<br />
	由于yaffs_read主要涉及yaffs2文件系统内部的流程，因此不再详述。比较有特点的是，对于yaffs2文件系统，它并没有使用传统的页缓存的概念。在yaffs_device数据结构里面有&ldquo;yaffs_ChunkCache *srCache;&rdquo;一个成员变量，其数据类型：<br />
	111 /* Special sequence number for bad block that failed to be marked bad */<br />
	112 #define YAFFS_SEQUENCE_BAD_BLOCK&nbsp;&nbsp;&nbsp; 0xFFFF0000<br />
	113&nbsp;&nbsp;&nbsp;<br />
	114 /* ChunkCache is used for short read/write operations.*/<br />
	115 typedef struct {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
	116&nbsp;&nbsp;&nbsp;&nbsp; struct yaffs_ObjectStruct *object;<br />
	117&nbsp;&nbsp;&nbsp;&nbsp; int chunkId;<br />
	118&nbsp;&nbsp;&nbsp;&nbsp; int lastUse;<br />
	119&nbsp;&nbsp;&nbsp;&nbsp; int dirty;<br />
	120&nbsp;&nbsp;&nbsp;&nbsp; int nBytes;&nbsp;&nbsp;&nbsp;&nbsp; /* Only valid if the cache is dirty */<br />
	121&nbsp;&nbsp;&nbsp;&nbsp; int locked;&nbsp;&nbsp;&nbsp;&nbsp; /* Can&#39;t push out or flush while locked. */<br />
	122 #ifdef CONFIG_YAFFS_YAFFS2<br />
	123&nbsp;&nbsp;&nbsp;&nbsp; __u8 *data;<br />
	124 #else<br />
	125&nbsp;&nbsp;&nbsp;&nbsp; __u8 data[YAFFS_BYTES_PER_CHUNK];<br />
	126 #endif<br />
	127 } yaffs_ChunkCache;&nbsp;&nbsp;<br />
	每一次，在cache中，查找是否存在对应的cache的时候，它的查找函数是这样的：<br />
	4015 /* Find a cached chunk */<br />
	4016 static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object *obj,<br />
	4017&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int chunkId)<br />
	4018 {&nbsp;<br />
	4019&nbsp;&nbsp;&nbsp;&nbsp; yaffs_Device *dev = obj-&gt;myDev;<br />
	4020&nbsp;&nbsp;&nbsp;&nbsp; int i;<br />
	4021&nbsp;&nbsp;&nbsp;&nbsp; if (dev-&gt;nShortOpCaches &gt; 0) {<br />
	4022&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (i = 0; i &lt; dev-&gt;nShortOpCaches; i++) {<br />
	4023&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (dev-&gt;srCache[i].object == obj &amp;&amp;<br />
	4024&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dev-&gt;srCache[i].chunkId == chunkId) {<br />
	4025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dev-&gt;cacheHits++;<br />
	4026&nbsp;&nbsp;&nbsp;<br />
	4027&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return &amp;dev-&gt;srCache[i];<br />
	4028&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	4029&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	4030&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	4031&nbsp;&nbsp;&nbsp;&nbsp; return NULL;<br />
	4032 }<br />
	从上面的查找中，可以发现，它并不像ext2那样，用hash或者radix_tree那样将页缓存组织起来。它的组织方式，和nand设备是一样的。哈，这也许是yaffs2移植性强的一个体现吧。它不局限于在Linux中，不局限于是否支持MTD，只要是一个OS，任意类型的，都可以被移植进去。</p>
<p>
	所以，回归上文，其实之前申请的alloc_disk，request_queue之类的玩意，都是坑爹的啊。所以，对于Nand设备的读写并没有其他块设备那么复杂啊。之前的那个request_fn竟然都木有用上。<br />
	<span style="display: none">&nbsp;</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/10/07/from-nand-driver-to-yaffs2/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>SD卡读写流程</title>
		<link>http://www.tek-life.org/2011/10/06/sd-read-write-routine/</link>
		<comments>http://www.tek-life.org/2011/10/06/sd-read-write-routine/#comments</comments>
		<pubDate>Thu, 06 Oct 2011 03:46:45 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[File System]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10651</guid>
		<description><![CDATA[本流程分析针对2.6.29Kernel on Goldfish Platform. SD卡的读写操作同其他块设备一样，都是异步的过程。当进程把request发到块设备请求队列后，在真正读写时，mq-&#62;thread进程会被激活。这个进程准确说属于内核线程，其函数执行主体如下： 44 static int mmc_queue_thread(void *d) 45 { 46&#160;&#160;&#160;&#160; struct mmc_queue *mq = d; 47&#160;&#160;&#160;&#160; struct request_queue *q = mq-&#62;queue; 48 49&#160;&#160;&#160;&#160; current-&#62;flags &#124;= PF_MEMALLOC; 50 51&#160;&#160;&#160;&#160; down(&#38;mq-&#62;thread_sem); 52&#160;&#160;&#160;&#160; do { 53&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct request *req = &#8230; <a href="http://www.tek-life.org/2011/10/06/sd-read-write-routine/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>
	本流程分析针对2.6.29Kernel on Goldfish Platform.<br />
	SD卡的读写操作同其他块设备一样，都是异步的过程。当进程把request发到块设备请求队列后，在真正读写时，mq-&gt;thread进程会被激活。这个进程准确说属于内核线程，其函数执行主体如下：<br />
	44 static int mmc_queue_thread(void *d)<br />
	45 {<br />
	46&nbsp;&nbsp;&nbsp;&nbsp; struct mmc_queue *mq = d;<br />
	47&nbsp;&nbsp;&nbsp;&nbsp; struct request_queue *q = mq-&gt;queue;<br />
	48<br />
	49&nbsp;&nbsp;&nbsp;&nbsp; current-&gt;flags |= PF_MEMALLOC;<br />
	50<br />
	51&nbsp;&nbsp;&nbsp;&nbsp; down(&amp;mq-&gt;thread_sem);<br />
	52&nbsp;&nbsp;&nbsp;&nbsp; do {<br />
	53&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct request *req = NULL;<br />
	54<br />
	55&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_irq(q-&gt;queue_lock);<br />
	56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set_current_state(TASK_INTERRUPTIBLE);<br />
	57&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!blk_queue_plugged(q))<br />
	58&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req = elv_next_request(q);<br />
	59&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mq-&gt;req = req;<br />
	60&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock_irq(q-&gt;queue_lock);<br />
	61<br />
	62&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!req) {<br />
	63&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (kthread_should_stop()) {<br />
	64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set_current_state(TASK_RUNNING);<br />
	65&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
	66&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	67&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; up(&amp;mq-&gt;thread_sem);<br />
	68&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; schedule();<br />
	69&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; down(&amp;mq-&gt;thread_sem);<br />
	70&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br />
	71&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	72&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set_current_state(TASK_RUNNING);<br />
	73<br />
	74&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mq-&gt;issue_fn(mq, req);<br />
	75&nbsp;&nbsp;&nbsp;&nbsp; } while (1);<br />
	76&nbsp;&nbsp;&nbsp;&nbsp; up(&amp;mq-&gt;thread_sem);<br />
	77<br />
	78&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />
	79 }<br />
	通过51行和76行，保证只有一个线程操作mq。<br />
	接下来，第74行调用mq-&gt;issue_fn，即： mmc_blk_issue_rq。<br />
	264 static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)<br />
	265 {<br />
	266&nbsp;&nbsp;&nbsp;&nbsp; struct mmc_blk_data *md = mq-&gt;data;<br />
	267&nbsp;&nbsp;&nbsp;&nbsp; struct mmc_card *card = md-&gt;queue.card;<br />
	268&nbsp;&nbsp;&nbsp;&nbsp; struct mmc_blk_request brq;<br />
	269&nbsp;&nbsp;&nbsp;&nbsp; int ret = 1, disable_multi = 0;<br />
	270<br />
	271 #ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME<br />
	272&nbsp;&nbsp;&nbsp;&nbsp; if (mmc_bus_needs_resume(card-&gt;host)) {<br />
	273&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_resume_bus(card-&gt;host);<br />
	274&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_blk_set_blksize(md, card);<br />
	275&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	276 #endif<br />
	277<br />
	278&nbsp;&nbsp;&nbsp;&nbsp; mmc_claim_host(card-&gt;host);<br />
	279<br />
	280&nbsp;&nbsp;&nbsp;&nbsp; do {<br />
	281&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct mmc_command cmd;<br />
	282&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u32 readcmd, writecmd, status = 0;<br />
	283<br />
	284&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memset(&amp;brq, 0, sizeof(struct mmc_blk_request));<br />
	285&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.mrq.cmd = &amp;brq.cmd;<br />
	286&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.mrq.data = &amp;brq.data;<br />
	287<br />
	288&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.cmd.arg = req-&gt;sector;<br />
	289&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!mmc_card_blockaddr(card))<br />
	290&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.cmd.arg &lt;&lt;= 9;<br />
	291&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;<br />
	292&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.data.blksz = 512;<br />
	293&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.stop.opcode = MMC_STOP_TRANSMISSION;<br />
	294&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.stop.arg = 0;<br />
	295&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;<br />
	296&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.data.blocks = req-&gt;nr_sectors;<br />
	297<br />
	298&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br />
	299&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * The block layer doesn&#39;t support all sector count<br />
	300&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * restrictions, so we need to be prepared for too big<br />
	301&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * requests.<br />
	302&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
	303&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (brq.data.blocks &gt; card-&gt;host-&gt;max_blk_count)<br />
	304&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.data.blocks = card-&gt;host-&gt;max_blk_count;<br />
	305<br />
	306&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br />
	307&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * After a read error, we redo the request one sector at a time<br />
	308&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * in order to accurately determine which sectors can be read<br />
	309&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * successfully.<br />
	310&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
	311&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (disable_multi &amp;&amp; brq.data.blocks &gt; 1)<br />
	312&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.data.blocks = 1;<br />
	313<br />
	314&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (brq.data.blocks &gt; 1) {<br />
	315&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* SPI multiblock writes terminate using a special<br />
	316&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * token, not a STOP_TRANSMISSION request.<br />
	317&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
	318&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!mmc_host_is_spi(card-&gt;host)<br />
	319&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; || rq_data_dir(req) == READ)<br />
	320&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.mrq.stop = &amp;brq.stop;<br />
	321&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; readcmd = MMC_READ_MULTIPLE_BLOCK;<br />
	322&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writecmd = MMC_WRITE_MULTIPLE_BLOCK;<br />
	323&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />
	324&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.mrq.stop = NULL;<br />
	325&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; readcmd = MMC_READ_SINGLE_BLOCK;<br />
	326&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writecmd = MMC_WRITE_BLOCK;<br />
	327&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	328<br />
	329&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (rq_data_dir(req) == READ) {<br />
	330&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.cmd.opcode = readcmd;<br />
	331&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.data.flags |= MMC_DATA_READ;<br />
	332&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />
	333&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.cmd.opcode = writecmd;<br />
	334&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.data.flags |= MMC_DATA_WRITE;<br />
	335&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	336<br />
	337&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_set_data_timeout(&amp;brq.data, card);<br />
	338<br />
	339&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.data.sg = mq-&gt;sg;<br />
	340&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.data.sg_len = mmc_queue_map_sg(mq);<br />
	341<br />
	342&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br />
	343&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Adjust the sg list so it is the same size as the<br />
	344&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * request.<br />
	345&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
	346&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (brq.data.blocks != req-&gt;nr_sectors) {<br />
	347&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int i, data_size = brq.data.blocks &lt;&lt; 9;<br />
	348&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct scatterlist *sg;<br />
	349<br />
	350&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for_each_sg(brq.data.sg, sg, brq.data.sg_len, i) {<br />
	351&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data_size -= sg-&gt;length;<br />
	352&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (data_size &lt;= 0) {<br />
	353&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sg-&gt;length += data_size;<br />
	354&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i++;<br />
	355&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
	356&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	357&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	358&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.data.sg_len = i;<br />
	359&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	360<br />
	361&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_queue_bounce_pre(mq);<br />
	362<br />
	363&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_wait_for_req(card-&gt;host, &amp;brq.mrq);<br />
	364<br />
	365&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_queue_bounce_post(mq);<br />
	366<br />
	367&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br />
	368&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Check for errors here, but don&#39;t jump to cmd_err<br />
	369&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * until later as we need to wait for the card to leave<br />
	370&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * programming mode even when things go wrong.<br />
	371&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
	372&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (brq.cmd.error || brq.data.error || brq.stop.error) {<br />
	373&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (brq.data.blocks &gt; 1 &amp;&amp; rq_data_dir(req) == READ) {<br />
	374&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Redo read one sector at a time */<br />
	375&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_WARNING &quot;%s: retrying using single &quot;<br />
	376&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;block read\n&quot;, req-&gt;rq_disk-&gt;disk_name);<br />
	377&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; disable_multi = 1;<br />
	378&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br />
	379&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	380&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; status = get_card_status(card, req);<br />
	381&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else if (disable_multi == 1) {<br />
	382&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; disable_multi = 0;<br />
	383&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	384<br />
	385&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (brq.cmd.error) {<br />
	386&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_ERR &quot;%s: error %d sending read/write &quot;<br />
	387&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;command, response %#x, card status %#x\n&quot;,<br />
	388&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req-&gt;rq_disk-&gt;disk_name, brq.cmd.error,<br />
	389&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.cmd.resp[0], status);<br />
	390&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	391<br />
	392&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (brq.data.error) {<br />
	393&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (brq.data.error == -ETIMEDOUT &amp;&amp; brq.mrq.stop)<br />
	394&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* &#39;Stop&#39; response contains card status */<br />
	395&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; status = brq.mrq.stop-&gt;resp[0];<br />
	396&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_ERR &quot;%s: error %d transferring data,&quot;<br />
	397&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot; sector %u, nr %u, card status %#x\n&quot;,<br />
	398&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req-&gt;rq_disk-&gt;disk_name, brq.data.error,<br />
	399&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (unsigned)req-&gt;sector,<br />
	400&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (unsigned)req-&gt;nr_sectors, status);<br />
	401&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	402<br />
	403&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (brq.stop.error) {<br />
	404&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_ERR &quot;%s: error %d sending stop command, &quot;<br />
	405&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;response %#x, card status %#x\n&quot;,<br />
	406&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req-&gt;rq_disk-&gt;disk_name, brq.stop.error,<br />
	407&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brq.stop.resp[0], status);<br />
	408&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	409<br />
	410&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!mmc_host_is_spi(card-&gt;host) &amp;&amp; rq_data_dir(req) != READ) {<br />
	411&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do {<br />
	412&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int err;<br />
	413<br />
	414&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd.opcode = MMC_SEND_STATUS;<br />
	415&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd.arg = card-&gt;rca &lt;&lt; 16;<br />
	416&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;<br />
	417&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err = mmc_wait_for_cmd(card-&gt;host, &amp;cmd, 5);<br />
	418&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (err) {<br />
	419&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_ERR &quot;%s: error %d requesting status\n&quot;,<br />
	420&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req-&gt;rq_disk-&gt;disk_name, err);<br />
	421&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto cmd_err;<br />
	422&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	423&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br />
	424&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Some cards mishandle the status bits,<br />
	425&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * so make sure to check both the busy<br />
	426&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * indication and the card state.<br />
	427&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
	428&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } while (!(cmd.resp[0] &amp; R1_READY_FOR_DATA) ||<br />
	429&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (R1_CURRENT_STATE(cmd.resp[0]) == 7));<br />
	430<br />
	431 #if 0<br />
	432&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (cmd.resp[0] &amp; ~0&#215;00000900)<br />
	433&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_ERR &quot;%s: status = %08x\n&quot;,<br />
	434&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req-&gt;rq_disk-&gt;disk_name, cmd.resp[0]);<br />
	435&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (mmc_decode_status(cmd.resp))<br />
	436&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto cmd_err;<br />
	437 #endif<br />
	438&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	439<br />
	440&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (brq.cmd.error || brq.stop.error || brq.data.error) {<br />
	441&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (rq_data_dir(req) == READ) {<br />
	442&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br />
	443&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * After an error, we redo I/O one sector at a<br />
	444&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * time, so we only reach here after trying to<br />
	445&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * read a single sector.<br />
	446&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
	447&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_irq(&amp;md-&gt;lock);<br />
	448&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = __blk_end_request(req, -EIO, brq.data.blksz);<br />
	449&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock_irq(&amp;md-&gt;lock);<br />
	450&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br />
	451&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	452&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto cmd_err;<br />
	453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	454<br />
	455&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br />
	456&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * A block was successfully transferred.<br />
	457&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
	458&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_irq(&amp;md-&gt;lock);<br />
	459&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = __blk_end_request(req, 0, brq.data.bytes_xfered);<br />
	460&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock_irq(&amp;md-&gt;lock);<br />
	461&nbsp;&nbsp;&nbsp;&nbsp; } while (ret);<br />
	462<br />
	463&nbsp;&nbsp;&nbsp;&nbsp; mmc_release_host(card-&gt;host);<br />
	464<br />
	465&nbsp;&nbsp;&nbsp;&nbsp; return 1;<br />
	466<br />
	467&nbsp; cmd_err:<br />
	468&nbsp;&nbsp;&nbsp;&nbsp; /*<br />
	469&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * If this is an SD card and we&#39;re writing, we can first<br />
	470&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * mark the known good sectors as ok.<br />
	471&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *<br />
	472&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * If the card is not SD, we can still ok written sectors<br />
	473&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * as reported by the controller (which might be less than<br />
	474&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * the real number of written sectors, but never more).<br />
	475&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
	476&nbsp;&nbsp;&nbsp;&nbsp; if (mmc_card_sd(card)) {<br />
	477&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u32 blocks;<br />
	478<br />
	479&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blocks = mmc_sd_num_wr_blocks(card);<br />
	480&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (blocks != (u32)-1) {<br />
	481&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_irq(&amp;md-&gt;lock);<br />
	482&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = __blk_end_request(req, 0, blocks &lt;&lt; 9);<br />
	483&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock_irq(&amp;md-&gt;lock);<br />
	484&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	485&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />
	486&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_irq(&amp;md-&gt;lock);<br />
	487&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = __blk_end_request(req, 0, brq.data.bytes_xfered);<br />
	488&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock_irq(&amp;md-&gt;lock);<br />
	489&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	490<br />
	491&nbsp;&nbsp;&nbsp;&nbsp; mmc_release_host(card-&gt;host);<br />
	492<br />
	493&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_irq(&amp;md-&gt;lock);<br />
	494&nbsp;&nbsp;&nbsp;&nbsp; while (ret)<br />
	495&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));<br />
	496&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock_irq(&amp;md-&gt;lock);<br />
	497<br />
	498&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />
	499 }<br />
	278行和491行保证了，当前握有card-&gt;host的唯一性。<br />
	280行~360行，根据当前的request，再次组织一个新的block request,通过363行，进行读写。<br />
	186 /**<br />
	187&nbsp; *&nbsp; mmc_wait_for_req &#8211; start a request and wait for completion<br />
	188&nbsp; *&nbsp; @host: MMC host to start command<br />
	189&nbsp; *&nbsp; @mrq: MMC request to start<br />
	190&nbsp; *<br />
	191&nbsp; *&nbsp; Start a new MMC custom command request for a host, and wait<br />
	192&nbsp; *&nbsp; for the command to complete. Does not attempt to parse the<br />
	193&nbsp; *&nbsp; response.<br />
	194&nbsp; */<br />
	195 void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)<br />
	196 {<br />
	197&nbsp;&nbsp;&nbsp;&nbsp; DECLARE_COMPLETION_ONSTACK(complete);<br />
	198<br />
	199&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;done_data = &amp;complete;<br />
	200&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;done = mmc_wait_done;<br />
	201<br />
	202&nbsp;&nbsp;&nbsp;&nbsp; mmc_start_request(host, mrq);//maybe have a long time.<br />
	203<br />
	204&nbsp;&nbsp;&nbsp;&nbsp; wait_for_completion(&amp;complete);//wait until the data completed.the sem also anipulated by interrupt.<br />
	205 }<br />
	186行~205行，是个真正的读写过程。通过complete保证了：只有数据读写完毕，这个函数才返回。否则，将一直等待（等待的过程在204行）。<br />
	123 static void<br />
	124 mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)<br />
	125 {<br />
	126 #ifdef CONFIG_MMC_DEBUG<br />
	127&nbsp;&nbsp;&nbsp;&nbsp; unsigned int i, sz;<br />
	128&nbsp;&nbsp;&nbsp;&nbsp; struct scatterlist *sg;<br />
	129 #endif<br />
	130<br />
	131&nbsp;&nbsp;&nbsp;&nbsp; pr_debug(&quot;%s: starting CMD%u arg %08x flags %08x\n&quot;,<br />
	132&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_hostname(host), mrq-&gt;cmd-&gt;opcode,<br />
	133&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;cmd-&gt;arg, mrq-&gt;cmd-&gt;flags);<br />
	134<br />
	135&nbsp;&nbsp;&nbsp;&nbsp; if (mrq-&gt;data) {<br />
	136&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pr_debug(&quot;%s:&nbsp;&nbsp;&nbsp;&nbsp; blksz %d blocks %d flags %08x &quot;<br />
	137&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;tsac %d ms nsac %d\n&quot;,<br />
	138&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_hostname(host), mrq-&gt;data-&gt;blksz,<br />
	139&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;data-&gt;blocks, mrq-&gt;data-&gt;flags,<br />
	140&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;data-&gt;timeout_ns / 1000000,<br />
	141&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;data-&gt;timeout_clks);<br />
	142&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	143<br />
	144&nbsp;&nbsp;&nbsp;&nbsp; if (mrq-&gt;stop) {<br />
	145&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pr_debug(&quot;%s:&nbsp;&nbsp;&nbsp;&nbsp; CMD%u arg %08x flags %08x\n&quot;,<br />
	146&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_hostname(host), mrq-&gt;stop-&gt;opcode,<br />
	147&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;stop-&gt;arg, mrq-&gt;stop-&gt;flags);<br />
	148&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	149<br />
	150&nbsp;&nbsp;&nbsp;&nbsp; WARN_ON(!host-&gt;claimed);<br />
	151<br />
	152&nbsp;&nbsp;&nbsp;&nbsp; led_trigger_event(host-&gt;led, LED_FULL);<br />
	153<br />
	154&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;cmd-&gt;error = 0;<br />
	155&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;cmd-&gt;mrq = mrq;<br />
	156&nbsp;&nbsp;&nbsp;&nbsp; if (mrq-&gt;data) {<br />
	157&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BUG_ON(mrq-&gt;data-&gt;blksz &gt; host-&gt;max_blk_size);<br />
	158&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BUG_ON(mrq-&gt;data-&gt;blocks &gt; host-&gt;max_blk_count);<br />
	159&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BUG_ON(mrq-&gt;data-&gt;blocks * mrq-&gt;data-&gt;blksz &gt;<br />
	160&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; host-&gt;max_req_size);<br />
	161<br />
	162 #ifdef CONFIG_MMC_DEBUG<br />
	163&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sz = 0;<br />
	164&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for_each_sg(mrq-&gt;data-&gt;sg, sg, mrq-&gt;data-&gt;sg_len, i)<br />
	165&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sz += sg-&gt;length;<br />
	166&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BUG_ON(sz != mrq-&gt;data-&gt;blocks * mrq-&gt;data-&gt;blksz);<br />
	167 #endif<br />
	168<br />
	169&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;cmd-&gt;data = mrq-&gt;data;<br />
	170&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;data-&gt;error = 0;<br />
	171&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;data-&gt;mrq = mrq;<br />
	172&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (mrq-&gt;stop) {<br />
	173&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;data-&gt;stop = mrq-&gt;stop;<br />
	174&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;stop-&gt;error = 0;<br />
	175&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;stop-&gt;mrq = mrq;<br />
	176&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	177&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	178&nbsp;&nbsp;&nbsp;&nbsp; host-&gt;ops-&gt;request(host, mrq);//对于goldish会调用goldfish_mmc_request<br />
	179 }<br />
	178行注释，调用goldfish_mmc_request进行真正的读写。<br />
	400 static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req)<br />
	401 {<br />
	402&nbsp;&nbsp;&nbsp;&nbsp; struct goldfish_mmc_host *host = mmc_priv(mmc);<br />
	403<br />
	404&nbsp;&nbsp;&nbsp;&nbsp; WARN_ON(host-&gt;mrq != NULL);<br />
	405<br />
	406&nbsp;&nbsp;&nbsp;&nbsp; host-&gt;mrq = req;<br />
	407&nbsp;&nbsp;&nbsp;&nbsp; goldfish_mmc_prepare_data(host, req);//parameters be written and ready<br />
	408&nbsp;&nbsp;&nbsp;&nbsp; goldfish_mmc_start_command(host, req-&gt;cmd);//data been transported<br />
	409<br />
	410&nbsp;&nbsp;&nbsp;&nbsp; /* this is to avoid accidentally being detected as an SDIO card in mmc_attach_sdio() */<br />
	411&nbsp;&nbsp;&nbsp;&nbsp; if (req-&gt;cmd-&gt;opcode == SD_IO_SEND_OP_COND &amp;&amp;<br />
	412&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req-&gt;cmd-&gt;flags == (MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR)) {<br />
	413&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req-&gt;cmd-&gt;error = -EINVAL;<br />
	414&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	415 }<br />
	第408行，调用goldfish_mmc_request<br />
	156 static void<br />
	157 goldfish_mmc_start_command(struct goldfish_mmc_host *host, struct mmc_command *cmd)<br />
	158 {<br />
	159&nbsp;&nbsp;&nbsp;&nbsp; u32 cmdreg;<br />
	160&nbsp;&nbsp;&nbsp;&nbsp; u32 resptype;<br />
	161&nbsp;&nbsp;&nbsp;&nbsp; u32 cmdtype;<br />
	162<br />
	163&nbsp;&nbsp;&nbsp;&nbsp; host-&gt;cmd = cmd;<br />
	164<br />
	165&nbsp;&nbsp;&nbsp;&nbsp; resptype = 0;<br />
	166&nbsp;&nbsp;&nbsp;&nbsp; cmdtype = 0;<br />
	167<br />
	168&nbsp;&nbsp;&nbsp;&nbsp; /* Our hardware needs to know exact type */<br />
	169&nbsp;&nbsp;&nbsp;&nbsp; switch (mmc_resp_type(cmd)) {<br />
	170&nbsp;&nbsp;&nbsp;&nbsp; case MMC_RSP_NONE:<br />
	171&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
	172&nbsp;&nbsp;&nbsp;&nbsp; case MMC_RSP_R1:<br />
	173&nbsp;&nbsp;&nbsp;&nbsp; case MMC_RSP_R1B:<br />
	174&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* resp 1, 1b, 6, 7 */<br />
	175&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resptype = 1;<br />
	176&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
	177&nbsp;&nbsp;&nbsp;&nbsp; case MMC_RSP_R2:<br />
	178&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resptype = 2;<br />
	179&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
	180&nbsp;&nbsp;&nbsp;&nbsp; case MMC_RSP_R3:<br />
	181&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resptype = 3;<br />
	182&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
	183&nbsp;&nbsp;&nbsp;&nbsp; default:<br />
	184&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dev_err(mmc_dev(host-&gt;mmc), &quot;Invalid response type: %04x\n&quot;, mmc_resp_type(cmd));<br />
	185&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
	186&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	187<br />
	188&nbsp;&nbsp;&nbsp;&nbsp; if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) {<br />
	189&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmdtype = OMAP_MMC_CMDTYPE_ADTC;<br />
	190&nbsp;&nbsp;&nbsp;&nbsp; } else if (mmc_cmd_type(cmd) == MMC_CMD_BC) {<br />
	191&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmdtype = OMAP_MMC_CMDTYPE_BC;<br />
	192&nbsp;&nbsp;&nbsp;&nbsp; } else if (mmc_cmd_type(cmd) == MMC_CMD_BCR) {<br />
	193&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmdtype = OMAP_MMC_CMDTYPE_BCR;<br />
	194&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />
	195&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmdtype = OMAP_MMC_CMDTYPE_AC;<br />
	196&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	197<br />
	198&nbsp;&nbsp;&nbsp;&nbsp; cmdreg = cmd-&gt;opcode | (resptype &lt;&lt;&nbsp; <img src='http://www.tek-life.org/wp-includes/images/smilies/icon_cool.gif' alt='8)' class='wp-smiley' /> | (cmdtype &lt;&lt; 12);<br />
	199<br />
	200&nbsp;&nbsp;&nbsp;&nbsp; if (host-&gt;bus_mode == MMC_BUSMODE_OPENDRAIN)<br />
	201&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmdreg |= 1 &lt;&lt; 6;<br />
	202<br />
	203&nbsp;&nbsp;&nbsp;&nbsp; if (cmd-&gt;flags &amp; MMC_RSP_BUSY)<br />
	204&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmdreg |= 1 &lt;&lt; 11;<br />
	205<br />
	206&nbsp;&nbsp;&nbsp;&nbsp; if (host-&gt;data &amp;&amp; !(host-&gt;data-&gt;flags &amp; MMC_DATA_WRITE))<br />
	207&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmdreg |= 1 &lt;&lt; 15;<br />
	208<br />
	209&nbsp;&nbsp;&nbsp;&nbsp; GOLDFISH_MMC_WRITE(host, MMC_ARG, cmd-&gt;arg);<br />
	210&nbsp;&nbsp;&nbsp;&nbsp; GOLDFISH_MMC_WRITE(host, MMC_CMD, cmdreg);<br />
	211 }<br />
	这个过程可能会等一段时间。<br />
	什么时候，才知道数据读写完毕呢？通过中断。当数据读写完毕后，host会向系统发起一个中断。在中断中，将调用第200行的mmc_wait_done。其中断函数的代码如下：<br />
	291 static irqreturn_t goldfish_mmc_irq(int irq, void *dev_id)<br />
	292 {<br />
	293&nbsp;&nbsp;&nbsp;&nbsp; struct goldfish_mmc_host * host = (struct goldfish_mmc_host *)dev_id;<br />
	294&nbsp;&nbsp;&nbsp;&nbsp; u16 status;<br />
	295&nbsp;&nbsp;&nbsp;&nbsp; int end_command = 0;<br />
	296&nbsp;&nbsp;&nbsp;&nbsp; int end_transfer = 0;<br />
	297&nbsp;&nbsp;&nbsp;&nbsp; int transfer_error = 0;<br />
	298&nbsp;&nbsp;&nbsp;&nbsp; int state_changed = 0;<br />
	299&nbsp;&nbsp;&nbsp;&nbsp; int cmd_timeout = 0;<br />
	300<br />
	301&nbsp;&nbsp;&nbsp;&nbsp; while ((status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS)) != 0) {<br />
	302&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);<br />
	303<br />
	304&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (status &amp; MMC_STAT_END_OF_CMD) {<br />
	305&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end_command = 1;<br />
	306&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	307<br />
	308&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (status &amp; MMC_STAT_END_OF_DATA) {<br />
	309&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end_transfer = 1;<br />
	310&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	311&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (status &amp; MMC_STAT_STATE_CHANGE) {<br />
	312&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; state_changed = 1;<br />
	313&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	314<br />
	315&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (status &amp; MMC_STAT_CMD_TIMEOUT) {<br />
	316&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end_command = 0;<br />
	317&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd_timeout = 1;<br />
	318&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	319&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	320<br />
	321&nbsp;&nbsp;&nbsp;&nbsp; if (cmd_timeout) {<br />
	322&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct mmc_request *mrq = host-&gt;mrq;<br />
	323&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;cmd-&gt;error = -ETIMEDOUT;<br />
	324&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; host-&gt;mrq = NULL;<br />
	325&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_request_done(host-&gt;mmc, mrq);<br />
	326&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	327<br />
	328&nbsp;&nbsp;&nbsp;&nbsp; if (end_command) {<br />
	329&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goldfish_mmc_cmd_done(host, host-&gt;cmd);<br />
	330&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	331&nbsp;&nbsp;&nbsp;&nbsp; if (transfer_error)<br />
	332&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goldfish_mmc_xfer_done(host, host-&gt;data);<br />
	333&nbsp;&nbsp;&nbsp;&nbsp; else if (end_transfer) {<br />
	334&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; host-&gt;dma_done = 1;<br />
	335&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goldfish_mmc_end_of_data(host, host-&gt;data);<br />
	336&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	337&nbsp;&nbsp;&nbsp;&nbsp; if (state_changed) {<br />
	338&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u32 state = GOLDFISH_MMC_READ(host, MMC_STATE);<br />
	339&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pr_info(&quot;%s: Card detect now %d\n&quot;, __func__,<br />
	340&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (state &amp; MMC_STATE_INSERTED));<br />
	341&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_detect_change(host-&gt;mmc, 0);<br />
	342&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	343<br />
	344&nbsp;&nbsp;&nbsp;&nbsp; if (!end_command &amp;&amp; !end_transfer &amp;&amp;<br />
	345&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; !transfer_error &amp;&amp; !state_changed &amp;&amp; !cmd_timeout) {<br />
	346&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS);<br />
	347&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dev_info(mmc_dev(host-&gt;mmc),&quot;spurious irq 0x%04x\n&quot;, status);<br />
	348&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (status != 0) {<br />
	349&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);<br />
	350&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE, 0);<br />
	351&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	352&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	353<br />
	354&nbsp;&nbsp;&nbsp;&nbsp; return IRQ_HANDLED;<br />
	355 }<br />
	在第333~336行，如果数据传输完毕后，会执行335行的goldfish_mmc_end_of_data()，注意host-&gt;dma_done设置为1，下面的程序会调用到。<br />
	252 static void<br />
	253 goldfish_mmc_end_of_data(struct goldfish_mmc_host *host, struct mmc_data *data)<br />
	254 {<br />
	255&nbsp;&nbsp;&nbsp;&nbsp; if (!host-&gt;dma_in_use) {<br />
	256&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goldfish_mmc_xfer_done(host, data);<br />
	257&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />
	258&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	259&nbsp;&nbsp;&nbsp;&nbsp; if (host-&gt;dma_done)<br />
	260&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goldfish_mmc_xfer_done(host, data);<br />
	261 }<br />
	由于之前host-&gt;dma_done设置为1，那么执行259~260行。即调用goldfish_mmc_xfer_done<br />
	213 static void<br />
	214 goldfish_mmc_xfer_done(struct goldfish_mmc_host *host, struct mmc_data *data)<br />
	215 {<br />
	216&nbsp;&nbsp;&nbsp;&nbsp; if (host-&gt;dma_in_use) {<br />
	217&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enum dma_data_direction dma_data_dir;<br />
	218<br />
	219&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (data-&gt;flags &amp; MMC_DATA_WRITE)<br />
	220&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dma_data_dir = DMA_TO_DEVICE;<br />
	221&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />
	222&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dma_data_dir = DMA_FROM_DEVICE;<br />
	223<br />
	224&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (dma_data_dir == DMA_FROM_DEVICE) {<br />
	225&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // we don&#39;t really have DMA, so we need to copy from our platform driver buffer<br />
	226&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; uint8_t* dest = (uint8_t *)sg_virt(data-&gt;sg);<br />
	227&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memcpy(dest, host-&gt;virt_base, data-&gt;sg-&gt;length);<br />
	228&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	229<br />
	230&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; host-&gt;data-&gt;bytes_xfered += data-&gt;sg-&gt;length;<br />
	231<br />
	232&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dma_unmap_sg(mmc_dev(host-&gt;mmc), data-&gt;sg, host-&gt;sg_len, dma_data_dir);<br />
	233&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	234<br />
	235&nbsp;&nbsp;&nbsp;&nbsp; host-&gt;data = NULL;<br />
	236&nbsp;&nbsp;&nbsp;&nbsp; host-&gt;sg_len = 0;<br />
	237<br />
	238&nbsp;&nbsp;&nbsp;&nbsp; /* NOTE:&nbsp; MMC layer will sometimes poll-wait CMD13 next, issuing<br />
	239&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * dozens of requests until the card finishes writing data.<br />
	240&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * It&#39;d be cheaper to just wait till an EOFB interrupt arrives&#8230;<br />
	241&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
	242<br />
	243&nbsp;&nbsp;&nbsp;&nbsp; if (!data-&gt;stop) {<br />
	244&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; host-&gt;mrq = NULL;<br />
	245&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_request_done(host-&gt;mmc, data-&gt;mrq);<br />
	246&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />
	247&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	248<br />
	249&nbsp;&nbsp;&nbsp;&nbsp; goldfish_mmc_start_command(host, data-&gt;stop);<br />
	250 }<br />
	第245行，调用了mmc_request_done<br />
	69 /**<br />
	70&nbsp; *&nbsp; mmc_request_done &#8211; finish processing an MMC request<br />
	71&nbsp; *&nbsp; @host: MMC host which completed request<br />
	72&nbsp; *&nbsp; @mrq: MMC request which request<br />
	73&nbsp; *<br />
	74&nbsp; *&nbsp; MMC drivers should call this function when they have completed<br />
	75&nbsp; *&nbsp; their processing of a request.<br />
	76&nbsp; */<br />
	77 void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)<br />
	78 {<br />
	79&nbsp;&nbsp;&nbsp;&nbsp; struct mmc_command *cmd = mrq-&gt;cmd;<br />
	80&nbsp;&nbsp;&nbsp;&nbsp; int err = cmd-&gt;error;<br />
	81<br />
	82&nbsp;&nbsp;&nbsp;&nbsp; if (err &amp;&amp; cmd-&gt;retries &amp;&amp; mmc_host_is_spi(host)) {<br />
	83&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (cmd-&gt;resp[0] &amp; R1_SPI_ILLEGAL_COMMAND)<br />
	84&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd-&gt;retries = 0;<br />
	85&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	86<br />
	87&nbsp;&nbsp;&nbsp;&nbsp; if (err &amp;&amp; cmd-&gt;retries) {<br />
	88&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pr_debug(&quot;%s: req failed (CMD%u): %d, retrying&#8230;\n&quot;,<br />
	89&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_hostname(host), cmd-&gt;opcode, err);<br />
	90<br />
	91&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd-&gt;retries&#8211;;<br />
	92&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd-&gt;error = 0;<br />
	93&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; host-&gt;ops-&gt;request(host, mrq);<br />
	94&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />
	95&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; led_trigger_event(host-&gt;led, LED_OFF);<br />
	96<br />
	97&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pr_debug(&quot;%s: req done (CMD%u): %d: %08x %08x %08x %08x\n&quot;,<br />
	98&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_hostname(host), cmd-&gt;opcode, err,<br />
	99&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd-&gt;resp[0], cmd-&gt;resp[1],<br />
	100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd-&gt;resp[2], cmd-&gt;resp[3]);<br />
	101<br />
	102&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (mrq-&gt;data) {<br />
	103&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pr_debug(&quot;%s:&nbsp;&nbsp;&nbsp;&nbsp; %d bytes transferred: %d\n&quot;,<br />
	104&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_hostname(host),<br />
	105&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;data-&gt;bytes_xfered, mrq-&gt;data-&gt;error);<br />
	106&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	107<br />
	108&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (mrq-&gt;stop) {<br />
	109&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pr_debug(&quot;%s:&nbsp;&nbsp;&nbsp;&nbsp; (CMD%u): %d: %08x %08x %08x %08x\n&quot;,<br />
	110&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mmc_hostname(host), mrq-&gt;stop-&gt;opcode,<br />
	111&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;stop-&gt;error,<br />
	112&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;stop-&gt;resp[0], mrq-&gt;stop-&gt;resp[1],<br />
	113&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;stop-&gt;resp[2], mrq-&gt;stop-&gt;resp[3]);<br />
	114&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	115<br />
	116&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (mrq-&gt;done)<br />
	117&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrq-&gt;done(mrq);<br />
	118&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	119 }<br />
	最终会调用117行的mrq-&gt;done，即mmc_wait_done<br />
	181 static void mmc_wait_done(struct mmc_request *mrq)<br />
	182 {<br />
	183&nbsp;&nbsp;&nbsp;&nbsp; complete(mrq-&gt;done_data);<br />
	184 }<br />
	183行中的mrq-&gt;done_data被设置为了&amp;complete(看mmc_wait_for_req)。<br />
	4824 /**<br />
	4825&nbsp; * complete: &#8211; signals a single thread waiting on this completion<br />
	4826&nbsp; * @x:&nbsp; holds the state of this particular completion<br />
	4827&nbsp; *<br />
	4828&nbsp; * This will wake up a single thread waiting on this completion. Threads will be<br />
	4829&nbsp; * awakened in the same order in which they were queued.<br />
	4830&nbsp; *<br />
	4831&nbsp; * See also complete_all(), wait_for_completion() and related routines.<br />
	4832&nbsp; */<br />
	4833 void complete(struct completion *x)<br />
	4834 {<br />
	4835&nbsp;&nbsp;&nbsp;&nbsp; unsigned long flags;<br />
	4836<br />
	4837&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_irqsave(&amp;x-&gt;wait.lock, flags);<br />
	4838&nbsp;&nbsp;&nbsp;&nbsp; x-&gt;done++;<br />
	4839&nbsp;&nbsp;&nbsp;&nbsp; __wake_up_common(&amp;x-&gt;wait, TASK_NORMAL, 1, 0, NULL);<br />
	4840&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock_irqrestore(&amp;x-&gt;wait.lock, flags);<br />
	4841 }<br />
	4842 EXPORT_SYMBOL(complete);<br />
	看4838行，done++后，再调用complete上的进程，这时候，进程就可以结束 wait_for_completion(&amp;complete)了。wait_for_completion代码如下：<br />
	4898 /**<br />
	4899&nbsp; * wait_for_completion: &#8211; waits for completion of a task<br />
	4900&nbsp; * @x:&nbsp; holds the state of this particular completion<br />
	4901&nbsp; *<br />
	4902&nbsp; * This waits to be signaled for completion of a specific task. It is NOT<br />
	4903&nbsp; * interruptible and there is no timeout.<br />
	4904&nbsp; *<br />
	4905&nbsp; * See also similar routines (i.e. wait_for_completion_timeout()) with timeout<br />
	4906&nbsp; * and interrupt capability. Also see complete().<br />
	4907&nbsp; */<br />
	4908 void __sched wait_for_completion(struct completion *x)<br />
	4909 {<br />
	4910&nbsp;&nbsp;&nbsp;&nbsp; wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);<br />
	4911 }<br />
	4912 EXPORT_SYMBOL(wait_for_completion);<br />
	4887 static long __sched<br />
	4888 wait_for_common(struct completion *x, long timeout, int state)<br />
	4889 {<br />
	4890&nbsp;&nbsp;&nbsp;&nbsp; might_sleep();<br />
	4891<br />
	4892&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_irq(&amp;x-&gt;wait.lock);<br />
	4893&nbsp;&nbsp;&nbsp;&nbsp; timeout = do_wait_for_common(x, timeout, state);<br />
	4894&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock_irq(&amp;x-&gt;wait.lock);<br />
	4895&nbsp;&nbsp;&nbsp;&nbsp; return timeout;<br />
	4896 }<br />
	4861 static inline long __sched<br />
	4862 do_wait_for_common(struct completion *x, long timeout, int state)<br />
	4863 {<br />
	4864&nbsp;&nbsp;&nbsp;&nbsp; if (!x-&gt;done) {<br />
	4865&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DECLARE_WAITQUEUE(wait, current);<br />
	4866<br />
	4867&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait.flags |= WQ_FLAG_EXCLUSIVE;<br />
	4868&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __add_wait_queue_tail(&amp;x-&gt;wait, &amp;wait);<br />
	4869&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do {<br />
	4870&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (signal_pending_state(state, current)) {<br />
	4871&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeout = -ERESTARTSYS;<br />
	4872&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
	4873&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	4874&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __set_current_state(state);<br />
	4875&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock_irq(&amp;x-&gt;wait.lock);<br />
	4876&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeout = schedule_timeout(timeout);<br />
	4877&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_irq(&amp;x-&gt;wait.lock);<br />
	4878&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } while (!x-&gt;done &amp;&amp; timeout);<br />
	4879&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __remove_wait_queue(&amp;x-&gt;wait, &amp;wait);<br />
	4880&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!x-&gt;done)<br />
	4881&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return timeout;<br />
	4882&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	4883&nbsp;&nbsp;&nbsp;&nbsp; x-&gt;done&#8211;;<br />
	4884&nbsp;&nbsp;&nbsp;&nbsp; return timeout ?: 1;<br />
	4885 }<br />
	着重看4878行。<br />
	至此，基本上，mmc读写请求的整个过程就分析结束了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/10/06/sd-read-write-routine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>文件读写，从请求到IO启动</title>
		<link>http://www.tek-life.org/2011/09/26/read-write-request-io/</link>
		<comments>http://www.tek-life.org/2011/09/26/read-write-request-io/#comments</comments>
		<pubDate>Mon, 26 Sep 2011 09:28:26 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[File System]]></category>
		<category><![CDATA[Linux Kernel]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10646</guid>
		<description><![CDATA[在Linux中，块设备的读写是一个比较复杂的过程。如果再加上VFS的话，层次就更多了。实际上VFS和块设备驱动联系的不是非常密切。在VFS中，我们会看到当发生读写请求的时候，会调用ll_rw_block函数或者submit_bh函数，其中ll_rw_block是对submit_bh的封装。这个函数，实际上就是从VFS到实际设备读写的必经之路。关于这一点，有很多用systemtap来观测io的脚本就是在submit_bh函数安装一个stub（有关io检测的相关的文章，可以参看淘宝的大牛诸霸的博客）。 在这里，我们不再谈论VFS的东西，而是从submit_bh开始看起，然后到数据被读出，进程又继续执行的流程。 submit_bh的功能正如其函数的名字那样&#8211;&#8220;提交buffer head&#8221;。那么，具体提交给谁呢？由谁来提交呢？其实块设备的读写似乎是一个C/S架构的服务器。客户端是各个进行io操作的进程，服务器端就是设备的请求队列。进程把请求的信息包装成一个request的数据结构，然后，挂载在服务端，即块设备的请求队列中。我前面说的那句话&#8220;似乎是一个C/S架构的&#8221;，而不是真正C/S架构的。因为，C/S架构来源于网络程序，客户端进程把数据发往正在监听的服务端，然后服务端的进程从网络缓冲区中经过网络协议的层层解压&#8220;剥皮&#8221;，拿到数据。而我们谈论这个文件数据的读写并不真正是C/S架构的，原因就是，客户端的发送和服务端的接收请求全由一个进程，即用户进程来完成的。这一点应该很好理解，因为在OS中，除了中断以及异常处理没有上下文外，其他的都有进程上下文，因此，从submit读写请求到接收读写请求，当然就是进程自己的事情了，当然，如果你要是抬杠&#8220;在内核中完全可以由一个专门的线程用来处理服务端的事情&#8221;，我也无话可说。原理上，你这个抬杠当然是行得通的。说到这里，我想起了微内核的MINIX，就此打住，继续回到他们的处理过程中。 当用户进程提交请求，并挂载到块设备队列的过程中，还涉及一个IO调度的问题，即是，用户进程在提交一个请求时，遇到了调度算法，这个调度算法做的事情很简单，它检查这个请求和正在排队的请求能否合二为一。如果不能合二为一，那就直接挂上去。至此，一个进程所要做的工作基本上就结束了。可是，请求被相应的时机呢？什么时候，它的请求才能满足？ 我们清楚，一个硬件设备，特别是块设备，让它来读取数据然后内核再从端口里面把数据提出来，或者说，通过DMA的方式，直接从磁盘中拿到数据，这个数据读出来的过程是很漫长的，这个漫长是相对于CPU来说的，绝对时间其实是很短暂的。一般情况下，用户进程在往块设备的请求队列上挂请求的时候，发现队列为空的话，会将该队列插入到一个全局的队列中(tq_disk，从名字中，我们也能够看出来是task queue for disk的缩写)。如果队列不为空，那么说明该队列已经加入到tq_disk的全局队列中了，既然该块设备的读写请求队列不为空，那么要利用调度策略，看时候能够和正在该块设备上排队的读写请求和二为一了。这里有一个很恰当的比方：当我们去饭店吃饭的时候，如果你要点的菜如果和师傅正在炒或者准备炒的菜一样的话，炒菜师傅会把两个人点的菜一块炒，特别是学校的食堂，每到吃饭高峰期，人很多，因此，学生们一般都会问服务员，下面要做的是什么菜，如果要节省时间的话，就要师傅下面要炒的菜了。在这里，磁盘的调度原理就是这个样子，貌似很简单哇。其实，有些时候，我们可能并不需要一个请求队列，比如，将来计算机的磁盘全部是SSD了，不再用机械磁盘了，都是电读写的，那么这个IO调度说不定就要被废除了。然后，也就不再需要请求队列了。一个请求到了，然后马上就发送到驱动程序，驱动程序想设备发送命令，读取数据。而在Linux的内核中，已经考虑到这一点了，如果进程进行同步IO的话，就直接启动驱动程序进行IO读写了（请参考代码段一）。 前面说到，将读写请求挂载在块设备的请求队列时，如果不为空的话，会看能否进行IO调度，不能调度的话，会向块设备请求队列插入一个请求。然后进程的任务就完成了。如果插入请求的时候，发现这个队列上的请求非常多，那么怎么办呢？进程就会主动的启动磁盘IO让这些请求队列赶紧执行(请参考代码片段二)。 上面设计到的进程启动磁盘IO，都算是主动的。除了主动的时候，还有被动的情况。当进程将请求挂载在块设备请求队列的时候，它是要用其中的数据的。什么时候用呢？该用的时候就用呀，不过用的时候，会检测相关的数据是否被读出了，如果没有读出，那么进程就被阻塞，然后启动磁盘IO(请参考代码片段三)。这一点在Linux2.6的内核中稍微进行了改进，设置了一个request数量阈值，如果大于这个阈值，那么就启动磁盘IO。 在Linux2.6的内核中，还增加了一个启动IO磁盘的时机，即，读写请求被插入到某个块设备的请求队列时，设置了一个定时器，保证在某个时间点之内，一定要启动磁盘IO。 启动磁盘IO后，数据怎么读出就跟进程没什么关系了。进程在使用的时候，就会查看它要用的buffer缓冲区是否locked，如果否，就说明已经读好了，如果是，那么就继续启动磁盘然后等待(代码片段三)。 以上基本上就分析完了。在2.6的内核中，在request和buffer head中又加了一个bio，不过仅仅加了个bio并不影响理解。另外，单单看块设备驱动，并不能够解决读写请求发送到块设备请求队列，然后块设备又怎样的把读写的数据读入到buffer中。当然了，在这一个南大富士通的赵磊大牛写过一个系列的文章&#8220;写一个块设备驱动&#8221;，一共120页。对我的帮助还是蛮大，当初凌晨看到2点半，然后又加上一个上午，基本上算是一口气看完了。写得不错，希望对块设备驱动有兴趣的同学，可以google一下，看看。 注：本文还非常naive，错误难免，如果发现，请批评指正。 附件：参考2.4.31内核 代码片段一: &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; submit_bh-&#62;__make_request 1000 static int __make_request(request_queue_t * q, int rw, 1001&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct buffer_head * bh) 1002 { 1003&#160;&#160;&#160;&#160; unsigned int sector, count, &#8230; <a href="http://www.tek-life.org/2011/09/26/read-write-request-io/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>
	在Linux中，块设备的读写是一个比较复杂的过程。如果再加上VFS的话，层次就更多了。实际上VFS和块设备驱动联系的不是非常密切。在VFS中，我们会看到当发生读写请求的时候，会调用ll_rw_block函数或者submit_bh函数，其中ll_rw_block是对submit_bh的封装。这个函数，实际上就是从VFS到实际设备读写的必经之路。关于这一点，有很多用systemtap来观测io的脚本就是在submit_bh函数安装一个stub（有关io检测的相关的文章，可以参看淘宝的大牛诸霸的博客）。</p>
<p>
	在这里，我们不再谈论VFS的东西，而是从submit_bh开始看起，然后到数据被读出，进程又继续执行的流程。<br />
	submit_bh的功能正如其函数的名字那样&ndash;&ldquo;提交buffer head&rdquo;。那么，具体提交给谁呢？由谁来提交呢？其实块设备的读写似乎是一个C/S架构的服务器。客户端是各个进行io操作的进程，服务器端就是设备的请求队列。进程把请求的信息包装成一个request的数据结构，然后，挂载在服务端，即块设备的请求队列中。我前面说的那句话&ldquo;似乎是一个C/S架构的&rdquo;，而不是真正C/S架构的。因为，C/S架构来源于网络程序，客户端进程把数据发往正在监听的服务端，然后服务端的进程从网络缓冲区中经过网络协议的层层解压&ldquo;剥皮&rdquo;，拿到数据。而我们谈论这个文件数据的读写并不真正是C/S架构的，原因就是，客户端的发送和服务端的接收请求全由一个进程，即用户进程来完成的。这一点应该很好理解，因为在OS中，除了中断以及异常处理没有上下文外，其他的都有进程上下文，因此，从submit读写请求到接收读写请求，当然就是进程自己的事情了，当然，如果你要是抬杠&ldquo;在内核中完全可以由一个专门的线程用来处理服务端的事情&rdquo;，我也无话可说。原理上，你这个抬杠当然是行得通的。说到这里，我想起了微内核的MINIX，就此打住，继续回到他们的处理过程中。<br />
	当用户进程提交请求，并挂载到块设备队列的过程中，还涉及一个IO调度的问题，即是，用户进程在提交一个请求时，遇到了调度算法，这个调度算法做的事情很简单，它检查这个请求和正在排队的请求能否合二为一。如果不能合二为一，那就直接挂上去。至此，一个进程所要做的工作基本上就结束了。可是，请求被相应的时机呢？什么时候，它的请求才能满足？<br />
	我们清楚，一个硬件设备，特别是块设备，让它来读取数据然后内核再从端口里面把数据提出来，或者说，通过DMA的方式，直接从磁盘中拿到数据，这个数据读出来的过程是很漫长的，这个漫长是相对于CPU来说的，绝对时间其实是很短暂的。一般情况下，用户进程在往块设备的请求队列上挂请求的时候，发现队列为空的话，会将该队列插入到一个全局的队列中(tq_disk，从名字中，我们也能够看出来是task queue for disk的缩写)。如果队列不为空，那么说明该队列已经加入到tq_disk的全局队列中了，既然该块设备的读写请求队列不为空，那么要利用调度策略，看时候能够和正在该块设备上排队的读写请求和二为一了。这里有一个很恰当的比方：当我们去饭店吃饭的时候，如果你要点的菜如果和师傅正在炒或者准备炒的菜一样的话，炒菜师傅会把两个人点的菜一块炒，特别是学校的食堂，每到吃饭高峰期，人很多，因此，学生们一般都会问服务员，下面要做的是什么菜，如果要节省时间的话，就要师傅下面要炒的菜了。在这里，磁盘的调度原理就是这个样子，貌似很简单哇。其实，有些时候，我们可能并不需要一个请求队列，比如，将来计算机的磁盘全部是SSD了，不再用机械磁盘了，都是电读写的，那么这个IO调度说不定就要被废除了。然后，也就不再需要请求队列了。一个请求到了，然后马上就发送到驱动程序，驱动程序想设备发送命令，读取数据。而在Linux的内核中，已经考虑到这一点了，如果进程进行同步IO的话，就直接启动驱动程序进行IO读写了（请参考代码段一）。<br />
	前面说到，将读写请求挂载在块设备的请求队列时，如果不为空的话，会看能否进行IO调度，不能调度的话，会向块设备请求队列插入一个请求。然后进程的任务就完成了。如果插入请求的时候，发现这个队列上的请求非常多，那么怎么办呢？进程就会主动的启动磁盘IO让这些请求队列赶紧执行(请参考代码片段二)。<br />
	上面设计到的进程启动磁盘IO，都算是主动的。除了主动的时候，还有被动的情况。当进程将请求挂载在块设备请求队列的时候，它是要用其中的数据的。什么时候用呢？该用的时候就用呀，不过用的时候，会检测相关的数据是否被读出了，如果没有读出，那么进程就被阻塞，然后启动磁盘IO(请参考代码片段三)。这一点在Linux2.6的内核中稍微进行了改进，设置了一个request数量阈值，如果大于这个阈值，那么就启动磁盘IO。<br />
	在Linux2.6的内核中，还增加了一个启动IO磁盘的时机，即，读写请求被插入到某个块设备的请求队列时，设置了一个定时器，保证在某个时间点之内，一定要启动磁盘IO。<br />
	启动磁盘IO后，数据怎么读出就跟进程没什么关系了。进程在使用的时候，就会查看它要用的buffer缓冲区是否locked，如果否，就说明已经读好了，如果是，那么就继续启动磁盘然后等待(代码片段三)。<br />
	以上基本上就分析完了。在2.6的内核中，在request和buffer head中又加了一个bio，不过仅仅加了个bio并不影响理解。另外，单单看块设备驱动，并不能够解决读写请求发送到块设备请求队列，然后块设备又怎样的把读写的数据读入到buffer中。当然了，在这一个南大富士通的赵磊大牛写过一个系列的文章&ldquo;写一个块设备驱动&rdquo;，一共120页。对我的帮助还是蛮大，当初凌晨看到2点半，然后又加上一个上午，基本上算是一口气看完了。写得不错，希望对块设备驱动有兴趣的同学，可以google一下，看看。</p>
<p>
	注：本文还非常naive，错误难免，如果发现，请批评指正。</p>
<p>
	附件：参考2.4.31内核<br />
	代码片段一:<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; submit_bh-&gt;__make_request<br />
	1000 static int __make_request(request_queue_t * q, int rw,<br />
	1001&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct buffer_head * bh)<br />
	1002 {<br />
	1003&nbsp;&nbsp;&nbsp;&nbsp; unsigned int sector, count, sync;<br />
	1004&nbsp;&nbsp;&nbsp;&nbsp; int max_segments = MAX_SEGMENTS;<br />
	1005&nbsp;&nbsp;&nbsp;&nbsp; struct request * req, *freereq = NULL;<br />
	1006&nbsp;&nbsp;&nbsp;&nbsp; int rw_ahead, max_sectors, el_ret;<br />
	1007&nbsp;&nbsp;&nbsp;&nbsp; struct list_head *head, *insert_here;<br />
	1008&nbsp;&nbsp;&nbsp;&nbsp; int latency;<br />
	1009&nbsp;&nbsp;&nbsp;&nbsp; elevator_t *elevator = &amp;q-&gt;elevator;<br />
	1010&nbsp;&nbsp;&nbsp;&nbsp; int should_wake = 0;<br />
	1011<br />
	1012&nbsp;&nbsp;&nbsp;&nbsp; count = bh-&gt;b_size &gt;&gt; 9;<br />
	1013&nbsp;&nbsp;&nbsp;&nbsp; sector = bh-&gt;b_rsector;<br />
	1014&nbsp;&nbsp;&nbsp;&nbsp; sync = test_and_clear_bit(BH_Sync, &amp;bh-&gt;b_state);<br />
	1015<br />
	.。。。。。。。。。。。。。。。。。。。。。。。。。<br />
	1176 out:<br />
	1177&nbsp;&nbsp;&nbsp;&nbsp; if (freereq)<br />
	1178&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blkdev_release_request(freereq);<br />
	1179&nbsp;&nbsp;&nbsp;&nbsp; if (should_wake)<br />
	1180&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get_request_wait_wakeup(q, rw);<br />
	1181&nbsp;&nbsp;&nbsp;&nbsp; if (sync)<br />
	1182&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __generic_unplug_device(q);//进程发起启动磁盘IO<br />
	1183 &nbsp;&nbsp;&nbsp;&nbsp;spin_unlock_irq(&amp;io_request_lock);<br />
	1184&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />
	1185 end_io:<br />
	1186&nbsp;&nbsp;&nbsp;&nbsp; bh-&gt;b_end_io(bh, test_bit(BH_Uptodate, &amp;bh-&gt;b_state));<br />
	1187&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />
	1188 }<br />
	代码片段二：<br />
	__make_request-&gt;__get_request_wait<br />
	&nbsp;643 static struct request *__get_request_wait(request_queue_t *q, int rw)<br />
	&nbsp;644 {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
	&nbsp;645&nbsp;&nbsp;&nbsp;&nbsp; register struct request *rq;<br />
	&nbsp;646&nbsp;&nbsp;&nbsp;&nbsp; DECLARE_WAITQUEUE(wait, current);<br />
	&nbsp;647<br />
	&nbsp;648&nbsp;&nbsp;&nbsp;&nbsp; add_wait_queue_exclusive(&amp;q-&gt;wait_for_requests, &amp;wait);<br />
	&nbsp;649<br />
	&nbsp;650&nbsp;&nbsp;&nbsp;&nbsp; do {<br />
	&nbsp;651&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set_current_state(TASK_UNINTERRUPTIBLE);<br />
	&nbsp;652&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_irq(&amp;io_request_lock);<br />
	&nbsp;653&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (blk_oversized_queue(q) || q-&gt;rq.count == 0) {<br />
	&nbsp;654&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __generic_unplug_device(q);//进程发起启动磁盘IO<br />
	&nbsp;655&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock_irq(&amp;io_request_lock);<br />
	&nbsp;656&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; schedule();<br />
	&nbsp;657&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_irq(&amp;io_request_lock);<br />
	&nbsp;658&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;659&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rq = get_request(q, rw);<br />
	&nbsp;660&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock_irq(&amp;io_request_lock);<br />
	&nbsp;661&nbsp;&nbsp;&nbsp;&nbsp; } while (rq == NULL);<br />
	&nbsp;662&nbsp;&nbsp;&nbsp;&nbsp; remove_wait_queue(&amp;q-&gt;wait_for_requests, &amp;wait);<br />
	&nbsp;663&nbsp;&nbsp;&nbsp;&nbsp; current-&gt;state = TASK_RUNNING;<br />
	&nbsp;664<br />
	&nbsp;665&nbsp;&nbsp;&nbsp;&nbsp; return rq;<br />
	&nbsp;666 }<br />
	&nbsp;667<br />
	代码片段三：<br />
	&nbsp;180 /*&nbsp;<br />
	&nbsp;181&nbsp; * Note that the real wait_on_buffer() is an inline function that checks<br />
	&nbsp;182&nbsp; * that the buffer is locked before calling this, so that unnecessary disk<br />
	&nbsp;183&nbsp; * unplugging does not occur.<br />
	&nbsp;184&nbsp; */<br />
	&nbsp;185 void __wait_on_buffer(struct buffer_head * bh)<br />
	&nbsp;186 {<br />
	&nbsp;187&nbsp;&nbsp;&nbsp;&nbsp; struct task_struct *tsk = current;<br />
	&nbsp;188&nbsp;&nbsp;&nbsp;&nbsp; DECLARE_WAITQUEUE(wait, tsk);<br />
	&nbsp;189<br />
	&nbsp;190&nbsp;&nbsp;&nbsp;&nbsp; get_bh(bh);<br />
	&nbsp;191&nbsp;&nbsp;&nbsp;&nbsp; add_wait_queue(&amp;bh-&gt;b_wait, &amp;wait);<br />
	&nbsp;192&nbsp;&nbsp;&nbsp;&nbsp; do {<br />
	&nbsp;193&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set_task_state(tsk, TASK_UNINTERRUPTIBLE);<br />
	&nbsp;194&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!buffer_locked(bh))<br />
	&nbsp;195&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
	&nbsp;196&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br />
	&nbsp;197&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * We must read tq_disk in TQ_ACTIVE after the<br />
	&nbsp;198&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * add_wait_queue effect is visible to other cpus.<br />
	&nbsp;199&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * We could unplug some line above it wouldn&#39;t matter<br />
	&nbsp;200&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * but we can&#39;t do that right after add_wait_queue<br />
	&nbsp;201&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * without an smp_mb() in between because spin_unlock<br />
	&nbsp;202&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * has inclusive semantics.<br />
	&nbsp;203&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Doing it here is the most efficient place so we<br />
	&nbsp;204&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * don&#39;t do a suprious unplug if we get a racy<br />
	&nbsp;205&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * wakeup that make buffer_locked to return 0, and<br />
	&nbsp;206&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * doing it here avoids an explicit smp_mb() we<br />
	&nbsp;207&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * rely on the implicit one in set_task_state.<br />
	&nbsp;208&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
	&nbsp;209&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; run_task_queue(&amp;tq_disk);<br />
	&nbsp;210&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; schedule();<br />
	&nbsp;211&nbsp;&nbsp;&nbsp;&nbsp; } while (buffer_locked(bh));<br />
	&nbsp;212&nbsp;&nbsp;&nbsp;&nbsp; tsk-&gt;state = TASK_RUNNING;<br />
	&nbsp;213&nbsp;&nbsp;&nbsp;&nbsp; remove_wait_queue(&amp;bh-&gt;b_wait, &amp;wait);<br />
	&nbsp;214&nbsp;&nbsp;&nbsp;&nbsp; put_bh(bh);<br />
	&nbsp;215 }<br />
	__wait_on_buffer-&gt;run_task_queue<br />
	119 static inline void run_task_queue(task_queue *list)<br />
	120 {&nbsp;&nbsp;&nbsp;<br />
	121&nbsp;&nbsp;&nbsp;&nbsp; if (TQ_ACTIVE(*list))<br />
	122&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __run_task_queue(list);<br />
	123 }<br />
	__wait_on_buffer-&gt;run_task_queue-&gt;__run_task_queue<br />
	334 void __run_task_queue(task_queue *list)<br />
	335 {<br />
	336&nbsp;&nbsp;&nbsp;&nbsp; struct list_head head, *next;<br />
	337&nbsp;&nbsp;&nbsp;&nbsp; unsigned long flags;<br />
	338<br />
	339&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_irqsave(&amp;tqueue_lock, flags);<br />
	340&nbsp;&nbsp;&nbsp;&nbsp; list_add(&amp;head, list);<br />
	341&nbsp;&nbsp;&nbsp;&nbsp; list_del_init(list);<br />
	342&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock_irqrestore(&amp;tqueue_lock, flags);<br />
	343<br />
	344&nbsp;&nbsp;&nbsp;&nbsp; next = head.next;<br />
	345&nbsp;&nbsp;&nbsp;&nbsp; while (next != &amp;head) {<br />
	346&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void (*f) (void *);<br />
	347&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct tq_struct *p;<br />
	348&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void *data;<br />
	349<br />
	350&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p = list_entry(next, struct tq_struct, list);<br />
	351&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; next = next-&gt;next;<br />
	352&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f = p-&gt;routine;<br />
	353&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data = p-&gt;data;<br />
	354&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wmb();<br />
	355&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;sync = 0;<br />
	356&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (f)<br />
	357&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f(data);//这里对于普通的磁盘，就是generic_unplug_device，和代码片段一以及二是一个启动IO操作，<br />
	358&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //其实这个函数还是包装了一下，最直接的是q-&gt;request_fn<br />
	359&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	360 }&nbsp;&nbsp;</p>
<p>
	参考：<br />
	1.《Linux内核源码情景分析(下册)》,第八章，设备驱动<br />
	2.《深入Linux内核架构》，第六章，设备驱动程序<br />
	3.《写一个块设备驱动》，赵磊</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/09/26/read-write-request-io/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>如何挂载一个文件系统</title>
		<link>http://www.tek-life.org/2011/08/18/how-to-mount-file_system/</link>
		<comments>http://www.tek-life.org/2011/08/18/how-to-mount-file_system/#comments</comments>
		<pubDate>Thu, 18 Aug 2011 13:35:02 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[File System]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10640</guid>
		<description><![CDATA[挂载一个文件系统中最重要的数据结构有以下3个： 1.&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; file_system_type 要挂载的文件系统类型。 2.&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; super_block其中有怎样获取该文件系统相关数据的方法。 3.&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; vfsmount 这个数据结构起到被挂载文件系统和挂载点文件系统的枢纽所用 这几个数据结构的关系，请着重看下图的红色椭圆形区域： &#160; &#160; 对照上图，我们可以得出结论，如果要挂载一个文件系统需要做的工作： 1.查找相应的文件系统类型，such as ext2 ext3 or ntfs or yaffs2 or rootfs etc. 2.查找相应的挂载点，方法：一路摸索，顺藤摸瓜。找到其dentry和inode. 3.生成一个vfsmount，这个数据结构是挂载点目录以及正在挂载的这个文件系统的根目录（依据的数据结构是：mnt_mountpoing和mnt_root）的枢纽（非常重要）,并将这个vfsmount放在到hashtable中。这个hashtable的hash值运算依据的是挂载点目录以及挂载点inode.除了挂载到hashtable中外，还要链到父挂载点的子链表中。 放在hashtable的原因是，将来在lookup其内的目录或者文件时，需要根据挂载点的目录和挂载点的inode取hash值快速得到vfsmount。 4.主要的工作完成后，还需要把正在挂载的这个文件系统的根目录的inode和dentry取出来放在内存中，其中dentry的值还要赋给vfsmount的mnt_root. 当然这个第4步骤和第3步骤可能会有混合，主要是为了给vfsmount-&#62;mnt_root赋值，所以，需要取该文件系统的根目录。 &#160; 按照以上的分析，我们对照review一下内核的源码的流程（为了方便，将与分析无关的语句去掉了）： 2393 SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, &#8230; <a href="http://www.tek-life.org/2011/08/18/how-to-mount-file_system/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>
	挂载一个文件系统中最重要的数据结构有以下3个：<br />
	1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; file_system_type 要挂载的文件系统类型。<br />
	2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super_block其中有怎样获取该文件系统相关数据的方法。<br />
	3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vfsmount 这个数据结构起到被挂载文件系统和挂载点文件系统的枢纽所用<br />
	这几个数据结构的关系，请着重看下图的红色椭圆形区域：</p>
<p>
	&nbsp;<img alt="" src="http://www.tek-life.org/wp-content/uploads/ckfinder/images/mount.png" /><br />
	&nbsp;<br />
	对照上图，我们可以得出结论，如果要挂载一个文件系统需要做的工作：<br />
	1.查找相应的文件系统类型，such as ext2 ext3 or ntfs or yaffs2 or rootfs etc.<br />
	2.查找相应的挂载点，方法：一路摸索，顺藤摸瓜。找到其dentry和inode.<br />
	3.生成一个vfsmount，这个数据结构是挂载点目录以及正在挂载的这个文件系统的根目录（依据的数据结构是：mnt_mountpoing和mnt_root）的枢纽（非常重要）,并将这个vfsmount放在到hashtable中。这个hashtable的hash值运算依据的是挂载点目录以及挂载点inode.除了挂载到hashtable中外，还要链到父挂载点的子链表中。<br />
	放在hashtable的原因是，将来在lookup其内的目录或者文件时，需要根据挂载点的目录和挂载点的inode取hash值快速得到vfsmount。<br />
	4.主要的工作完成后，还需要把正在挂载的这个文件系统的根目录的inode和dentry取出来放在内存中，其中dentry的值还要赋给vfsmount的mnt_root.<br />
	当然这个第4步骤和第3步骤可能会有混合，主要是为了给vfsmount-&gt;mnt_root赋值，所以，需要取该文件系统的根目录。<br />
	&nbsp;<br />
	按照以上的分析，我们对照review一下内核的源码的流程（为了方便，将与分析无关的语句去掉了）：<br />
	2393 SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,<br />
	2394&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char __user *, type, unsigned long, flags, void __user *, data)<br />
	2395 {<br />
	&hellip;&hellip;.//做些参数检测，然后将用户空间的相关变量拷贝到内核区间<br />
	2419<br />
	2420&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags,<br />
	2421&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (void *) data_page);<br />
	&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
	2191 long do_mount(char *dev_name, char *dir_name, char *type_page,<br />
	2192&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long flags, void *data_page)<br />
	2193 {<br />
	&hellip;做些参数检测工作，然后寻找挂载点。<br />
	2211&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; retval = kern_path(dir_name, LOOKUP_FOLLOW, &amp;path);<br />
	2212&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (retval)<br />
	2213&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return retval;<br />
	&hellip;.<br />
	//下面根据参数，选择相应的函数，我们假定最一般的情况，选择do_new_mount<br />
	2244&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (flags &amp; MS_REMOUNT)<br />
	2245&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; retval = do_remount(&amp;path, flags &amp; ~MS_REMOUNT, mnt_flags,<br />
	2246&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data_page);<br />
	2247&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (flags &amp; MS_BIND)<br />
	2248&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; retval = do_loopback(&amp;path, dev_name, flags &amp; MS_REC);<br />
	2249&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;else if (flags &amp; (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))<br />
	2250&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; retval = do_change_type(&amp;path, flags);<br />
	2251&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (flags &amp; MS_MOVE)<br />
	2252&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; retval = do_move_mount(&amp;path, dev_name);<br />
	2253&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />
	2254&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;retval = do_new_mount(&amp;path, type_page, flags, mnt_flags,<br />
	2255&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dev_name, data_page);<br />
	&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
	1877 /*<br />
	1878&nbsp; * create a new mount for userspace and request it to be added into the<br />
	1879&nbsp; * namespace&#39;s tree<br />
	1880&nbsp; */<br />
	1881 static int do_new_mount(struct path *path, char *type, int flags,<br />
	1882&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int mnt_flags, char *name, void *data)<br />
	1883 {<br />
	&hellip;做一些其他工作后，开始调用do_kenn_mount,从传参的情况，可以推测，要做的工作是：<br />
	1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 找到相关的文件系统<br />
	2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从即将挂载的设备中，取出节点，然后初始化vfsmount<br />
	3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 将返回值赋给mnt.<br />
	那么最重要的vfsmount已经得到<br />
	1894&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mnt = do_kern_mount(type, flags, name, data);<br />
	1895&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (IS_ERR(mnt))<br />
	1896&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return PTR_ERR(mnt);<br />
	1897<br />
	1898&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err = do_add_mount(mnt, path, mnt_flags);<br />
	1899&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (err)<br />
	1900&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mntput(mnt);<br />
	1901&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return err;<br />
	1902 }<br />
	&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>	do_new_mount-&gt;do_kern_mount-&gt; vfs_kern_mount<br />
	964 vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)<br />
	&nbsp;965 {<br />
	下面分配一个vfsmount<br />
	&nbsp;975&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mnt = alloc_vfsmnt(name);<br />
	&nbsp;976&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!mnt)<br />
	&nbsp;977&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto out;<br />
	&#8230;<br />
	根据情况，调用不同的函数，将vfsmount数据结构填好。以及分配superblock，以及相关的dentry和inode.<br />
	&nbsp;992&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (type-&gt;mount) {<br />
	&nbsp;993&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; root = type-&gt;mount(type, flags, name, data);<br />
	&nbsp;994&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (IS_ERR(root)) {<br />
	&nbsp;995&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = PTR_ERR(root);<br />
	&nbsp;996&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto out_free_secdata;<br />
	&nbsp;997&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;998&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mnt-&gt;mnt_root = root;<br />
	&nbsp;999&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mnt-&gt;mnt_sb = root-&gt;d_sb;<br />
	1000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />
	1001&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = type-&gt;get_sb(type, flags, name, data, mnt);<br />
	1002&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (error &lt; 0)<br />
	1003&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto out_free_secdata;<br />
	&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
	do_new_mount-&gt;do_add_mount<br />
	1938 static int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)<br />
	1939 {<br />
	&hellip;.<br />
	为了防止前面在获取文件系统内部根目录时其他进程已经挂载到该挂载点（可能是其他文件系统，也可能是正在挂载的文件系统）因此要检测一下<br />
	1956&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (path-&gt;mnt-&gt;mnt_sb == newmnt-&gt;mnt_sb &amp;&amp;<br />
	1957&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; path-&gt;mnt-&gt;mnt_root == path-&gt;dentry)<br />
	1958&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto unlock;<br />
	1959<br />
	1960&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err = -EINVAL;<br />
	1961&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (S_ISLNK(newmnt-&gt;mnt_root-&gt;d_inode-&gt;i_mode))<br />
	1962&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto unlock;<br />
	1963<br />
	1964&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; newmnt-&gt;mnt_flags = mnt_flags;<br />
	//把vfsmount插入到合适的链表中,注意，在这里面还将vfsmount中的成员mnt_mountpoint重新赋值为path-&gt;dentry.而不是在vfs_kern_mount时赋的mnt_root<br />
	1965&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err = graft_tree(newmnt, path);<br />
	1966<br />
	1967 unlock:<br />
	1968&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; up_write(&amp;namespace_sem);<br />
	1969&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return err;<br />
	1970 }<br />
	&nbsp;<br />
	从这个代码跟踪，可以发现，基本上挂载一个文件系统依据了我们前面分析的步骤。<br />
	欢迎批评指正。<br />
	&lt;完&gt;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/08/18/how-to-mount-file_system/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>通过简单的例子,学习systemtap</title>
		<link>http://www.tek-life.org/2011/07/29/learning-systemtap/</link>
		<comments>http://www.tek-life.org/2011/07/29/learning-systemtap/#comments</comments>
		<pubDate>Fri, 29 Jul 2011 09:09:55 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10631</guid>
		<description><![CDATA[看例子先!&#160; 怎样遍历数组： 1. &#160;# 2. &#160;# Print the system call count by process name in descending order. 3. &#160;# 4. 5. &#160;global syscalls 6. 7. &#160;probe begin { 8. &#160; &#160; print (&#34;Collecting data&#8230; Type Ctrl-C to exit and display &#8230; <a href="http://www.tek-life.org/2011/07/29/learning-systemtap/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>
	看例子先!&nbsp;</p>
<div>
	怎样遍历数组：</div>
<div>
	1. &nbsp;#</div>
<div>
	2. &nbsp;# Print the system call count by process name in descending order.</div>
<div>
	3. &nbsp;#</div>
<div>
	4.</div>
<div>
	5. &nbsp;global syscalls</div>
<div>
	6.</div>
<div>
	7. &nbsp;probe begin {</div>
<div>
	8. &nbsp; &nbsp; print (&quot;Collecting data&#8230; Type Ctrl-C to exit and display results\n&quot;)</div>
<div>
	9. &nbsp;}</div>
<div>
	10.</div>
<div>
	11. probe syscall.* {</div>
<div>
	12. &nbsp; &nbsp;syscalls[execname()]++</div>
<div>
	13. }</div>
<div>
	14.</div>
<div>
	15. probe end {</div>
<div>
	16. &nbsp; &nbsp;printf (&quot;%-10s %-s\n&quot;, &quot;#SysCalls&quot;, &quot;Process Name&quot;)</div>
<div>
	17. &nbsp; &nbsp;foreach (proc in syscalls-)</div>
<div>
	18. &nbsp; &nbsp; &nbsp; printf(&quot;%-10d %-s\n&quot;, syscalls[proc], proc)</div>
<div>
	19. }</div>
<div>
	&nbsp;</div>
<div>
	这中间,比较难理解的是第17行,那么它的解释如下：</div>
<div>
	The variable proc is an index variable that iterates over the range of values possible for the array index of syscalls. Also note the en dash (-) after syscalls that denotes that the iteration runs in reverse order. This character ensures that the number of system calls made print in descending order. To print in ascending order, change the script tosyscalls+.</div>
<div>
	&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div>
	&nbsp;</div>
<div>
	例子二：怎样探测内核变量或者局部变量</div>
<div>
	&nbsp;</div>
<div>
	通过stap的L参数可以查看支持的内核变量.在stap的man手册中,有下面一句:</div>
<div>
	&nbsp;</div>
<div>
	stap the -L option should be the used to get possible variables</div>
<div>
	&nbsp;</div>
<div>
	例如：</div>
<div>
	&nbsp;</div>
<div>
	$stap -L &#39;kernel.function(&quot;sys_read&quot;)&#39;</div>
<div>
	kernel.function(&quot;sys_read@/build/buildd/linux-2.6.38/fs/read_write.c:402&quot;) $fd:unsigned int $buf:char* $count:size_t $fput_needed:int</div>
<div>
	&nbsp;</div>
<div>
	从这个例子中,可以看到,对于内核中的变量,通过$加上变量的名字就可以访问了,但是并不是内核里面所有的变量都可以访问,使用前,请先用stap -L 检测一下!</div>
<div>
	&nbsp;</div>
<div>
	不过在使用的过程中,可能会碰到一些问题,比如对于char * name的变量,用printf(&quot;%s\n&quot;,$name)输出的时候,会报错,错误的类型是&quot;type mismatch&quot;.为什么呢?因为name本身是一个指针,需要转换一下才可以.转换函数可以查看man stapfuncs,不过,有些机器上安装的man手册不太完整,完整的可以看这个网站:http://linux.die.net/man/5/stapfuncs&nbsp;</div>
<div>
	在下面列举一些:</div>
<div>
	&nbsp;</div>
<div>
	kernel_string:string (addr:long)</div>
<div>
	Copy a 0-terminated string from kernel space at given address.</div>
<div>
	kernel_long:long (addr:long)</div>
<div>
	Copy a long from kernel space at given address.</div>
<div>
	kernel_int:long (addr:long)</div>
<div>
	Copy an int from kernel space at given address.</div>
<div>
	kernel_short:long (addr:long)</div>
<div>
	Copy a short from kernel space at given address.</div>
<div>
	kernel_char:long (addr:long)</div>
<div>
	Copy a char from kernel space at given address.</div>
<div>
	user_string:string (addr:long)</div>
<div>
	Copy a string from user space at given address. If the access would fault, return &quot;&lt;unknown&gt;&quot; and signal no errors.</div>
<div>
	user_string2:string (addr:long, err_msg:string)</div>
<div>
	Copy a string from user space at given address. If the access would fault, return instead the err_msg value.</div>
<div>
	user_string_warn:string (addr:long)</div>
<div>
	Copy a string from user space at given address. If the access would fault, signal a warning and return &quot;&lt;unknown&gt;&quot;.</div>
<div>
	&nbsp;</div>
<div>
	使用其中kernel_string() 就可以把char *name的值取出来:kernel_string($name).</div>
<div>
	&nbsp;</div>
<div>
	除了可以检测到内核里面的变量之外,还可以检测到系统中具有全局意义的变量,比如进程的名字,进程的ID,以及UID等,这些变量被stap包装为一个函数,列表如下:</div>
<div>
	&nbsp;</div>
<div>
	tid() The id of the current thread.</div>
<div>
	pid() The process (task group) id of the current thread.</div>
<div>
	uid() The id of the current user.</div>
<div>
	execname() The name of the current process.</div>
<div>
	cpu() The current cpu number.</div>
<div>
	gettimeofday_s() Number of seconds since epoch.可以有us ms等</div>
<div>
	get_cycles() Snapshot of hardware cycle counter.</div>
<div>
	pp() A string describing the probe point being currently handled.</div>
<div>
	probefunc() If known, the name of the function in which this probe was placed.</div>
<div>
	&nbsp;</div>
<div>
	例如下面这个例子,这个例子在运行stap后有哪些进程调用do_fork,以及调用时候的标志是什么:</div>
<div>
	&nbsp;</div>
<div>
	1. &nbsp;#!/usr/bin/stap</div>
<div>
	2.</div>
<div>
	3. &nbsp;global proc_counter</div>
<div>
	4.</div>
<div>
	5. &nbsp;probe begin {</div>
<div>
	6. &nbsp;print (&quot;Started monitoring creation of new processes&#8230;.Press ^C to terminate\n&quot;)</div>
<div>
	7. &nbsp;printf (&quot;%-25s %-10s %-s\n&quot;, &quot;Process Name&quot;, &quot;#Process ID&quot;, &quot;#Clone Flags&quot;)</div>
<div>
	8. &nbsp;}</div>
<div>
	9.</div>
<div>
	10. probe kernel.function(&quot;do_fork&quot;) {</div>
<div>
	11. &nbsp; &nbsp;proc_counter++</div>
<div>
	12. &nbsp; &nbsp;printf(&quot;%-25s %-10d 0x%-x\n&quot;, execname(), pid(), $clone_flags)</div>
<div>
	13. }</div>
<div>
	14.</div>
<div>
	15. probe end {</div>
<div>
	16. &nbsp; &nbsp;printf (&quot;\n%d processes forked during the observed period\n&quot;, proc_counter)</div>
<div>
	17.}</div>
<div>
	在上面这个例子中,调用了execname()来访问进程的名字,pid()来访问进程号,$clone_flags来说明其中使用的标志.</div>
<div>
	&nbsp;</div>
<div>
	除此之外,对于syscall还有一些特殊的探测变量：argstr :参数列表，只对syscall有。</div>
<div>
	name: syscall的名字</div>
<div>
	retstr.对syscall.return才有</div>
<div>
	使用这些变量的时候,不用加前面的$符号.</div>
<div>
	&nbsp;</div>
<div>
	另外，systemtap还内置了一些函数：比如我想只跟踪某个程序调用sys_read的情况.怎么判断程序的名字是否与execname()相等呢? 使用systemtap内置的isinstr函数就可以了.例如:</div>
<div>
	&nbsp;</div>
<div>
	probe syscall.open{</div>
<div>
	{&nbsp;</div>
<div>
	&nbsp; if(isinstr(execname(),&quot;ls&quot;)</div>
<div>
	&nbsp; &nbsp; &nbsp;printf(&quot;%s \n&quot;,execname())}&nbsp;</div>
<div>
	}&nbsp;</div>
<div>
	&nbsp;</div>
<div>
	如果是ls程序的话,那么就把该程序的名字打印出来</div>
<div>
	&nbsp;</div>
<div>
	&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</div>
<div>
	&nbsp;</div>
<div>
	好了,最后在贴一个调试程序的例子:</div>
<div>
	例子3，</div>
<div>
	&nbsp; 1 probe syscall.* {</div>
<div>
	&nbsp; 2 &nbsp; &nbsp; en = execname();</div>
<div>
	&nbsp; 3 &nbsp; &nbsp; ui = uid();</div>
<div>
	&nbsp; 4 &nbsp; &nbsp; eui = euid();</div>
<div>
	&nbsp; 5 &nbsp; &nbsp; if (en == &quot;&lt;redacted&gt;&quot;)</div>
<div>
	&nbsp; 6 &nbsp; &nbsp; {</div>
<div>
	&nbsp; 7 &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;%s(%d): %s(%s)&quot;, en, pid(), name, argstr);</div>
<div>
	&nbsp; 8 &nbsp; &nbsp;&nbsp;</div>
<div>
	&nbsp; 9 &nbsp; &nbsp; &nbsp; &nbsp; if (ui != eui) {</div>
<div>
	&nbsp;10 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf(&quot; as %d/%d &quot;, ui, eui);</div>
<div>
	&nbsp;11 &nbsp; &nbsp; &nbsp; &nbsp; } else {</div>
<div>
	&nbsp;12 &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot; as %d &quot;, ui);</div>
<div>
	&nbsp;13 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	&nbsp;14 &nbsp; &nbsp; &nbsp;}</div>
<div>
	&nbsp;15 } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div>
<div>
	&nbsp;16&nbsp;</div>
<div>
	&nbsp;17 probe syscall.*.return {</div>
<div>
	&nbsp;18 &nbsp; &nbsp; &nbsp; &nbsp; en = execname();</div>
<div>
	&nbsp;19 &nbsp; &nbsp; &nbsp; &nbsp; if (en == &quot;&lt;redacted&gt;&quot;) {</div>
<div>
	&nbsp;20 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;= %s\n&quot;, retstr);</div>
<div>
	&nbsp;21 &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>
	&nbsp;</div>
<div>
	This produces output with system call arguments and return values helpfully decoded for you; it looks like:</div>
<div>
	&nbsp;</div>
<div>
	&nbsp; &lt;redacted&gt;(14087): open(&quot;/etc/passwd&quot;, O_RDONLY) as 2315/0 = 3 &nbsp;[...] &nbsp;&lt;redacted&gt;(14087): close(1) as 2315/0 = -9 (EBADF)</div>
<div>
	&nbsp;</div>
<div>
	&lt;全文完&gt;</div>
<div>
	注:ubuntu下安装systemtap请参考以前的blog.其他发行版请直接看tutorial. 欢迎批评指正!</div>
<p>
	&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/07/29/learning-systemtap/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>外网主机ssh和vnc访问内网主机</title>
		<link>http://www.tek-life.org/2011/07/26/ssh-vnc/</link>
		<comments>http://www.tek-life.org/2011/07/26/ssh-vnc/#comments</comments>
		<pubDate>Tue, 26 Jul 2011 02:35:15 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10625</guid>
		<description><![CDATA[之前，孤陋寡闻，以为外网访问内网纯属扯淡！早上开shlug的邮件列表，看到这个thread。实验了一下，夷~，竟然可以。太棒了！ 外网通过ssh访问内网的方法： $ ssh -f -N -R 7070:127.0.0.1:22&#160;外网主机用户名@外网主机ip $ ssh 内网主机用户名@127.0.0.1&#160;-p 7070 原理：将外网7070的端口映射到内网主机的22端口 外网通过VNC访问内网的方法： 同ssh访问的方法一样，我们还可以使用VNC服务： 在内网主机上安装好VNC，然后，开启一个session(默认的端口是从5900开始的) 在内网主机操作： $vnc4server :1 $vnc4passwd //设定VNC连接密码 $ssh -f -N -R 1234:127.0.0.1:5901&#160;外网主机用户名@外网主机ip 好，已经将5901绑定到外网主机的1234端口了。 然后，操作外网主机： 在外网主机菜单Applications-&#62;Internet-&#62;Remote Desktop Viewer 选择VNC协议，填入127.0.0.1:1234 然后， 会要求输入密码，就是内网主机通过vnc4passwd设定的密码了。 OK啦~]]></description>
			<content:encoded><![CDATA[<p>
	之前，孤陋寡闻，以为外网访问内网纯属扯淡！早上开shlug的邮件列表，看到这个thread。实验了一下，夷~，竟然可以。太棒了！</p>
<p>
	外网通过ssh访问内网的方法：<br />
	$ ssh -f -N -R 7070:<a _mce_href="http://127.0.0.1:22/" _mce_style="text-decoration: none; color: #2288bb;" href="http://127.0.0.1:22/" style="text-decoration: none; color: rgb(34, 136, 187); ">127.0.0.1:22</a>&nbsp;外网主机用户名@外网主机ip<br />
	$ ssh 内网主机用户名@<a _mce_href="http://127.0.0.1/" _mce_style="text-decoration: none; color: #2288bb;" href="http://127.0.0.1/" style="text-decoration: none; color: rgb(34, 136, 187); ">127.0.0.1</a>&nbsp;-p 7070<br />
	原理：将外网7070的端口映射到内网主机的22端口</p>
<p>
	外网通过VNC访问内网的方法：<br />
	同ssh访问的方法一样，我们还可以使用VNC服务：<br />
	在内网主机上安装好VNC，然后，开启一个session(默认的端口是从5900开始的)<br />
	在内网主机操作：<br />
	$vnc4server :1<br />
	$vnc4passwd //设定VNC连接密码<br />
	$ssh -f -N -R 1234:<a _mce_href="http://127.0.0.1:5901/" _mce_style="text-decoration: none; color: #2288bb;" href="http://127.0.0.1:5901/" style="text-decoration: none; color: rgb(34, 136, 187); ">127.0.0.1:5901</a>&nbsp;外网主机用户名@外网主机ip<br />
	好，已经将5901绑定到外网主机的1234端口了。<br />
	然后，操作外网主机：<br />
	在外网主机菜单Applications-&gt;Internet-&gt;Remote Desktop Viewer<br />
	选择VNC协议，填入<a _mce_href="http://127.0.0.1:1234/" _mce_style="text-decoration: none; color: #2288bb;" href="http://127.0.0.1:1234/" style="text-decoration: none; color: rgb(34, 136, 187); ">127.0.0.1:1234</a><br />
	然后， 会要求输入密码，就是内网主机通过vnc4passwd设定的密码了。</p>
<p>
	OK啦~</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/07/26/ssh-vnc/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Linux系统调用宏展开笔记</title>
		<link>http://www.tek-life.org/2011/06/28/linux-syscall-extend/</link>
		<comments>http://www.tek-life.org/2011/06/28/linux-syscall-extend/#comments</comments>
		<pubDate>Tue, 28 Jun 2011 08:56:51 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Kernel]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10617</guid>
		<description><![CDATA[不知道从哪个版本的内核开始，系统调用变成宏了。在2.6.38的内核里面追踪了一下，以SYSCALL_DEFINE1为例： &#160; SYSCALL_DEFINE1（name,&#8230;）展开sys_name,后面的数字，若为1则是一个参数，若为2则为2个参数 &#160; 具体的宏，展开跟踪流程如下： #define SYSCALL_DEFINE1(name, &#8230;) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#124; &#160;&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#8230; <a href="http://www.tek-life.org/2011/06/28/linux-syscall-extend/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>
	不知道从哪个版本的内核开始，系统调用变成宏了。在2.6.38的内核里面追踪了一下，以SYSCALL_DEFINE1为例：</p>
<div>
<div>
		&nbsp;</div>
<div>
		SYSCALL_DEFINE1（name,&#8230;）展开sys_name,后面的数字，若为1则是一个参数，若为2则为2个参数</div>
<div>
		&nbsp;</div>
<div>
		具体的宏，展开跟踪流程如下：</div>
<div>
		#define SYSCALL_DEFINE1(name, &#8230;) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp;&nbsp;</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v &nbsp;&nbsp;</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; #define SYSCALL_DEFINEx(x, sname, &#8230;) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;v &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; #define __SYSCALL_DEFINEx(x, name, &#8230;) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\ &nbsp;&nbsp;</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \ &nbsp;&nbsp;</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;#define __SC_DECL1(t1, a1) &nbsp;t1 a1</div>
<div>
		&nbsp;&nbsp;</div>
<div>
		拿一个例子来对照一下:&nbsp;</div>
<div>
		SYSCALL_DEFINE1(brk, unsigned long, brk) &nbsp;</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v &nbsp;&nbsp;</div>
<div>
		SYSCALL_DEFINE1(1, _brk, unsigned long, brk)</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v &nbsp;&nbsp;</div>
<div>
		__SYSCALL_DEFINE1(1, _brk, unsigned long ,brk)</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v &nbsp;&nbsp;</div>
<div>
		asmlinkage long sys_brk(unsigned long brk)</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div>
<div>
		&nbsp;</div>
<p>
		SYSCALL_DEFINE1(brk, unsigned long, brk)&#8212;&gt; long sys_brk(unsigned long brk)</p>
</div>
<div>
	{</div>
<div>
	&nbsp; &nbsp; unsigned long rlim, retval;</div>
<div>
	&nbsp; &nbsp; unsigned long newbrk, oldbrk;</div>
<div>
	&nbsp; &nbsp; struct mm_struct *mm = current-&gt;mm;</div>
<div>
	&nbsp; &nbsp; unsigned long min_brk;</div>
<div>
	&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div>
<div>
	&nbsp; &nbsp; &#8230;&#8230;</div>
<div>
	&nbsp;</div>
<div>
	&nbsp; &nbsp; retval = mm-&gt;brk;</div>
<div>
	&nbsp; &nbsp; up_write(&amp;mm-&gt;mmap_sem);</div>
<div>
	&nbsp; &nbsp; return retval;</div>
<div>
	}</div>
<div>
	&nbsp;</div>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/06/28/linux-syscall-extend/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Xen-ARM Guest DOM启动以及VCPU切换</title>
		<link>http://www.tek-life.org/2011/06/15/guest-dom-vcpu-switch/</link>
		<comments>http://www.tek-life.org/2011/06/15/guest-dom-vcpu-switch/#comments</comments>
		<pubDate>Wed, 15 Jun 2011 03:18:48 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[XENARM]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10603</guid>
		<description><![CDATA[/* 从hypervisor到DOM的转换比较复杂，简单的步骤有两个阶段: 1.将guest的contest的寄存器信息赋值，其中还包括一些DOM设置信息 2.调用scheduler通过__switch_to调度到设置的VCPU上 */ &#8212;&#8212;&#8212;&#8212;hypervisor&#8212;&#8212;&#8212;&#8212;- construct_guest_dom() { &#8230;.. new_thread(v, dsi.v_kernentry, vstack_end, vstartinfo_start); &#8230;.. } void new_thread(struct vcpu *d, unsigned long start_pc, unsigned long start_stack, unsigned long start_info) { unsigned long *domain_stack; struct cpu_info *ci; struct cpu_user_regs *domain_context; struct cpu_user_regs &#8230; <a href="http://www.tek-life.org/2011/06/15/guest-dom-vcpu-switch/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>/*<br />
从hypervisor到DOM的转换比较复杂，简单的步骤有两个阶段:<br />
1.将guest的contest的寄存器信息赋值，其中还包括一些DOM设置信息<br />
2.调用scheduler通过__switch_to调度到设置的VCPU上<br />
*/<br />
&#8212;&#8212;&#8212;&#8212;hypervisor&#8212;&#8212;&#8212;&#8212;-<br />
construct_guest_dom()<br />
{<br />
&#8230;..<br />
new_thread(v, dsi.v_kernentry, vstack_end, vstartinfo_start);<br />
&#8230;..<br />
}</p>
<p>void new_thread(struct vcpu *d, unsigned long start_pc, unsigned long start_stack, unsigned long start_info)<br />
{<br />
unsigned long *domain_stack;<br />
struct cpu_info *ci;<br />
struct cpu_user_regs *domain_context;<br />
struct cpu_user_regs *regs = &amp;d-&gt;arch.guest_context.user_regs;</p>
<p>domain_stack = alloc_xenheap_pages(STACK_ORDER);<br />
if(domain_stack == NULL) {<br />
return;<br />
}</p>
<p>ci = (struct cpu_info *)domain_stack;<br />
ci-&gt;cur_vcpu = d;</p>
<p>domain_stack += (STACK_SIZE &#8211; sizeof(struct cpu_user_regs))/sizeof(unsigned long);</p>
<p>domain_context = (struct cpu_user_regs *)domain_stack;<br />
domain_context-&gt;r0 = 0;<br />
domain_context-&gt;r12 = start_info;//参数传过来<br />
domain_context-&gt;r13 = start_stack;<br />
domain_context-&gt;r15 = start_pc;//DOM的启始地址</p>
<p>domain_context-&gt;psr = 0&#215;13;</p>
<p>regs-&gt;r13 = (unsigned long)domain_stack;<br />
regs-&gt;r14 = (unsigned long)ret_to_user;<br />
}</p>
<p>调度VCPU代码：<br />
open_softirq(SCHEDULE_SOFTIRQ, __enter_scheduler);</p>
<p>__enter_scheduler调用__switch_to,传给__switch_to的有三个参数：</p>
<p>#define switch_to(prev,next,last)                                       \<br />
do {                                                                    \<br />
__switch_to(prev,&amp;prev-&gt;arch.guest_context, &amp;next-&gt;arch.guest_context);   \<br />
} while (0)</p>
<p>ENTRY(__switch_to)<br />
disable_irq    ip            @ ensure IRQs are disabled<br />
add     ip, r1, #(OFFSET_USER_REGS + OFFSET_R4)<br />
save_ctx:<br />
stmia   ip, {r4 &#8211; sl, fp, ip, sp, lr}      @ Store most regs on stack</p>
<p>mrc    p15, 0, r4, c3, c0, 0<br />
str    r4, [r1, #(OFFSET_SYS_REGS + OFFSET_VDACR)]<br />
load_ctx:<br />
ldr    r4, [r2, #(OFFSET_SYS_REGS + OFFSET_VDACR)]<br />
mcr    p15, 0, r4, c3, c0, 0</p>
<p>add    ip, r2, #(OFFSET_USER_REGS + OFFSET_R4)<br />
enable_irq    r4<br />
ldmia   ip,  {r4 &#8211; sl, fp, ip, sp, pc}       @ Load all regs saved previously</p>
<p>@    mov    pc, lr<br />
nop<br />
nop<br />
b    .</p>
<p>&#8212;&#8212;&#8212;&#8212;-dom&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
ENTRY(stext)<br />
ldr r10, start_info //start_info的地址 -&gt; r10<br />
str r12, [r10]      //r12的值 -&gt; xen_start_info变量(start_info标记处)</p>
<p>ldr sp, __init_sp   //sp-&gt;13</p>
<p>b   start_kernel //进入Guest KERNEL</p>
<p>start_info:<br />
.long   xen_start_info</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/06/15/guest-dom-vcpu-switch/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>怎样向Android社区提交代码</title>
		<link>http://www.tek-life.org/2011/05/27/how-to-contribute-code-to-android/</link>
		<comments>http://www.tek-life.org/2011/05/27/how-to-contribute-code-to-android/#comments</comments>
		<pubDate>Fri, 27 May 2011 09:19:59 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Se]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10588</guid>
		<description><![CDATA[向开源社区贡献代码的方式很多，Android采用repo的方式。 由于在提交patch的时候，必须使用repo upload命令，所以，首先要安装repo在自己的项目中。安装repo在自己项目的步骤分两步： 1-1. $ curl&#160;http://android.git.kernel.org/repo &#62; ~/bin/repo $ chmod a+x ~/bin/repo 1-2.然后把~/bin加入到PATH环境变量中，如果不加的话，用repo就需要全路径，比较麻烦，不管怎样，下面这一步是可选的： $ export PATH=~/bin:$PATH 2.安装repo到自己的项目中，只有安装到自己项目中后，才可以使用repo，即使是repo help也不例外。 $ repo init -u git://android.git.kernel.org/platform/manifest.git 题外话：使用repo比使用git更麻烦，在教育网中，repo platform几乎是不能完成的任务，当然，可以使用git proxy，不过这种方法并不好使，一个简单且方便的方法是搞一个VPS 。 如果自己要提交的project没有在manifest管辖的范围内，那就需要自己去修改.repo/manifest.xml文件了。 比如，我要修改的Project项目名称是kernel/common，路径是kernel/common，而这个Project并没有在manifest.xml，所以在.repo/mainfest.xml文件中添加这么一行：&#160; &#60;project path=&#34;kernel/common&#34; name=&#34;kernel/common/&#34; /&#62; 然后运行repo sync去下载整个platform，整个platform的容量基本上2GB左右，如果没有必要下载那么多的项目，修改.repo/manifest.xml，只保留自己的项目就可以了。 比如，我只需要其中的platform/build项目，那么在.repo/manifest.xml中只保留&#60;project path=&#34;build&#34;&#62;的条目就可以了，更改.repo/manifest.xml修改为： &#60;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&#62; &#8230; <a href="http://www.tek-life.org/2011/05/27/how-to-contribute-code-to-android/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>
	向开源社区贡献代码的方式很多，Android采用repo的方式。</p>
<p>
	由于在提交patch的时候，必须使用repo upload命令，所以，首先要安装<a href="http://source.android.com/source/version-control.html">repo</a>在自己的项目中。安装repo在自己项目的步骤分两步：<br />
	1-1.<br />
	$ curl&nbsp;<a href="http://android.git.kernel.org/repo" style="text-decoration: none; color: #2288bb;">http://android.git.kernel.org/repo</a> &gt; ~/bin/repo<br />
	$ chmod a+x ~/bin/repo</p>
<p>
	1-2.然后把~/bin加入到PATH环境变量中，如果不加的话，用repo就需要全路径，比较麻烦，不管怎样，下面这一步是可选的：</p>
<p>
	$ export PATH=~/bin:$PATH</p>
<p>
	2.安装repo到自己的项目中，只有安装到自己项目中后，才可以使用repo，即使是repo help也不例外。</p>
<p>
	$ repo init -u git://<a href="http://android.git.kernel.org/platform/manifest.git" style="text-decoration: none; color: #2288bb;">android.git.kernel.org/platform/manifest.git</a></p>
<p>
	题外话：使用repo比使用git更麻烦，在教育网中，repo platform几乎是不能完成的任务，当然，可以使用git proxy，不过这种方法并不好使，一个简单且方便的方法是搞一个VPS 。</p>
<div>
	如果自己要提交的project没有在manifest管辖的范围内，那就需要自己去修改.repo/manifest.xml文件了。<br />
	比如，我要修改的Project项目名称是kernel/common，路径是kernel/common，而这个Project并没有在manifest.xml，所以在.repo/mainfest.xml文件中添加这么一行：&nbsp;</p>
<p>
		&lt;project path=&quot;kernel/common&quot; name=&quot;kernel/common/&quot; /&gt;</p>
<p>
		然后运行repo sync去下载整个platform，整个platform的容量基本上2GB左右，如果没有必要下载那么多的项目，修改.repo/manifest.xml，只保留自己的项目就可以了。<br />
		比如，我只需要其中的platform/build项目，那么在.repo/manifest.xml中只保留&lt;project path=&quot;build&quot;&gt;的条目就可以了，更改.repo/manifest.xml修改为：</p>
<blockquote class="gmail_quote" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0.8ex; border-left-width: 1px; border-left-color: #cccccc; border-left-style: solid; padding-left: 1ex;">
<p>
			&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</p>
<blockquote>
<p>
				&lt;manifest&gt;<br />
				&lt;remote&nbsp; name=&quot;korg&quot;<br />
				fetch=&quot;git://<a href="http://android.git.kernel.org/" style="text-decoration: none; color: #2288bb;">android.git.kernel.org/</a>&quot;<br />
				review=&quot;<a href="http://review.source.android.com/" style="text-decoration: none; color: #2288bb;">review.source.android.com</a>&quot; /&gt;<br />
				&lt;default revision=&quot;master&quot;<br />
				remote=&quot;korg&quot; /&gt;<br />
				&lt;project path=&quot;build&quot; name=&quot;platform/build&quot;&gt;<br />
				&lt;copyfile src=&quot;core/<a href="http://root.mk/" style="text-decoration: none; color: #2288bb;">root.mk</a>&quot; dest=&quot;Makefile&quot; /&gt;<br />
				&lt;/project&gt;<br />
				&lt;/manifest&gt;</p>
</blockquote>
</blockquote>
<p>
		在repo sync的时候，可能会弹出如下的错误：</p>
<p>
		error: revision master in kernel/common not found</p>
<p>
		这是因为，在manifest.xml中，默认的revision为master。因此，需要在相关的项目中设置revision。 例如：我需要kernel/common项目中的android-2.6.27分支,那么，应该在manifest.xml中写为：<br />
		&lt;project path=&quot;kernel/common&quot; name=&quot;kernel/common&quot; revision=&quot;android-2.6.27&quot;/&gt;</p>
<p>
		repo sync完成后，进入相关的项目,建立一个分支，修改后，并提交.</p>
<blockquote class="gmail_quote" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0.8ex; border-left-width: 1px; border-left-color: #cccccc; border-left-style: solid; padding-left: 1ex;">
<p>
			例如，<br />
			$cd kernel/common #进入项目目录中<br />
			$repo start goldfish . #建立一个新的goldfish分支<br />
			$git checkout goldfish #切换到goldfish分支上<br />
			完成修改任务<br />
			$git add .<br />
			$git commit</p>
</blockquote>
<div>
		Android社区要求提交的commit：</div>
<ul style="padding-top: 0px; padding-right: 2.5em; padding-bottom: 0px; padding-left: 2.5em; margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.5em; margin-left: 0px; line-height: 1.4;">
<li style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.25em; margin-left: 0px; text-indent: 0px; padding: 0px;">
			Start with a one-line summary (60 characters max), followed by a blank line.</li>
<li style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.25em; margin-left: 0px; text-indent: 0px; padding: 0px;">
			The description should focus on what issue it solves, and how it solves it. The second part is somewhat optional when implementing new features, though desirable.</li>
<li style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.25em; margin-left: 0px; text-indent: 0px; padding: 0px;">
			Include a brief note of any assumptions or background information that may be important when another contributor works on this feature next year.</li>
</ul>
<div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.25em; margin-left: 0px; text-indent: 0px; padding: 0px;">
		&nbsp;</div>
<p>
		然后就可以repo upload了，不过在upload的时候，需要在<a href="https://review.source.android.com/" style="text-decoration: none; color: #2288bb;">https://review.source.android.com/</a>中注册并提交自己的ssh public keys.<br />
		提交完成ssh public keys后，就OK啦，成功后，会自动返回一个review。然后等待人工review，这一切，都可以在<a href="https://review.source.android.com/" style="text-decoration: none; color: #2288bb;">https://review.source.android.com</a> 中看到。</p>
<p>
		<span style="color: #ff0000;">友情提示</span>：欢迎转载，转载请说明出处。如果有任何问题，欢迎批评,交流，指正。</p>
</div>
<div>
	&lt;全文完&gt;</div>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/05/27/how-to-contribute-code-to-android/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>关于Makefile</title>
		<link>http://www.tek-life.org/2011/05/07/%e5%85%b3%e4%ba%8emakefile/</link>
		<comments>http://www.tek-life.org/2011/05/07/%e5%85%b3%e4%ba%8emakefile/#comments</comments>
		<pubDate>Sat, 07 May 2011 02:59:20 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10585</guid>
		<description><![CDATA[几乎所有的Linux项目都是使用make来进行项目管理的.make是依据Makefile来进行工作的.因此书写和阅读Makefile就是Linux程序员必备的一项基本功. &#160; 对于像我这样的newbie，写一个稍微复杂的Makefile必定会错误百出，原因是Makefile的命令语法与bash有相似之处,但是还有许多细节上的差别,比如 赋值bash要求&#34;=&#34;两边是不能有空格的,但是Makefile就允许. 在bash中用if条件判断,就像VB一样,但是如果要在Makefile中使用的话,需要写到一行中,如果写不下,需要用&#34;\&#34;来告诉make,下面的一行和上面的这一行属于一个逻辑行. Makefile还对TAB,空格有严格的要求,这个仿佛就像是Python.如果你不TAB就写一个命令,make不认为是一个命令,而如果写个ifdef而用了TAB的话,make会把ifdef当作一个命令来执行,而不是一个条件判断. exit在Makefile中貌似起不到Bash中的效果,你要在Makefile中控制其执行情况必须用error才可以. 读Makefile就是执果索因(这是别人总结的),根据结果顺瓜摸藤,就比较容易了.而对于Makefile的语法如果单单能够要求读懂的话，也是比较简单。 &#160; 一般情况，在一个项目中，用Makefile就完全能够进行项目管理了。但一些项目会由像Rules.mk,Post.mk之类的东西让我们感到望而生畏，仿佛Make是一项深不可测的东西。其实Rules.mk和Post.mk同1.mk&#160;2.mk没有什么区别。在一个论坛里面有下面一句话： IIRC, traditionally, when people set up Makefiles and used files named&#160;rules.mk&#160;and&#160;make.mk, they put all the platform-independent stuff in&#160;make.mk, and all the platform-specific stuff in&#160;rules.mk. And then their Makefiles just included &#8230; <a href="http://www.tek-life.org/2011/05/07/%e5%85%b3%e4%ba%8emakefile/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><meta content="text/html; charset=utf-8" http-equiv="content-type" /></p>
<div>
	几乎所有的Linux项目都是使用make来进行项目管理的.make是依据Makefile来进行工作的.因此书写和阅读Makefile就是Linux程序员必备的一项基本功.</div>
<div>
	&nbsp;</div>
<div>
	对于像我这样的newbie，写一个稍微复杂的Makefile必定会错误百出，原因是Makefile的命令语法与bash有相似之处,但是还有许多细节上的差别,比如</div>
<div>
<ul style="padding-top: 0px; padding-right: 2.5em; padding-bottom: 0px; padding-left: 2.5em; margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.5em; margin-left: 0px; line-height: 1.4; ">
<li style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0.25em; margin-left: 0px; text-indent: 0px; ">
			赋值bash要求&quot;=&quot;两边是不能有空格的,但是Makefile就允许.</li>
<li style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0.25em; margin-left: 0px; text-indent: 0px; ">
			在bash中用if条件判断,就像VB一样,但是如果要在Makefile中使用的话,需要写到一行中,如果写不下,需要用&quot;\&quot;来告诉make,下面的一行和上面的这一行属于一个逻辑行.</li>
<li style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0.25em; margin-left: 0px; text-indent: 0px; ">
			Makefile还对TAB,空格有严格的要求,这个仿佛就像是Python.如果你不TAB就写一个命令,make不认为是一个命令,而如果写个ifdef而用了TAB的话,make会把ifdef当作一个命令来执行,而不是一个条件判断.</li>
<li style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0.25em; margin-left: 0px; text-indent: 0px; ">
			exit在Makefile中貌似起不到Bash中的效果,你要在Makefile中控制其执行情况必须用error才可以.</li>
</ul>
</div>
<div>
	读Makefile就是执果索因(这是别人总结的),根据结果顺瓜摸藤,就比较容易了.而对于Makefile的语法如果单单能够要求读懂的话，也是比较简单。</div>
<div>
	&nbsp;</div>
<div>
	一般情况，在一个项目中，用Makefile就完全能够进行项目管理了。但一些项目会由像Rules.mk,Post.mk之类的东西让我们感到望而生畏，仿佛Make是一项深不可测的东西。其实<a href="http://rules.xn--mkpost-j76j.mk/" style="text-decoration: none; color: rgb(34, 136, 187); ">Rules.mk和Post.mk</a>同<a href="http://1.mk/" style="text-decoration: none; color: rgb(34, 136, 187); ">1.mk</a>&nbsp;<a href="http://2.mk/" style="text-decoration: none; color: rgb(34, 136, 187); ">2.mk</a>没有什么区别。在一个论坛里面有下面一句话：</div>
<div>
<blockquote class="gmail_quote" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex; "><p>
		IIRC, traditionally, when people set up Makefiles and used files named&nbsp;<a href="http://rules.mk/" style="text-decoration: none; color: rgb(34, 136, 187); ">rules.mk</a>&nbsp;and&nbsp;<a href="http://make.mk/" style="text-decoration: none; color: rgb(34, 136, 187); ">make.mk</a>, they put all the platform-independent stuff in&nbsp;<a href="http://make.mk/" style="text-decoration: none; color: rgb(34, 136, 187); ">make.mk</a>, and all the platform-specific stuff in&nbsp;<a href="http://rules.mk/" style="text-decoration: none; color: rgb(34, 136, 187); ">rules.mk</a>. And then their Makefiles just included those, and each Makefile contained the things specific to each thing that was being built (like source filenames and such).</p></blockquote>
<div>
		&nbsp;<a href="http://www.linuxquestions.org/questions/programming-9/rule-mk-and-make-mk-for-makefile-365498/" style="text-decoration: none; color: rgb(34, 136, 187); ">http://www.linuxquestions.org/questions/programming-9/rule-mk-and-make-mk-for-makefile-365498/</a></div>
</div>
<div>
	&nbsp;</div>
<div>
	关于Makefile的学习，有一本介绍Makefile的电子书,写得非常好,它是由陈皓撰写,内容与文笔一样的优秀的&#8211;《跟我一起写Makefile》。需要下载的朋友，可以google之。BTW，能够表达能力极强的程序员着实太少，倘若都能够像陈皓这样，我们国家的计算机事业应该比印度高不少了吧。他的主页：<a href="http://coolshell.cn/" style="text-decoration: none; color: rgb(34, 136, 187); ">http://coolshell.cn</a>。</div>
<div>
	&nbsp;</div>
<div>
	&lt;!&#8212;全文完&#8212;&gt;</div>
<div>
	&nbsp;</div>
<div>
	附录：</div>
<div>
	关于项目上的东西，以后时间久了，看它就可以了。</div>
<div>
	xenarm中的Makefile</div>
<div>
	Makefile生成的目标文件有4个:&nbsp;xen-bin&nbsp;xen&nbsp;xen.gz xen-syms</div>
<div>
	&nbsp;</div>
<div>
	<b><font class="Apple-style-span" color="#FF0000">xen-bin&nbsp;</font></b></div>
<div>
	arch/arm/Makefile&nbsp;</div>
<div>
	17 $(TARGET): $(TARGET)-bin $(TARGET)-syms</div>
<div>
	18 &nbsp; &nbsp; @cp $(TARGET)-syms $@</div>
<blockquote class="webkit-indent-blockquote" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">
<blockquote class="gmail_quote" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex; "><p>
		fuck，xen-syms和xen原来是一个东西！</p></blockquote>
</blockquote>
<div>
<div>
		&#8230;&nbsp;</div>
<div>
		29 $(TARGET)-bin: $(TARGET)-syms</div>
<div>
		30 &nbsp; &nbsp; $(OBJCOPY) $(OBJCOPYFLAGS) $&lt; $@</div>
<div>
		&nbsp;</div>
<div>
		<b><font class="Apple-style-span" color="#FF0000">xen-syms</font></b></div>
<div>
		&nbsp;20 $(TARGET)-syms: arch-$(TARGET_MACHINE)/start.o $(ALL_OBJS) xen.lds</div>
<div>
<div>
			&nbsp;21 &nbsp; &nbsp; $(OBJCOPY) -I binary -O elf32-littlearm -B arm 24.bmp image.o</div>
<div>
			&nbsp;22 &nbsp; &nbsp; $(OBJCOPY) -I binary -O elf32-littlearm -B arm 8&#215;16 en.o</div>
<div>
			&nbsp;23 &nbsp; &nbsp; #$(OBJCOPY) -I binary -O elf32-littlearm -B arm shut.wav sound.o</div>
<div>
			&nbsp;24 &nbsp; &nbsp; #$(OBJCOPY) -I binary -O elf32-littlearm -B arm mini mini.o</div>
<div>
			&nbsp;25 &nbsp; &nbsp; $(LD) $(LDFLAGS) -N -T xen.lds arch-$(TARGET_MACHINE)/start.o $(ALL_OBJS &nbsp; &nbsp;) en.o image.o -o $@&nbsp;</div>
</p></div>
<div>
		&nbsp;</div>
<div>
		<b><font class="Apple-style-span" color="#FF0000">xen</font></b></div>
</div>
<div>
	./Rules.mk</div>
<div>
	&nbsp;30 TARGET &nbsp;:= $(BASEDIR)/xen</div>
<div>
	&nbsp;</div>
<div>
	<font class="Apple-style-span" color="#FF0000"><b>xen.gz</b></font></div>
<div>
	./Makefile</div>
<div>
<div>
		&nbsp;18 $(TARGET).gz: $(TARGET)</div>
<div>
		&nbsp;19 &nbsp; &nbsp; gzip -f -9 &lt; $&lt; &gt; $@.new</div>
<div>
		&nbsp;20 &nbsp; &nbsp; mv $@.new $@</div>
</div>
<div>
	&nbsp;</div>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/05/07/%e5%85%b3%e4%ba%8emakefile/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>关于setjmp和longjmp</title>
		<link>http://www.tek-life.org/2011/03/29/%e5%85%b3%e4%ba%8esetjmp%e5%92%8clongjmp/</link>
		<comments>http://www.tek-life.org/2011/03/29/%e5%85%b3%e4%ba%8esetjmp%e5%92%8clongjmp/#comments</comments>
		<pubDate>Tue, 29 Mar 2011 02:00:29 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[C]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10570</guid>
		<description><![CDATA[前段时间看C专家编程遇到setjmp和longjmp,没有特别在意.这两天看深入理解计算机系统,再次提到setjmp和longjmp.觉得很巧妙,写个小程序测试一下其特性: 1 //longjmp每一次都跳到setjmp的地方,正所谓是保存了当时的堆栈情况 2 //如此看来,setjmp和longjmp的配对,和fork()函数有可比性: 3 //fork()一个函数执行两次,返回两个不同的值,一个是0,一个是subprocess id. 4 //而,longjmp则跳转到setjmp的位置,setjmp返回的值第一次是0,以后就由longjmp的参数决定 5 6 #include &#60;stdio.h&#62; 7 #include &#60;setjmp.h&#62; 8 #include &#60;stdlib.h&#62; 9 10 sigjmp_buf buf; 11 12 void fun() 13 { 14 printf(&#8220;%s&#8230;&#8230;&#8230;.&#8221;,__func__); 15 longjmp(buf, 2); 16 exit(0); 17 } 18 &#8230; <a href="http://www.tek-life.org/2011/03/29/%e5%85%b3%e4%ba%8esetjmp%e5%92%8clongjmp/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>前段时间看C专家编程遇到setjmp和longjmp,没有特别在意.这两天看深入理解计算机系统,再次提到setjmp和longjmp.觉得很巧妙,写个小程序测试一下其特性:</p>
<p>1 //longjmp每一次都跳到setjmp的地方,正所谓是保存了当时的堆栈情况<br />
2 //如此看来,setjmp和longjmp的配对,和fork()函数有可比性:<br />
3 //fork()一个函数执行两次,返回两个不同的值,一个是0,一个是subprocess id.<br />
4 //而,longjmp则跳转到setjmp的位置,setjmp返回的值第一次是0,以后就由longjmp的参数决定<br />
5<br />
6 #include &lt;stdio.h&gt;<br />
7 #include &lt;setjmp.h&gt;<br />
8 #include &lt;stdlib.h&gt;<br />
9<br />
10 sigjmp_buf buf;<br />
11<br />
12 void fun()<br />
13 {<br />
14         printf(&#8220;%s&#8230;&#8230;&#8230;.&#8221;,__func__);<br />
15         longjmp(buf, 2);<br />
16         exit(0);<br />
17 }<br />
18 void bar()<br />
19 {<br />
20         printf(&#8220;%s\n&#8221;,__func__);<br />
21         exit(0);<br />
22 }<br />
23<br />
24 void main()<br />
25 {<br />
26<br />
27         int n;<br />
28         printf(&#8220;Starting&#8230;&#8230;&#8230;&#8221;);<br />
29         n=setjmp(buf);//第一次返回为0,下一次调用的情景是遇到longjmp,返回值为longjmp的第二个参数<br />
30         printf(&#8220;Restarting&#8230;&#8230;&#8230;&#8221;);<br />
31         if(n ==1)<br />
32                 fun();<br />
33         if(n ==2 )<br />
34                 bar();<br />
35<br />
36         while(1)<br />
37         {<br />
38                 longjmp(buf,1);<br />
39                 printf(&#8220;I cannot appear.\n&#8221;);<br />
40         }<br />
41         exit(0);<br />
42 }<br />
~<br />
输出:<br />
haifeng@Tek-life:~/tmp$ ./a.out<br />
Starting&#8230;&#8230;&#8230;Restarting&#8230;&#8230;&#8230;Restarting&#8230;&#8230;&#8230;fun&#8230;&#8230;&#8230;.Restarting&#8230;&#8230;&#8230;bar</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/03/29/%e5%85%b3%e4%ba%8esetjmp%e5%92%8clongjmp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在Android Console中添加Busybox和Bash</title>
		<link>http://www.tek-life.org/2011/03/22/%e5%9c%a8android-console%e4%b8%ad%e6%b7%bb%e5%8a%a0busybox%e5%92%8cbash/</link>
		<comments>http://www.tek-life.org/2011/03/22/%e5%9c%a8android-console%e4%b8%ad%e6%b7%bb%e5%8a%a0busybox%e5%92%8cbash/#comments</comments>
		<pubDate>Tue, 22 Mar 2011 09:21:49 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Goldfish]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10568</guid>
		<description><![CDATA[整个过程分两部分： 第一部分：将busybox 和 bash 通过adb push 推到/data目录下 第二部分：更改init.rc，将PATH环境写入ramdisk.img中 第一部分： 下载bash和busybox,然后按照下面的步骤，step by step:（由于bash和busybox的链接大部分已经失效，我手动编译了两个，作为附件放在页面底部） #adb shell mkdir /data/busybox #adb push busybox /data/busyobx #adb push bash /data/busybox //adb shell，进入android #cd /data/busyobx #chmod +x busybox bash #./busybox &#8211;install //将程序安装在当前目录下 #export PATH=/data/busybox:$PATH 至此，已经可以使用busybox和bash了。但是很麻烦，每次都需要在前面加上busybox。 因此，最好是将环境变量直接在初始化的时候就指定，因此需要更改ramdisk.img中的init.rc 第二部分 &#8230; <a href="http://www.tek-life.org/2011/03/22/%e5%9c%a8android-console%e4%b8%ad%e6%b7%bb%e5%8a%a0busybox%e5%92%8cbash/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>整个过程分两部分：<br />
第一部分：将busybox 和 bash 通过adb push 推到/data目录下<br />
第二部分：更改init.rc，将PATH环境写入ramdisk.img中<br />
第一部分：<br />
下载bash和busybox,然后按照下面的步骤，step by<br />
step:（由于bash和busybox的链接大部分已经失效，我手动编译了两个，作为附件放在页面底部）<br />
#adb shell mkdir /data/busybox<br />
#adb push busybox /data/busyobx<br />
#adb push bash /data/busybox<br />
//adb shell，进入android<br />
#cd /data/busyobx<br />
#chmod +x busybox bash<br />
#./busybox &#8211;install //将程序安装在当前目录下<br />
#export PATH=/data/busybox:$PATH<br />
至此，已经可以使用busybox和bash了。但是很麻烦，每次都需要在前面加上busybox。<br />
因此，最好是将环境变量直接在初始化的时候就指定，因此需要更改ramdisk.img中的init.rc<br />
第二部分<br />
更改init.rc<br />
1）将ramdisk.img复制其他目录，名称改为ramdisk.img.gz，解压<br />
#gunzip ramdisk.img.gz<br />
//新建一个文件夹ramdisk，进入<br />
#cpio -i -F ../ramdisk.img<br />
这时，可到ramdisk中看看去~<br />
2）修改init.rc，在PATH中加上busybox 路径<br />
3）重新打包成镜像，并使用新镜像启动emulator<br />
#cpio -i -t -F ../ramdisk.img &gt; list<br />
#cpio -o -H newc -O rd_busybox.img &lt; list</p>
<p>//使用 -ramdisk 参数，指定所使用的镜像文件<br />
&#8211;over&#8212;<br />
bash:<a href="https://docs.google.com/uc?id=0ByKC30p2j_DDMmM3Zjk5YzItM2U2NC00NGZkLTk4MGYtMmU3ZDgyNTY0MWUz&amp;export=download&amp;hl=zh_CN">https://docs.google.com/uc?id=0ByKC30p2j_DDMmM3Zjk5YzItM2U2NC00NGZkLTk4MGYtMmU3ZDgyNTY0MWUz&amp;export=download&amp;hl=zh_CN</a><br />
busybox:<br />
这里面有一堆 <a href="http://www.busybox.net/downloads/binaries">http://www.busybox.net/downloads/binaries</a><br />
也可以使用：<a href="https://docs.google.com/uc?id=0ByKC30p2j_DDM2RhMWEyNjEtZDVlZC00MDcyLTk3MjktZDczMzc5YTc4NDI5&amp;export=download&amp;hl=zh_CN">https://docs.google.com/uc?id=0ByKC30p2j_DDM2RhMWEyNjEtZDVlZC00MDcyLTk3MjktZDczMzc5YTc4NDI5&amp;export=download&amp;hl=zh_CN</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/03/22/%e5%9c%a8android-console%e4%b8%ad%e6%b7%bb%e5%8a%a0busybox%e5%92%8cbash/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>在ubuntu中通过ndiswrapper使用无线网卡</title>
		<link>http://www.tek-life.org/2011/03/20/%e5%9c%a8ubuntu%e4%b8%ad%e4%bd%bf%e7%94%a8%e6%97%a0%e7%ba%bf%e7%bd%91%e5%8d%a1/</link>
		<comments>http://www.tek-life.org/2011/03/20/%e5%9c%a8ubuntu%e4%b8%ad%e4%bd%bf%e7%94%a8%e6%97%a0%e7%ba%bf%e7%bd%91%e5%8d%a1/#comments</comments>
		<pubDate>Sun, 20 Mar 2011 04:06:01 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10564</guid>
		<description><![CDATA[首先，科普一下ndiswrapper: NDISwrapper is a free software driver wrapper that enables the use of Windows XP drivers for network devices (PCI cards, USB modems, and routers), on Unix-like operating systems. NDISwrapper works by implementing the Windows kernel and NDIS APIs, and &#8230; <a href="http://www.tek-life.org/2011/03/20/%e5%9c%a8ubuntu%e4%b8%ad%e4%bd%bf%e7%94%a8%e6%97%a0%e7%ba%bf%e7%bd%91%e5%8d%a1/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>首先，科普一下ndiswrapper:</p>
<p><strong>NDISwrapper</strong> is a <a href="http://en.wikipedia.org/wiki/Free_software">free software</a> <a href="http://en.wikipedia.org/wiki/Driver_wrapper">driver wrapper</a> that enables the use of <a href="http://en.wikipedia.org/wiki/Windows_XP">Windows XP</a> drivers for network devices (<a title="PCI card" href="http://en.wikipedia.org/wiki/PCI_card">PCI cards</a>, <a title="USB modem" href="http://en.wikipedia.org/wiki/USB_modem">USB modems</a>, and <a title="Router" href="http://en.wikipedia.org/wiki/Router">routers</a>), on <a href="http://en.wikipedia.org/wiki/Unix-like">Unix-like</a> operating systems. NDISwrapper works by implementing the Windows <a title="Kernel (computer science)" href="http://en.wikipedia.org/wiki/Kernel_%28computer_science%29">kernel</a> and <a title="Network Driver Interface Specification" href="http://en.wikipedia.org/wiki/Network_Driver_Interface_Specification">NDIS</a> <a title="API" href="http://en.wikipedia.org/wiki/API">APIs</a>, and dynamically linking the Windows drivers to this implementation. It therefore works only on systems based on the <a title="Instruction set architecture" href="http://en.wikipedia.org/wiki/Instruction_set_architecture">architectures</a> supported by Windows, namely <a href="http://en.wikipedia.org/wiki/IA-32">IA-32</a> or <a href="http://en.wikipedia.org/wiki/X86-64">x86-64</a>.</p>
<p>对于那些厂商提供Linux驱动的网络硬件，就不许要这个ndiswrapper啦。我使用的是NETGEAR，是老美生产的无线网卡，但不提共LINUX的驱动。具体型号是NETGEAR WNA3100，之前在Ubuntu10.10上，使用自编译的ndswripper,老连不上。但是使用debian可以。</p>
<p>今天，由于Debian老出问题，而且ubuntu10.10也推出了中国定制版，于是，便安装了Ubuntu。在图书馆，都使用无线的网络。因此，要安装包，必须先到xp下面下载后，在重启进入Ubuntu安装，相当麻烦。</p>
<p>由于NETWEAR官方没有出LINUX的驱动，因此需采用ndiswrapper这个工具来加载windows驱动。在ubuntu中，已经自带了ndiswrapper程序，位于/usr/sbin/ndiswrapper-1.9.</p>
<p>具体使用的方法，可以查看Help.</p>
<p>先ndiswrapper-1.9 -i bcmwlhigh5.inf.inf #安装windows driver.<br />
然后ndiswrapper-1.9 -m #将配置信息写到modprobe中。<br />
然后sudo modprobe ndiswrapper #将模块插入到KERNEL中。<br />
这个时候，基本上就OK了。<br />
可以使用nm-tool查看网络信息：</p>
<blockquote><p>haifeng@haifeng-Alviso:~$ nm-tool</p>
<p>NetworkManager Tool</p>
<p>State: connected</p>
<p>- Device: eth0 &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
Type:              Wired<br />
Driver:            8139too<br />
State:             unavailable<br />
Default:           no<br />
HW Address:        00:E0:4C:4F:03:BB</p>
<p>Capabilities:<br />
Carrier Detect:  yes<br />
Speed:           10 Mb/s</p>
<p>Wired Properties<br />
Carrier:         off</p>
<p>- Device: wlan0  [Auto Hust-wireless] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
Type:              802.11 WiFi<br />
Driver:            ndiswrapper<br />
State:             connected<br />
Default:           yes<br />
HW Address:        00:26:F2:4F:AF:A0</p>
<p>Capabilities:<br />
Speed:           52 Mb/s</p>
<p>Wireless Properties<br />
WEP Encryption:  yes<br />
WPA Encryption:  yes<br />
WPA2 Encryption: yes</p>
<p>Wireless Access Points (* = current AP)<br />
Hust-wireless:   Infra, 3C:E5:A6:93:F5:70, Freq 2437 MHz, Rate 54 Mb/s, Strength 35<br />
*Hust-wireless:  Infra, 3C:E5:A6:93:40:F0, Freq 2462 MHz, Rate 54 Mb/s, Strength 79<br />
Hust-wireless:   Infra, 3C:E5:A6:93:7A:90, Freq 2437 MHz, Rate 54 Mb/s, Strength 26<br />
Hust-wireless:   Infra, 3C:E5:A6:93:F6:F0, Freq 2437 MHz, Rate 54 Mb/s, Strength 45<br />
Connectify-me:   Infra, 00:1F:3A:42:94:9D, Freq 2437 MHz, Rate 54 Mb/s, Strength 31 WPA2</p>
<p>IPv4 Settings:<br />
Address:         115.156.228.246<br />
Prefix:          24 (255.255.255.0)<br />
Gateway:         115.156.228.254</p>
<p>DNS:             202.114.0.242</p></blockquote>
<p>采用系统自带的ndiswrapper可能会有问题，我认证不成功，说是，用户名密码错误。sign!</p>
<p>在ubuntu的仓库中下载相应的安装包，我下载的地址：<br />
<a href="https://294855.info/001/http://ubuntu.uestc.edu.cn/ubuntu/pool/main/n/ndiswrapper/">http://ubuntu.uestc.edu.cn/ubuntu/pool/main/n/ndiswrapper/</a><br />
下载ndiswrapper-common_1.56-3_all.deb ndiswrapper-utils-1.9_1.56-3_i386.deb<br />
两个最新版本的deb即可。<br />
然后dpkg -i 安装之。</p>
<p>然后 ndiswrapper -i ;ndiswrapper -m 就OK了。<br />
采用1.56版本的这个ndiswrapper木有出现问题。</p>
<p>Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/03/20/%e5%9c%a8ubuntu%e4%b8%ad%e4%bd%bf%e7%94%a8%e6%97%a0%e7%ba%bf%e7%bd%91%e5%8d%a1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ubuntu 10.10 安装systemtap 补充</title>
		<link>http://www.tek-life.org/2011/03/18/ubuntu-10-10-%e5%ae%89%e8%a3%85systemtap-%e8%a1%a5%e5%85%85/</link>
		<comments>http://www.tek-life.org/2011/03/18/ubuntu-10-10-%e5%ae%89%e8%a3%85systemtap-%e8%a1%a5%e5%85%85/#comments</comments>
		<pubDate>Fri, 18 Mar 2011 03:10:47 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10559</guid>
		<description><![CDATA[前几天，看到yufeng的日志(http://blog.yufeng.info/archives/1098)有介绍ubuntu10.10安装systemtap的方法，尝试并安装成功(http://haifeng-hust.blogspot.com/2011/03/ubuntu1010systemtap.html)。但是这种方法安装出来的stap在pass3编译ko的时候经常出问题。因此利用deb包半手工安装。 在http://ftp.debian.org/debian/pool/main/s/systemtap/的页面中有两个版本1.4.1和1.2.5.就目前10.10版本的ubuntu，安装1.4.1时依赖包libdw1需要1.4.9版本，但是对于deb包，最高的就是1.4.7版本。因此只要降低systemtap的版本。 由于在安装1.4.7的时候，没有安装成功，这时候，无论安装什么包都会提示sudo apt-get install -f，非常烦人。除去提示的方法就是用sudo dpkg -r 卸载刚才没有安装成功的包。 到http://ftp.debian.org/debian/pool/main/s/systemtap/上面下载1.2.5版本的三个包systemtap-runtime systemtap-common systemtap.然后在安装systemtap1.2.5的时候，会发生libsqlite依赖包版本过低的问题。因此需要升级libsqlite，升级包地址请猛击：http://packages.debian.org/squeeze/i386/libsqlite3-0/download 然后安装之。这时候，就可以安装systemtap了。 然后测试： sudo tap -ve &#8216;probe begin{printf(&#8220;Hello world\n&#8221;);exit();}&#8217; sudo tap -ve &#8216;probe kernel.function(&#8220;sys_open&#8221;).return{printf(&#8220;%s\n&#8221;,execname());exit();}&#8217; 另外一个比较纠结的问题是，有时候会莫名其妙重启系统。另外，这个stap的版本较老，一些参数比如 -l 不能识别。]]></description>
			<content:encoded><![CDATA[<p>前几天，看到yufeng的日志(<a href="http://blog.yufeng.info/archives/1098)">http://blog.yufeng.info/archives/1098)</a>有介绍ubuntu10.10安装systemtap的方法，尝试并安装成功(<a href="http://haifeng-hust.blogspot.com/2011/03/ubuntu1010systemtap.html)%E3%80%82">http://haifeng-hust.blogspot.com/2011/03/ubuntu1010systemtap.html)。</a>但是这种方法安装出来的stap在pass3编译ko的时候经常出问题。因此利用deb包半手工安装。</p>
<p>在<a href="http://ftp.debian.org/debian/pool/main/s/systemtap/">http://ftp.debian.org/debian/pool/main/s/systemtap/</a>的页面中有两个版本1.4.1和1.2.5.就目前10.10版本的ubuntu，安装1.4.1时依赖包libdw1需要1.4.9版本，但是对于deb包，最高的就是1.4.7版本。因此只要降低systemtap的版本。</p>
<p>由于在安装1.4.7的时候，没有安装成功，这时候，无论安装什么包都会提示sudo apt-get install<br />
-f，非常烦人。除去提示的方法就是用sudo dpkg -r 卸载刚才没有安装成功的包。</p>
<p>到<a href="http://ftp.debian.org/debian/pool/main/s/systemtap/">http://ftp.debian.org/debian/pool/main/s/systemtap/</a>上面下载1.2.5版本的三个包systemtap-runtime<br />
systemtap-common<br />
systemtap.然后在安装systemtap1.2.5的时候，会发生libsqlite依赖包版本过低的问题。因此需要升级libsqlite，升级包地址请猛击：<a href="http://packages.debian.org/squeeze/i386/libsqlite3-0/download">http://packages.debian.org/squeeze/i386/libsqlite3-0/download</a></p>
<p>然后安装之。这时候，就可以安装systemtap了。</p>
<p>然后测试：<br />
sudo tap -ve &#8216;probe begin{printf(&#8220;Hello world\n&#8221;);exit();}&#8217;<br />
sudo tap -ve &#8216;probe<br />
kernel.function(&#8220;sys_open&#8221;).return{printf(&#8220;%s\n&#8221;,execname());exit();}&#8217;</p>
<p>另外一个比较纠结的问题是，有时候会莫名其妙重启系统。另外，这个stap的版本较老，一些参数比如 -l 不能识别。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/03/18/ubuntu-10-10-%e5%ae%89%e8%a3%85systemtap-%e8%a1%a5%e5%85%85/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>使用VNCServer</title>
		<link>http://www.tek-life.org/2011/03/17/10553/</link>
		<comments>http://www.tek-life.org/2011/03/17/10553/#comments</comments>
		<pubDate>Thu, 17 Mar 2011 02:51:55 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10553</guid>
		<description><![CDATA[通过VNCServer，可以远程使用桌面，而不用老是对着黑漆漆的tty。安装步骤如下： 安装vnc4server haifeng@Tek-life:~$ sudo apt-get install vnc4server 然后配置vncserver haifeng@Tek-life:~$ vim .vnc/xstartup 1 #!/bin/sh 2 3 # Uncomment the following two lines for normal desktop: 4 unset SESSION_MANAGER 5 # exec /etc/X11/xinit/xinitrc 6 7 #[ -x /etc/vnc/xstartup ] &#38;&#38; exec /etc/vnc/xstartup &#8230; <a href="http://www.tek-life.org/2011/03/17/10553/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>通过VNCServer，可以远程使用桌面，而不用老是对着黑漆漆的tty。安装步骤如下：</p>
<p>安装vnc4server<br />
haifeng@Tek-life:~$ sudo apt-get install vnc4server</p>
<p>然后配置vncserver<br />
haifeng@Tek-life:~$ vim .vnc/xstartup<br />
1 #!/bin/sh<br />
2<br />
3 # Uncomment the following two lines for normal desktop:<br />
4 unset SESSION_MANAGER<br />
5 # exec /etc/X11/xinit/xinitrc<br />
6<br />
7 #[ -x /etc/vnc/xstartup ] &amp;&amp; exec /etc/vnc/xstartup<br />
8 #[ -r $HOME/.Xresources ] &amp;&amp; xrdb $HOME/.Xresources<br />
9 xsetroot -solid grey<br />
10 #vncconfig -iconic &amp;<br />
11 x-terminal-emulator -geometry 80&#215;24+10+10 -ls -title &#8220;$VNCDESKTOP Desktop&#8221; &amp;<br />
12 exec /etc/X11/xinit/xinitrc<br />
13 #x-window-manager &amp;<br />
14 #gnome-session &amp;</p>
<p>好了，配置结束<br />
创建密码<br />
haifeng@Tek-life:~$ vncpasswd<br />
Password:<br />
Verify:<br />
然后，开个VNC服务<br />
haifeng@Tek-life:~$ vncserver :20<br />
xauth:  /var/run/gdm/auth-for-haifeng-OFLDyx/database not writable, changes will be ignored</p>
<p>New &#8216;Tek-life:20 (haifeng)&#8217; desktop is Tek-life:20</p>
<p>Starting applications specified in /home/haifeng/.vnc/xstartup<br />
Log file is /home/haifeng/.vnc/Tek-life:20.log<br />
这样，其他用户登录的时候，就用:<br />
vncserver ip :20<br />
比如，我的IP是192.168.205.110,那么用vncview登录时候，就填入：<br />
192.168.205.110:20<br />
然后密码就填刚才vncpasswd时设定的密码。<br />
如果要想关闭，很简单：<br />
haifeng@Tek-life:~$ vncserver -kill :20<br />
就OK了。<br />
截个屏：<br />
<a href="http://www.tek-life.org/wp-content/uploads/2011/03/Screenshot-2.png"><img class="aligncenter size-large wp-image-10557" title="Screenshot-2" src="http://www.tek-life.org/wp-content/uploads/2011/03/Screenshot-2-1024x640.png" alt="" width="640" height="400" /></a></p>
<p>Enjoy it!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/03/17/10553/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>在ubuntu10.10中安装Systemtap</title>
		<link>http://www.tek-life.org/2011/03/16/%e5%9c%a8ubuntu10-10%e4%b8%ad%e5%ae%89%e8%a3%85systemtap/</link>
		<comments>http://www.tek-life.org/2011/03/16/%e5%9c%a8ubuntu10-10%e4%b8%ad%e5%ae%89%e8%a3%85systemtap/#comments</comments>
		<pubDate>Wed, 16 Mar 2011 07:42:10 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10551</guid>
		<description><![CDATA[SystemTAP是个好东西。属于居家必备的工具之一。这两天安装了一下，其中还蛮折腾的。 安装步骤： sudo apt-get install systemtap Ubuntu Desktop默认没有安装kernel debug info的包，systemtap无法追踪内核信息。查看内核版本 haifeng@Tek-life:~$ uname -a Linux Tek-life 2.6.35-27-generic #48-Ubuntu SMP Tue Feb 22 20:25:29 UTC 2011 i686 GNU/Linux 从这里下载对应的kernel debug info包，安装 sudo dpkg -i linux-image-2.6.35-27-generic-dbgsym_2.6.35-27.48_i386.ddeb 至此内核追踪已经可以执行，但module的信息还需要多做些工作 sudo apt-get install elfutils 将以下内容保存为script.sh,并改变为可执行 1 &#8230; <a href="http://www.tek-life.org/2011/03/16/%e5%9c%a8ubuntu10-10%e4%b8%ad%e5%ae%89%e8%a3%85systemtap/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>SystemTAP是个好东西。属于居家必备的工具之一。这两天安装了一下，其中还蛮折腾的。</p>
<p>安装步骤：<br />
sudo apt-get install systemtap<br />
Ubuntu Desktop默认没有安装kernel debug info的包，systemtap无法追踪内核信息。查看内核版本<br />
haifeng@Tek-life:~$ uname -a Linux Tek-life 2.6.35-27-generic #48-Ubuntu SMP Tue Feb 22 20:25:29 UTC 2011 i686 GNU/Linux<br />
从这里下载对应的kernel debug info包，安装<br />
sudo dpkg -i linux-image-2.6.35-27-generic-dbgsym_2.6.35-27.48_i386.ddeb<br />
至此内核追踪已经可以执行，但module的信息还需要多做些工作<br />
sudo apt-get install elfutils<br />
将以下内容保存为script.sh,并改变为可执行<br />
1 #!/bin/bash<br />
2 for file in `find /usr/lib/debug -name &#8216;*.ko&#8217; -print`<br />
3 do<br />
4       buildid=`eu-readelf -n $file| grep Build.ID: | awk &#8216;{print $3}&#8217;`<br />
5       dir=`echo $buildid | cut -c1-2`<br />
6       fn=`echo $buildid | cut -c3-`<br />
7       rm -rf /usr/lib/debug/.build-id<br />
8       mkdir -p /usr/lib/debug/.build-id/$dir<br />
9       ln -s $file /usr/lib/debug/.build-id/$dir/$fn<br />
10       ln -s $file /usr/lib/debug/.build-id/$dir/${fn}.debug<br />
11 done<br />
基本上就OK了。<br />
测试：<br />
haifeng@Tek-life:~$ stap -l &#8216;module(&#8220;*&#8221;).function(&#8220;*&#8221;)&#8217;<br />
不输出错误信息。<br />
如果输出错误信息的话，核实安装的linux-image-debug那个ddeb包。如果确实不是包的问题，升级KERNEL。我就是升级了KERNEL，才OK的。搞了2天，他妈的，教育网网速太慢了。</p>
<p>再测试Hello World</p>
<p>haifeng@Tek-life:~$ sudo stap -ve &#8216;probe begin{printf(&#8220;hello&#8221;); exit();}&#8217;<br />
Pass 1: parsed user script and 72 library script(s) using 18892virt/12732res/1880shr kb, in 140usr/10sys/149real ms.<br />
Pass 2: analyzed script: 1 probe(s), 1 function(s), 0 embed(s), 0 global(s) using 19156virt/13260res/2008shr kb, in 10usr/0sys/5real ms.<br />
Pass 3: using cached /home/haifeng/.systemtap/cache/60/stap_606e63ba024e3858ea3a7591bcec7a4c_670.c<br />
Pass 4: using cached /home/haifeng/.systemtap/cache/60/stap_606e63ba024e3858ea3a7591bcec7a4c_670.ko<br />
Pass 5: starting run.<br />
helloPass 5: run completed in 0usr/10sys/300real ms.</p>
<p>如果其中哪一个pass出错，同样核实安装的ddeb包是否和内核版本对应。确实对应，那么就升级自己的KERNEL，照此再做一遍。</p>
<p>这两天再对照一些Tutorial,学习一下。<br />
参考：<br />
1.http://sourceware.org/systemtap/wiki/SystemtapOnUbuntu<br />
2.http://www.ningoo.net/html/2010/use_systemtap_on_ubuntu.html<br />
3.http://blog.yufeng.info/archives/1098</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/03/16/%e5%9c%a8ubuntu10-10%e4%b8%ad%e5%ae%89%e8%a3%85systemtap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>让oklinux跑起来</title>
		<link>http://www.tek-life.org/2011/03/02/oklinux-run/</link>
		<comments>http://www.tek-life.org/2011/03/02/oklinux-run/#comments</comments>
		<pubDate>Wed, 02 Mar 2011 12:39:18 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10540</guid>
		<description><![CDATA[oklinux是让修改过的linux跑在okl4虚拟机上。网上缺乏针对于oklinux的运行总结。在下不才，折腾好久，才编译运行成功，遂记录之，以方便后来者。 下载地址： okl4:http://wiki.ok-labs.com/downloads/release-3.0/okl4_3.0.tar.gz linux:http://wiki.ok-labs.com/downloads/release-3.0/oklinux_2.6.24.9-patch.4.tar.gz 分别解压okl4和linux.在okl4的目录中建一个指向Linux目录的软连接： $cd okl4_3.0 $ln -s ../linux-2.6.24.9-patch.4 linux 编译源码还需要交叉编译器。交叉编译器的下载地址 EABI:http://wiki.ok-labs.com/downloads/release-3.0/arm-linux-gnueabi-4.2.4.tar.gz OABI：http://www.ertos.nicta.com.au/downloads/tools/arm-linux-3.4.4.tar.gz 将这两个交叉交叉编译器解压后，更改okl4_3.0/tools/toolchains.py中的： gnu_arm_eabi_toolchain . gnu_arm_nptl_toolchain . arm_linux_toolchain，使之指向自己本机中编译器的正确位置。 然后，进入okl4_3.0，运行： ./tools/build.py machine=versatile project=linux wombat=true TOOLCHAIN=gnu_arm_eabi_toolchain pistachio.TOOLCHAIN=gnu_arm_toolchain PYFREEZE=False kdb_serial=True KDB_BREAKIN=False 可能出现的错误以及解决方法： 1.提示arm-linux-g++找不到。 在PATH中加上相应的arm-linux-g++的bin目录就可以了。 export PATH=$PATH:/path/to/bin/ 2.提示phthon版本不正确。 okl4采用python2.4版本。因此,安装python2.4。在ubuntu下sudo apt-get install python2.4 &#8230; <a href="http://www.tek-life.org/2011/03/02/oklinux-run/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>oklinux是让修改过的linux跑在okl4虚拟机上。网上缺乏针对于oklinux的运行总结。在下不才，折腾好久，才编译运行成功，遂记录之，以方便后来者。<br />
下载地址：<br />
okl4:http://wiki.ok-labs.com/downloads/release-3.0/okl4_3.0.tar.gz<br />
linux:http://wiki.ok-labs.com/downloads/release-3.0/oklinux_2.6.24.9-patch.4.tar.gz<br />
分别解压okl4和linux.在okl4的目录中建一个指向Linux目录的软连接：<br />
$cd okl4_3.0<br />
$ln -s ../linux-2.6.24.9-patch.4 linux</p>
<p>编译源码还需要交叉编译器。交叉编译器的下载地址<br />
EABI:http://wiki.ok-labs.com/downloads/release-3.0/arm-linux-gnueabi-4.2.4.tar.gz<br />
OABI：http://www.ertos.nicta.com.au/downloads/tools/arm-linux-3.4.4.tar.gz<br />
将这两个交叉交叉编译器解压后，更改okl4_3.0/tools/toolchains.py中的：<br />
gnu_arm_eabi_toolchain . gnu_arm_nptl_toolchain . arm_linux_toolchain，使之指向自己本机中编译器的正确位置。</p>
<p>然后，进入okl4_3.0，运行：<br />
./tools/build.py machine=versatile project=linux wombat=true TOOLCHAIN=gnu_arm_eabi_toolchain pistachio.TOOLCHAIN=gnu_arm_toolchain PYFREEZE=False kdb_serial=True KDB_BREAKIN=False<br />
可能出现的错误以及解决方法：<br />
1.提示arm-linux-g++找不到。<br />
在PATH中加上相应的arm-linux-g++的bin目录就可以了。<br />
export PATH=$PATH:/path/to/bin/<br />
2.提示phthon版本不正确。<br />
okl4采用python2.4版本。因此,安装python2.4。在ubuntu下sudo apt-get install python2.4 .<br />
然后改变/usr/bin/python连接的位置指向python2.4即可<br />
3.在生成ext2ramdisk的时候出现错误。这个时候，可以按照如下的办法进行操作：<br />
先安装genext2fs.<br />
然后更改 tools/kenge.py<br />
改1438行开始的<br />
def Ext2FS(self, size, dev):<br />
genext2fs = SConscript(os.path.join(self.oklinux_dir, &#8220;tools&#8221;,<br />
&#8220;genext2fs&#8221;, &#8220;SConstruct&#8221;),<br />
build_dir=os.path.join(self.builddir,<br />
&#8220;native&#8221;, &#8220;tools&#8221;, &#8220;genext2fs&#8221;),<br />
duplicate=0)<br />
ramdisk = self.builddir + os.sep + &#8220;ext2ramdisk&#8221;<br />
cmd = self.Command(ramdisk, Dir(os.path.join(self.builddir,<br />
&#8220;install&#8221;)),<br />
genext2fs[0].abspath +<br />
&#8221; -b $EXT2FS_SIZE -d $SOURCE -f $EXT2FS_DEV<br />
$TARGET&#8221;,<br />
EXT2FS_SIZE=size, EXT2FS_DEV=dev)</p>
<p>Depends(cmd, genext2fs)</p>
<p># always regenerate the ramdisk file<br />
AlwaysBuild(cmd)<br />
return cmd<br />
为</p>
<p>def Ext2FS(self, size, dev):<br />
#genext2fs = SConscript(os.path.join(self.oklinux_dir, &#8220;tools&#8221;, &#8220;genext2fs&#8221;, &#8220;SConstruct&#8221;),<br />
#                      build_dir=os.path.join(self.builddir, &#8220;native&#8221;, &#8220;tools&#8221;, &#8220;genext2fs&#8221;),<br />
#                       duplicate=0)<br />
ramdisk = self.builddir + os.sep + &#8220;ext2ramdisk&#8221;<br />
cmd = self.Command(ramdisk, Dir(os.path.join(self.builddir,<br />
&#8220;install&#8221;)),<br />
#   genext2fs[0].abspath +<br />
&#8220;genext2fs -b $EXT2FS_SIZE -d $SOURCE -D $EXT2FS_DEV $TARGET&#8221;,<br />
EXT2FS_SIZE=size, EXT2FS_DEV=dev)</p>
<p># Depends(cmd, genext2fs)</p>
<p># always regenerate the ramdisk file<br />
AlwaysBuild(cmd)<br />
return cmd<br />
至此，便可以生成img文件<br />
－－－－－－－－－－－－－－－－－－任务完成一大半&#8212;&#8212;&#8211;<br />
然后安装qemu-system-arm<br />
下载<br />
http://wiki.ok-labs.com/downloads/release-3.0/qemu-okl.0.9.1.tar.gz<br />
编译<br />
# ./configure &#8211;target-list=arm-softmmu &#8211;disable-sdl &#8211;static &#8211;disable-gfx-check<br />
# make<br />
运行<br />
# qemu-system-arm -M versatileab -start-addr 0&#215;04100000 -nographic -kernel path/to/images/image.elf</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/03/02/oklinux-run/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>汇编代码杂记</title>
		<link>http://www.tek-life.org/2011/03/01/asm-arm-felling/</link>
		<comments>http://www.tek-life.org/2011/03/01/asm-arm-felling/#comments</comments>
		<pubDate>Tue, 01 Mar 2011 08:08:35 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[ARM]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10535</guid>
		<description><![CDATA[一直没有在汇编中写过代码。而且读一些复杂的代码，还有一些吃力。并且自己在ARM体系结构方面的基本功非常薄弱，仅仅是看过杜春雷同学写的那本《ARM体系结构与编程》的教材，偶尔看一下网上同学总结的一些ARM指令的野史笔记。 由于要追踪一下项目中页表的建立流程，由于枝叶比较繁茂，并且深入汇编代码之中，因此，不能充分利用野蛮并且笨拙的Printk，于是便需要在汇编中调用自己写的C函数，然后让流程重新回到汇编中，继续徜徉。 让汇编调用C中的函数，在网上的文章一抓一大把，可是让C中的函数重新返回到汇编中，不着痕迹得继续运行，却没有这样的总结性文章。无奈之下，重新从download.csdn.net中下载到ARM指令集（以前看过，但由于一次误操作，将之永久驱逐出俺的硬盘中），By the way，这个ARM指令集确实不错，有需要的同学到csdn资源中心去自己寻找吧。下面把俺的情况说一下： ENTRY(cpu_arm926_set_pte_ext) mov r3, r1 eor     r3, r3, #L_PTE_PRESENT &#124; L_PTE_YOUNG &#124; L_PTE_WRITE &#124; L_PTE_DIRTY bic     r2, r3, #PTE_SMALL_AP_MASK bic     r2, r2, #PTE_TYPE_MASK orr     r2, r2, #PTE_TYPE_SMALL tst &#8230; <a href="http://www.tek-life.org/2011/03/01/asm-arm-felling/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>一直没有在汇编中写过代码。而且读一些复杂的代码，还有一些吃力。并且自己在ARM体系结构方面的基本功非常薄弱，仅仅是看过杜春雷同学写的那本《ARM体系结构与编程》的教材，偶尔看一下网上同学总结的一些ARM指令的野史笔记。</p>
<p>由于要追踪一下项目中页表的建立流程，由于枝叶比较繁茂，并且深入汇编代码之中，因此，不能充分利用野蛮并且笨拙的Printk，于是便需要在汇编中调用自己写的C函数，然后让流程重新回到汇编中，继续徜徉。</p>
<p>让汇编调用C中的函数，在网上的文章一抓一大把，可是让C中的函数重新返回到汇编中，不着痕迹得继续运行，却没有这样的总结性文章。无奈之下，重新从download.csdn.net中下载到ARM指令集（以前看过，但由于一次误操作，将之永久驱逐出俺的硬盘中），By the way，这个ARM指令集确实不错，有需要的同学到csdn资源中心去自己寻找吧。下面把俺的情况说一下：</p>
<div id="_mcePaste">ENTRY(cpu_arm926_set_pte_ext)</div>
<div id="_mcePaste">mov r3, r1</div>
<div id="_mcePaste">eor     r3, r3, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY</div>
<div id="_mcePaste">bic     r2, r3, #PTE_SMALL_AP_MASK</div>
<div id="_mcePaste">bic     r2, r2, #PTE_TYPE_MASK</div>
<div id="_mcePaste">orr     r2, r2, #PTE_TYPE_SMALL</div>
<div id="_mcePaste">tst     r3, #L_PTE_USER                 @ User?</div>
<div id="_mcePaste">orrne   r2, r2, #PTE_SMALL_AP_URO_SRW</div>
<div id="_mcePaste">tst     r3, #L_PTE_WRITE | L_PTE_DIRTY  @ Write and Dirty?</div>
<div id="_mcePaste">orreq   r2, r2, #PTE_SMALL_AP_UNO_SRW</div>
<div id="_mcePaste">tst     r3, #L_PTE_PRESENT | L_PTE_YOUNG        @ Present and Young?</div>
<div id="_mcePaste">movne   r2, #0</div>
<div id="_mcePaste">b  hypervisor_set_pte</div>
<p>我要做的事情是，在hypervisor_set_pte之前调用一个自写的C函数。由于后面还有b hypervisor_set_pte.而hypervisor_set_pte这个函数返回后，返回到lr所指定的地址中去，因此在调用自写的C函数时，必须要保存lr的值。<br />
至此，在b hypervisor_set_pte之前加上：</p>
<div><span style="font-size: small;"><span style="line-height: 24px;">stmfd sp!, {lr}</span></span></div>
<div>bl test</div>
<div><span style="font-size: small;"><span style="line-height: 24px;">ldmfd sp!, {lr}</span></span></div>
<p>这样似乎很完美了。可是编译执行后，虽然是完成了test函数的功能，但却出现了取值异常。根据分析，不应该出现得啊。后来，经过同学的分析，是不是有可能破坏了其他的寄存器？我测试了一下，把r0清0后，然后b hypervisor_set_pte，编译运行的结果也出现错误，显然，确实是其他寄存器需要被hypervisor_set_pte使用。于是，</p>
<div><span style="font-size: small;"><span style="line-height: 24px;">stmfd sp!,{r0,lr}</span></span></div>
<div><span style="font-size: small;"><span style="line-height: 24px;">bl test</span></span></div>
<div><span style="font-size: small;"><span style="line-height: 24px;">ldmfd sp!,{r0,lr}</span></span></div>
<p>编译运行的结果仍然不正确。索性，</p>
<div><span style="font-size: small;"><span style="line-height: 24px;">stmfd sp!, {r0-r12, lr}</span></span></div>
<div><span style="font-size: small;"><span style="line-height: 24px;">bl test</span></span></div>
<div><span style="font-size: small;"><span style="line-height: 24px;">ldmfd sp!, {r0-r12, lr}</span></span></div>
<p>编译运行后，哦，世界一片宁静。太棒了。<br />
由此，写汇编一定要仔细，谨慎。尽量做到万无一失，就好似做POJ上的题目，每一种条件都要想到，否则一直WA。通过这个错误，马上连想到，第一次写驱动时，malloc后忘记free的窘状，明明一切逻辑都正确，为什么运行不起来？哦，原来是内存耗完了。</p>
<p>写此篇，以纪念在ARM汇编中写上第一行代码。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/03/01/asm-arm-felling/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>关于asm-offsets的一些说明</title>
		<link>http://www.tek-life.org/2011/02/23/%e5%85%b3%e4%ba%8easm-offsets%e7%9a%84%e4%b8%80%e4%ba%9b%e8%af%b4%e6%98%8e/</link>
		<comments>http://www.tek-life.org/2011/02/23/%e5%85%b3%e4%ba%8easm-offsets%e7%9a%84%e4%b8%80%e4%ba%9b%e8%af%b4%e6%98%8e/#comments</comments>
		<pubDate>Wed, 23 Feb 2011 04:07:53 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Kernel]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10532</guid>
		<description><![CDATA[对Kbuild里面的asm-offsets一直不太清楚，找到了一个RFC,并简单翻译了一下。详见http://marc.info/?l=linux-kernel&#038;m=100219616028442&#038;w=2 Almost every architecture generates Assembler values to map the offsets of fields in C structures, about the only exception is i386 and that is because its offsets are hard coded into entry.S. Every arch has done it differently, none &#8230; <a href="http://www.tek-life.org/2011/02/23/%e5%85%b3%e4%ba%8easm-offsets%e7%9a%84%e4%b8%80%e4%ba%9b%e8%af%b4%e6%98%8e/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>对Kbuild里面的asm-offsets一直不太清楚，找到了一个RFC,并简单翻译了一下。详见http://marc.info/?l=linux-kernel&#038;m=100219616028442&#038;w=2</p>
<p>Almost every architecture generates Assembler values to map the offsets of fields in C structures, about the only exception is i386 and that is because its offsets are hard coded into entry.S.  Every arch has done it differently, none of them have got it exactly right.<br />
几乎所有的体系结构都会产生汇编的值去和C语言中的数据结构各个域的偏移相对应。但x86是一个例外，因为它的偏移是硬编码的，具体可以参考entry.S.没一种体系结构都是不一样的，但几乎没有一个能够对应的十分准确。</p>
<p>As part of kbuild 2.5 I am standardizing on one method for generating Assembler offsets.  This change is required for kbuild 2.5 but it can be added to 2.4 without disturbing the current kbuild, I want to do this gradually now instead of a single massive change in kernel 2.5.  I will be issuing per architecture changes for generating Assembler offsets against 2.4.<br />
从kbuild2.5开始，我就开始做了一个标准化的方法去生成汇编的相对偏移。如果你的kbuild是2.4版本的，也可以是用kbuild2.5的这个标准化方法。我想逐步的把这个方法加入到2.5的Linux Kernel中，重点是针对各个架构的不同点。</p>
<p>The kbuild 2.5 method for generating Assembler offsets satisfies these requirements:<br />
在Kbuild 2.5中，要使用这个标准化的方法，需要满足的一下几点：<br />
* No manual intervention required.  Many architectures rely on users  running make dep after changing config options that affect the  Assembler offsets.  If the user forgets to run make dep then the C  and Assembler code is out of sync &#8211; totally unacceptable.  This is  completely fixed in kbuild 2.5; I cannot do a complete fix in kbuild  2.4 but it is still better than the existing manual system.<br />
不需要人工干预。你只要配置好config，然后make dep就可以啦。如果忘记了make dep，那么后果很严重，C和汇编代码就不会对应，会产生错误。不过，如果你采用2.5,这个问题已经被修复了。虽然我没有在kbuild2.4里面这样做，但是，如果你是用kbuild2.4这样的版本，总比完全手动要好的多吧。<br />
* Standard name for the related files.  There are 6+ different names  for the files used to generate Assembler offsets, kbuild 2.5 uses  asm-offsets.[csh] on all architectures.<br />
相关文件都采用了标准名字。有6个不同名字的文件用来产生汇编的偏移,从kbuild2.5开始，在所有的体系结构中，只采用了asm-offsets.c .h 和.s<br />
* Allows for multiple parallel compiles from the same source tree.  Writing the generated asm-offsets.h to include/asm is not an option,  it prevents concurrent compiles.<br />
在同一个工作目录下，允许并行编译。通过向include/asm下面写声称的asm-offsets.h，会阻止并发编译。<br />
* The method must work in native and cross compile mode and give  exactly the same results.  Some 2.4 code only works in native mode,  some architectures have different methods for native and cross  compile with different output formats.  Yeuch!<br />
使用这种方法必须能够保证采用编译器和交叉编译器都能得到同样的计算结果。在2.4版本中就不太好。<br />
* Standard scripts for generating the output.  Every arch does it  differently in 2.4, standards are good!<br />
采用标准化的脚本来产生输出，这一点在2.4版本中已经做得比较好了。<br />
* Correct dependency trees.  Because 2.4 make dep does not scan .S  files, there is little or no dependency information.  Even if the  offsets are regenerated, the affected Assembler code does not always  get rebuilt.  kbuild 2.5 handles dependencies for Assembler as well  as C; I cannot get kbuild 2.4 perfect but I can improve on the  existing (or non-existent) 2.4 dependencies.<br />
还是依赖问题。在2.4版本中make dep 不会扫描.S文件。即使重新编译声称asmoffsets，那些汇编代码也不会重新编译。在2.5版本中已经解决了这个问题。我不能让2.4版本完美，但我会想办法提高2.4版本所存在的依赖问题<br />
All architectures will define arch/$(ARCH)/asm-offsets.c.  This has a standard prologue for the macros that convert offsets to Assembler, followed by arch specific field references.<br />
所有的架构会定义asm-offsets.c文件，这是一个标准的宏文件将会根据体系结构的信息产生相应汇编代码中的偏移位置。<br />
arch/$(ARCH)/asm-offsets.s is generated from arch/$(ARCH)/asm-offsets.c using standard rules, although kbuild 2.4 needs some tweaking.<br />
asm-offsets.s由asm-offsets.c产生。<br />
arch/$(ARCH)/asm-offsets.h is generated from arch/$(ARCH)/asm-offsets.s by a semi-standard script.  Most of the script is common to all architectures but the precise format of the Assembler output is arch specific.<br />
asm-offsets.h由asm-offsets.s产生。各个平台产生代码所是用的脚本大部分都是一样的，但在细微的格式上可能会产生平台差别。<br />
The final result is included in *only* the Assembler programs that need it, as #include  asm-offsets.h&#8221; with -I arch/$(ARCH) in the relevant Makefiles.  Hard coding relative paths in source files is a pet hate, use #include &#8220;localname.h&#8221; and -I instead.  Including the generated<br />
file in C code is not allowed, it severly pollutes the dependency chain, to the extent that any config change can force a complete recompile, unacceptable.<br />
通过在makefile中采用 －I arch/$(ARCH)参数，使最后的结果包含在了要使用的汇编程序中。如果要是用硬编码的话就在汇编源文件中用#include来包含,C文件就不需要了，切记！否则，会产生不必要的麻烦。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/02/23/%e5%85%b3%e4%ba%8easm-offsets%e7%9a%84%e4%b8%80%e4%ba%9b%e8%af%b4%e6%98%8e/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>XEN-ARM定时器管理算法简要总结</title>
		<link>http://www.tek-life.org/2011/01/20/xen-arm%e5%ae%9a%e6%97%b6%e5%99%a8%e7%ae%a1%e7%90%86%e7%ae%97%e6%b3%95%e7%ae%80%e8%a6%81%e6%80%bb%e7%bb%93/</link>
		<comments>http://www.tek-life.org/2011/01/20/xen-arm%e5%ae%9a%e6%97%b6%e5%99%a8%e7%ae%a1%e7%90%86%e7%ae%97%e6%b3%95%e7%ae%80%e8%a6%81%e6%80%bb%e7%bb%93/#comments</comments>
		<pubDate>Thu, 20 Jan 2011 09:29:01 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[XENARM]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2011/01/20/xen-arm%e5%ae%9a%e6%97%b6%e5%99%a8%e7%ae%a1%e7%90%86%e7%ae%97%e6%b3%95%e7%ae%80%e8%a6%81%e6%80%bb%e7%bb%93/</guid>
		<description><![CDATA[/* XEN-ARM的定时器优先队列管理算法采用的是最小堆。关于最小堆的知识，请参考 《算法导论》第二版第6章堆。 下面是简要流程。欢迎讨论 */ /*初始化定时器*/ void __init timer_init(void) { static struct timer *dummy_heap; int i; open_softirq(TIMER_SOFTIRQ, timer_softirq_action); /* * All CPUs initially share an empty dummy heap. Only those CPUs that * are brought online will be dynamically allocated &#8230; <a href="http://www.tek-life.org/2011/01/20/xen-arm%e5%ae%9a%e6%97%b6%e5%99%a8%e7%ae%a1%e7%90%86%e7%ae%97%e6%b3%95%e7%ae%80%e8%a6%81%e6%80%bb%e7%bb%93/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div id="_mcePaste">/*</div>
<div id="_mcePaste">XEN-ARM的定时器优先队列管理算法采用的是最小堆。关于最小堆的知识，请参考</div>
<div id="_mcePaste">《算法导论》第二版第6章堆。</div>
<div id="_mcePaste">下面是简要流程。欢迎讨论</div>
<div id="_mcePaste">*/</div>
<div id="_mcePaste">/*初始化定时器*/</div>
<div id="_mcePaste">void __init timer_init(void)</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">static struct timer *dummy_heap;</div>
<div id="_mcePaste">int i;</div>
<div id="_mcePaste">open_softirq(TIMER_SOFTIRQ, timer_softirq_action);</div>
<div id="_mcePaste">/*</div>
<div id="_mcePaste">* All CPUs initially share an empty dummy heap. Only those CPUs that</div>
<div id="_mcePaste">* are brought online will be dynamically allocated their own heap.</div>
<div id="_mcePaste">*/</div>
<div id="_mcePaste">SET_HEAP_SIZE(&amp;dummy_heap, 0);</div>
<div id="_mcePaste">SET_HEAP_LIMIT(&amp;dummy_heap, 0);</div>
<div id="_mcePaste">for ( i = 0; i &lt; NR_CPUS; i++ )</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">spin_lock_init(&amp;timers[i].lock);</div>
<div id="_mcePaste">timers[i].heap = &amp;dummy_heap;</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">/*</div>
<div id="_mcePaste">当软中断发生时，函数执行</div>
<div id="_mcePaste">在定时器优先队列中查找满足expires的定时器，从队列中摘除，然后执行之</div>
<div id="_mcePaste">*/</div>
<div id="_mcePaste">static void timer_softirq_action(void)</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">int           cpu = smp_processor_id();</div>
<div id="_mcePaste">struct timer *t, **heap;</div>
<div id="_mcePaste">s_time_t      now;</div>
<div id="_mcePaste">void        (*fn)(void *);</div>
<div id="_mcePaste">void         *data;</div>
<div id="_mcePaste">DPRINTK(5, &#8220;%s:%d\n&#8221;, __FUNCTION__, __LINE__);</div>
<div id="_mcePaste">spin_lock_irq(&amp;timers[cpu].lock);</div>
<div id="_mcePaste">do {</div>
<div id="_mcePaste">heap = timers[cpu].heap;</div>
<div id="_mcePaste">now  = NOW();</div>
<div id="_mcePaste">//以此取定时器优先队列中最优先定时器</div>
<div id="_mcePaste">//然后执行其过程</div>
<div id="_mcePaste">while ( (GET_HEAP_SIZE(heap) != 0) &amp;&amp; ( (t = heap[1])-&gt;expires &lt; (now + (s_time_t)TIMER_SLOP) ) )</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">remove_entry(heap, t);</div>
<div id="_mcePaste">timers[cpu].running = t;</div>
<div id="_mcePaste">fn   = t-&gt;function;</div>
<div id="_mcePaste">data = t-&gt;data;</div>
<div id="_mcePaste">//printk(&#8220;fn:%x&#8221;, fn);</div>
<div id="_mcePaste">spin_unlock_irq(&amp;timers[cpu].lock);</div>
<div id="_mcePaste">//执行定时器操作</div>
<div id="_mcePaste">(*fn)(data);</div>
<div id="_mcePaste">DPRINTK(5, &#8220;%s:%d\n&#8221;, __FUNCTION__, __LINE__);</div>
<div id="_mcePaste">spin_lock_irq(&amp;timers[cpu].lock);</div>
<div id="_mcePaste">/* Heap may have grown while the lock was released. */</div>
<div id="_mcePaste">heap = timers[cpu].heap;</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">timers[cpu].running = NULL;</div>
<div id="_mcePaste">}while ( !reprogram_timer(GET_HEAP_SIZE(heap) ? heap[1]-&gt;expires : 0) );//如果有定时器，小于now()，那么就继续loop</div>
<div id="_mcePaste">DPRINTK(5, &#8220;%s:%d\n&#8221;, __FUNCTION__, __LINE__);</div>
<div id="_mcePaste">spin_unlock_irq(&amp;timers[cpu].lock);</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">/*</div>
<div id="_mcePaste">Delete @t from @heap. Return TRUE if new top of heap.</div>
<div id="_mcePaste">摘除定时器会用到down_heap 和 up_heap来调整最小堆。</div>
<div id="_mcePaste">*/</div>
<div id="_mcePaste">static int remove_entry(struct timer **heap, struct timer *t)</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">int sz = GET_HEAP_SIZE(heap);</div>
<div id="_mcePaste">int pos = t-&gt;heap_offset;</div>
<div id="_mcePaste">//t-&gt;heap_offset</div>
<div id="_mcePaste">t-&gt;heap_offset = 0;</div>
<div id="_mcePaste">//&#8211;如果删除的元素是最后的，那么就不需要做调整啦~~,仅仅是heap-size减一就可以咯</div>
<div id="_mcePaste">if ( unlikely(pos == sz) )</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">SET_HEAP_SIZE(heap, sz-1);</div>
<div id="_mcePaste">goto out;</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">//&#8211;把最后一个元素填上来，这是最小堆的精髓！</div>
<div id="_mcePaste">heap[pos] = heap[sz];</div>
<div id="_mcePaste">heap[pos]-&gt;heap_offset = pos;</div>
<div id="_mcePaste">SET_HEAP_SIZE(heap, &#8211;sz);</div>
<div id="_mcePaste">//如果是中间节点，并且小于父母的，那么就需要up_heap</div>
<div id="_mcePaste">//如果是根结点，或者暂时不小于父母的，那么就是down_heap</div>
<div id="_mcePaste">if ( (pos &gt; 1) &amp;&amp; (heap[pos]-&gt;expires &lt; heap[pos&gt;&gt;1]-&gt;expires) )</div>
<div id="_mcePaste">up_heap(heap, pos);</div>
<div id="_mcePaste">else</div>
<div id="_mcePaste">down_heap(heap, pos);</div>
<div id="_mcePaste">out:</div>
<div id="_mcePaste">return (pos == 1);</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">/* Float element @pos up @heap. */</div>
<div id="_mcePaste">static void up_heap(struct timer **heap, int pos)</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">struct timer *t = heap[pos];</div>
<div id="_mcePaste">//根节点的expires最小-－</div>
<div id="_mcePaste">//具体算法是，自底向上，循环保证最小堆的性质</div>
<div id="_mcePaste">while ( (pos &gt; 1) &amp;&amp; (t-&gt;expires &lt; heap[pos&gt;&gt;1]-&gt;expires) )</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">heap[pos] = heap[pos&gt;&gt;1];</div>
<div id="_mcePaste">heap[pos]-&gt;heap_offset = pos;</div>
<div id="_mcePaste">pos &gt;&gt;= 1;</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">heap[pos] = t;</div>
<div id="_mcePaste">t-&gt;heap_offset = pos;</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">/* Sink down element @pos of @heap. */</div>
<div id="_mcePaste">static void down_heap(struct timer **heap, int pos)</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">//自顶向下插入一个元素时的方法</div>
<div id="_mcePaste">int sz = GET_HEAP_SIZE(heap), nxt;</div>
<div id="_mcePaste">struct timer *t = heap[pos];</div>
<div id="_mcePaste">while ( (nxt = (pos &lt;&lt; 1)) &lt;= sz )</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">//nxt是left-subtree nxt+1是right-subtree</div>
<div id="_mcePaste">if ( ((nxt+1) &lt;= sz) &amp;&amp; (heap[nxt+1]-&gt;expires &lt; heap[nxt]-&gt;expires) )</div>
<div id="_mcePaste">nxt++;//&#8211;better than the book of &lt; the introduction of algorithm &gt;</div>
<div id="_mcePaste">if ( heap[nxt]-&gt;expires &gt; t-&gt;expires )//如果保证了最小堆，就是找到了～～</div>
<div id="_mcePaste">break;</div>
<div id="_mcePaste">heap[pos] = heap[nxt];</div>
<div id="_mcePaste">heap[pos]-&gt;heap_offset = pos;</div>
<div id="_mcePaste">pos = nxt;</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">heap[pos] = t;</div>
<div id="_mcePaste">t-&gt;heap_offset = pos;</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">/*下面附上相关的一些函数*/</div>
<div id="_mcePaste">/****************************************************************************</div>
<div id="_mcePaste">* HEAP OPERATIONS.</div>
<div id="_mcePaste">*/</div>
<div id="_mcePaste">#define GET_HEAP_SIZE(_h)     ((int)(((u16 *)(_h))[0]))</div>
<div id="_mcePaste">#define SET_HEAP_SIZE(_h,_v)  (((u16 *)(_h))[0] = (u16)(_v))</div>
<div id="_mcePaste">#define GET_HEAP_LIMIT(_h)    ((int)(((u16 *)(_h))[1]))</div>
<div id="_mcePaste">#define SET_HEAP_LIMIT(_h,_v) (((u16 *)(_h))[1] = (u16)(_v))</div>
<div id="_mcePaste">/****************************************************************************</div>
<div id="_mcePaste">* TIMER OPERATIONS.</div>
<div id="_mcePaste">*/</div>
<div id="_mcePaste">static inline void __add_timer(struct timer *timer)</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">static int tmp_count;</div>
<div id="_mcePaste">printk(&#8220;__add_timer:%d\n&#8221;,tmp_count++);</div>
<div id="_mcePaste">int cpu = timer-&gt;cpu;</div>
<div id="_mcePaste">if ( add_entry(&amp;timers[cpu].heap, timer) ){//如果是在最高优先级，那么就需要调用一下softirq</div>
<div id="_mcePaste">cpu_raise_softirq(cpu, TIMER_SOFTIRQ);</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">static inline void __stop_timer(struct timer *timer)</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">int cpu = timer-&gt;cpu;</div>
<div id="_mcePaste">if ( remove_entry(timers[cpu].heap, timer) )//如果删除了最优先的timer,才需要</div>
<div id="_mcePaste">cpu_raise_softirq(cpu, TIMER_SOFTIRQ);</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">static inline void timer_lock(struct timer *timer)</div>
<div id="_mcePaste">void set_timer(struct timer *timer, s_time_t expires)</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">unsigned long flags;</div>
<div id="_mcePaste">timer_lock_irqsave(timer, flags);</div>
<div id="_mcePaste">//现检测timer是否在队列中</div>
<div id="_mcePaste">if ( active_timer(timer) )</div>
<div id="_mcePaste">__stop_timer(timer);//从队列中除去先</div>
<div id="_mcePaste">timer-&gt;expires = expires;//更新expires</div>
<div id="_mcePaste">if ( likely(!timer-&gt;killed) )</div>
<div id="_mcePaste">__add_timer(timer);</div>
<div id="_mcePaste">timer_unlock_irqrestore(timer, flags);</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">void stop_timer(struct timer *timer)</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">unsigned long flags;</div>
<div id="_mcePaste">timer_lock_irqsave(timer, flags);</div>
<div id="_mcePaste">if ( active_timer(timer) )</div>
<div id="_mcePaste">__stop_timer(timer);</div>
<div id="_mcePaste">timer_unlock_irqrestore(timer, flags);</div>
<div id="_mcePaste">}</div>
<div id="_mcePaste">int reprogram_timer(s_time_t timeout)</div>
<div id="_mcePaste">{</div>
<div id="_mcePaste">s_time_t    now;</div>
<div id="_mcePaste">s_time_t    expire;</div>
<div id="_mcePaste">if ( timeout == 0 )</div>
<div id="_mcePaste">return 1;</div>
<div id="_mcePaste">now = NOW();</div>
<div id="_mcePaste">expire = timeout &#8211; now; /* value from now */</div>
<div id="_mcePaste">if ( expire &lt;= 0 )</div>
<div id="_mcePaste">return 0;</div>
<div id="_mcePaste">return 1;</div>
<div id="_mcePaste">}</div>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/01/20/xen-arm%e5%ae%9a%e6%97%b6%e5%99%a8%e7%ae%a1%e7%90%86%e7%ae%97%e6%b3%95%e7%ae%80%e8%a6%81%e6%80%bb%e7%bb%93/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>gcc装载具体库的的方法总结</title>
		<link>http://www.tek-life.org/2011/01/20/gcc%e9%93%be%e6%8e%a5%e5%85%b7%e4%bd%93%e5%ba%93%e7%9a%84%e7%9a%84%e6%96%b9%e6%b3%95%e6%80%bb%e7%bb%93-2/</link>
		<comments>http://www.tek-life.org/2011/01/20/gcc%e9%93%be%e6%8e%a5%e5%85%b7%e4%bd%93%e5%ba%93%e7%9a%84%e7%9a%84%e6%96%b9%e6%b3%95%e6%80%bb%e7%bb%93-2/#comments</comments>
		<pubDate>Thu, 20 Jan 2011 03:24:22 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[C]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2011/01/20/gcc%e9%93%be%e6%8e%a5%e5%85%b7%e4%bd%93%e5%ba%93%e7%9a%84%e7%9a%84%e6%96%b9%e6%b3%95%e6%80%bb%e7%bb%93-2/</guid>
		<description><![CDATA[注：该总结主要来自:bugfly z的buzz.感谢bugfly z，以及回帖的盆友，如果有不完善的地方，欢迎拍砖。 1.环境变量LD_PRELOAD LD_PRELOAD 后面接的是具体的库文件全路径，可以接多个 参考：http://blog.csdn.net/haoel/archive/2007/05/09/1602108.aspx 2.是方案1的变体。直接把库名加到列表加到/etc/ld.so.preload. 该方法会影响到系统里所有应用。 程序加载时，LD_PRELOAD加载路径优先级高于/etc/ld.so.preload 3. 环境变量LD_LIBRARY_PATH LD_LIBRARY_PATH指定查找路径，这个路径优先级别高于系统预设的路径 假如现在需要在已有的环境变量上添加新的路径名，则采用如下方式： LD_LIBRARY_PATH=NEWDIRS:$LD_LIBRARY_PATH.（newdirs是新的路径串） 4. gcc 中的 -L 和 －l参数  －Wl参数 放在/lib和/usr/lib和/usr/local/lib里的库直接用-l参数就能链接了，但如果库文件没放在这三个目录里，而是放在其他目录里，这时另外一个参数-L就派上用场了，比如常用的X11的库，它在/usr/X11R6/lib目录下，我们编译时就要用-L/usr/X11R6/lib -lX11参数，-L参数跟着的是库文件所在的目录名。 -Wl,表示后面的参数将传给link程序ld 5.LD程序硬编码了一些库搜索路径 查看方法： 00653000-0066e000 r-xp 00000000 08:07 1320479 /lib/ld-2.11.1.so 0066e000-0066f000 r&#8211;p 0001a000 08:07 1320479 /lib/ld-2.11.1.so 0066f000-00670000 &#8230; <a href="http://www.tek-life.org/2011/01/20/gcc%e9%93%be%e6%8e%a5%e5%85%b7%e4%bd%93%e5%ba%93%e7%9a%84%e7%9a%84%e6%96%b9%e6%b3%95%e6%80%bb%e7%bb%93-2/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div><span>注：该总结主要来自:<a id="r.th" title="bugfly z" href="http://www.google.com/profiles/100910585380276862823">bugfly z</a>的buzz.感谢bugfly z，以及回帖的盆友，如果有不完善的地方，欢迎拍砖。</span></div>
<div><span style="font-family: verdana;">1.环境变量LD_PRELOAD</span></div>
<div><span style="font-family: verdana;">LD_PRELOAD 后面接的是具体的库文件全路径，可以接多个</span></div>
<div><span style="font-family: verdana;">参考：http://blog.csdn.net/haoel/archive/2007/05/09/1602108.aspx</span></div>
<div><span style="font-family: verdana;">2.是方案1的变体。直接把库名加到列表加到/etc/ld.so.preload. 该方法会影响到系统里所有应用。</span></div>
<div><span style="font-family: verdana;">程序加载时，LD_PRELOAD加载路径优先级高于/etc/ld.so.preload</span></div>
<div><span style="font-family: verdana;">3. 环境变量LD_LIBRARY_PATH</span></div>
<div><span style="font-family: verdana;"><span style="font-family: verdana;"><span>LD_LIBRARY_PATH</span></span>指定查找路径，这个路径优先级别高于系统预设的路径</span></div>
<div><span style="font-family: verdana;"> 假如现在需要在已有的环境变量上添加新的路径名，则采用如下方式：</span></div>
<div><span style="font-family: verdana;"> LD_LIBRARY_PATH=NEWDIRS:$LD_LIBRARY_PATH.（newdirs是新的路径串）</span></div>
<div><span style="font-family: verdana;">4. gcc 中的 -L 和 －l参数  －Wl参数</span></div>
<div><span style="font-family: verdana;">放在/lib和/usr/lib和/usr/local/lib里的库直接用-l参数就能链接了，但如果库文件没放在这三个目录里，而是放在其他目录里，这时另外一个参数-L就派上用场了，比如常用的X11的库，它在/usr/X11R6/lib目录下，我们编译时就要用-L/usr/X11R6/lib -lX11参数，-L参数跟着的是库文件所在的目录名。</span></div>
<p><span style="font-family: verdana; line-height: 15px;">-Wl,表示后面的参数将传给link程序ld</span></p>
<p><span style="font-family: verdana; line-height: 15px;">5.LD程序硬编码了一些库搜索路径</span></p>
<div><span style="font-family: verdana;">查看方法：</span></div>
<div><span style="font-family: verdana;">00653000-0066e000 r-xp 00000000 08:07 1320479 /lib/ld-2.11.1.so</span></div>
<div><span style="font-family: verdana;">0066e000-0066f000 r&#8211;p 0001a000 08:07 1320479 /lib/ld-2.11.1.so</span></div>
<div><span style="font-family: verdana;">0066f000-00670000 rw-p 0001b000 08:07 1320479 /lib/ld-2.11.1.so</span></div>
<div><span style="font-family: verdana;">$ strings /lib/ld-2.11.1.so | grep lib</span></div>
<div><span style="font-family: verdana;">&#8230;</span></div>
<div><span style="font-family: verdana;">display library search paths</span></div>
<div><span style="font-family: verdana;">/lib/</span></div>
<div><span style="font-family: verdana;">/usr/lib/</span></div>
<div><span style="font-family: verdana;">/lib/i486-linux-gnu/</span></div>
<div><span style="font-family: verdana;">/usr/lib/i486-linux-gnu/</span></div>
<div><span style="font-family: verdana;">&#8230;</span></div>
<div><span style="font-family: verdana;">上面就是默认的装载路径, 直接在装载器里hardcode,</span></div>
<p><span style="font-family: verdana; line-height: 15px;">5.ldconfig也可以起到作用</span></p>
<div><span style="font-family: verdana;">ldconfig命令的用途,主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如前介绍,lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件.缓存文件默认为/etc /ld.so.cache,此文件保存已排好序的动态链接库名字列表. </span></div>
<p><span style="font-family: verdana; line-height: 15px;">运行ldconfig -v 可以看到所有的系统加载库</span></p>
<div><span style="font-family: verdana;">－－－－－－－－－－－－－－－</span></div>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/01/20/gcc%e9%93%be%e6%8e%a5%e5%85%b7%e4%bd%93%e5%ba%93%e7%9a%84%e7%9a%84%e6%96%b9%e6%b3%95%e6%80%bb%e7%bb%93-2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>xenarm irq routine</title>
		<link>http://www.tek-life.org/2011/01/06/xenarm-irq-routine-3/</link>
		<comments>http://www.tek-life.org/2011/01/06/xenarm-irq-routine-3/#comments</comments>
		<pubDate>Thu, 06 Jan 2011 05:08:56 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[XENARM]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2011/01/06/xenarm-irq-routine-3/</guid>
		<description><![CDATA[/* 当一个异常出现以后，ARM微处理器会执行以下几步操作： （这些是中断发生时，自动处理的）  1、 将下一条指令的地址存入相应连接寄存器LR，以便程序在处理异常返回时能从正确的位置重新开始执行。若异常是从ARM状态进入，LR寄存器中保存的是下一 条指令的地址（当前PC＋4或PC＋8，与异常的类型有关）；若异常是从Thumb状态进入，则在LR寄存器中保存当前 PC的偏移量，这样，异常处理程序就不需要确定异常是从何种状态进入的。例如：在软件中断异常SWI，指令MOV PC，R14_svc总是返回到下一条指令，不管SWI是在ARM状态执行，还是在Thumb状态执行。  2、将CPSR复制到相应的SPSR中。  3、根据异常类型，强制设置CPSR的运行模式位。  4、 强制PC从相关的异常向量地址取下一条指令执行，从而跳转到相应的异常处理程序处。还可以设置中断禁止位，以禁止中断发生.当有异常发生时，处理器会跳转 到对应的0xffff0000起始的向量处取指令，然后，通过b指令散转到异常处理代码．因为ARM中b指令是相对跳转，而 且只有+/-32MB的寻址范围，所以把__stubs_start~__stubs_end之间的异常处理代码复制到了0xffff0200起始处．这 里可直接用b指令跳转过去，这样比使用绝对跳转（ldr)效率高。 */         b       vector_undefined_instruction + stubs_offset        ldr     pc, .LCvswi + stubs_offset        b       vector_prefetch_abort + stubs_offset        b       vector_data_abort + stubs_offset        b       vector_addrexcptn + stubs_offset        b       vector_irq + stubs_offset        b       vector_FIQ + stubs_offset #define INSTALL_VECTOR_STUB(name, offset, &#8230; <a href="http://www.tek-life.org/2011/01/06/xenarm-irq-routine-3/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><span id="internal-source-marker_0.7439267693734235" style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">/*</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> 当一个异常出现以后，ARM微处理器会执行以下几步操作： （这些是中断发生时，自动处理的）</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> 1、 将下一条指令的地址存入相应连接寄存器LR，以便程序在处理异常返回时能从正确的位置重新开始执行。若异常是从ARM状态进入，LR寄存器中保存的是下一 条指令的地址（当前PC＋4或PC＋8，与异常的类型有关）；若异常是从Thumb状态进入，则在LR寄存器中保存当前 PC的偏移量，这样，异常处理程序就不需要确定异常是从何种状态进入的。例如：在软件中断异常SWI，指令MOV PC，R14_svc总是返回到下一条指令，不管SWI是在ARM状态执行，还是在Thumb状态执行。</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> 2、将CPSR复制到相应的SPSR中。</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> 3、根据异常类型，强制设置CPSR的运行模式位。</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> 4、 强制PC从相关的异常向量地址取下一条指令执行，从而跳转到相应的异常处理程序处。还可以设置中断禁止位，以禁止中断发生.当有异常发生时，处理器会跳转 到对应的0xffff0000起始的向量处取指令，然后，通过b指令散转到异常处理代码．因为ARM中b指令是相对跳转，而 且只有+/-32MB的寻址范围，所以把__stubs_start~__stubs_end之间的异常处理代码复制到了0xffff0200起始处．这 里可直接用b指令跳转过去，这样比使用绝对跳转（ldr)效率高。</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"> </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">*/</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        b       vector_undefined_instruction + stubs_offset</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     pc, .LCvswi + stubs_offset</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        b       vector_prefetch_abort + stubs_offset</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        b       vector_data_abort + stubs_offset</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        b       vector_addrexcptn + stubs_offset</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        b       vector_irq + stubs_offset</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        b       vector_FIQ + stubs_offset</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">#define INSTALL_VECTOR_STUB(name, offset, mode, correction, branch_table)       </font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">        vector_##name:                                                  </font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">                vector_stub     offset, mode, correction, branch_table</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        .macro vector_stub      offset, mode, correction, branch_table</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        sub     sp, sp, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">16</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        sub     lr, lr, #correction @修正返回地址</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        stmia   sp!, {r0, lr}</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        mrs     r0, spsr @spsr保存的是cpsr的值</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        mov     lr, #offset</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        stmia   sp!, {r0, lr}</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        mrs     lr, cpsr</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        eor     lr, lr, #(mode ^ PSR_MODE_SVC)</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        msr     spsr_cxsf, lr   @改变spsr以重启irq       @ </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">switch</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> to SVC_32 mode</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        and     r0, r0, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">15</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        adr     lr, branch_table</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     lr, [lr, r0, lsl #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">2</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">]    @根据spsr内容来决定进入哪一个irq_</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        sub     r0, sp, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">16</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        movs    pc, lr                          @ Changes mode and branches</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        .endm</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">__irq_svc</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">:</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        save_svc_context</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">#ifdef  CONFIG_MACHINE_VERSATILE</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        get_irqnr_preamble r5, lr @ minsung</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">#endif</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">1</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">:      get_irqnr_and_base r0, r6, r5, lr</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        movne   r1, sp  </font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @ routine called with r0 = irq number, r1 = </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#00ff00"><font size="2">struct</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> pt_regs *</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        adrne   lr, 1b</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        bne     asm_do_IRQ</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline">        </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">/* restore svc mode register */</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     r0, [sp, #S_PSR]                @ irqs are already disabled</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        msr     spsr_cxsf, r0</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldmia   sp, {r0 &#8211; pc}^                  @ load r0 &#8211; pc, cpsr</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        .macro save_svc_context</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        sub     sp, sp, #S_FRAME_SIZE</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">SPFIX(  tst     sp, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">4</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                 )</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">SPFIX(  bicne   sp, sp, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">4</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">             )</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        stmib   sp, {r1 &#8211; r12}</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldmia   r0, {r1 &#8211; r4}</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        add     r5, sp, #S_SP</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        add     r0, sp, #S_FRAME_SIZE</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">SPFIX(  addne   r0, r0, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">4</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">  )</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        str     r1, [sp]                @ Save real R0</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        mov     r1, lr</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        stmia   r5, {r0 &#8211; r4}</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        .endm</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">/*</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> 从下面这段程序可以看出来，</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">        如何获得goldfish中断号，</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">        如何获得goldfish状态</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">        如果没有中断号，还可以设置条件描述符</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"> </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">*/</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     base, =IO_ADDRESS(GOLDFISH_INTERRUPT_BASE)</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     irqnr, [base, #GOLDFISH_INTERRUPT_NUMBER]</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     irqstat, [base, #GOLDFISH_INTERRUPT_STATUS]</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        teq     irqstat, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline">        </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">/* EQ will be set if no irqs pending */</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        .endm</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">/*</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">         时间中断来了，仅仅是打开了一个软中断而已。</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">*/</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">irqreturn_t goldfish_timer_interrupt(</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#00ff00"><font size="2">int</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> irq, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#00ff00"><font size="2">void</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> *dev_id, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#00ff00"><font size="2">struct</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> cpu_user_regs *regs)</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">{</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline">    </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#00ff00"><font size="2">uint32_t</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> timer_base = IO_ADDRESS(GOLDFISH_TIMER_BASE);</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">    timer_tick(regs);</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">    raise_softirq(TIMER_SOFTIRQ);</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">    __raw_writel(</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">1</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">, timer_base + TIMER_CLEAR_INTERRUPT);</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">    goldfish_timer_set_next_event(LATCH);</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline">    </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">return</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> IRQ_HANDLED;</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">}</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">//软中断，跟定时器又联系上了。</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#00ff00"><font size="2">static</font></font></font></span><span style="font-style:normal;vertical-align:baseline"> </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#00ff00"><font size="2">void</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> timer_softirq_action(</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#00ff00"><font size="2">void</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">)</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">{</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline">                </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#00ff00"><font size="2">int</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">           cpu = smp_processor_id();</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline">                </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#00ff00"><font size="2">struct</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> timer *t, **heap;</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                s_time_t      now;</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline">                </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#00ff00"><font size="2">void</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        (*fn)(</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#00ff00"><font size="2">void</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> *);</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline">                </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#00ff00"><font size="2">void</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">         *data;</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                DPRINTK(</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">5</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">&#8220;</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">%s</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">:</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">%dn</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">&#8220;</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">, __FUNCTION__, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">__LINE__</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">);</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                spin_lock_irq(&#038;timers[cpu].lock);  </font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline">                </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">do</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> {</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                        heap = timers[cpu].heap;</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                        now  = NOW();</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline">                        </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">while</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> ( (GET_HEAP_SIZE(heap) != </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">) &#038;&#038; ( (t = heap[</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">1</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">])->expires < (now + (s_time_t)TIMER_SLOP) ) )</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                        {</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                                remove_entry(heap, t);</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                                timers[cpu].running = t;</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                                fn   = t->function;</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                                data = t->data;</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">//      printk(&#8220;fn:%x&#8221;, fn);</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                                spin_unlock_irq(&#038;timers[cpu].lock);</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                                (*fn)(data);</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">//执行定时器routine</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                                DPRINTK(</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">5</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">&#8220;</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">%s</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">:</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">%dn</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">&#8220;</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">, __FUNCTION__, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">__LINE__</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">);</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                                spin_lock_irq(&#038;timers[cpu].lock);</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline">                                </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">/* Heap may have grown while the lock was released. */</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                                heap = timers[cpu].heap;</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                        }</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                        timers[cpu].running = </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">NULL</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">;</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                }</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline">                </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">while</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> ( !reprogram_timer(GET_HEAP_SIZE(heap) ? heap[</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">1</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">]->expires : </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">) );</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                DPRINTK(</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">5</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">&#8220;</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">%s</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">:</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">%dn</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">&#8220;</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">, __FUNCTION__, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">__LINE__</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">);</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                spin_unlock_irq(&#038;timers[cpu].lock);</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">}</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">/*</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> 从异常返回</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> 异常处理完毕之后，ARM微处理器会执行以下几步操作从异常返回：</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> 1、将连接寄存器LR的值减去相应的偏移量后送到PC中。</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> 2、将SPSR复制回CPSR中。</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> 3、若在进入异常处理时设置了中断禁止位，要在此清除。</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"> </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">*/</font></font></font></span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2011/01/06/xenarm-irq-routine-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>回顾2010</title>
		<link>http://www.tek-life.org/2010/12/31/review_2010/</link>
		<comments>http://www.tek-life.org/2010/12/31/review_2010/#comments</comments>
		<pubDate>Fri, 31 Dec 2010 07:49:51 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[XENARM]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10500</guid>
		<description><![CDATA[这一年就要过完了，照例要总结一下，这一年都做了一些什么。明年的这个时候应该都拿到offer了。等待毕业和入职了。每想到此，既兴奋又紧张。明年的一年又是紧张的一年，不知道系统能否顺利做完。 这一年最大的收获，应该是入了Linux的门，并且看了不少关于编译链接，计算机系统结构和操作系统方面的书。感觉羽翼一点点丰满，不过，当然距离大牛还有很长的路要走。 上半年的时候，基本上把精力都放在了Linux驱动方面了。看过了宋宝华的Linux驱动开发的书和Linux Device Driver，除此之外，还看了unstanding linux kenrel.和Linux kernel development. 下半年把精力都放在了项目上，移植了yaffs到xenarm上，并且将原来支持的128MB支持到了768MB，当然最大能够达到950MB。将xenheap的大小扩大到了64MB。这一块，我感觉，还是蛮有成就感。内存太小，肯定干不成事情。跑一个android，最少需要70MB以上的，原来的128MB一定是不够用的。特别是，当yaffs在xenarm里面跑成功以后，那种感觉是难以形容的。每看到自己写的代码，就像看到自己的孩子似的。现在的xenarm已经能够跑起来两个android了。不过缺点是，相关的设备虚拟化程度仍然不够。调度算法仍然不完整，调度两个android，明显bug太多，另外，由于设备虚拟化的问题，现在只能跑两个一模一样的系统。因为要公用nand嘛，nand里放的是root fs.在下半年，也看了一些书，这些书，增加了我的一些自信。《自己动手写OS－第二版》《程序员自我修养－－链接装载与库》《代码揭秘》《CODE》其中前两本，给我的感觉很棒。除此之外，还看了PHP和SHELL方面的两本书，同时还看了正则表达式方面的书。当然，看过之后，没有怎么用，都忘记了，不过重新拾起来应该比较快。 去年的这个时候，我按照月份，把每个月所做的事情都列了一下。今年，想到此，便想到了在实验室每个月7号写的QA，我想也许是因为，我对QA已经恐惧了，所以，不写的那么具体了。总结一下，以告慰自己，又过了一年，没有虚度年华。]]></description>
			<content:encoded><![CDATA[<p>这一年就要过完了，照例要总结一下，这一年都做了一些什么。明年的这个时候应该都拿到offer了。等待毕业和入职了。每想到此，既兴奋又紧张。明年的一年又是紧张的一年，不知道系统能否顺利做完。</p>
<p>这一年最大的收获，应该是入了Linux的门，并且看了不少关于编译链接，计算机系统结构和操作系统方面的书。感觉羽翼一点点丰满，不过，当然距离大牛还有很长的路要走。<br />
上半年的时候，基本上把精力都放在了Linux驱动方面了。看过了宋宝华的Linux驱动开发的书和Linux Device Driver，除此之外，还看了unstanding linux kenrel.和Linux kernel development.<br />
下半年把精力都放在了项目上，移植了yaffs到xenarm上，并且将原来支持的128MB支持到了768MB，当然最大能够达到950MB。将xenheap的大小扩大到了64MB。这一块，我感觉，还是蛮有成就感。内存太小，肯定干不成事情。跑一个android，最少需要70MB以上的，原来的128MB一定是不够用的。特别是，当yaffs在xenarm里面跑成功以后，那种感觉是难以形容的。每看到自己写的代码，就像看到自己的孩子似的。现在的xenarm已经能够跑起来两个android了。不过缺点是，相关的设备虚拟化程度仍然不够。调度算法仍然不完整，调度两个android，明显bug太多，另外，由于设备虚拟化的问题，现在只能跑两个一模一样的系统。因为要公用nand嘛，nand里放的是root fs.在下半年，也看了一些书，这些书，增加了我的一些自信。《自己动手写OS－第二版》《程序员自我修养－－链接装载与库》《代码揭秘》《CODE》其中前两本，给我的感觉很棒。除此之外，还看了PHP和SHELL方面的两本书，同时还看了正则表达式方面的书。当然，看过之后，没有怎么用，都忘记了，不过重新拾起来应该比较快。</p>
<p>去年的这个时候，我按照月份，把每个月所做的事情都列了一下。今年，想到此，便想到了在实验室每个月7号写的QA，我想也许是因为，我对QA已经恐惧了，所以，不写的那么具体了。总结一下，以告慰自己，又过了一年，没有虚度年华。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/12/31/review_2010/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>xenarm代码片段注释</title>
		<link>http://www.tek-life.org/2010/12/31/xenarm%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5%e6%b3%a8%e8%a7%86/</link>
		<comments>http://www.tek-life.org/2010/12/31/xenarm%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5%e6%b3%a8%e8%a7%86/#comments</comments>
		<pubDate>Fri, 31 Dec 2010 02:00:22 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[XENARM]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/12/31/xenarm%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5%e6%b3%a8%e8%a7%86/</guid>
		<description><![CDATA[ENTRY(ret_to_user)      test_all_events:        disable_irq     r1                      @ ensure IRQs are disabled               bl      get_irq_stat                    @ get softirq pending status        ldr     r1, [r0, #OFFSET_SOFTIRQ_PENDING]@指针操作,返回值放到r0中         ldr     r0, =0xffff         tst     r1, r0                          @ bit mask check is needed!!!         beq     test_guest_events        enable_irq      r1                      @ sti       @ r1.. ok??????         bl      do_softirq        b       test_all_events /* disable_irq &#8230; <a href="http://www.tek-life.org/2010/12/31/xenarm%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5%e6%b3%a8%e8%a7%86/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><span id="internal-source-marker_0.7242648479421818" style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">ENTRY(ret_to_user)      </font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">test_all_events</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">:</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        disable_irq     r1                      @ ensure IRQs are disabled      </font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        bl      get_irq_stat                    @ get softirq pending status</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     r1, [r0, #OFFSET_SOFTIRQ_PENDING]@指针操作,返回值放到r0中</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     r0, =</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0xffff</font></font></font></span><span style="font-style:normal;vertical-align:baseline"> </span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        tst     r1, r0                          @ bit mask check is needed!!!</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        beq     test_guest_events</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        enable_irq      r1                      @ sti       @ r1.. ok??????</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        bl      do_softirq</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        b       test_all_events</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">/*</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> disable_irq r1</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">        .macro  disable_irq, temp</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">        msr     cpsr_c, #PSR_STATUS_I | PSR_MODE_SVC //只更改控制位0~8,禁止中断标志位</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">        .endm</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"> </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">*/</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">ENTRY(test_guest_events)</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        vcpu    r10                           @从在xenheap中堆栈页的最下部分得到vcpu结构</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     r11, [r10, #OFFSET_VCPU_INFO] @获得vcpu_info</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldrb    r9, [r11, #OFFSET_EVTCHN_UPCALL_MASK]     @r9为evtchn_upcall_mask</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        tst     r9, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0xFF</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                                 ＠如果r9为非零则，bne,否则，要处理upcall信息</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        bne     restore  @如果不为</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">,那么就b restore</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldrb    r12, [r11, #OFFSET_EVTCHN_UPCALL_PENDING]  @r12为evtchn_upcall_pending</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        tst     r12, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0xFF</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        beq     restore  @如果为</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">,那么也 b restore 没有evtchn @如果r12仍然为</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">,那么说明没有guest events事件要处理</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline">        </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">/*</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">        .macro  vcpu    rd                   @通过这种方式得到vcpuinfo.STACK上面是context，下面是cpuinfo</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">                ldr     rd, =(~(STACK_SIZE &#8211; 1))</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">                and     rd, r13, rd</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">                ldr     rd, [rd]</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">        .endm</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline">        </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">*/</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">/*</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> * Send event to guest domain</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> * 操作的都是vcpu->arch.guest_context.内容</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2"> * 但是把domain_guest的信息全部保存在了vcpu->arch.guest_context.sysusrregs的vksp中</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"> </span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">*/</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">ENTRY(do_upcall)</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        vcpu    r10</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     r11, [r10, #OFFSET_VCPU_INFO]                           @r11指向的是vcpu_info</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        add     r10, r10, #(OFFSET_ARCH_VCPU + OFFSET_GUEST_CONTEXT)    @r10指向了guest_context</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @&#8212;&#8212;&#8211;lr指向event_callback</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     lr, [r10, #OFFSET_HYPERVISOR_CALLBACK]                  @ lr event_callback&#8211;</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        cmp     lr, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                                                  @ 如果没有设置call back,那么就restore</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        beq     restore</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline">        </span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @&#8211;upcall_mask，                                                屏蔽掉evtchn</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        mov     r9, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0&#215;01</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        strb    r9, [r11, #OFFSET_EVTCHN_UPCALL_MASK]   @r11指向的是vcpu_info</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        mov     r4, #PSR_MODE_SVC</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        str     r4, [r10, #(OFFSET_SYS_REGS + OFFSET_VPSR)]     @r10指向的是guest_context svc</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @&#8211;将domain_context保存在vksp的下面</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @ Load virtual kernel stack pointer</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     r11, [r10, #(OFFSET_SYS_REGS + OFFSET_VKSP)]    @r11指向guest_context指定的stack pointer&#8211;domain_stack ?</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @ Align VKSP in </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">8</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">-byte boundary</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        sub     r12, r11, #S_FRAME_SIZE @ r12指向domain_context</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        tst     r12, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">4</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        bicne   r12, r12, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">4</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        tst     r12, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">4</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">1</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">:</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        bne     1b</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @ Update effective virtual kernel stack pointer.更新的vksp r12指向了新的stack top&#8211;这样便于保存信息</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        str     r12, [r10, #(OFFSET_SYS_REGS + OFFSET_VKSP)]@改变stack了,stack top 距离原来的stack 有</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">1</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">个user regs</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @ Create bounce frame in guest stack</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @ 将domain_context的信息都保存在vksp下面的一个S_FRAME_SIZE区间中</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldmia   sp!, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">{</font></b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">r0-r8</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">}</font></b></font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        stmia   r12!, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">{</font></b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">r0-r8</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">}</font></b></font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline">        </span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldmia   sp!, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">{</font></b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">r0-r8</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">}</font></b></font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        stmia   r12!, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">{</font></b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">r0-r8</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">}</font></b></font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline">        </span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        sub     r12, r12, #S_FRAME_SIZE @ r12重新指向vksp下面的位置，因为上面的</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">4</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">条指令，已经更改了它的位置</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     r0, =.LCupcall</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        stmia   r0, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">{</font></b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">r12,lr</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">}</font></b></font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline">                </span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldmia   r0, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">{</font></b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">sp, lr</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">}</font></b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">^ </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">//lr = event_upcall 和sp＝vksp</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     r0, [r12, #S_CONTEXT] @ r0 context</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     r1, [r12, #S_PSR]     @ r1 psr</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     r2, [r10, #(OFFSET_SYS_REGS + OFFSET_VFAR)] @r2 far</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     r3, [r10, #(OFFSET_SYS_REGS + OFFSET_VFSR)] @r3 fsr</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        mov     r7, #PSR_MODE_USR                           @user_mod -> r7</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        msr     spsr, r7                                    @r7->spsr</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     r5, =DOMAIN_KERNEL_VALUE</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        mcr     p15, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">, r5, c3, c0, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">   @ Load DAC</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        movs    pc, lr</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">ENTRY(restore)</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">@与swi放在一起看，会更清楚一些</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @进入到guest的环境中</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @将sp内容保存在数据结构vcpu context中，设置进入guest的一些寄存器c3 cpsr r0~r15.(原来的</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                        guest信息全部保存在此？莫非是)</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @将sp内容全部弹出</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        vcpu    r4</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        add     r5, r4, #(OFFSET_ARCH_VCPU + OFFSET_GUEST_CONTEXT)</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">//r5保存了guest_context</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     r1, [sp, #S_PSR] </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">//r1 保存了psr 是 13 svc</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        and     r3, r1, #PSR_MODE_MASK</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        str     r3, [r5, #(OFFSET_SYS_REGS + OFFSET_VPS)]</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">//保存spr到guest_context.sys_regs中</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        bic     r1, r1, #PSR_MODE_MASK </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">//位清除指令</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        orr     r1, r1, #PSR_MODE_USR </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff6060"><font size="2">//0&#215;10</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        cmp     r3, #PSR_MODE_USR</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldreq   r7, =DOMAIN_HYPERVISOR_VALUE</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldrne   r7, =DOMAIN_KERNEL_VALUE</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        mcr     p15, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">, r7, c3, c0, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">   @改变域控制</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        mrc     p15, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">, r3, c2, c0, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">0</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">           @ arbitrary read of cp15</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        mov    r3, r3                           @ wait </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">for</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> completion</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        sub     pc, pc, #</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">4</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">                      @ flush instruction pipeline</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        @下面的操作sp指向的栈地址把原来存储的寄存器全部弹出了</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldr     lr, [sp, #S_PC]!                @ Get PC                          ?</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">1</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        msr     spsr, r1                        @ save in spsr_svc user模式       ?</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">2</font></font></font></span></p>
<p><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        ldmdb   sp, </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">{</font></b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">r0 &#8211; lr</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffffff"><font style="background-color:#ff6060"><b><font size="2">}</font></b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">^                  @ Get calling r0 &#8211; lr        LDM DB 传送前地址-</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">4</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">  ^的作用是用户模式寄存器</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        mov     r0, r0</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        add     sp, sp, #S_FRAME_SIZE &#8211; S_PC    @这里sp指针指向framesize后部</font></font></font></span><br /><span style="font-style:normal;vertical-align:baseline">        </span><br /><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2">        movs    pc, lr                          @ </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ffff00"><font size="2">return</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#000000"><font size="2"> &#038; move spsr_svc into cpsr  ?</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="verdana"><font color="#ff40ff"><font size="2">4</font></font></font></span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/12/31/xenarm%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5%e6%b3%a8%e8%a7%86/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>like-linux系统链表原理简单分析</title>
		<link>http://www.tek-life.org/2010/12/14/like-linux-link/</link>
		<comments>http://www.tek-life.org/2010/12/14/like-linux-link/#comments</comments>
		<pubDate>Tue, 14 Dec 2010 07:51:53 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10489</guid>
		<description><![CDATA[&#160;&#160;1 /* &#160;&#160;2 &#160;like-linux系统下链表结构分析 &#160;&#160;3 &#160;*在like-linux系统中，链表的可移植性比较好，并不像在大学学到的链表结构那样的死板。其原理在很多linux的讲解中都有描述。 &#160;&#160;4 &#160;*原理部分可以看一下李先静老师写的《系统程序员成长之路》一书。 &#160;&#160;5 &#160;*本文根据xenarm中的链表来具体得理一下在like-linux系统中的链表原理。 &#160;&#160;6 &#160; &#160;&#160;7 &#160;*因为时间有限，只是把相关宏定义简单扩展，并把代码做了注释和筛捡。没有成文，望各位看官见谅 &#160;&#160;8 &#160;*/ &#160;&#160;9 &#160;10 &#160;11 /* &#160;12 &#160;*定义了一个相关结构和spinlock &#160;13 &#160;*/ &#160;14 spinlock_t consistent_lock; &#160;15 struct&#160;arm_vm_region { &#160;16 &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;struct&#160;list_head&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;vm_list; &#160;17 &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;unsigned&#160;long&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; vm_start; &#160;18 &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;unsigned&#160;long&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; vm_end; &#160;19 &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;struct&#160;page&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#8230; <a href="http://www.tek-life.org/2010/12/14/like-linux-link/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><font face="monospace"><br />
<font color="#00ff00">&nbsp;&nbsp;1 </font><font color="#ff6060">/*</font><br />
<font color="#00ff00">&nbsp;&nbsp;2 </font><font color="#ff6060">&nbsp;like-linux系统下链表结构分析</font><br />
<font color="#00ff00">&nbsp;&nbsp;3 </font><font color="#ff6060">&nbsp;*在like-linux系统中，链表的可移植性比较好，并不像在大学学到的链表结构那样的死板。其原理在很多linux的讲解中都有描述。</font><br />
<font color="#00ff00">&nbsp;&nbsp;4 </font><font color="#ff6060">&nbsp;*原理部分可以看一下李先静老师写的《系统程序员成长之路》一书。</font><br />
<font color="#00ff00">&nbsp;&nbsp;5 </font><font color="#ff6060">&nbsp;*本文根据xenarm中的链表来具体得理一下在like-linux系统中的链表原理。</font><br />
<font color="#00ff00">&nbsp;&nbsp;6 </font><font color="#ff6060">&nbsp;</font><br />
<font color="#00ff00">&nbsp;&nbsp;7 </font><font color="#ff6060">&nbsp;*因为时间有限，只是把相关宏定义简单扩展，并把代码做了注释和筛捡。没有成文，望各位看官见谅</font><br />
<font color="#00ff00">&nbsp;&nbsp;8 </font><font color="#ff6060">&nbsp;</font><font color="#ff6060">*/</font><br />
<font color="#00ff00">&nbsp;&nbsp;9 </font><br />
<font color="#00ff00">&nbsp;10 </font><br />
<font color="#00ff00">&nbsp;11 </font><font color="#ff6060">/*</font><br />
<font color="#00ff00">&nbsp;12 </font><font color="#ff6060">&nbsp;*定义了一个相关结构和spinlock</font><br />
<font color="#00ff00">&nbsp;13 </font><font color="#ff6060">&nbsp;</font><font color="#ff6060">*/</font><br />
<font color="#00ff00">&nbsp;14 </font>spinlock_t consistent_lock;<br />
<font color="#00ff00">&nbsp;15 </font><font color="#00ff00">struct</font>&nbsp;arm_vm_region {<br />
<font color="#00ff00">&nbsp;16 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">struct</font>&nbsp;list_head&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vm_list;<br />
<font color="#00ff00">&nbsp;17 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">unsigned</font>&nbsp;<font color="#00ff00">long</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vm_start;<br />
<font color="#00ff00">&nbsp;18 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">unsigned</font>&nbsp;<font color="#00ff00">long</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vm_end;<br />
<font color="#00ff00">&nbsp;19 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">struct</font>&nbsp;page&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *vm_pages;<br />
<font color="#00ff00">&nbsp;20 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">int</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vm_active;<br />
<font color="#00ff00">&nbsp;21 </font>};<br />
<font color="#00ff00">&nbsp;22 </font><br />
<font color="#00ff00">&nbsp;23 </font><font color="#ff6060">/*</font><br />
<font color="#00ff00">&nbsp;24 </font><font color="#ff6060">&nbsp;*初始化一个链表头</font><br />
<font color="#00ff00">&nbsp;25 </font><font color="#ff6060">&nbsp;*链表头不同于链表节点，在实际分配中，链表头存放的是边界范围等一些概括信息</font><br />
<font color="#00ff00">&nbsp;26 </font><font color="#ff6060">&nbsp;*</font><br />
<font color="#00ff00">&nbsp;27 </font><font color="#ff6060">&nbsp;</font><font color="#ff6060">*/</font><br />
<font color="#00ff00">&nbsp;28 </font><font color="#00ff00">static</font>&nbsp;<font color="#00ff00">struct</font>&nbsp;arm_vm_region consistent_head = {<font color="#ff6060">//初始化一个链表头</font><br />
<font color="#00ff00">&nbsp;29 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.vm_list&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= LIST_HEAD_INIT(consistent_head.vm_list),<br />
<font color="#00ff00">&nbsp;30 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.vm_start&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = CONSISTENT_BASE,<br />
<font color="#00ff00">&nbsp;31 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.vm_end&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = CONSISTENT_END,<br />
<font color="#00ff00">&nbsp;32 </font>};<br />
<font color="#00ff00">&nbsp;33 </font><br />
<font color="#00ff00">&nbsp;34 </font><font color="#00ff00">static</font>&nbsp;<font color="#00ff00">struct</font>&nbsp;arm_vm_region * arm_vm_region_alloc(<font color="#00ff00">struct</font>&nbsp;arm_vm_region *head, <font color="#00ff00">size_t</font>&nbsp;size, gfp_t gfp)<br />
<font color="#00ff00">&nbsp;35 </font>{<br />
<font color="#00ff00">&nbsp;36 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ff6060">//head头放的是区间总体范围</font><br />
<font color="#00ff00">&nbsp;37 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">unsigned</font>&nbsp;<font color="#00ff00">long</font>&nbsp;addr = head-&gt;vm_start, end = head-&gt;vm_end &#8211; size;<br />
<font color="#00ff00">&nbsp;38 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">unsigned</font>&nbsp;<font color="#00ff00">long</font>&nbsp;flags;<br />
<font color="#00ff00">&nbsp;39 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">struct</font>&nbsp;arm_vm_region *c, *new;<br />
<font color="#00ff00">&nbsp;40 </font><br />
<font color="#00ff00">&nbsp;41 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new = xmalloc(<font color="#00ff00">struct</font>&nbsp;arm_vm_region);<br />
<font color="#00ff00">&nbsp;42 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">if</font>&nbsp;(!new)<br />
<font color="#00ff00">&nbsp;43 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">goto</font>&nbsp;out;<br />
<font color="#00ff00">&nbsp;44 </font><br />
<font color="#00ff00">&nbsp;45 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spin_lock_irqsave(&amp;consistent_lock, flags);<br />
<font color="#00ff00">&nbsp;46 </font><br />
<font color="#00ff00">&nbsp;47 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ff6060">/*</font><br />
<font color="#00ff00">&nbsp;48 </font><font color="#ff6060">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *遍历一下链表的各个节点，寻找合适的区间范围</font><br />
<font color="#00ff00">&nbsp;49 </font><font color="#ff6060">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *list_for_each_entry宏是一个关键，其实是一个for循环，只不过设计的比较巧妙，依靠了链表的结构</font><br />
<font color="#00ff00">&nbsp;50 </font><font color="#ff6060">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ＊其具体定义在下面</font><br />
<font color="#00ff00">&nbsp;51 </font><font color="#ff6060">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="#ff6060">*/</font><br />
<font color="#00ff00">&nbsp;52 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list_for_each_entry(c, &amp;head-&gt;vm_list, vm_list) {<br />
<font color="#00ff00">&nbsp;53 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">if</font>&nbsp;((addr + size) &lt; addr)<font color="#ff6060">//越界</font><br />
<font color="#00ff00">&nbsp;54 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">goto</font>&nbsp;nospc;<br />
<font color="#00ff00">&nbsp;55 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">if</font>&nbsp;((addr + size) &lt;= c-&gt;vm_start)<font color="#ff6060">//可以夹到vm_region块之间</font><br />
<font color="#00ff00">&nbsp;56 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">goto</font>&nbsp;found;<br />
<font color="#00ff00">&nbsp;57 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addr = c-&gt;vm_end;<font color="#ff6060">//addr 为下一个的结束地址</font><br />
<font color="#00ff00">&nbsp;58 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">if</font>&nbsp;(addr &gt; end)<br />
<font color="#00ff00">&nbsp;59 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">goto</font>&nbsp;nospc;<br />
<font color="#00ff00">&nbsp;60 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<font color="#00ff00">&nbsp;61 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ff6060">/*</font><br />
<font color="#00ff00">&nbsp;62 </font><font color="#ff6060">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *注意，若上面的链表仅仅有一个头的话，没有问题，直接到下面这个found就ok了。</font><br />
<font color="#00ff00">&nbsp;63 </font><font color="#ff6060">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="#ff6060">*/</font><br />
<font color="#00ff00">&nbsp;64 </font>&nbsp;<font color="#ffff00">found</font>:<br />
<font color="#00ff00">&nbsp;65 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ff6060">/*</font><br />
<font color="#00ff00">&nbsp;66 </font><font color="#ff6060">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Insert this entry _before_ the one we found.</font><br />
<font color="#00ff00">&nbsp;67 </font><font color="#ff6060">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="#ff6060">*/</font><br />
<font color="#00ff00">&nbsp;68 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list_add_tail(&amp;new-&gt;vm_list, &amp;c-&gt;vm_list);<br />
<font color="#00ff00">&nbsp;69 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new-&gt;vm_start = addr;<br />
<font color="#00ff00">&nbsp;70 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new-&gt;vm_end = addr + size;<br />
<font color="#00ff00">&nbsp;71 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new-&gt;vm_active = <font color="#ff40ff">1</font>;<br />
<font color="#00ff00">&nbsp;72 </font><br />
<font color="#00ff00">&nbsp;73 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spin_unlock_irqrestore(&amp;consistent_lock, flags);<br />
<font color="#00ff00">&nbsp;74 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">return</font>&nbsp;new;<br />
<font color="#00ff00">&nbsp;75 </font><br />
<font color="#00ff00">&nbsp;76 </font>&nbsp;<font color="#ffff00">nospc</font>:<br />
<font color="#00ff00">&nbsp;77 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spin_unlock_irqrestore(&amp;consistent_lock, flags);<br />
<font color="#00ff00">&nbsp;78 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xfree(new);<br />
<font color="#00ff00">&nbsp;79 </font>&nbsp;<font color="#ffff00">out</font>:<br />
<font color="#00ff00">&nbsp;80 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">return</font>&nbsp;<font color="#ff40ff">NULL</font>;<br />
<font color="#00ff00">&nbsp;81 </font>}<br />
<font color="#00ff00">&nbsp;82 </font><br />
<font color="#00ff00">&nbsp;83 </font><font color="#ff6060">/*</font><font color="#ff6060">*</font><br />
<font color="#00ff00">&nbsp;84 </font><font color="#ff6060">&nbsp;* list_for_each_entry&nbsp;&nbsp;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iterate over list of given type</font><br />
<font color="#00ff00">&nbsp;85 </font><font color="#ff6060">&nbsp;* @pos:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the type * to use as a loop counter.</font><br />
<font color="#00ff00">&nbsp;86 </font><font color="#ff6060">&nbsp;* @head:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; the head for your list.</font><br />
<font color="#00ff00">&nbsp;87 </font><font color="#ff6060">&nbsp;* @member:&nbsp;&nbsp;&nbsp;&nbsp; the name of the list_struct within the struct.</font><br />
<font color="#00ff00">&nbsp;88 </font><font color="#ff6060">&nbsp;</font><font color="#ff6060">*/</font><br />
<font color="#00ff00">&nbsp;89 </font><font color="#ff40ff">#define list_for_each_entry(pos, head, member)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</font><br />
<font color="#00ff00">&nbsp;90 </font><font color="#ff40ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#ffff00">for</font><font color="#ff40ff">&nbsp;(pos = list_entry((head)-&gt;next, typeof(*pos), member);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</font><br />
<font color="#00ff00">&nbsp;91 </font><font color="#ff40ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;pos-&gt;member != (head);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</font><br />
<font color="#00ff00">&nbsp;92 </font><font color="#ff40ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos = list_entry(pos-&gt;member.next, typeof(*pos), member))</font><br />
<font color="#00ff00">&nbsp;93 </font><br />
<font color="#00ff00">&nbsp;94 </font><font color="#ff6060">/*</font><font color="#ff6060">*</font><br />
<font color="#00ff00">&nbsp;95 </font><font color="#ff6060">&nbsp;* list_entry &#8211; get the struct for this entry</font><br />
<font color="#00ff00">&nbsp;96 </font><font color="#ff6060">&nbsp;* @ptr:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the &amp;struct list_head pointer.</font><br />
<font color="#00ff00">&nbsp;97 </font><font color="#ff6060">&nbsp;* @type:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; the type of the struct this is embedded in.</font><br />
<font color="#00ff00">&nbsp;98 </font><font color="#ff6060">&nbsp;* @member:&nbsp;&nbsp;&nbsp;&nbsp; the name of the list_struct within the struct.</font><br />
<font color="#00ff00">&nbsp;99 </font><font color="#ff6060">&nbsp;</font><font color="#ff6060">*/</font><br />
<font color="#00ff00">100 </font><font color="#ff40ff">#define list_entry(ptr, type, member) \</font><br />
<font color="#00ff00">101 </font><font color="#ff40ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((type *)((</font><font color="#00ff00">char</font><font color="#ff40ff">&nbsp;*)(ptr)-(</font><font color="#00ff00">unsigned</font><font color="#ff40ff">&nbsp;</font><font color="#00ff00">long</font><font color="#ff40ff">)(&amp;((type *)</font><font color="#ff40ff">0</font><font color="#ff40ff">)-&gt;member)))</font><br />
<font color="#00ff00">102 </font><br />
<font color="#00ff00">103 </font><font color="#ff6060">/*</font><br />
<font color="#00ff00">104 </font><font color="#ff6060">&nbsp;&nbsp; **********************************************************************************************</font><br />
<font color="#00ff00">105 </font><font color="#ff6060">&nbsp;</font><font color="#ff6060">*/</font><br />
<font color="#00ff00">106 </font><font color="#00ff00">static</font>&nbsp;<font color="#00ff00">void</font>&nbsp;* __dma_alloc(<font color="#ff6060">/*</font><font color="#ff6060">struct device *dev,</font><font color="#ff6060">*/</font><font color="#00ff00">size_t</font>&nbsp;size, dma_addr_t *handle<font color="#ff6060">/*</font><font color="#ff6060">, gfp_t gfp</font><font color="#ff6060">*/</font>,pgprot_t prot)<br />
<font color="#00ff00">107 </font>{<br />
<font color="#00ff00">108 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">struct</font>&nbsp;page_info *page;<br />
<font color="#00ff00">109 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">struct</font>&nbsp;arm_vm_region *c;<br />
<font color="#00ff00">110 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">unsigned</font>&nbsp;<font color="#00ff00">long</font>&nbsp;order;<br />
<font color="#00ff00">111 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">unsigned</font>&nbsp;<font color="#00ff00">long</font>&nbsp;flags;<br />
<font color="#00ff00">112 </font><br />
<font color="#00ff00">113 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ff6060">//若没有初始化，就&#8230;</font><br />
<font color="#00ff00">114 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">if</font>&nbsp;(!consistent_pte[<font color="#ff40ff">0</font>]) {<br />
<font color="#00ff00">115 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ERR <font color="#ff40ff">&quot;</font><font color="#ffff00">%s</font><font color="#ff40ff">: not initialised</font><font color="#ffff00">\n</font><font color="#ff40ff">&quot;</font>, <font color="#ff40ff">__func__</font>);<br />
<font color="#00ff00">116 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">return</font>&nbsp;<font color="#ff40ff">NULL</font>;<br />
<font color="#00ff00">117 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<font color="#00ff00">118 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size = round_pgup(size);<br />
<font color="#00ff00">119 </font><br />
<font color="#00ff00">120 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ff6060">//若分配的空间太大了，超过了，那么就&#8230;</font><br />
<font color="#00ff00">121 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">if</font>&nbsp;(<br />
<font color="#00ff00">122 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size &gt;= (CONSISTENT_END &#8211; CONSISTENT_BASE)) {<br />
<font color="#00ff00">123 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_WARNING <font color="#ff40ff">&quot;coherent allocation too big &quot;</font><br />
<font color="#00ff00">124 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#ff40ff">&quot;(requested </font><font color="#ffff00">%#x</font><font color="#ff40ff">)</font><font color="#ffff00">\n</font><font color="#ff40ff">&quot;</font>, size);<br />
<font color="#00ff00">125 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">goto</font>&nbsp;no_page;<br />
<font color="#00ff00">126 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<font color="#00ff00">127 </font><br />
<font color="#00ff00">128 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ff6060">//转换为伙伴算法的尺寸</font><br />
<font color="#00ff00">129 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;order = get_order_from_bytes(size);<br />
<font color="#00ff00">130 </font><br />
<font color="#00ff00">131 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local_irq_save(flags);<br />
<font color="#00ff00">132 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;page = alloc_heap_pages(<font color="#ff40ff">0</font>, order);<font color="#ff6060">//分配的是实际的页</font><br />
<font color="#00ff00">133 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local_irq_restore(flags);<br />
<font color="#00ff00">134 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">if</font>&nbsp;(!page)<br />
<font color="#00ff00">135 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">goto</font>&nbsp;no_page;<br />
<font color="#00ff00">136 </font><br />
<font color="#00ff00">137 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c = arm_vm_region_alloc(&amp;consistent_head, size,<br />
<font color="#00ff00">138 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#ff6060">/*</font><font color="#ff6060">&nbsp;gfp &amp;</font><font color="#ff6060">*/</font>&nbsp;~(__GFP_DMA | __GFP_HIGHMEM));<font color="#ff6060">//分配的是页框</font><br />
<font color="#00ff00">139 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">if</font>&nbsp;(c) {<br />
<font color="#00ff00">140 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pte_t *pte;<br />
<font color="#00ff00">141 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">struct</font>&nbsp;page_info *end = page + (<font color="#ff40ff">1</font>&nbsp;&lt;&lt; order);<br />
<font color="#00ff00">142 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">int</font>&nbsp;idx = CONSISTENT_PTE_INDEX(c-&gt;vm_start);<br />
<font color="#00ff00">143 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u32 off = CONSISTENT_OFFSET(c-&gt;vm_start) &amp; (PTRS_PER_PTE-<font color="#ff40ff">1</font>);<br />
<font color="#00ff00">144 </font><br />
<font color="#00ff00">145 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pte = consistent_pte[idx] + off;<br />
<font color="#00ff00">146 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c-&gt;vm_pages = page;<br />
<font color="#00ff00">147 </font><br />
<font color="#00ff00">148 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*handle = page_to_phys(page);<font color="#ff6060">//page_to_dma(/*dev,*/ page);</font><br />
<font color="#00ff00">149 </font><br />
<font color="#00ff00">150 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">do</font>&nbsp;{<br />
<font color="#00ff00">151 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*pte = l1e_from_paddr(page_to_phys(page), prot);<font color="#ff6060">//mk_pte(page, prot);</font><br />
<font color="#00ff00">152 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<font color="#00ff00">153 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;page++;<br />
<font color="#00ff00">154 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pte++;<br />
<font color="#00ff00">155 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;off++;<br />
<font color="#00ff00">156 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">if</font>&nbsp;(off &gt;= PTRS_PER_PTE) {<br />
<font color="#00ff00">157 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;off = <font color="#ff40ff">0</font>;<br />
<font color="#00ff00">158 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cpu_flush_cache_page((<font color="#00ff00">unsigned</font>&nbsp;<font color="#00ff00">long</font>)consistent_pte[idx]);<br />
<font color="#00ff00">159 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pte = consistent_pte[++idx];<br />
<font color="#00ff00">160 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<font color="#00ff00">161 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <font color="#ffff00">while</font>&nbsp;(size -= PAGE_SIZE);<br />
<font color="#00ff00">162 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cpu_flush_cache_page((<font color="#00ff00">unsigned</font>&nbsp;<font color="#00ff00">long</font>)consistent_pte[idx]);<br />
<font color="#00ff00">163 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ff6060">/*</font><br />
<font color="#00ff00">164 </font><font color="#ff6060">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Free the otherwise unused pages.</font><br />
<font color="#00ff00">165 </font><font color="#ff6060">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="#ff6060">*/</font><br />
<font color="#00ff00">166 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">while</font>&nbsp;(page &lt; end) {<font color="#ff6060">//若分配的页数没有全部被映射，于是释放掉没有被映射的页数</font><br />
<font color="#00ff00">167 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;free_heap_pages(<font color="#ff40ff">0</font><font color="#ff6060">/*</font><font color="#ff6060">MEMZONE_DMADOM</font><font color="#ff6060">*/</font>,page,order);<br />
<font color="#00ff00">168 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;page++;<br />
<font color="#00ff00">169 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<font color="#00ff00">170 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">return</font>&nbsp;(<font color="#00ff00">void</font>&nbsp;*)c-&gt;vm_start;<br />
<font color="#00ff00">171 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<font color="#00ff00">172 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ff6060">//如果没有分配vm成功的话，那么就free</font><br />
<font color="#00ff00">173 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">if</font>&nbsp;(page)<br />
<font color="#00ff00">174 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;free_heap_pages(<font color="#ff40ff">0</font><font color="#ff6060">/*</font><font color="#ff6060">MEMZONE_DMADOM</font><font color="#ff6060">*/</font>,page,order);<br />
<font color="#00ff00">175 </font>&nbsp;<font color="#ffff00">no_page</font>:<br />
<font color="#00ff00">176 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printk(<font color="#ff40ff">&quot;No enough memory for DMA</font><font color="#ffff00">\n</font><font color="#ff40ff">&quot;</font>);<br />
<font color="#00ff00">177 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*handle = ~<font color="#ff40ff">0</font>;<br />
<font color="#00ff00">178 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">return</font>&nbsp;<font color="#ff40ff">NULL</font>;<br />
<font color="#00ff00">179 </font>}<br />
<font color="#00ff00">180 </font><br />
<font color="#00ff00">181 </font>pgprot_t pgprot_kernel;<br />
<font color="#00ff00">182 </font><font color="#ff6060">/*</font><br />
<font color="#00ff00">183 </font><font color="#ff6060">&nbsp;* Allocate a writecombining region, in much the same way as</font><br />
<font color="#00ff00">184 </font><font color="#ff6060">&nbsp;* dma_alloc_coherent above.</font><br />
<font color="#00ff00">185 </font><font color="#ff6060">&nbsp;</font><font color="#ff6060">*/</font><br />
<font color="#00ff00">186 </font><font color="#00ff00">void</font>&nbsp;*<br />
<font color="#00ff00">187 </font>dma_alloc_writecombine(<font color="#ff6060">/*</font><font color="#ff6060">struct device *dev, </font><font color="#ff6060">*/</font><font color="#00ff00">size_t</font>&nbsp;size, dma_addr_t *handle<font color="#ff6060">/*</font><font color="#ff6060">, gfp_t gfp</font><font color="#ff6060">*/</font>)<br />
<font color="#00ff00">188 </font>{<br />
<font color="#00ff00">189 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">return</font>&nbsp;__dma_alloc(<font color="#ff6060">/*</font><font color="#ff6060">dev, </font><font color="#ff6060">*/</font>size, handle, <font color="#ff6060">/*</font><font color="#ff6060">gfp,</font><font color="#ff6060">*/</font><br />
<font color="#00ff00">190 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pgprot_writecombine(pgprot_kernel));<br />
<font color="#00ff00">191 </font>}<br />
<font color="#00ff00">192 </font><br />
<font color="#00ff00">193 </font><font color="#ff6060">/*</font><br />
<font color="#00ff00">194 </font><font color="#ff6060">&nbsp;* Initialise the consistent memory allocation.</font><br />
<font color="#00ff00">195 </font><font color="#ff6060">&nbsp;* 先把pgd给初始化了</font><br />
<font color="#00ff00">196 </font><font color="#ff6060">&nbsp;</font><font color="#ff6060">*/</font><br />
<font color="#00ff00">197 </font><font color="#00ff00">int</font>&nbsp;consistent_init(<font color="#00ff00">void</font>)<br />
<font color="#00ff00">198 </font>{<br />
<font color="#00ff00">199 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pde_t *pgd;<br />
<font color="#00ff00">200 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pte_t *pte;<br />
<font color="#00ff00">201 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">void</font>&nbsp;*pt;<br />
<font color="#00ff00">202 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">int</font>&nbsp;ret = <font color="#ff40ff">0</font>, i = <font color="#ff40ff">0</font>;<br />
<font color="#00ff00">203 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u32 base = CONSISTENT_BASE;<br />
<font color="#00ff00">204 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pgprot_kernel = PTE_TYPE_SMALL<font color="#ff6060">/*</font><font color="#ff6060">&nbsp;| PTE_BUFFERABLE | PTE_CACHEABLE</font><font color="#ff6060">*/</font>&nbsp;| PTE_SMALL_AP_UNO_SRW;<font color="#ff6060">//L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE | L_PTE_EXEC;</font><br />
<font color="#00ff00">205 </font><br />
<font color="#00ff00">206 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">do</font>&nbsp;{<br />
<font color="#00ff00">207 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pt = alloc_xenheap_page();<font color="#ff6060">//pte_alloc_kernel(pgd, base);</font><br />
<font color="#00ff00">208 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;clear_page(pt);<br />
<font color="#00ff00">209 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;idle_pg_table[pgd_index(base)] =l2e_from_page(virt_to_page(pt), PMD_TYPE_TABLE | PMD_DOMAIN(DOMAIN_IO));<br />
<font color="#00ff00">210 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cpu_flush_cache_page((<font color="#00ff00">unsigned</font>&nbsp;<font color="#00ff00">long</font>) &amp;idle_pg_table[pgd_index(base)]);<br />
<font color="#00ff00">211 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">if</font>&nbsp;(!pt) {<br />
<font color="#00ff00">212 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ERR <font color="#ff40ff">&quot;</font><font color="#ffff00">%s</font><font color="#ff40ff">: no pte tables</font><font color="#ffff00">\n</font><font color="#ff40ff">&quot;</font>, <font color="#ff40ff">__func__</font>);<br />
<font color="#00ff00">213 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret = -<font color="#ff40ff">ENOMEM</font>;<br />
<font color="#00ff00">214 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">break</font>;<br />
<font color="#00ff00">215 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<font color="#00ff00">216 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ff6060">//这个数组，是为了方便以后分配dma页的时候，建立页表</font><br />
<font color="#00ff00">217 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;consistent_pte[i++] = pt;<br />
<font color="#00ff00">218 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base += (<font color="#ff40ff">1</font>&nbsp;&lt;&lt; PGDIR_SHIFT);<br />
<font color="#00ff00">219 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <font color="#ffff00">while</font>&nbsp;(base &lt; CONSISTENT_END);<br />
<font color="#00ff00">220 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printk(<font color="#ff40ff">&quot;consistent init ok</font><font color="#ffff00">\n</font><font color="#ff40ff">&quot;</font>);<br />
<font color="#00ff00">221 </font><br />
<font color="#00ff00">222 </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ffff00">return</font>&nbsp;ret;<br />
<font color="#00ff00">223 </font>}<br />
</font></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/12/14/like-linux-link/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ARM Domain中的域管理简述</title>
		<link>http://www.tek-life.org/2010/12/06/arm-domain%e4%b8%ad%e7%9a%84%e5%9f%9f%e7%ae%a1%e7%90%86%e7%ae%80%e8%bf%b0/</link>
		<comments>http://www.tek-life.org/2010/12/06/arm-domain%e4%b8%ad%e7%9a%84%e5%9f%9f%e7%ae%a1%e7%90%86%e7%ae%80%e8%bf%b0/#comments</comments>
		<pubDate>Mon, 06 Dec 2010 02:45:25 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[EmbeddedXEN]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10484</guid>
		<description><![CDATA[ 1 /* 2  *ARM Domain中的域管理简述  3  *在ARM中，没有像x86那样的ring0,ring1,ring2,ring3的机制,那么它的安全管理是通过CP15协处理器的c3寄存器以及页目录表项来联合进行安全管理的。  4  *本文试图去简要分析一下在ARM体系结构中CP15协处理器中C3寄存器是如何进行安全管理的。另外关于页目录表项中的AP位也附带介绍。  5  *由于鄙人水平有限，分析不当的地方，望各位达人拍砖。  6  *  7  */  8 /*  9  先看一下一级页目录表项中的描述符 10 31                20            12 1110  9  8        5  4  3 2 1 0 11  &#124;&#8212;&#8212;段基址&#8212;&#8211;&#124;&#8212;&#8211;SBZ&#8212;&#8212;&#124;-AP-&#124;SBZ&#124;&#8211;Domain&#8211;&#124;IMP&#124;C&#124;B&#124;&#8212;&#124; 12 13 段基址  :下一级描述符的基地址. 14 SBZ     :是should be zero. 15 AP      :Access Permissions. 16 Domain  :即是本文所述的重点了。 &#8230; <a href="http://www.tek-life.org/2010/12/06/arm-domain%e4%b8%ad%e7%9a%84%e5%9f%9f%e7%ae%a1%e7%90%86%e7%ae%80%e8%bf%b0/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><span style="font-family: monospace;"><span style="color: #00ff00;"> 1 </span><span style="color: #ff6060;">/*</span></span><br />
<span style="font-family: monospace;"></span><span style="font-family: monospace;"><span style="color: #00ff00;">2 </span><span style="color: #ff6060;"> *ARM Domain中的域管理简述</span></span><br />
<span style="color: #00ff00;"> 3 </span><span style="color: #ff6060;"> *在ARM中，没有像x86那样的ring0,ring1,ring2,ring3的机制,那么它的安全管理是通过CP15协处理器的c3寄存器以及页目录表项来联合进行安全管理的。</span><br />
<span style="color: #00ff00;"> 4 </span><span style="color: #ff6060;"> *本文试图去简要分析一下在ARM体系结构中CP15协处理器中C3寄存器是如何进行安全管理的。另外关于页目录表项中的AP位也附带介绍。</span><br />
<span style="color: #00ff00;"> 5 </span><span style="color: #ff6060;"> *由于鄙人水平有限，分析不当的地方，望各位达人拍砖。</span><br />
<span style="color: #00ff00;"> 6 </span><span style="color: #ff6060;"> *</span><br />
<span style="color: #00ff00;"> 7 </span><span style="color: #ff6060;"> </span><span style="color: #ff6060;">*/</span><br />
<span style="color: #00ff00;"> 8 </span><span style="color: #ff6060;">/*</span><br />
<span style="color: #00ff00;"> 9 </span><span style="color: #ff6060;"> 先看一下一级页目录表项中的描述符</span><br />
<span style="color: #00ff00;">10 </span><span style="color: #ff6060;">31                20            12 1110  9  8        5  4  3 2 1 0</span><br />
<span style="color: #00ff00;">11 </span><span style="color: #ff6060;"> |&#8212;&#8212;段基址&#8212;&#8211;|&#8212;&#8211;SBZ&#8212;&#8212;|-AP-|SBZ|&#8211;Domain&#8211;|IMP|C|B|&#8212;|</span><br />
<span style="color: #00ff00;">12 </span><br />
<span style="color: #00ff00;">13 </span><span style="color: #ff6060;">段基址  :下一级描述符的基地址.</span><br />
<span style="color: #00ff00;">14 </span><span style="color: #ff6060;">SBZ     :是should be zero.</span><br />
<span style="color: #00ff00;">15 </span><span style="color: #ff6060;">AP      :Access Permissions.</span><br />
<span style="color: #00ff00;">16 </span><span style="color: #ff6060;">Domain  :即是本文所述的重点了。</span><br />
<spa style="color: #00ff00;">17 </span><span style="color: #ff6060;">IMP     :Implementation defined.</span><br />
<span style="color: #00ff00;">18 </span><span style="color: #ff6060;">C       :cachable</span><br />
<span style="color: #00ff00;">19 </span><span style="color: #ff6060;">B       :bufferable</span><br />
<span style="color: #00ff00;">20 </span><span style="color: #ff6060;">*/</span><br />
<span style="color: #00ff00;">21 </span><span style="color: #ff6060;">/*</span><br />
<span style="color: #00ff00;">22 </span><span style="color: #ff6060;"> 在ARM体系结构中，硬件安全检查的优先顺序是，先检查domain,在检查AP和cp15中的c1，确定访问权限</span><br />
<span style="color: #00ff00;">23 </span><br />
<span style="color: #00ff00;">24 </span><span style="color: #ff6060;"> 大致的思路是：现在来了一个虚拟地址　VA,根据va,mmu工作，查到了一级页描述符，然后查一下DOM,这里domain占４位，对于２进制来说，即是可以表示１６个数，假若目前描述符中的domain是12,也就是1100，那么MMU就去看一下CP15中的c3寄存器的第12*2-1和12*2两位（c3一共是３２位，每一个domain是用２位）,如果是01呢？如果是01的话，接着要继续去看AP和C1了。</span><br />
<span style="color: #00ff00;">25 </span><span style="color: #ff6060;"> 这就是c3的工作思路了。其实很简单。就是我现把第０～１５个域对应的权限安排好，然后就按照程序的需求，由程序员将０～１５个域的序号按照需要填入描述符表中。MMU在工作的时候，查到域序号后，再到C3中按照查到的域序号去看权限位是00,还是01,还是10,还是11。根据不同的权限位，决定是否去看AP位。</span><br />
<span style="color: #00ff00;">26 </span><span style="color: #ff6060;"> 这个道理就像生活中的事情：小弟有个事情需要跟老板请假，先跟大老板说，大老板如果心情好，就说恩，可以，这样小弟的请假就通过了。倘若，大老板心情不高兴，但碍于面子，就说，你这个事情看看组里面同意不，于是小弟就需要跟组里面和小老板去商量，看他们是否同意请假。大老板就是Manger，小老板和组里面的就算是Client了。其实在ARM的c3机制中也只定义了这两个有效的权限级别:01和11,倘若出现00和10，则会抛出异常来。01对应的是Client，11对应的是Manger.</span><br />
<span style="color: #00ff00;">27 </span><br />
<span style="color: #00ff00;">28 </span><span style="color: #ff6060;"> 下面拿一个例子来说明</span><br />
<span style="color: #00ff00;">29 </span><span style="color: #ff6060;"> </span><span style="color: #ff6060;">*/</span><br />
<span style="color: #00ff00;">30 </span><span style="color: #ff6060;">/*</span><br />
<span style="color: #00ff00;">31 </span><span style="color: #ff6060;"> *定义了４个DOMAIN,和对应的c3</span><br />
<span style="color: #00ff00;">32 </span><span style="color: #ff6060;"> </span><span style="color: #ff6060;">*/</span><br />
<span style="color: #00ff00;">33 </span>        mov     r5, #(DOMAIN_VALUE(DOMAIN_SUPERVISOR, DOMAIN_MANAGER)   | \<br />
<span style="color: #00ff00;">34 </span>                      DOMAIN_VALUE(DOMAIN_HYPERVISOR, DOMAIN_MANAGER)   | \<br />
<span style="color: #00ff00;">35 </span>                      DOMAIN_VALUE(DOMAIN_USER, DOMAIN_MANAGER)         | \<br />
<span style="color: #00ff00;">36 </span>                      DOMAIN_VALUE(DOMAIN_IO, DOMAIN_CLIENT))<br />
<span style="color: #00ff00;">37 </span><br />
<span style="color: #00ff00;">38 </span>        mcr     p15, <span style="color: #ff40ff;">0</span>, r5, c3, c0, <span style="color: #ff40ff;">0</span>   @ Load DAC<br />
<span style="color: #00ff00;">39 </span><br />
<span style="color: #00ff00;">40 </span><br />
<span style="color: #00ff00;">41 </span><span style="color: #ff6060;">/*</span><br />
<span style="color: #00ff00;">42 </span><span style="color: #ff6060;"> *将具体的域描述符安装到PDE中</span><br />
<span style="color: #00ff00;">43 </span><span style="color: #ff6060;"> </span><span style="color: #ff6060;">*/</span><br />
<span style="color: #00ff00;">44 </span><br />
<span style="color: #00ff00;">45 </span><span style="color: #ffff00;">mem_map_table</span>:<br />
<span style="color: #00ff00;">46 </span>        MAP_ENTRY(<span style="color: #ff40ff;">0xC0000000</span>, <span style="color: #ff40ff;">0xC0000000</span>, <span style="color: #ff40ff;">64</span>, PDE_TYPE_HYPERVISOR)<br />
<span style="color: #00ff00;">47 </span>        MAP_ENTRY(<span style="color: #ff40ff;">0xFF000000</span>, <span style="color: #ff40ff;">0xC0000000</span>, <span style="color: #ff40ff;">2</span>, PDE_TYPE_HYPERVISOR)<br />
<span style="color: #00ff00;">48 </span>        MAP_ENTRY(<span style="color: #ff40ff;">0xE0000000</span>, <span style="color: #ff40ff;">0&#215;10000000</span>, <span style="color: #ff40ff;">1</span>, PDE_TYPE_IO)<br />
<span style="color: #00ff00;">49 </span>        MAP_ENTRY(<span style="color: #ff40ff;">0</span>,<span style="color: #ff40ff;">0</span>,<span style="color: #ff40ff;">0</span>,<span style="color: #ff40ff;">0</span>)<br />
<span style="color: #00ff00;">50 </span><span style="color: #ff6060;">/*</span><br />
<span style="color: #00ff00;">51 </span><span style="color: #ff6060;"> *具体的宏定义</span><br />
<span style="color: #00ff00;">52 </span><span style="color: #ff6060;"> </span><span style="color: #ff6060;">*/</span><br />
<span style="color: #00ff00;">53 </span><span style="color: #ff40ff;">#define PDE_AP_SRW_UNO          (</span><span style="color: #ff40ff;">0&#215;01</span><span style="color: #ff40ff;"> &lt;&lt; </span><span style="color: #ff40ff;">10</span><span style="color: #ff40ff;">)</span><br />
<span style="color: #00ff00;">54 </span><span style="color: #ff40ff;">#define PDE_AP_SRW_URO          (</span><span style="color: #ff40ff;">0&#215;02</span><span style="color: #ff40ff;"> &lt;&lt; </span><span style="color: #ff40ff;">10</span><span style="color: #ff40ff;">)</span><br />
<span style="color: #00ff00;">55 </span><span style="color: #ff40ff;">#define PDE_AP_SRW_URW          (</span><span style="color: #ff40ff;">0&#215;03</span><span style="color: #ff40ff;"> &lt;&lt; </span><span style="color: #ff40ff;">10</span><span style="color: #ff40ff;">)</span><br />
<span style="color: #00ff00;">56 </span><br />
<span style="color: #00ff00;">57 </span><span style="color: #ff40ff;">#define PDE_BUFFERABLE                  (</span><span style="color: #ff40ff;">0&#215;04</span><span style="color: #ff40ff;">)</span><br />
<span style="color: #00ff00;">58 </span><span style="color: #ff40ff;">#define PDE_CACHEABLE                   (</span><span style="color: #ff40ff;">0&#215;08</span><span style="color: #ff40ff;">)</span><br />
<span style="color: #00ff00;">59 </span><br />
<span style="color: #00ff00;">60 </span><span style="color: #ff40ff;">#define PDE_WRITEBACK                   (PDE_CACHEABLE | PDE_BUFFERABLE)</span><br />
<span style="color: #00ff00;">61 </span><span style="color: #ff40ff;">#define PDE_WRITETHROUGH                (PDE_CACHEABLE)</span><br />
<span style="color: #00ff00;">62 </span><span style="color: #ff40ff;">#define PDE_SHARED                              (</span><span style="color: #ff40ff;">0</span><span style="color: #ff40ff;">)</span><br />
<span style="color: #00ff00;">63 </span><br />
<span style="color: #00ff00;">64 </span><span style="color: #ff40ff;">#define PDE_DOMAIN_HYPERVISOR   (DOMAIN_HYPERVISOR &lt;&lt; </span><span style="color: #ff40ff;">5</span><span style="color: #ff40ff;">)</span><span style="color: #ff6060;">//00b&#8211;0</span><br />
<span style="color: #00ff00;">65 </span><span style="color: #ff40ff;">#define PDE_DOMAIN_SUPERVISOR   (DOMAIN_SUPERVISOR &lt;&lt; </span><span style="color: #ff40ff;">5</span><span style="color: #ff40ff;">)</span><span style="color: #ff6060;">//01b&#8211;1</span><br />
<span style="color: #00ff00;">66 </span><span style="color: #ff40ff;">#define PDE_DOMAIN_USER         (DOMAIN_USER &lt;&lt; </span><span style="color: #ff40ff;">5</span><span style="color: #ff40ff;">)      </span><span style="color: #ff6060;">//11b&#8211;3</span><br />
<span style="color: #00ff00;">67 </span><span style="color: #ff40ff;">#define PDE_DOMAIN_IO           (DOMAIN_IO &lt;&lt; </span><span style="color: #ff40ff;">5</span><span style="color: #ff40ff;">)        </span><span style="color: #ff6060;">//10b&#8211;2</span><br />
<span style="color: #00ff00;">68 </span><br />
<span style="color: #00ff00;">69 </span><span style="color: #ff40ff;">#define PDE_TYPE_HYPERVISOR             (PDE_TYPE_SECTION | PDE_DOMAIN_HYPERVISOR | PDE_AP_SRW_UNO | PDE_WRITEBACK)</span><br />
<span style="color: #00ff00;">70 </span><span style="color: #ff40ff;">#define PDE_TYPE_IO                             (PDE_TYPE_SECTION | PDE_DOMAIN_IO | PDE_AP_SRW_URW)</span></p>
<p><span style="color: #00ff00;">71 </span><span style="color: #ff6060;">/*</span><br />
<span style="color: #00ff00;">72 </span><span style="color: #ff6060;"> 至此，我可能仍然没有把问题说清楚，如果仍有不明白的地方，欢迎讨论。</span><br />
<span style="color: #00ff00;">73 </span><span style="color: #ff6060;"> 如果说的有错误，希望得到纠正。</span><br />
<span style="color: #00ff00;">74 </span><span style="color: #ff6060;"> </span><span style="color: #ff6060;">*/</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/12/06/arm-domain%e4%b8%ad%e7%9a%84%e5%9f%9f%e7%ae%a1%e7%90%86%e7%ae%80%e8%bf%b0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Android on OKL4 Tutorials</title>
		<link>http://www.tek-life.org/2010/11/14/android-on-okl4-tutorials-2/</link>
		<comments>http://www.tek-life.org/2010/11/14/android-on-okl4-tutorials-2/#comments</comments>
		<pubDate>Sun, 14 Nov 2010 04:16:14 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/11/14/android-on-okl4-tutorials-2/</guid>
		<description><![CDATA[可能在博客里面xml内容显示不正常，移步docs.google，查看 http://docs.google.com/View?id=df6sncnm_899k8tqgng5 Android on OKL4 OKL4 3.0 was originally ported to run on the HTC Dream (found here). A student, Michael Hills, developed a rudimentary Android port that removes Linux and runs natively on OKL4 3.0 for his undergraduate thesis. &#8230; <a href="http://www.tek-life.org/2010/11/14/android-on-okl4-tutorials-2/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>可能在博客里面xml内容显示不正常，移步docs.google，查看 <a class="tabcontent" href="http://docs.google.com/View?id=df6sncnm_899k8tqgng5" id="publishedDocumentUrl" target="_blank"><font color="#112abb">http://docs.google.com/View?id=df6sncnm_899k8tqgng5</font></a></p>
<h1>Android on OKL4</h1>
<p>OKL4 3.0 was originally ported to run on the HTC Dream (found <a href="http://ertos.nicta.com.au/software/okl4htcdream/"><font color="#660068">here</font></a>). A student, Michael Hills, developed a rudimentary Android port that <font color="#ff0000">removes Linux</font> and runs natively on OKL4 3.0 for his undergraduate thesis. The supplied code demonstrates the Lunar Lander application found in the Android SDK running on the Dalvik VM on OKL4.</p>
<h2>Getting the source</h2>
<p><b>1. First create a base directory to contain all of the source of this project.</b></p>
<div><b>2.Then download the Android repo tool and place it into a directory that is in your environment&#8217;s PATH.</b></div>
<div><b>3.Then inside the project base directory, create a directory for the Android source and initialize the repo.</b></div>
<div>    </div>
<blockquote style="margin-right:0px"><div>You may need to prepare your build environment to compile Android as specified at <a href="http://source.android.com/source/index.html">http://source.android.com/source/index.html</a>. </div>
<pre>curl http://android.git.kernel.org/repo >~/bin/repo
chmod a+x ~/bin/repo
mkdir android-1.5r2
cd android-1.5r2
repo init -u git://android.git.kernel.org/platform/manifest.git -b android-1.5r2</pre>
</blockquote>
<div><b>4.Before downloading the Android source, we need to add the project for the HTC Dream hardware</b></div>
<blockquote dir="ltr" style="margin-right:0px"><div>Create a local_manifest.xml file inside android-1.5r2/.repo with the contents:</div>
<div><?xml version="1.0" encoding="UTF-8"?></div>
<div><manifest>   </div>
<div>
<project path="kernel" name="kernel/msm" revision="refs/heads/android-msm-2.6.27"/>    </div>
<div>
<project path="vendor/htc/dream-open" name="platform/vendor/htc/dream-open" revision="cupcake"/></div>
<div></manifest></div>
</blockquote>
<p><b>5.Then download the Android source by running the command:</b></p>
<blockquote dir="ltr" style="margin-right:0px"><div>repo sync</div>
</blockquote>
<div><b>6.create a file named buildspec.mk in the android-1.5r2 directory.</b></div>
<div> </div>
<div>All development targets the Android Developer Phone 1 (HTC Dream) and not the simulator. Therefore it is handy to tell the Android build system to default to building for the phone. To do this create a file named buildspec.mk in the android-1.5r2 directory.</div>
<blockquote style="margin-right:0px"><div> </div>
<div>The content of buildspec.mk is:</div>
<div> </div>
<div>TARGET_PRODUCT:=htc_dream</div>
<div> </div>
</blockquote>
<div> </div>
<div><b>7.Following this, download </b><a href="http://docs.google.com/downloads/okl4-android.tar.bz2"><b>okl4-android.tar.bz2</b></a><b> which contains everything else that is not Android.(在下载的这个包中，已经包含了所有的内容了，不用再单独下载okl-android了)</b></div>
<p style="text-align:center"><img src="http://docs.google.com/File?id=df6sncnm_900gzkj78hr_b" style="height:auto;width:648px"></p>
<div> </div>
<blockquote style="margin-right:0px"><div>Place the tarball into the base project directory and then extract it.</div>
<pre>tar xf okl4-android.tar.bz2
</pre>
<p>Part of this tarball is a directory with diffs that will be used to patch the Android tree with the changes made during porting. A script has been provided to patch the tree with these diffs in the base directory.</p>
<pre>./patch.py android-1.5r2 diffs
</pre>
<p>&#8230; where android-1.5r2 is the android source tree directory, and diffs is the directory containing the diffs.</p>
</blockquote>
<div><b>8.下载Android1.5r2 SDK</b></div>
<div>        </div>
<div>        Next the Android 1.5r2 SDK is required for the Android simulator which is used to optimise Java libraries (explained later). However it appears to no longer be available for download on the Android site. Instead, I have tested that using the 1.5r3 SDK works correctly. Download the SDK from <a href="http://developer.android.com/sdk/index.html"><font color="#660068">http://developer.android.com/sdk/index.html</font></a> and follow the instructions listed there on how to add the 1.5r3 platform to the SDK.(PS:其实是1.5R2 SDK可以下载的<a href="http://developer.android.com/sdk/older_releases.html">http://developer.android.com/sdk/older_releases.html</a>)</div>
<div> </div>
<p>        After extracting the tarball and patching the Android tree, the code base is now ready for building.</p>
<div> </div>
<div><b>号外：</b></div>
<div>The gcc toolchain that is used to compile both the Android and OKL4 trees is supplied with Android. If you are using Linux add the following directory to your path:</div>
<pre>./android-1.5r2/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin
</pre>
<p>If you are using Mac, then use:</p>
<pre>./android-1.5r2/prebuilt/darwin-x86/toolchain/arm-eabi-4.2.1/bin
</pre>
<p>However all development was done using Ubuntu 9.04 64-bit, and this has not been tested on a Mac.</p>
<p>In addition to this, <b>the OKL4 kernel uses a different toolchain</b> to the userland components. The kernel does not compile with the Android toolchain. Instead, use the arm-3.4.4 toolchain listed on <a href="http://ertos.nicta.com.au/software/prebuilt/binaries.pml"><font color="#660068">http://ertos.nicta.com.au/software/prebuilt/binaries.pml</font></a> and add the toolchain&#8217;s bin/ directory to your PATH.</p>
<h2>Building the source</h2>
<p>Both Android and OKL4 have their own build system, and so building the source is a little more complicated than just running a single make command. <b>Libraries and binaries that are compiled in the Android source tree must be linked to OKL4</b> libraries and <b>then placed into the in-memory file system which is done using the <font color="#ff0000">Elfweaver tool</font> in the OKL4 tree</b>.</p>
<p>The directory layout of the source should look like the following:</p>
<pre>./android-1.5r2/
./okl4-android-3.0/
./fs/
</pre>
<p>The first two directories are self-explanatory. <b>The third directory contains files that will be placed into the in-memory file system</b>.</p>
<p>The base directory contains several scripts to ease compilation.</p>
<h3>Getting Started</h3>
<p>One script will build all necessary parts of the Android tree (Java libraries, Bionic libc, Dalvik, and other misc libs), and do a full build of the OKL4 tree.</p>
<pre><b><font face="Arial Black" size="3">./makeall.sh</font>
</b></pre>
<p>However, Dalvik uses optimised Java libraries at runtime. The optimising tool was not ported and isn&#8217;t executed at runtime, so these optimised dex libraries must be produced beforehand and inserted into the in-memory file system.</p>
<p>The optimisation process uploads the built Java library jars to the simulator and invokes the optimisation tool on them. The optimised dex files are then downloaded back and placed into the fs/data/dalvik-cache directory where Dalvik can find them at runtime.</p>
<p>To start the simulator:</p>
<pre>export ANDROID_PRODUCT_OUT=path/to/android-sdk-linux/platforms/android-1.5/images
<b><font face="Arial Black">path/to/android-sdk-linux/tools/emulator
</font></b></pre>
<p><font color="#ff0000">Once the simulator has finished loading</font>, it is time to optimise the Java libraries. This is done by attempting to run a Java program with the jars you want optimised specified in the classpath.</p>
<p>In the project base directory run:</p>
<pre><b><font face="Arial Black">./optimise.sh LunarLander</font></b>
</pre>
<p>The argument to optimise.sh specifies the application jar that you wished to be optimised, currently only LunarLander is available. The jar file for LunarLander is located in fs/data/java, and the script automatically looks for specified jars in that location.</p>
<p>Now the boot image is ready to be built, this is done in the OKL4 tree.</p>
<pre>cd okl4-android-3.0
./make.sh dream
</pre>
<p>Calling make.sh will compile the OKL4 tree, and then build a boot image containing the files in the fs directory. Files to be placed in memory are specified in okl4-android-3.0/projects/sos/SConstruct. Make sure the latest dalvikvm binary and Java libraries are present in the file-system when compiling and booting the image onto the phone. </p>
</p>
<p>Once the boot image is ready, it can be loaded onto the phone using:</p>
<pre>./run.sh dream
</pre>
<p>This script simply calls a fastboot modified for OKL4. More information about this can be found at <a href="http://ertos.nicta.com.au/software/okl4htcdream/"><font color="#660068">http://ertos.nicta.com.au/software/okl4htcdream/</font></a> under &#8220;Running the code on the HTC Dream&#8221;.</p>
<p>The LunarLander application should load, and you can use the S key to start and restart the game, Q to rotate left, W to rotate right, and Spacebar for thrust.</p>
<h3>A Development Cycle</h3>
<p>A standard development cycle might involve changes to code in either the Android or OKL4 trees. Rebuilding the OKL4 tree is as simple as ./make.sh dream, however the Android tree is a bit more complicated since we&#8217;re using only a subset, and a complete build takes a long time. </p>
</p>
<p>Generally, you will need to rebuild Java or C/C++ libraries. A script has been provided to build individual libraries. For C/C++ libraries, in the base directory:</p>
<pre>./phonelibs.sh c dvm cutils
</pre>
<p>This will build libc, libdvm, and libcutils.</p>
<p>For Java libraries, in the android-1.5r2/ directory:</p>
<pre>make core framework
</pre>
<p>This will rebuild the Java core library and runtime framework library, which are both jars.</p>
<p>If you modify Dalvik main (dalvik/dalvikvm/Main.c), it needs to be rebuilt using:</p>
<pre>make dalvikvm
</pre>
<p>The object file will be made, and ignore the following error messages which are a result of modifying makefiles to build static libraries.</p>
<p>The dalvikvm binary is statically linked with libraries from both Android and OKL4, and therefore if C/C++ libraries in either tree are modified, dalvikvm must be recompiled.</p>
<p>In the base directory:</p>
<pre>make dalvik TARGET=dream
</pre>
<p>This will place a dalvikvm binary into fs/bin/.</p>
<p>If Java libraries are modified, they need to be re-optimised. In the base directory with the Android simulator running in the background:</p>
<pre>./optimise.sh <app.jar>
</pre>
<p>&#8230; where app.jar is the Dalvik application you wish to run that has been extracted from an apk file.</p>
<p>Once everything is ready to go, simply build and load the boot image from the okl4-android-3.0 directory.</p>
<pre>./make.sh dream
./run.sh dream
</pre>
<h2>Bugs and Limitations</h2>
<ul>
<li>A bug exists with the in-memory file system, and I do not know the exact cause. In the process of inserting files into the boot image, some files are occasionally corrupted and results in strange runtime behaviour. If the boot image fails to load the LunarLander application, then adding extra files to fs/data/java can make the bug go away (temporarily). The bug revealed itself early on during my thesis, but disappeared for the most of the year, only to reappear again towards the very end. </li>
<li>Static linking is used because I never implemented shared libraries in my OS personality. Any library that you wish to use from the Android tree mustbe compiled statically, and some of the time Android libraries are configured to produce shared libraries only. The Android.mk file in the library&#8217;s directory must be modified to build a static library, and this can be done by replacing &#8220;include $(BUILD_SHARED_LIBRARY)&#8221; with &#8220;include $(BUILD_STATIC_LIBRARY). </li>
<li>C++ static initialisers are not called. This is a process which is meant to be done before calling main. Constructors of objects declared as static are meant to be executed, but they are not and this can cause strange behaviour with uninitialised objects. </li>
<li>Only the LunarLander application was tested to run unmodified. It is highly likely that most programs will not work as I re-implemented the application-side libraries. This was done to get something running in the thesis to be able to benchmark the system. This is not a complete port of Android on OKL4. </li>
</ul>
<h2>Contact</h2>
<p>If you have any questions about this project, please email <a href="mailto:mikeh@cse.unsw.edu.au">Michael Hills (mikeh@cse.unsw.edu.au)</a>.</p>
<div> </div>
<div> </div>
<div> </div>
</p>
<div>  </div>
<div> </div>
<p></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/11/14/android-on-okl4-tutorials-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>xen-arm 算法技巧赏析（0）</title>
		<link>http://www.tek-life.org/2010/11/12/xen-arm-map_free/</link>
		<comments>http://www.tek-life.org/2010/11/12/xen-arm-map_free/#comments</comments>
		<pubDate>Fri, 12 Nov 2010 00:58:04 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[XENARM]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10476</guid>
		<description><![CDATA[1 /* 2 原以为，在平时的工程实现中，算法几乎是用不上的。但在看内核的过程中，发现算法还是比较重要的。 3 尤其是一些细微而又精巧的程序设计技巧。这些技巧是需要掌握的。 4 5 下面的这段程序摘自：xen-arm中，的内存初始化部分。 6 功能：从first_page页开始的nr_pages页在页位图中，表示为空闲页。 7 页位图，位于_end之后。_end标识xen-arm kernel的结束地址。 8 9 */ 10 static void map_free(unsigned long first_page, unsigned long nr_pages) 11 { 12 unsigned long start_off, end_off, curr_idx, end_idx; 13 14 #ifndef NDEBUG 15 &#8230; <a href="http://www.tek-life.org/2010/11/12/xen-arm-map_free/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><span style="font-family: monospace;"><span style="color: #00ff00;"> 1 </span><span style="color: #ff6060;">/*</span><br />
<span style="color: #00ff00;"> 2 </span><span style="color: #ff6060;"> 原以为，在平时的工程实现中，算法几乎是用不上的。但在看内核的过程中，发现算法还是比较重要的。</span><br />
<span style="color: #00ff00;"> 3 </span><span style="color: #ff6060;"> 尤其是一些细微而又精巧的程序设计技巧。这些技巧是需要掌握的。</span><br />
<span style="color: #00ff00;"> 4 </span><span style="color: #ff6060;"> </span><br />
<span style="color: #00ff00;"> 5 </span><span style="color: #ff6060;"> 下面的这段程序摘自：xen-arm中，的内存初始化部分。</span><br />
<span style="color: #00ff00;"> 6 </span><span style="color: #ff6060;"> 功能：从first_page页开始的nr_pages页在页位图中，表示为空闲页。</span><br />
<span style="color: #00ff00;"> 7 </span><span style="color: #ff6060;"> 页位图，位于_end之后。_end标识xen-arm kernel的结束地址。</span><br />
<span style="color: #00ff00;"> 8 </span><span style="color: #ff6060;"> </span><br />
<span style="color: #00ff00;"> 9 </span><span style="color: #ff6060;"> </span><span style="color: #ff6060;">*/</span><br />
<span style="color: #00ff00;">10 </span><span style="color: #00ff00;">static</span> <span style="color: #00ff00;">void</span> map_free(<span style="color: #00ff00;">unsigned</span> <span style="color: #00ff00;">long</span> first_page, <span style="color: #00ff00;">unsigned</span> <span style="color: #00ff00;">long</span> nr_pages)<br />
<span style="color: #00ff00;">11 </span>{<br />
<span style="color: #00ff00;">12 </span> <span style="color: #00ff00;">unsigned</span> <span style="color: #00ff00;">long</span> start_off, end_off, curr_idx, end_idx;<br />
<span style="color: #00ff00;">13 </span><br />
<span style="color: #00ff00;">14 </span><span style="color: #ff40ff;">#ifndef NDEBUG</span><br />
<span style="color: #00ff00;">15 </span> <span style="color: #00ff00;">unsigned</span> <span style="color: #00ff00;">long</span> i;<br />
<span style="color: #00ff00;">16 </span> <span style="color: #ff6060;">/*</span><span style="color: #ff6060;"> Check that the block isn&#8217;t already freed. </span><span style="color: #ff6060;">*/</span><br />
<span style="color: #00ff00;">17 </span> <span style="color: #ffff00;">for</span> ( i = <span style="color: #ff40ff;">0</span>; i &lt; nr_pages; i++ )<br />
<span style="color: #00ff00;">18 </span> ASSERT(allocated_in_map(first_page + i));<span style="color: #ff6060;">//ASSERT:如果条件不成立，就报错</span><br />
<span style="color: #00ff00;">19 </span><span style="color: #ff40ff;">#endif</span><br />
<span style="color: #00ff00;">20 </span><br />
<span style="color: #00ff00;">21 </span> curr_idx  = (first_page-min_page) / PAGES_PER_MAPWORD;<br />
<span style="color: #00ff00;">22 </span> start_off = (first_page-min_page) &amp; (PAGES_PER_MAPWORD-<span style="color: #ff40ff;">1</span>);<br />
<span style="color: #00ff00;">23 </span> end_idx   = (first_page-min_page + nr_pages) / PAGES_PER_MAPWORD;<br />
<span style="color: #00ff00;">24 </span> end_off   = (first_page-min_page + nr_pages) &amp; (PAGES_PER_MAPWORD-<span style="color: #ff40ff;">1</span>);<br />
<span style="color: #00ff00;">25 </span><br />
<span style="color: #00ff00;">26 </span> <span style="color: #ffff00;">if</span> ( curr_idx == end_idx )<br />
<span style="color: #00ff00;">27 </span> {<br />
<span style="color: #00ff00;">28 </span> <span style="color: #ff6060;">//这个算法还是比较巧妙的</span><br />
<span style="color: #00ff00;">29 </span> <span style="color: #ff6060;">//1&lt;&lt;start_off &#8211; 1 从start_off位开始，全部为1</span><br />
<span style="color: #00ff00;">30 </span> <span style="color: #ff6060;">/*</span><br />
<span style="color: #00ff00;">31 </span><span style="color: #ff6060;"> -(1&lt;&lt;end_off) 从0 ~ end_off 开始，全部为0&#8211;</span><br />
<span style="color: #00ff00;">32 </span><span style="color: #ff6060;"> 通过这种方式，把0~end_off之间全部变为0 ，而end_off ~31之间为1</span><br />
<span style="color: #00ff00;">33 </span><span style="color: #ff6060;"> </span><span style="color: #ff6060;">*/</span><br />
<span style="color: #00ff00;">34 </span> <span style="color: #ff6060;">//两者之或，就是从start_off ~ end_off 之间全部为0了</span><br />
<span style="color: #00ff00;">35 </span> alloc_bitmap[curr_idx] &amp;= -(<span style="color: #ff40ff;">1UL</span>&lt;&lt;end_off) | ((<span style="color: #ff40ff;">1UL</span>&lt;&lt;start_off)-<span style="color: #ff40ff;">1</span>);<br />
<span style="color: #00ff00;">36 </span> }<br />
<span style="color: #00ff00;">37 </span> <span style="color: #ffff00;">else</span><br />
<span style="color: #00ff00;">38 </span> {<br />
<span style="color: #00ff00;">39 </span> alloc_bitmap[curr_idx] &amp;= (<span style="color: #ff40ff;">1UL</span>&lt;&lt;start_off)-<span style="color: #ff40ff;">1</span>;<span style="color: #ff6060;">//start_off~31之间的为0,妙哉！</span><br />
<span style="color: #00ff00;">40 </span> <span style="color: #ffff00;">while</span> ( ++curr_idx != end_idx ) alloc_bitmap[curr_idx] = <span style="color: #ff40ff;">0</span>;<br />
<span style="color: #00ff00;">41 </span> alloc_bitmap[curr_idx] &amp;= -(<span style="color: #ff40ff;">1UL</span>&lt;&lt;end_off);<br />
<span style="color: #00ff00;">42 </span> }<br />
<span style="color: #00ff00;">43 </span>}<br />
<span style="color: #00ff00;">44 </span></span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/11/12/xen-arm-map_free/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>printf和标准输出［z］</title>
		<link>http://www.tek-life.org/2010/10/12/printf%e5%92%8c%e6%a0%87%e5%87%86%e8%be%93%e5%87%ba%ef%bc%bbz%ef%bc%bd/</link>
		<comments>http://www.tek-life.org/2010/10/12/printf%e5%92%8c%e6%a0%87%e5%87%86%e8%be%93%e5%87%ba%ef%bc%bbz%ef%bc%bd/#comments</comments>
		<pubDate>Tue, 12 Oct 2010 09:53:45 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10471</guid>
		<description><![CDATA[原文：http://blog.21ic.com/user1/3783/archives/2007/42870.html printf和标准输出 上次写到main函数的参数传递.现在继续往下进行.最近忙实验室的事情,看了一周 的文章,也没啥进展,周末写点技术贴,放松一下:-) 进入main函数后,就要调用printf(&#8220;Hello World!&#8221;);了.顺便将C语言参数传递提 一下.字符串&#8221;Hello World!&#8221;编译器是当作字符串常量来处理的,虽然printf是在main 内部调用,但&#8221;Hello World!&#8221;可不是放在main的栈中,字符串常量至少是放到.data段的 ,准确说是放在只读数据段.rodata,这个我在工作站上验证了一把.假如编辑的文件名是 hello.c,首先编译生成elf格式二进制文件gcc hello.c -o hello然后用命令objdump -s hello -s参数会将所有段信息dump出来.你会看到&#8221;Hello World!&#8221;位于.rodata段. printf()是个标准C库函数.虽然功能简单,但实现起来却不容易.这是个和平台相关的 函数.在pc上,printf输出是输出到终端屏幕,在嵌入式设备上,一般printf()是输出到串口. 同是调用printf(),最终输出的设备却不同,从直觉的肯定是感觉printf()底层和平台是相 关的.那么printf()是怎样实现的呢? 可以看一下C库程序的代码,这里以uClibc为例. int printf(const char * __restrict format, &#8230;) { va_list arg; int rv; va_start(arg, format); rv = &#8230; <a href="http://www.tek-life.org/2010/10/12/printf%e5%92%8c%e6%a0%87%e5%87%86%e8%be%93%e5%87%ba%ef%bc%bbz%ef%bc%bd/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>原文：http://blog.21ic.com/user1/3783/archives/2007/42870.html</p>
<p>printf和标准输出</p>
<p>上次写到main函数的参数传递.现在继续往下进行.最近忙实验室的事情,看了一周<br />
的文章,也没啥进展,周末写点技术贴,放松一下:-)</p>
<p>进入main函数后,就要调用printf(&#8220;Hello World!&#8221;);了.顺便将C语言参数传递提<br />
一下.字符串&#8221;Hello World!&#8221;编译器是当作字符串常量来处理的,虽然printf是在main<br />
内部调用,但&#8221;Hello World!&#8221;可不是放在main的栈中,字符串常量至少是放到.data段的<br />
,准确说是放在只读数据段.rodata,这个我在工作站上验证了一把.假如编辑的文件名是<br />
hello.c,首先编译生成elf格式二进制文件gcc hello.c -o hello然后用命令objdump -s hello -s参数会将所有段信息dump出来.你会看到&#8221;Hello World!&#8221;位于.rodata段.</p>
<p>printf()是个标准C库函数.虽然功能简单,但实现起来却不容易.这是个和平台相关的<br />
函数.在pc上,printf输出是输出到终端屏幕,在嵌入式设备上,一般printf()是输出到串口.<br />
同是调用printf(),最终输出的设备却不同,从直觉的肯定是感觉printf()底层和平台是相<br />
关的.那么printf()是怎样实现的呢?</p>
<p>可以看一下C库程序的代码,这里以uClibc为例.<br />
int printf(const char * __restrict format, &#8230;)<br />
{<br />
va_list arg;<br />
int rv;</p>
<p>va_start(arg, format);<br />
rv = vfprintf(stdout, format, arg);<br />
va_end(arg);</p>
<p>return rv;<br />
}<br />
printf支持字符串格式化输出,具体参数处理这里不提.可以看到printf()调用了<br />
vfprintf(),vfprintf()第一个参数是stdout是标准输出设备.标准输出设备是个结构体,<br />
最重要的成员就是他的描述符,其值为1.</p>
<p>跟进vfprintf()函数看,里面是复杂的参数处理,因为printf()的参数形式很灵活,<br />
所以在vfprintf()里面要对传进来的参数进行解析处理,形成最终的输出格式.有兴趣的<br />
可以看一下,借助这个可以让你在一个没有操作系统的平台上实现你自己的printf()<br />
函数.这样你在裸机上调程序时输出调试信息就更方便些(实际上uClinux的printk就是<br />
这么干的).</p>
<p>vfprintf()在参数处理之后，就是输出了，输出调用的是putc()，进入putc()然后<br />
再跟进几层函数，发现调用了linux系统调用write()。呵呵，是的，输出就是借助操作<br />
系统代码完成的。在write之前所有的代码都是C库的代码，可以说是和平台无关的。<br />
而涉及到具体输出，就要调用操作系统提供给你的接口。系统调用的原理就是通过一定<br />
手段（一般是trap陷阱）进入操作系统的内核空间，调用操作系统代码来完成某些任务。</p>
<p>Linux系统调用针对不同平台有不同的实现方式。这个以后再讲。调用write()后，<br />
进入内核空间，首先来到的就是sys_write()，这个函数代码位于fs/read_write.c中。<br />
一进入sys_write()，就要根据传进来的fd描述符找到相应的file结构。对于标准输出，<br />
fd=1，每个进程的进程控制块都有一个打开文件的数组。file结构就是根据fd在这个<br />
数组中查找到相应的结构。找到结构后，就会调用file-&gt;write()来向外输出。具体输出<br />
到哪里，就要看file结构对应的设备驱动是什么。一般嵌入式系统可以从串口将信息输<br />
出，那么file-&gt;write()最底层就是调用的串口驱动的类似transmit_char的函数。</p>
<p>有关linux的设备驱动有很多书介绍,整个驱动的结构很复杂,我这里也没必要提了.<br />
至于终端设备怎样挂在驱动队列里面,怎么根据标准输出的描述符找到相应的驱动结构<br />
有兴趣的莊printf()函数看,里面是复杂的参数处理,因为printf()的参数形式很灵活,<br />
所以在vfprintf()里面要对传进来的参数进行解析处理,形成最终的输出格式.有兴趣的<br />
可以看一下,借助这个可以让你在一个没有操作系统的平台上实现你自己的printf()<br />
函数.这样你在裸机上调程序时输出调试信息就更方便些(实际上uClinux的printk就是<br />
这么干的).</p>
<p>vfprintf()在参数处理之后，就是输出了，输出调用的是putc()，进入putc()然后<br />
再跟进几层函数，发现调用了linux系统调用write()。呵呵，是的，输出就是借助操作<br />
系统代码完成的。在write之前所有的代码都是C库的代码，可以说是和平台无关的。<br />
而涉及到具体输出，就要调用操作系统提供给你的接口。系统调用的原理就是通过一定<br />
手段（一般是trap陷阱）进入操作系统的内核空间，调用操作系统代码来完成某些任务。</p>
<p>Linux系统调用针对不同平台有不同的实现方式。这个以后再讲。调用write()后，<br />
进入内核空间，首先来到的就是sys_write()，这个函数代码位于fs/read_write.c中。<br />
一进入sys_write()，就要根据传进来的fd描述符找到相应的file结构。对于标准输出，<br />
fd=1，每个进程的进程控制块都有一个打开文件的数组。file结构就是根据fd在这个<br />
数组中查找到相应的结构。找到结构后，就会调用file-&gt;write()来向外输出。具体输出<br />
到哪里，就要看file结构对应的设备驱动是什么。一般嵌入式系统可以从串口将信息输<br />
出，那么file-&gt;write()最底层就是调用的串口驱动的类似transmit_char的函数。</p>
<p>有关linux的设备驱动有很多书介绍,整个驱动的结构很复杂,我这里也没必要提了.<br />
至于终端设备怎样挂在驱动队列里面,怎么根据标准输出的描述符找到相应的驱动结构<br />
有兴趣的请查阅相关资料.<br />
&#8211;<br />
手段（一般是trap陷阱）进入操作系统的内核空间，调用操作系统代码来完成某些任务。</p>
<p>Linux系统调用针对不同平台有不同的实现方式。这个以后再讲。调用write()后，<br />
进入内核空间，首先来到的就是sys_write()，这个函数代码位于fs/read_write.c中。<br />
一进入sys_write()，就要根据传进来的fd描述符找到相应的file结构。对于标准输出，<br />
fd=1，每个进程的进程控制块都有一个打开文件的数组。file结构就是根据fd在这个<br />
数组中查找到相应的结构。找到结构后，就会调用file-&gt;write()来向外输出。具体输出<br />
到哪里，就要看file结构对应的设备驱动是什么。一般嵌入式系统可以从串口将信息输<br />
出，那么file-&gt;write()最底层就是调用的串口驱动的类似transmit_char的函数。</p>
<p>有关linux的设备驱动有很多书介绍,整个驱动的结构很复杂,我这里也没必要提了.<br />
至于终端设备怎样挂在驱动队列里面,怎么根据标准输出的描述符找到相应的驱动结构<br />
有兴趣的请查阅相关资料.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/10/12/printf%e5%92%8c%e6%a0%87%e5%87%86%e8%be%93%e5%87%ba%ef%bc%bbz%ef%bc%bd/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux系统调用［z］</title>
		<link>http://www.tek-life.org/2010/10/12/linux%e7%b3%bb%e7%bb%9f%e8%b0%83%e7%94%a8%ef%bc%bbz%ef%bc%bd/</link>
		<comments>http://www.tek-life.org/2010/10/12/linux%e7%b3%bb%e7%bb%9f%e8%b0%83%e7%94%a8%ef%bc%bbz%ef%bc%bd/#comments</comments>
		<pubDate>Tue, 12 Oct 2010 09:50:59 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Kernel]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10467</guid>
		<description><![CDATA[在网上，这篇文章已经很少见完整版了，就连内核之旅也不例外。因此，转过来，备份一下。原文，请猛击：http://blog.csdn.net/kanghua/archive/2007/10/22/1836840.aspx 摘要：本期重点和大家讨论系统调用机制。其中涉及到了一些及系统调用的性能、上下文深层问题，同时也穿插着讲述了一些内核调试方法。并且最后试验部分我们利用系统调用与相关内核服务完成了一个搜集系统调用序列的特定任务，该试验具有较强的实用和教学价值。 什么是系统调用 顾 名思意，系统调用说的是操作系统提供给用户程序调用的一组“特殊”接口。用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务，比如用户可以通 过文件系统相关的调用请求系统打开文件、关闭文件或读写文件，可以通过时钟相关的系统调用获得系统时间或设置系统时间等。 从逻辑上来说，系统调用可被看成是一个内核与用户空间程序交互的接口——它好比一个中间人，把用户进程的请求传达给内核，待内核把请求处理完毕后再将处理结果送回给用户空间。 系统服务之所以需要通过系统调用提供给用户空间的根本原因是为了对系统“保护”，因为我们知道Linux的运行空间分为内核空间与用户空间，它们各自运行在不同的级别中，逻辑上相互隔离。所以用户进程在通常情况下不允许访问内核数据，也无法使用内核函数，它们只能在用户空间操作用户数据，调用户用空间函数。比如我们熟悉的“hello world”程序（执行时）就是标准的户空间进程，它使用的打印函数printf就属于用户空间函数，打印的字符“hello word”字符串也属于用户空间数据。 但 是很多情况下，用户进程需要获得系统服务（调用系统程序），这时就必须利用系统提供给用户的“特殊”接口——系统调用了，它的特殊性主要在于规定了用户进 程进入内核的具体位置；换句话说用户访问内核的路径是事先规定好的，只能从规定位置进入内核，而不准许肆意跳入内核。有了这样的陷入内核的统一访问路径限 制才能保证内核安全无虞。我们可以形象地描述这种机制：作为一个游客，你可以买票要求进入野生动物园，但你必须老老实实的坐在观光车上，按照规定的路线观 光游览。当然，不准下车，因为那样太危险，不是让你丢掉小命，就是让你吓坏了野生动物。 Linux的系统调用 对于现代操作系统，系统调用是一种内核与用户空间通讯的普遍手段，Linux系统也不例外。但是Linux系统的系统调用相比很多Unix和windows等系统具有一些独特之处，无处不体现出Linux的设计精髓——简洁和高效。 Linux系统调用很多地方继承了Unix的系统调用（但不是全部），但Linux相比传统Unix的系统调用做了很多扬弃，它省去了许多Unix系统冗余的系统调用，仅仅保留了最基本和最有用的系统调用，所以Linux全部系统调用只有250个左右（而有些操作系统系统调用多达1000个以上）。 这些系统调用按照功能逻辑大致可分为“进程控制”、“文件系统控制”、“系统控制”、“存管管理”、“网络管理”、“socket控制”、“用户管理”、“进程间通信”几类，详细情况可参阅文章系统调用列表 如果你想详细看看系统调用的说明，可以使用man 2 syscalls 命令查看，或干脆到 &#60;内核源码目录&#62;/include/asm-i386/unistd.h源文件种找到它们的原本。 熟练了解和掌握上面这些系统调用是对系统程序员的必备要求，但对于一个开发内核者或内核开发者来[1]说死记硬背下这些调用还远远不够。如果你仅仅知道存在的调用而不知道为什么它们会存在，或只知道如何使用调用而不知道这些调用在系统中的主要用途，那么你离驾驭系统还有不小距离。 要弥补这个鸿沟，第一，你必须明白系统调用在内核里的主要用途。虽然上面给出了数种分类，不过总的概括来讲系统调用主要在系统中的用途无非以下几类： l        控制硬件——系统调用往往作为硬件资源和用户空间的抽象接口，比如读写文件时用到的write/read调用。 l        设置系统状态或读取内核数据——因为系统调用是用户空间和内核的唯一通讯手段[2]，所以用户设置系统状态，比如开/关某项内核服务（设置某个内核变量），或读取内核数据都必须通过系统调用。比如getpgid、getpriority、setpriority、sethostname l        进程管理——一系列调用接口是用来保证系统中进程能以多任务，在虚拟内存环境下得以运行。比如 fork、clone、execve、exit等 第二，什么服务应该存在于内核；或者说什么功能应该实现在内核而不是在用户空间。这个问题并不没有明确的答案，有些服务你可以选择在内核完成，也可以在用户空间完成。选择在内核完成通常基于以下考虑： l        服务必须获得内核数据，比如一些服务必须获得中断或系统时间等内核数据。 l        从安全角度考虑，在内核中提供的服务相比用户空间提供的毫无疑问更安全，很难被非法访问到。 l        从效率考虑，在内核实现服务避免了和用户空间来回传递数据以及保护现场等步骤，因此效率往往要比实现在用户空间高许多。比如,httpd等服务。 &#8230; <a href="http://www.tek-life.org/2010/10/12/linux%e7%b3%bb%e7%bb%9f%e8%b0%83%e7%94%a8%ef%bc%bbz%ef%bc%bd/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>在网上，这篇文章已经很少见完整版了，就连内核之旅也不例外。因此，转过来，备份一下。原文，请猛击：http://blog.csdn.net/kanghua/archive/2007/10/22/1836840.aspx<br />
<strong>摘要</strong>：本期重点和大家讨论系统调用机制。其中涉及到了一些及系统调用的性能、上下文深层问题，同时也穿插着讲述了一些内核调试方法。并且最后试验部分我们利用系统调用与相关内核服务完成了一个搜集系统调用序列的特定任务，该试验具有较强的实用和教学价值。</p>
<h2>什么是系统调用</h2>
<p>顾 名思意，系统调用说的是操作系统提供给用户程序调用的一组“特殊”接口。用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务，比如用户可以通 过文件系统相关的调用请求系统打开文件、关闭文件或读写文件，可以通过时钟相关的系统调用获得系统时间或设置系统时间等。</p>
<p>从逻辑上来说，系统调用可被看成是一个内核与用户空间程序交互的接口——它好比一个中间人，把用户进程的请求传达给内核，待内核把请求处理完毕后再将处理结果送回给用户空间。</p>
<p>系统服务之所以需要通过系统调用提供给用户空间的根本原因是为了对系统“保护”，因为我们知道Linux的运行空间分为内核空间与用户空间，它们各自运行在不同的级别中，逻辑上相互隔离。所以用户进程在通常情况下不允许访问内核数据，也无法使用内核函数，它们只能在用户空间操作用户数据，调用户用空间函数。比如我们熟悉的“hello world”程序（执行时）就是标准的户空间进程，它使用的打印函数printf就属于用户空间函数，打印的字符“hello word”字符串也属于用户空间数据。</p>
<p>但 是很多情况下，用户进程需要获得系统服务（调用系统程序），这时就必须利用系统提供给用户的“特殊”接口——系统调用了，它的特殊性主要在于规定了用户进 程进入内核的具体位置；换句话说用户访问内核的路径是事先规定好的，只能从规定位置进入内核，而不准许肆意跳入内核。有了这样的陷入内核的统一访问路径限 制才能保证内核安全无虞。我们可以形象地描述这种机制：作为一个游客，你可以买票要求进入野生动物园，但你必须老老实实的坐在观光车上，按照规定的路线观 光游览。当然，不准下车，因为那样太危险，不是让你丢掉小命，就是让你吓坏了野生动物。</p>
<h2>Linux的系统调用</h2>
<p>对于现代操作系统，系统调用是一种内核与用户空间通讯的普遍手段，Linux系统也不例外。但是Linux系统的系统调用相比很多Unix和windows等系统具有一些独特之处，无处不体现出Linux的设计精髓——简洁和高效。</p>
<p>Linux系统调用很多地方继承了Unix的系统调用（但不是全部），但Linux相比传统Unix的系统调用做了很多扬弃，它省去了许多Unix系统冗余的系统调用，仅仅保留了最基本和最有用的系统调用，所以Linux全部系统调用只有250个左右（而有些操作系统系统调用多达1000个以上）。</p>
<p>这些系统调用按照功能逻辑大致可分为“进程控制”、“文件系统控制”、“系统控制”、“存管管理”、“网络管理”、“socket控制”、“用户管理”、“进程间通信”几类，详细情况可参阅文章<a href="http://www-900.ibm.com/developerworks/cn/linux/kernel/syscall/part1/appendix.shtml">系统调用列表</a></p>
<p>如果你想详细看看系统调用的说明，可以使用man 2 syscalls 命令查看，或干脆到 &lt;内核源码目录&gt;/include/asm-i386/unistd.h源文件种找到它们的原本。</p>
<p>熟练了解和掌握上面这些系统调用是对系统程序员的必备要求，但对于一个开发内核者或内核开发者来<a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftn1">[1]</a>说死记硬背下这些调用还远远不够。如果你仅仅知道存在的调用而不知道为什么它们会存在，或只知道如何使用调用而不知道这些调用在系统中的主要用途，那么你离驾驭系统还有不小距离。</p>
<p>要弥补这个鸿沟，第一，你必须明白系统调用在内核里的主要用途。虽然上面给出了数种分类，不过总的概括来讲系统调用主要在系统中的用途无非以下几类：</p>
<p>l        控制硬件——系统调用往往作为硬件资源和用户空间的抽象接口，比如读写文件时用到的write/read调用。</p>
<p>l        设置系统状态或读取内核数据——因为系统调用是用户空间和内核的唯一通讯手段<a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftn2">[2]</a>，所以用户设置系统状态，比如开/关某项内核服务（设置某个内核变量），或读取内核数据都必须通过系统调用。比如getpgid、getpriority、setpriority、sethostname</p>
<p>l        进程管理——一系列调用接口是用来保证系统中进程能以多任务，在虚拟内存环境下得以运行。比如 fork、clone、execve、exit等</p>
<p>第二，什么服务应该存在于内核；或者说什么功能应该实现在内核而不是在用户空间。这个问题并不没有明确的答案，有些服务你可以选择在内核完成，也可以在用户空间完成。选择在内核完成通常基于以下考虑：</p>
<p>l        服务必须获得内核数据，比如一些服务必须获得中断或系统时间等内核数据。</p>
<p>l        从安全角度考虑，在内核中提供的服务相比用户空间提供的毫无疑问更安全，很难被非法访问到。</p>
<p>l        从效率考虑，在内核实现服务避免了和用户空间来回传递数据以及保护现场等步骤，因此效率往往要比实现在用户空间高许多。比如,httpd等服务。</p>
<p>l        如果内核和用户空间都需要使用该服务，那么最好实现在内核空间，比如随机数产生。</p>
<p>理解上述道理对掌握系统调用本质意义很大，希望网友们能从使用中多总结，多思考。</p>
<h2>系统调用、用户编程接口（API）、系统命令、和内核函数的关系</h2>
<p>系统调用并非直接和程序员或系统管理员打交道，它仅仅是一个通过软中断机制（我们后面讲述）向内核提交请求，获取内核服务的接口。而在实际使用中程序员调用的多是用户编程接口——API，而管理员使用的则多是系统命令。</p>
<p>用户编程接口其实是一个函数定义，说明了如何获得一个给定的服务，比如read()、malloc()、free（）、abs()等。它有可能和系统调用形式上一致，比如read()接口就和read系统调用对应，但这种对应并非一一对应，往往会出现几种不同的API内部用到统一个系统调用，比如malloc()、free（）内部利用brk( )系统调用来扩大或缩小进程的堆；或一个API利用了好几个系统调用组合完成服务。更有些API甚至不需要任何系统调用——因为它不必需要内核服务，如计算整数绝对值的abs（）接口。</p>
<p>另外要补充的是Linux的用户编程接口遵循了在Unix世界中最流行的应用编程界面标准——POSIX标准，这套标准定义了一系列API。在Linux中（Unix也如此）这些API主要是通过C库（libc）实现的，它除了定义的一些标准的C函数外，一个很重要的任务就是提供了一套封装例程（wrapper routine）将系统调用在用户空间包装后供用户编程使用。</p>
<p>不过封装并非必须的，如果你愿意直接调用，Linux内核也提供了一个syscall()函数来实现调用，我们看个例子来对比一下通过C库调用和直接调用的区别。</p>
<p>#include &lt;syscall.h&gt;</p>
<p>#include &lt;unistd.h&gt;</p>
<p>#include &lt;stdio.h&gt;</p>
<p>#include &lt;sys/types.h&gt;</p>
<p>int main(void) {</p>
<p>long ID1, ID2;</p>
<p>/*&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;*/</p>
<p>/* 直接系统调用*/</p>
<p>/* SYS_getpid (func no. is 20) */</p>
<p>/*&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;*/</p>
<p>ID1 = syscall(SYS_getpid);</p>
<p>printf (&#8220;syscall(SYS_getpid)=%ld\n&#8221;, ID1);</p>
<p>/*&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;*/</p>
<p>/* 使用&#8221;libc&#8221;封装的系统调用 */</p>
<p>/* SYS_getpid (Func No. is 20) */</p>
<p>/*&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;*/</p>
<p>ID2 = getpid();</p>
<p>printf (&#8220;getpid()=%ld\n&#8221;, ID2);</p>
<p>return(0);</p>
<p>}</p>
<p>系统命令相对编程接口更高了一层，它是内部引用API的可执行程序，比如我们常用的系统命令ls、hostname等。Linux的系统命令格式遵循系统V的传统，多数放在/bin和/sbin下（相关内容可看看shell等章节）。</p>
<p>有兴趣的话可以通过strace ls或strace hostname 命令查看一下它们用到的系统调用，你会发现诸如open、brk、fstat、ioctl 等系统调用被用在系统命令中。</p>
<p>下一个需要解释一下的问题是内核函数和系统调用的关系，内核函数大家不要想像的过于复杂，其实它们和普通函数很像，只不过在内核实现，因此要满足一些内核编程的要求<a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftn3">[3]</a>。系统调用是一层用户进入内核的接口，它本身并非内核函数，进入内核后，不同的系统调用会找到对应到各自的内核函数——换个专业说法就叫：系统调用服务服务例程。实际对请求服务的是内核函数而非调用接口。</p>
<p>比如系统调用 getpid实际就是调用内核函数sys_getpid。</p>
<p>asmlinkage long sys_getpid(void)</p>
<p>{</p>
<p>return current-&gt;tpid;</p>
<p>}</p>
<p>Linux系统种存在许多的内核函数，有些是内核文件种自己使用的，有些则是可以export出来供内核其他部分共同使用的，具体情况自己决定。</p>
<p>内核公开的内核函数——export出来的——可以使用命令ksyms 或 cat /proc/ksyms来查看。另外网上还有一本归纳分类内核函数的书叫作《The Linux Kernel API Book》，有兴趣的读者可以去看看。</p>
<p>总而言之，从用户角度向内核看，依次是系统命令、编程接口、系统调用和内核函数。再讲述了系统调用实现后，我们会回过头来看看整个执行路径。</p>
<h2>系统调用实现</h2>
<p>Linux中实现系统调用利用了0&#215;86体系结构中的软件中断<a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftn4">[4]</a>。软件中断和我们常说的中断(硬件中断)不同之处在于——它是通过软件指令触发而并非外设，也就是说又编程人员出发的一种异常，具体的讲就是调用<strong>int $0&#215;80</strong>汇编指令，这条汇编指令将产生向量为128的编程异常。</p>
<p>之所以系统调用需要借助异常实现，是因为当用户态的进程调用一个系统调用时，CPU便被切换到内核态执行内核函数<a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftn5">[5]</a>，而我们在i386体系结构部分已经讲述过了进入内核——进入高特权级别——必须经过系统的门机制，这里异常实际上就是通过系统门陷入内核（除了int 0&#215;80外用户空间还可以通过int3——向量3、into——向量4 、bound——向量5等异常指令进入内核，而其他异常用户空间程序无法利用，都是由系统使用的）。</p>
<p>我们更详细的解释一下这个过程。int $0&#215;80指令目的是产生一个编号为128的编程异常，这个编程异常对应的中断描述符表IDT中的第128项——也就是对应的系统门描述符。门描述符中含有一个预设的内核空间地址，它指向了系统调用处理程序：system_call()（别和系统调用服务程序混淆,这个程序在entry.S文件中用汇编语言编写）。</p>
<p>很显然所有的系统调用都会统一的转到这个地址，但Linux一共有2、3百个系统调用都从这里进入内核后又该如何派发它们到各自的服务程序去呢？别发昏，解决这个问题的方法非常简单：首先Linux为每个系统调用都进行了编号（0—NR_syscall），同时在内核中保存了一张系统调用表，该表中保存了系统调用编号和其对应的服务例程，因此在系统调入通过系统门陷入内核前，需要把系统调用号一并传入内核，在x86上，这个传递动作是通过在执行int0x80前把调用号装入eax寄存器实现的。这样系统调用处理程序一旦运行，就可以从eax中得到数据，然后再去系统调用表中寻找相应服务例程了。</p>
<p>除了需要传递系统调用号以外，许多系统调用还需要传递一些参数到内核，比如sys_write(unsigned int fd, const char * buf, size_t count)调用就需要传递文件描述符号fd和要写入的内容buf和写入字节数count等几个内容到内核。碰到这种情况，Linux会有6个寄存器使用来传递这些参数：eax (存放系统调用号)、 ebx、ecx、edx、esi及edi来存放这些额外的参数（以字母递增的顺序）。具体做法是在system_call( )中使用SAVE_ALL宏把这些寄存器的值保存在内核态堆栈中。</p>
<p>有始便有终，当服务例程结束时，system_call( ) 从eax获得系统调用的返回值，并把这个返回值存放在曾保存用户态 eax寄存器栈单元的那个位置上。然后跳转到ret_from_sys_call( )，终止系统调用处理程序的执行。</p>
<p>当进程恢复它在用户态的执行前，RESTORE_ALL宏会恢复用户进入内核前被保留到堆栈中的寄存器值。其中eax返回时会带回系统调用的返回码。（负数说明调用错误，0或正数说明正常完成）</p>
<p>我们可以通过分析一下getpid系统调用的真是过程来将上述概念具体化，分析getpid系统调用一个办法是查看entry.s中的代码细节，逐步跟踪源码来分析运行过程，另外就是可借助一些内核调试工具，动态跟踪运行路径。</p>
<p>假设我们的程序源文件名为getpid.c，内容是：</p>
<p>#include &lt;syscall.h&gt;</p>
<p>#include &lt;unistd.h&gt;</p>
<p>#include &lt;stdio.h&gt;</p>
<p>#include &lt;sys/types.h&gt;</p>
<p>int main(void) {</p>
<p>long ID;</p>
<p>ID = getpid();</p>
<p>printf (&#8220;getpid()=%ld\n&#8221;, ID);</p>
<p>return(0);</p>
<p>}</p>
<p>将其编译成名为getpid的执行文件”gcc –o getpid &lt;路径&gt;/getpid.c”, 我们使用KDB来产看它进入内核后的执行路径。</p>
<p>l         激活KDB (按下pause键，当然你必须已经给内核打了KDB补丁);设置内核断点 “bp sys_getpid” ;退出kdb “go”;然后执行./getpid 。瞬间，进入内核调试状态,执行路径停止在断点sys_getpid处。</p>
<p>l         在KDB&gt;提示符下，执行bt命令观察堆栈，发现调用的嵌套路径，可以看到在sys_getpid是在内核函数system_call中被嵌套调用的。</p>
<p>l         在KDB&gt;提示符下，执行rd命令查看寄存器中的数值，可以看到eax中存放的getpid调用号——0&#215;00000014(=20).</p>
<p>l         在KDB&gt;提示符下，执行ssb（或ss）命令跟踪内核代码执行路径,可以发现sys_getpid执行后，会返回system_call函数，然后接者转入ret_from_sys_call例程。（再往后还有些和调度有关其他例程，我们这里不说了它们了。）</p>
<p>结合用户空间的执行路径，大致该程序可归结为一下几个步骤：</p>
<p>1  该程序调用libc库的封装函数getpid。该封装函数中将系统调用号_NR_getpid（第20个）压入EAX寄存器，</p>
<p>2  调用软中断 int 0&#215;80 进入内核。</p>
<p><strong>（以下进入内核态）</strong><strong> </strong></p>
<p>3  在内核中首先执行system_call，接着执行根据系统调用号在调用表中查找到对应的系统调用服务例程sys_getpid。</p>
<p>4．执行sys_getpid服务例程。</p>
<p>5．执行完毕后，转入ret_from_sys_call例程，系统调用中返回。</p>
<p>内核调试是一个很有趣的话题，方法多种多样，我个人认为比较好用的是UML（user mode linux+gdb）和 KDB 这两个工具。尤其KDB对于调试小规模内核模块或查看内核运行路径很有效，对于它的使用方法可以看看<a href="http://www-900.ibm.com/developerWorks/cn/linux/l-kdbug/index.shtml"><strong>Linux </strong><strong>内核调试器内幕</strong></a>这片文章。</p>
<h2>系统调用思考</h2>
<p>系统调用的内在过程并不复杂，我们不再多说了，下面这节我们主要就系统调用所涉及的一些重要问题作一些讨论和分析，希望这样能更有助了解系统调用的精髓。</p>
<h3>调用上下文分析</h3>
<p>系统调用虽说是要进入内核执行，但它并非一个纯粹意义上的内核例程。首先它是代表用户进程的，这点决定了虽然它会陷入内核执行，但是上下文仍然是处于进程上下文中，因此可以访问进程的许多信息（比如current结构——当前进程的控制结构），而且可以被其他进程抢占（在从系统调用返回时，由system_call函数判断是否该再调度），可以休眠，还可接收信号<a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftn6">[6]</a>等等。</p>
<p>所 有这些特点都涉及到了进程调度的问题，我们这里不做深究，只要大家明白系统调用完成后，再回到或者说把控制权交回到发起调用的用户进程前，内核会有一次调 度。如果发现有优先级别更高的进程或当前进程的时间片用完，那么就会选择高优先级的进程或重新选择进程运行。除了再调度需要考虑外，再就是内核需要检查是 否有挂起的信号，如果发现当前进程有挂起的信号，那么还需要先返回用户空间处理信号处理例程（处于用户空间），然后再回到内核，重新返回用户空间，有些麻 烦但这个反复过程是必须的。</p>
<h3>调用性能问题</h3>
<p>系统调用需要从用户空间陷入内核空间，处理完后，又需要返回用户空间。其中除了系统调用服务例程的实际耗时外，陷入/返回过程和系统调用处理程序（查系统调用表、存储\恢复用户现场）也需要花销一些时间，这些时间加起来就是一个系统调用的响应速度。系统调用不比别的用户程序，它对性能要求很苛刻，因为它需要陷入内核执行，所以和其他内核程序一样要求代码简洁、执行迅速。幸好Linux具有令人难以置信的上下文切换速度，使得其进出内核都被优化得简洁高效；同时所有Linux系统调用处理程序和每个系统调用本身也都非常简洁。</p>
<p>绝大多数情况下，Linux系统调用性能是可以接受的，但是对于一些对性能要求非常高的应用来说，它们虽然希望利用系统调用的服务，但却希望加快相应速度，避免陷入/返回和系统调用处理程序带来的花销，因此采用由内核直接调用系统调用服务例程，最好的例子就HTTPD——它为了避免上述开销，从内核调用socket等系统调用服务例程。</p>
<h3>什么时候添加系统调用</h3>
<p>系统调用是用户空间和内核空间交互的唯一手段，但是这并非时说要完成交互功能非要添加新系统调用不可。添加系统调用需要修改内核源代码、重新编译内核，因此如果想灵活的和内核交互信息，最好使用一下几种方法。</p>
<p>l        编写字符驱动程序</p>
<p>利用字符驱动程序可以完成和内核交互数据的功能。它最大的好处在于可以模块式加载，这样以来就避免了编译内核等手续，而且调用接口固定，容易操作。</p>
<p>l        使用proc 文件系统</p>
<p>利用proc文件系统修订系统状态是一种很常见的手段，比如通过修改proc文件系统下的系统参数配置文件（/proc/sys），我们可以直接在运行时动态更改内核参数；再如，通过下面这条指令：echo 1 &gt; /proc/sys/net/ip_v4/ip_forward开启内核中控制IP转发的开关。类似的，还有许多内核选项可以直接通过proc文件系统进行查询和调整。</p>
<p>l        使用虚拟文件系统</p>
<p>有些内核开发者认为利用ioctl（）系统调用（字符设备驱动接口）往往会似的系统调用意义不明确，而且难控制。而将信息放入到proc文件系统中会使信息组织混乱，因此也不赞成过多使用。他们建议实现一种孤立的虚拟文件系统来代替ioctl()和/proc，因为文件系统接口清楚，而且便于用户空间访问，同时利用虚拟文件系统使得利用脚本执行系统管理任务更家方便、有效。</p>
<h2>实验部分</h2>
<h3>代码功能介绍</h3>
<p>我们希望收集Linux系统运行时系统调用被执行的信息，既实时获取系统调用日志。这些日志信息将能以可读形式实时的返回给用户空间，以便用户观察或做近一步的日志分析（如入侵检测等）。</p>
<p>所以简单的讲实验代码集需要完成以下几个基本功能：</p>
<p>第一：记录系统调用日志，将其写入缓冲区（内核中），以便用户读取；</p>
<p>第二：建立新的系统调用，以便将内核缓冲中的系统调用日志返回到用户空间。</p>
<p>第三：循环利用系统调用，以便能动态实时返回系统调用日志。</p>
<h3>代码结构体系介绍</h3>
<h4>基本函数</h4>
<p>代码功能一节介绍中的基本功能对应程序代码集中的三个子程序。它们分别是syscall_auydit、Sys_audit和auditd。接下来我们介绍代码具体结构。</p>
<h5>日志记录例程Syscall_audit</h5>
<p>syscall_audit该程序是一个内核态的服务例程，该例程负责记录系统调用的运行日志。</p>
<p>记录系统调用日志的具体做法是在内核中修改系<em>统调用处理程序</em><em>system_call<a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftn7"><strong>[7]</strong></a></em>，在其中需要监控的每个调用（在我们例子钟222个系统调用都监控了，当然你也可以根据自己需求有选择的监控）执行完毕后都插入一个日志记录指令，该指令会转去调用内核服务函数syscall_audit来记录该次调用的信息<a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftn8">[8]</a>。</p>
<p>Syscall_audit内核服务例程会建立了一个内核缓冲区来存放被记录的函数。当搜集的数据量到达一定阀值时（比如设定为到达缓冲区总大小的%80，这样作可避免在丢失新调用），唤醒系统调用进程取回数据。否则继续搜集，这时系统调用程序会堵塞在一个等待队列上，直到被唤醒，也就是说如果缓冲区还没接近满时，系统调用会等待（被挂起）它被填充。</p>
<h5>系统调用Sys_audit</h5>
<p>由于系统调用是在内核中被执行，因此记录其执行日志也应该在内核态收集，所以我们需要利用一个新的系统调用来完成将内核信息带回到用户空间——sys_audit就是我们新填加的系统调用，它功能非常简单，就是从缓冲区中取数据返回用户空间。</p>
<p>为了保证数据连续性，防止丢失。我们会建立一个内核缓冲区存放每刻搜集到的日志数据，并且当搜集的数据量到达一定阀值时（比如设定为到达缓冲区总大小的%80），系统调用进程就会被唤醒<a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftn9">[9]</a>，以取回数据。否则在日志搜集时，系统调用程序会堵塞在等待队列上，直到被唤醒，也就是说如果缓冲区还没接近满时，系统调用会等待它被填充。</p>
<h5>用户空间服务程序auditd</h5>
<p>不用多说，我们需要一个用户空间服务进程来不断的调用audit系统调用，取回系统中搜集到的的调用日志信息。要知道，长时间的调用日志序列对于分析入侵或系统行为等才有价值。</p>
<h5>把代码集成到内核中</h5>
<p>除了上面介绍的内容外，我们还需要一些辅助性，但却很必要的工作，这些工作将帮助我们将上述代码灵活地机结成一体，完成需要的功能。</p>
<p>n        其一是修改entry.S汇编代码，该代码中含有系统调用表和系统调用入口代码system_call。我们首先需要在系统调用表中加入新的系统调用（名为sys_audit,223号。.long SYMBOL_NAME(sys_audit)）；下来在系统调用入口中加入跳转到日志记录服务例程中（跳转 “je auditsys”， 而auditsys代码段会真正调用系统调用记录例程syscall_audit）；</p>
<p>n        其 二是填加代码文件audit.c，该文件中包含syscall_audit与系统调用sys_audit两个函数体，我们这里只说包含函数体，而并非函 数，是因为这里我们并不想把函数的实现在内核中写死，而是希望利用了函数指针，即做了两个钩子函数，来完成把具体函数实现放在模块中完成，以便能动态加 载，方便调试（请见下一节介绍）。</p>
<p>u      其三是修改i386_ksyms.c文件，再最后加入</p>
<p><em>extern void (*my_audit)(int,int);</em></p>
<p><em>EXPORT_SYMBOL(my_audit);</em></p>
<p><em>extern int(*my_sysaudit)(unsigned char,unsigned char*,unsigned short,unsigned char);</em></p>
<p><em>EXPORT_SYMBOL(my_sysaudit);</em></p>
<p>，这样做是为了导出内核符号表，以便能模块代码中能挂接上以上函数指针。</p>
<p>n        其四是修改内核原代码目录下/kernel自目录下的Makefile文件，很简单，只需要在obj-y    := 。。。。。最后加上audit.o，告诉编译内核是把audit.o编进去。</p>
<h5>关键代码解释</h5>
<p>我们的日志收集例程与取日志系统调用这两个关键函数的实现是放在内核模块中实现。其中有些需要解释的地方：</p>
<p>1.        模块编程的必要原则，如初始化、注销等都应该实现，所不同的是我们在初始化与注销时会分别挂上或卸下<a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftn10">[10]</a>了两个钩子函数的实现。</p>
<p>2.      我们系统调用日志记录采用了一个结构体：syscall_buf，它含有诸如系统调用号——syscall、进程ID——pid、调用程序名——comm[COMM_SIZE]等字段，共52字节；我们的内核缓冲区为audit_buf，它是一个可容纳100个syscall_buf的数组。</p>
<p>3.      系统调用实现极简单，要做的仅仅是利用__copy_to_user<a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftn11">[11]</a>将内核缓冲中的日志数据取到用户空间。为了提高效率，在缓冲区未满时（未到%80的阀值时），系统调用会挂起等待<em>wait_event_interruptible(buffer_wait, current_pos &gt;= AUDIT_BUF_SIZE*8/10)</em>；相应地当缓冲区收集快满时，则唤醒系统调用继续收集日志wake_up_interruptible(&amp;buffer_wait)。</p>
<p>4.      最后要补充说明一下，在auditd用户服务程序中调用我们新加的系统调用前必须利用宏_syscall4(int, audit, u8, type, u8 *, buf, u16, len, u8, reset)来“声明”该调用——展开成audit函数原形，以便进行格式转换和参数传递，否则系统不能识别。</p>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>取回数据</td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>Audit系统调用</td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>用户程序auditd</td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>系统调用服务例程sys_audit</td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>系统调用日志缓冲</td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>Audit.o模块</td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>my_sysaudit钩子函数</td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>my_audit钩子函数</td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>用户空间</td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>内核空间</td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>日志记录例程syscall_audit</td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>描述日志数据流向</td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>描述系统调用关系</td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td>程序体系图</td>
</tr>
</tbody>
</table>
<h3>STEP BY STEP</h3>
<p>下面具体讲述一下如何添加这个调用。</p>
<p>1 修改entry.S ——在其中的添加audit调用，并且在system_call中加入搜集例程。（该函数位于&lt;内核源代码&gt;/arch/i386/kernel/下）</p>
<p>2 添加audit.c文件到&lt;内核源代码&gt;/arch/i386/kernel/下——该文件中定义了</p>
<p>sys_audit和syscall_audit<strong> </strong>两个函数需要的钩子函数（my_audit和my_sysaudit），它们会在entry.S中被使用。</p>
<p>3 修改&lt;内核源代码&gt;/arch/i386/kernel/i386-kysms.c文件，在其中导出my_audit与my_sysaudit两个钩子函数。因为只有在内核符号表里导出，才可被其他内核函数使用，也就是说才能在模块中被挂上。</p>
<p>4 修改&lt;内核源代码&gt;/arch/i386/kernel/Makefile文件，将audit.c编译入内核。</p>
<p>到这可以重新编译内核了，新内核已经加入了检测点了。下一步是编写模块来实现系统调用与内核搜集服务例程的功能了。</p>
<p>1 编写名为audit的模块，其中除了加载、卸载模块函数以外主要实现了mod_sys_audit与mod_syscall_audit两个函数。它们会分别挂载到my_sysaudit和my_audit两个钩子上。</p>
<p>2 编译后将模块加载 insmod audit.o。（你可通过dmesg查看是加载信息）</p>
<p>3 修改/usr/include/asm/unistd.h ——在其中加入audit的系统调用号。这样用户空间才可找到audit系统调用了。</p>
<p>4 最后，我们写一个用户deamon程序，来循环调用audit系统调用，并把搜集到的信息打印到屏幕上。</p>
<p>完了。系统调用还有许多细节，请大家查看有关书记吧。不罗索了。再见。</p>
<p>相关代码请下载 <a href="http://download.csdn.net/source/267963">auditexample.tar</a> （实现于2.4.18内核）。</p>
<p><em>感谢</em><em>SAL</em><em>的开发者，例子程序基本框架来自于它们的灵感。</em><em> </em></p>
<hr size="1" /><a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftnref1">[1]</a>我们说的开发内核者指开发系统内核，比如开发驱动模块机制、开发系统调用机制；而内核开发者则是指在内核基础之上进行的开发，比如驱动开发、系统调用开发、文件系统开发、网络通讯协议开发等。我们杂志所关注的问题主要在内核开发层次，即利用内核提供的机制进行开发。</p>
<p><a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftnref2">[2]</a>对Linux而言，系统调用是用户程序访问内核的唯一手段，无论是/proc方式或设备文件方式归根到底都是利用系统调用完成的。</p>
<p><a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftnref3">[3]</a>内核编程相比用户程序编程有一些特点，简单的讲内核程序一般不能引用C库函数（除非你自己实现了，比如内核实现了不少C库种的String操作函数）；缺少内存保护措施；堆栈有限（因此调用嵌套不能过多）；而且由于调度关系，必须考虑内核执行路径的连续性，不能有长睡眠等行为。</p>
<p><a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftnref4">[4]</a>软件中断虽然叫中断，但实际上属于异常（更准确说是陷阱）——CPU发出的中断——而且是由编程者触发的一种特殊异常。</p>
<p><a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftnref5">[5]</a>系统调用过程可被理解成——由内核在核心态代表应用程序执行任务。</p>
<p><a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftnref6">[6]</a>除了进程上下文外，Linux系统中还有另一种上下文——它被成为中断上下文。中断上下文不同于进程上下文，它代表中断执行，所以和进程是异步进行而且可以说毫不相干的。这种上下文中的程序，要避免睡眠因为无法被抢占。</p>
<p><a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftnref7">[7]</a>System_call是个通用的系统调用服务程序，或说系统调用入口程序，因为任何一个系统调用都要经过system_call统一处理（查找系统调用表，跳转到相应调用的服务例程），所以任何一次系统调用的信息都可被syscall_audit记录下来。</p>
<p><a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftnref8">[8]</a> 这里我们主要记录诸如调用时刻、调用者PID、程序名等信息，这些信息可从xtime或current这些全局变量处取得。</p>
<p><a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftnref9">[9]</a> 这里需要利用等待队列，具体声明见DECLARE_WAIT_QUEUE_HEAD(buffer_wait)。</p>
<p><a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftnref10">[10]</a> 所谓挂上或卸下其实就是将函数指针指向模块中实现的函数或指向空函数，但要知道这些函数指针一定是要导出到内核符号表中的，否则找不到。</p>
<p><a href="mhtml:http://www.csdn.net/subject/linux/systemcall.mht#_ftnref11">[11]</a> 这是一个系统提供的内核函数，目的就是从内核向用户空间传递数据。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/10/12/linux%e7%b3%bb%e7%bb%9f%e8%b0%83%e7%94%a8%ef%bc%bbz%ef%bc%bd/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Printf从函数库到OS跟踪流程</title>
		<link>http://www.tek-life.org/2010/10/12/printf%e4%bb%8e%e5%87%bd%e6%95%b0%e5%ba%93%e5%88%b0os%e8%b7%9f%e8%b8%aa%e6%b5%81%e7%a8%8b/</link>
		<comments>http://www.tek-life.org/2010/10/12/printf%e4%bb%8e%e5%87%bd%e6%95%b0%e5%ba%93%e5%88%b0os%e8%b7%9f%e8%b8%aa%e6%b5%81%e7%a8%8b/#comments</comments>
		<pubDate>Tue, 12 Oct 2010 09:43:57 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Kernel]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10464</guid>
		<description><![CDATA[首先需要弄清楚：API－用户编程借口与 系统调用的区别，这里有一篇非常好的文章: http://www.tek-life.org/2010/10/12/linux%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%EF%BC%BBz%EF%BC%BD/ 另外，printf具体内部原理请见： http://www.tek-life.org/2010/10/12/printf%E5%92%8C%E6%A0%87%E5%87%86%E8%BE%93%E5%87%BA%EF%BC%BBz%EF%BC%BD/ 我仅仅是进行了code review arch/x86/boot/printf.c int printf(const char *fmt, &#8230;) { char printf_buf[1024]; va_list args; int printed; va_start(args, fmt); printed = vsprintf(printf_buf, fmt, args); va_end(args); puts(printf_buf); return printed; } void __attribute__((section(&#8220;.inittext&#8221;))) puts(const char *str) { int n &#8230; <a href="http://www.tek-life.org/2010/10/12/printf%e4%bb%8e%e5%87%bd%e6%95%b0%e5%ba%93%e5%88%b0os%e8%b7%9f%e8%b8%aa%e6%b5%81%e7%a8%8b/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><!-- p { margin-bottom: 0.21cm; } --><span>首先需要弄清楚：API－用户编程借口与 系统调用的区别，这里有一篇非常好的文章:</span></p>
<p>http://www.tek-life.org/2010/10/12/linux%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%EF%BC%BBz%EF%BC%BD/</p>
<p><span>另外，printf具体内部原理请见：</span></p>
<p><span> </span>http://www.tek-life.org/2010/10/12/printf%E5%92%8C%E6%A0%87%E5%87%86%E8%BE%93%E5%87%BA%EF%BC%BBz%EF%BC%BD/</p>
<p><span>我仅仅是进行了code review<br />
</span></p>
<p><span>arch/x86/boot/printf.c</span></p>
<p><span>int printf(const char *fmt, &#8230;) </span></p>
<p><span>{ </span></p>
<p><span>char printf_buf[1024]; </span></p>
<p><span>va_list args; </span></p>
<p><span>int printed; </span></p>
<p><span> </span></p>
<p><span>va_start(args, fmt); </span></p>
<p><span>printed = vsprintf(printf_buf, fmt, args); </span></p>
<p><span>va_end(args); </span></p>
<p><span> </span></p>
<p><span><strong>puts(printf_buf);</strong> </span></p>
<p><span> </span></p>
<p><span>return printed; </span></p>
<p><span>} </span></p>
<p><span>void __attribute__((section(&#8220;.inittext&#8221;))) puts(const char *str)</span></p>
<p><span>{ </span></p>
<p><span>int n = 0; </span></p>
<p><span>while (*str) { </span></p>
<p><span><strong>putchar</strong>(*str++); </span></p>
<p><span>n++; </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>/* </span></p>
<p><span>* These functions are in .inittext so they can be used to signal </span></p>
<p><span>* error during initialization. </span></p>
<p><span>*/ </span></p>
<p><span> </span></p>
<p><span>void __attribute__((section(&#8220;.inittext&#8221;))) <strong>putchar</strong>(int ch) </span></p>
<p><span>{ </span></p>
<p><span>unsigned char c = ch; </span></p>
<p><span> </span></p>
<p><span>if (c == &#8216;\n&#8217;) </span></p>
<p><span>putchar(&#8216;\r&#8217;);  /* \n -&gt; \r\n */ </span></p>
<p><span> </span></p>
<p><span>/* int $0&#215;10 is known to have bugs involving touching registers </span></p>
<p><span>it shouldn&#8217;t.  Be extra conservative&#8230; */ </span></p>
<p><span>asm volatile(&#8220;pushal; pushw %%ds; int $0&#215;10; popw %%ds; popal&#8221; </span></p>
<p><span>: : &#8220;b&#8221; (0&#215;0007), &#8220;c&#8221; (0&#215;0001), &#8220;a&#8221; (0x0e00|ch)); </span></p>
<p><span>} </span></p>
<p>我擦，跟踪错了！这是在实模式！调用<span>10</span>号中断显示。注释上写的很清楚，他们被安排在<span>inittext</span>段，当且进当在初始化的时候会调用。<span>Inittext</span>会被擦除的。</p>
<p><span>10</span>号调用的具体参数：</p>
<table cellspacing="0" cellpadding="4" width="100%">
<col width="256*"></col>
<tbody>
<tr>
<td width="100%" valign="TOP"><span>BIOS </span>呼叫 <span>INT 			10H</span>，<span>AH=0EH<br />
</span>在文字模式 			或 绘图模式下显示一字元，游标则右移一格 <span><br />
(AL) 			&lt;= </span>字元 <span>ascii<br />
(BH) 			&lt;= </span>页码 <span><br />
(BL) 			&lt;= </span>前景颜色码 <span>(</span>绘图模式时<span>)<br />
</span>注<span>:</span>所谓 <span>TTY </span>就是类似打字机输出方式，每显示一字元，游标则右移一格，<span><br />
</span>当移到最后一行时，游标则至跳下一列的最左边开始，当移到最后一列 			<span><br />
</span>时，萤幕则上卷一列</td>
</tr>
</tbody>
</table>
<p>看一下<span>C</span>库函数中的具体实现：</p>
<p>采用<span>ulibc</span></p>
<p><span>libc/stdio/printf.c:</span></p>
<p><span>int printf(const char * __restrict format, &#8230;){ </span></p>
<p><span>va_list arg; </span></p>
<p><span>int rv; </span></p>
<p><span> </span></p>
<p><span>va_start(arg, format); </span></p>
<p><span>rv =<strong> vfprintf</strong>(stdout, format, arg); </span></p>
<p><span>va_end(arg); </span></p>
<p><span> </span></p>
<p><span>return rv; </span></p>
<p><span>}</span></p>
<p><span>libc/stdio/old_vfprintf.c</span></p>
<p><span>int vfprintf(FILE * __restrict op, register const char * __restrict fmt, </span></p>
<p><span>va_list ap) </span></p>
<p><span>{ </span></p>
<p><span>union { </span></p>
<p><span>#ifdef LLONG_MAX </span></p>
<p><span>long long ll; </span></p>
<p><span>#endif </span></p>
<p><span>#if LONG_MAX != INT_MAX </span></p>
<p><span>long l; </span></p>
<p><span>#endif </span></p>
<p><span>int i; </span></p>
<p><span>} intarg; </span></p>
<p><span>int i, cnt, dataargtype, len; </span></p>
<p><span>const void *argptr = argptr; /* ok to be initialized. */ </span></p>
<p><span>register char *p; </span></p>
<p><span>const char *fmt0; </span></p>
<p><span>int preci, width; </span></p>
<p><span>#define upcase i </span></p>
<p><span>int radix, dpoint /*, upcase*/; </span></p>
<p><span>char tmp[65];           /* TODO &#8211; determine needed size from headers */ </span></p>
<p><span>char flag[sizeof(spec)]; </span></p>
<p><span>__STDIO_AUTO_THREADLOCK_VAR; </span></p>
<p><span> </span></p>
<p><span>__STDIO_AUTO_THREADLOCK(op); </span></p>
<p><span> </span></p>
<p><span>__STDIO_STREAM_VALIDATE(op); </span></p>
<p><span> </span></p>
<p><span>cnt = 0; </span></p>
<p><span> </span></p>
<p><span>if (__STDIO_STREAM_IS_NARROW_WRITING(op) </span></p>
<p><span>|| !__STDIO_STREAM_TRANS_TO_WRITE(op, __FLAG_NARROW) </span></p>
<p><span>) { </span></p>
<p><span> </span></p>
<p><span>while (*fmt) { </span></p>
<p><span>if (*fmt == &#8216;%&#8217;) { </span></p>
<p><span>fmt0 = fmt;                     /* save our position in case of bad format */ </span></p>
<p><span>++fmt; </span></p>
<p><span>width = -1;                     /* min field width */ </span></p>
<p><span>preci = -5;                     /* max string width or mininum digits */ </span></p>
<p><span>radix = 10;                     /* number base */ </span></p>
<p><span>dpoint = 0;                     /* found decimal point */ </span></p>
<p><span> </span></p>
<p><span>/* init flags */ </span></p>
<p><span>for (p =(char *) spec ; *p ; p++) { </span></p>
<p><span>flag[p-spec] = &#8216;\0&#8242;; </span></p>
<p><span>} </span></p>
<p><span>flag[FLAG_0_PAD] = &#8216; &#8216;; </span></p>
<p><span> </span></p>
<p><span>/* process optional flags */ </span></p>
<p><span>for (p = (char *)spec ; *p ; ) { </span></p>
<p><span>if (*fmt == *p) { </span></p>
<p><span>flag[p-spec] = *fmt++; </span></p>
<p><span>p = (char *)spec; /* restart scan */ </span></p>
<p><span>} else { </span></p>
<p><span>p++; </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>if (!flag[FLAG_PLUS]) { </span></p>
<p><span>flag[FLAG_PLUS] = flag[FLAG_SPACE]; </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>/* process optional width and precision */ </span></p>
<p><span>do { </span></p>
<p><span>if (*fmt == &#8216;.&#8217;) { </span></p>
<p><span>++fmt; </span></p>
<p><span>dpoint = 1; </span></p>
<p><span>} </span></p>
<p><span>if (*fmt == &#8216;*&#8217;) {      /* parameter width value */ </span></p>
<p><span>++fmt; </span></p>
<p><span>i = va_arg(ap, int); </span></p>
<p><span>} else { </span></p>
<p><span>for ( i = 0 ; (*fmt &gt;= &#8217;0&#8242;) &amp;&amp; (*fmt &lt;= &#8217;9&#8242;) ; ++fmt ) { </span></p>
<p><span>i = (i * 10) + (*fmt &#8211; &#8217;0&#8242;); </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>if (dpoint) {                                         preci = i; </span></p>
<p><span>if (i&lt;0) { </span></p>
<p><span>preci = -5; </span></p>
<p><span>} </span></p>
<p><span>} else { </span></p>
<p><span>width = i; </span></p>
<p><span>if (i&lt;0) { </span></p>
<p><span>width = -i; </span></p>
<p><span>flag[FLAG_MINUS_LJUSTIFY] = 1; </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span>} while ((*fmt == &#8216;.&#8217;) &amp;&amp; !dpoint ); </span></p>
<p><span> </span></p>
<p><span>/* process optional qualifier */ </span></p>
<p><span>p = (char *) qual_chars; </span></p>
<p><span>do { </span></p>
<p><span>if (*fmt == *p) { </span></p>
<p><span>++fmt; </span></p>
<p><span>break; </span></p>
<p><span>} </span></p>
<p><span>} while (*++p); </span></p>
<p><span>if ((p &#8211; qual_chars &lt; 2) &amp;&amp; (*fmt == *p)) { </span></p>
<p><span>p += ((sizeof(qual_chars)-2) / 2); </span></p>
<p><span>++fmt; </span></p>
<p><span>} </span></p>
<p><span>dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) &lt;&lt; 8; </span></p>
<p><span> </span></p>
<p><span>#if WANT_GNU_ERRNO </span></p>
<p><span>if (*fmt == &#8216;m&#8217;) { </span></p>
<p><span>flag[FLAG_PLUS] = &#8216;\0&#8242;; </span></p>
<p><span>flag[FLAG_0_PAD] = &#8216; &#8216;; </span></p>
<p><span>p = __glibc_strerror_r(errno, tmp, sizeof(tmp)); </span></p>
<p><span>goto print; </span></p>
<p><span>} </span></p>
<p><span>#endif </span></p>
<p><span> </span></p>
<p><span>/* process format specifier */ </span></p>
<p><span>for (p = (char *) u_spec ; *p ; p++) { </span></p>
<p><span>if (*fmt != *p) continue; </span></p>
<p><span>if (p-u_spec &lt; 1) {     /* print a % */ </span></p>
<p><span>goto charout; </span></p>
<p><span>} </span></p>
<p><span>if (p-u_spec &lt; 2) {     /* store output count in int ptr */ </span></p>
<p><span>_store_inttype(va_arg(ap, void *), </span></p>
<p><span>dataargtype, </span></p>
<p><span>(intmax_t) (cnt)); </span></p>
<p><span>goto nextfmt; </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>if (p-u_spec &lt; 10) { </span></p>
<p><span>if (*p == &#8216;p&#8217;) { </span></p>
<p><span>#if INTPTR_MAX == INT_MAX </span></p>
<p><span>dataargtype = 0; </span></p>
<p><span>#else </span></p>
<p><span>#error Fix dataargtype for pointers! </span></p>
<p><span>#endif </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>switch(dataargtype) { </span></p>
<p><span>case (PA_INT|PA_FLAG_LONG_LONG): </span></p>
<p><span>#ifdef LLONG_MAX </span></p>
<p><span>intarg.ll = va_arg(ap, long long); </span></p>
<p><span>argptr = &amp;intarg.ll; </span></p>
<p><span>break; </span></p>
<p><span>#endif </span></p>
<p><span>case (PA_INT|PA_FLAG_LONG): </span></p>
<p><span>#if LONG_MAX != INT_MAX </span></p>
<p><span>intarg.l = va_arg(ap, long); </span></p>
<p><span>argptr = &amp;intarg.l; </span></p>
<p><span>break; </span></p>
<p><span>#endif </span></p>
<p><span>default: </span></p>
<p><span>intarg.i = va_arg(ap, int); </span></p>
<p><span>argptr = &amp;intarg.i; </span></p>
<p><span>break; </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span> if (p-u_spec &lt; <img src='http://www.tek-life.org/wp-includes/images/smilies/icon_cool.gif' alt='8)' class='wp-smiley' /> { /* unsigned conversion */ </span></p>
<p><span>radix = u_radix[p-u_spec-2]; </span></p>
<p><span>upcase = ((*p == &#8216;x&#8217;) ? __UIM_LOWER : __UIM_UPPER); </span></p>
<p><span>if (*p == &#8216;p&#8217;) { </span></p>
<p><span>upcase = __UIM_LOWER; </span></p>
<p><span>flag[FLAG_HASH] = &#8216;p&#8217;; </span></p>
<p><span>} </span></p>
<p><span>p = _uintmaxtostr(tmp + sizeof(tmp) &#8211; 1, </span></p>
<p><span>(uintmax_t) </span></p>
<p><span>_load_inttype(dataargtype, argptr, radix), </span></p>
<p><span>radix, upcase); </span></p>
<p><span> </span></p>
<p><span>flag[FLAG_PLUS] = &#8216;\0&#8242;; /* meaningless for unsigned */ </span></p>
<p><span>if (*p != &#8217;0&#8242;) { /* non-zero */ </span></p>
<p><span>if (flag[FLAG_HASH]) { </span></p>
<p><span>if (radix == <img src='http://www.tek-life.org/wp-includes/images/smilies/icon_cool.gif' alt='8)' class='wp-smiley' /> { </span></p>
<p><span>*&#8211;p = &#8217;0&#8242;;     /* add leadding zero */ </span></p>
<p><span>} else if (radix != 10) { /* either 2 or 16 */ </span></p>
<p><span>flag[FLAG_PLUS] = &#8217;0&#8242;; </span></p>
<p><span>*&#8211;p = &#8216;b&#8217;; </span></p>
<p><span>if (radix == 16) { </span></p>
<p><span>*p = &#8216;x&#8217;; </span></p>
<p><span>if (*fmt == &#8216;X&#8217;) { </span></p>
<p><span>*p = &#8216;X&#8217;; </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span>} else if (flag[FLAG_HASH] == &#8216;p&#8217;) { /* null pointer */ </span></p>
<p><span>p = &#8220;(nil)&#8221;; </span></p>
<p><span>} </span></p>
<p><span>} else if (p-u_spec &lt; 10) { /* signed conversion */ </span></p>
<p><span>p = _uintmaxtostr(tmp + sizeof(tmp) &#8211; 1, </span></p>
<p><span>(uintmax_t) </span></p>
<p><span>_load_inttype(dataargtype, argptr, -radix), </span></p>
<p><span>-radix, upcase); </span></p>
<p><span> </span></p>
<p><span>} else if (p-u_spec &lt; 12) {     /* character or string */ </span></p>
<p><span>flag[FLAG_PLUS] = &#8216;\0&#8242;; </span></p>
<p><span>flag[FLAG_0_PAD] = &#8216; &#8216;; </span></p>
<p><span>if (*p == &#8216;c&#8217;) {        /* character */ </span></p>
<p><span>p = tmp; </span></p>
<p><span>*p = va_arg(ap, int); </span></p>
<p><span>/* This takes care of the &#8220;%c&#8221;,0 case */ </span></p>
<p><span>len = 1; </span></p>
<p><span>goto print_len_set; </span></p>
<p><span>} else {        /* string */ </span></p>
<p><span>p = va_arg(ap, char *); </span></p>
<p><span>if (!p) { </span></p>
<p><span>p = &#8220;(null)&#8221;; </span></p>
<p><span>preci = 6; </span></p>
<p><span>} else { </span></p>
<p><span>if (preci &lt; 0) { </span></p>
<p><span>preci = INT_MAX; </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span>len = strnlen(p, preci); </span></p>
<p><span>goto print_len_set; </span></p>
<p><span>} </span></p>
<p><span>#if defined(__UCLIBC_HAS_FLOATS__) || WANT_FLOAT_ERROR </span></p>
<p><span>} else if (p-u_spec &lt; 27) {             /* floating point */ </span></p>
<p><span>#endif /* defined(__UCLIBC_HAS_FLOATS__) || WANT_FLOAT_ERROR */ </span></p>
<p><span>#if defined(__UCLIBC_HAS_FLOATS__) </span></p>
<p><span>struct printf_info info; </span></p>
<p><span>if (preci &lt; 0) { </span></p>
<p><span>preci = 6; </span></p>
<p><span>} </span></p>
<p><span>info.width = width; </span></p>
<p><span>info.prec = preci; </span></p>
<p><span>info.spec = *fmt; </span></p>
<p><span>info.pad = flag[FLAG_0_PAD]; </span></p>
<p><span>info._flags = 0; </span></p>
<p><span>if (flag[FLAG_PLUS] == &#8216;+&#8217;) { </span></p>
<p><span>PRINT_INFO_SET_FLAG(&amp;info,showsign); </span></p>
<p><span>} else if (flag[FLAG_PLUS] == &#8216; &#8216;) { </span></p>
<p><span>PRINT_INFO_SET_FLAG(&amp;info,space); </span></p>
<p><span>} </span></p>
<p><span>if (flag[FLAG_HASH]) { </span></p>
<p><span>PRINT_INFO_SET_FLAG(&amp;info,alt); </span></p>
<p><span>} </span></p>
<p><span>if (flag[FLAG_MINUS_LJUSTIFY]) { </span></p>
<p><span>PRINT_INFO_SET_FLAG(&amp;info,left); </span></p>
<p><span>} </span></p>
<p><span>#if 1 </span></p>
<p><span>cnt += _fpmaxtostr(op, </span></p>
<p><span>(__fpmax_t) </span></p>
<p><span>((dataargtype == (8 &lt;&lt; 8)) </span></p>
<p><span>? va_arg(ap, long double) </span></p>
<p><span>: (long double) va_arg(ap, double)), </span></p>
<p><span>&amp;info, _fp_out_narrow); </span></p>
<p><span>#else </span></p>
<p><span>cnt += _fpmaxtostr(op, </span></p>
<p><span>(__fpmax_t) </span></p>
<p><span>((lval &gt; 1) </span></p>
<p><span>? va_arg(ap, long double) </span></p>
<p><span>: (long double) va_arg(ap, double)), </span></p>
<p><span>&amp;info, _fp_out_narrow); </span></p>
<p><span>#endif </span></p>
<p><span>goto nextfmt; </span></p>
<p><span>#elif WANT_FLOAT_ERROR </span></p>
<p><span>(void) ((lval &gt; 1) ? va_arg(ap, long double) </span></p>
<p><span>: va_arg(ap, double)); /* carry on */ </span></p>
<p><span>p = (char *) dbl_err; </span></p>
<p><span>#endif /* defined(__UCLIBC_HAS_FLOATS__) */ </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>#if WANT_GNU_ERRNO </span></p>
<p><span>print: </span></p>
<p><span>#endif </span></p>
<p><span>{                               /* this used to be printfield */ </span></p>
<p><span>/* cheaper than strlen call */ </span></p>
<p><span>/*                                      for ( len = 0 ; p[len] ; len++ ) { } */ </span></p>
<p><span>len = strnlen(p, SIZE_MAX); </span></p>
<p><span>print_len_set: </span></p>
<p><span>if ((*p == &#8216;-&#8217;) </span></p>
<p><span>#if WANT_GNU_ERRNO </span></p>
<p><span>&amp;&amp; (*fmt != &#8216;m&#8217;) </span></p>
<p><span>#endif </span></p>
<p><span>&amp;&amp; (*fmt != &#8216;s&#8217;)) { </span></p>
<p><span>flag[FLAG_PLUS] = *p++; </span></p>
<p><span>&#8211;len; </span></p>
<p><span>} </span></p>
<p><span>if (flag[FLAG_PLUS]) { </span></p>
<p><span>++len; </span></p>
<p><span>++preci; </span></p>
<p><span>if (flag[FLAG_PLUS] == &#8217;0&#8242;) { /* base 16 */ </span></p>
<p><span>++preci; /* account for x or X */ </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>if (preci &gt;= 0) { </span></p>
<p><span>if ((*fmt == &#8216;s&#8217;) </span></p>
<p><span>#if WANT_GNU_ERRNO </span></p>
<p><span>|| (*fmt == &#8216;m&#8217;) </span></p>
<p><span>#endif </span></p>
<p><span>) { </span></p>
<p><span>if (len &gt; preci) { </span></p>
<p><span>len = preci; </span></p>
<p><span>} else { </span></p>
<p><span>preci = len; </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span>preci -= len; </span></p>
<p><span>if (preci &lt; 0) { </span></p>
<p><span>preci = 0; </span></p>
<p><span>} </span></p>
<p><span>width -= preci; </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>width -= len; </span></p>
<p><span>if (width &lt; 0) { </span></p>
<p><span>width = 0; </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>if (preci &lt; 0) { </span></p>
<p><span>preci = 0; </span></p>
<p><span>if (!flag[FLAG_MINUS_LJUSTIFY] </span></p>
<p><span>/* &amp;&amp; flag[FLAG_PLUS] */ </span></p>
<p><span>&amp;&amp; (flag[FLAG_0_PAD] == &#8217;0&#8242;)) { </span></p>
<p><span>preci = width; </span></p>
<p><span>width = 0; </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>while (width + len + preci) { </span></p>
<p><span>unsigned char ch; </span></p>
<p><span>/* right padding || left padding */ </span></p>
<p><span>if ((!len &amp;&amp; !preci) </span></p>
<p><span>|| (width &amp;&amp; !flag[FLAG_MINUS_LJUSTIFY])) { </span></p>
<p><span>ch = &#8216; &#8216;; </span></p>
<p><span>&#8211;width; </span></p>
<p><span>} else if (flag[FLAG_PLUS]) { </span></p>
<p><span>ch = flag[FLAG_PLUS]; /* sign */ </span></p>
<p><span>if (flag[FLAG_PLUS]==&#8217;0&#8242;) {     /* base 16 case */ </span></p>
<p><span>flag[FLAG_PLUS] = *p++; /* get the x|X */ </span></p>
<p><span>} else { </span></p>
<p><span>flag[FLAG_PLUS] = &#8216;\0&#8242;; </span></p>
<p><span>} </span></p>
<p><span>&#8211;len; </span></p>
<p><span>} else if (preci) { </span></p>
<p><span>ch = &#8217;0&#8242;; </span></p>
<p><span>&#8211;preci; </span></p>
<p><span>} else { </span></p>
<p><span>ch = *p++; /* main field */ </span></p>
<p><span>&#8211;len; </span></p>
<p><span>} </span></p>
<p><span>++cnt; </span></p>
<p><span><strong>PUTC(ch, op)</strong>; </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span>goto nextfmt; </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>fmt = fmt0;     /* this was an illegal format */ </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>charout: </span></p>
<p><span>++cnt; </span></p>
<p><span><strong>PUTC(*fmt, op);</strong> /* normal char out */ </span></p>
<p><span> </span></p>
<p><span>nextfmt: </span></p>
<p><span>++fmt; </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>i = (__FERROR_UNLOCKED(op)) ? -1 : cnt; </span></p>
<p><span> </span></p>
<p><span>__STDIO_STREAM_VALIDATE(op); </span></p>
<p><span> </span></p>
<p><span>__STDIO_AUTO_THREADUNLOCK(op); </span></p>
<p><span> </span></p>
<p><span>return i; </span></p>
<p><span>} </span></p>
<p><span><em>libc/stdio/old_vfprintf.c</em></span></p>
<p><span>#define PUTC(C,F)      putc_unlocked((C),(F)) </span></p>
<p><span><em>include/stdio.h</em></span></p>
<p><span>define putc_unlocked(_ch, _fp) __PUTC_UNLOCKED(_ch, _fp) </span></p>
<p><span><em>libc/sysdeps/linux/common/bits/uClibc_stdio.h</em></span></p>
<p><span>#define __PUTC_UNLOCKED(__c, __stream)          (__fputc_unlocked)((__c),(__stream)) </span></p>
<p><span><em>libc/stdio/fputc.c</em></span></p>
<p><span>int __fputc_unlocked(int c, register FILE *stream) </span></p>
<p><span>{ </span></p>
<p><span>__STDIO_STREAM_VALIDATE(stream); </span></p>
<p><span> </span></p>
<p><span>/* First the fast path.  We&#8217;re good to go if putc macro enabled. */ </span></p>
<p><span>if (__STDIO_STREAM_CAN_USE_BUFFER_ADD(stream)) { </span></p>
<p><span>__STDIO_STREAM_BUFFER_ADD(stream, ((unsigned char) c)); </span></p>
<p><span>return (unsigned char) c; </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>/* Next quickest&#8230; writing and narrow oriented, but macro </span></p>
<p><span>* disabled and/or buffer is full. */ </span></p>
<p><span>if (__STDIO_STREAM_IS_NARROW_WRITING(stream) </span></p>
<p><span>|| !__STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_NARROW) </span></p>
<p><span>) { </span></p>
<p><span>if (__STDIO_STREAM_IS_FAKE_VSNPRINTF(stream)) { </span></p>
<p><span>return (unsigned char) c; </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>if (__STDIO_STREAM_BUFFER_SIZE(stream)) { /* Do we have a buffer? */ </span></p>
<p><span>/* The buffer is full and/or the stream is line buffered. */ </span></p>
<p><span>if (!__STDIO_STREAM_BUFFER_WAVAIL(stream) /* Buffer full? */ </span></p>
<p><span>&amp;&amp; __STDIO_COMMIT_WRITE_BUFFER(stream) /* Commit failed! */ </span></p>
<p><span>) { </span></p>
<p><span>goto BAD; </span></p>
<p><span>} </span></p>
<p><span>#ifdef __UCLIBC_MJN3_ONLY__ </span></p>
<p><span>#warning CONSIDER: Should we fail if the commit fails but we now have room? </span></p>
<p><span>#endif </span></p>
<p><span> </span></p>
<p><span>__STDIO_STREAM_BUFFER_ADD(stream, ((unsigned char) c)); </span></p>
<p><span> </span></p>
<p><span>if (__STDIO_STREAM_IS_LBF(stream)) { </span></p>
<p><span>if ((((unsigned char) c) == &#8216;\n&#8217;) </span></p>
<p><span>&amp;&amp; __STDIO_COMMIT_WRITE_BUFFER(stream)) { </span></p>
<p><span>/* Commit failed! */ </span></p>
<p><span>__STDIO_STREAM_BUFFER_UNADD(stream); /* Undo the write! */ </span></p>
<p><span>goto BAD; </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span>} else { </span></p>
<p><span>/* NOTE: Do not try to save space by moving uc to the top of </span></p>
<p><span>* the file, as that dramaticly increases runtime. */ </span></p>
<p><span>unsigned char uc = (unsigned char) c; </span></p>
<p><span>if (! <strong>__stdio_WRITE</strong>(stream, &amp;uc, 1)) { //</span>如果没有缓冲区，那么就输出到标准输出中</p>
<p><span>goto BAD; </span></p>
<p><span>} </span></p>
<p><span>} </span></p>
<p><span>return (unsigned char) c; </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>BAD: </span></p>
<p><span>return EOF; </span></p>
<p><span>} </span></p>
<p><span><em>libc/stdio/_WRITE.c</em></span></p>
<p><span>size_t attribute_hidden __stdio_WRITE(register FILE *stream, </span></p>
<p><span>register const unsigned char *buf, size_t bufsize) </span></p>
<p><span>{ </span></p>
<p><span>size_t todo; </span></p>
<p><span>ssize_t rv, stodo; </span></p>
<p><span> </span></p>
<p><span>__STDIO_STREAM_VALIDATE(stream); </span></p>
<p><span>assert(stream-&gt;__filedes &gt;= -1); </span></p>
<p><span>assert(__STDIO_STREAM_IS_WRITING(stream)); </span></p>
<p><span>assert(!__STDIO_STREAM_BUFFER_WUSED(stream)); /* Buffer must be empty. */ </span></p>
<p><span> </span></p>
<p><span>todo = bufsize; </span></p>
<p><span> </span></p>
<p><span>do { </span></p>
<p><span>if (todo == 0) {                /* Done? */ </span></p>
<p><span>__STDIO_STREAM_VALIDATE(stream); </span></p>
<p><span>return bufsize; </span></p>
<p><span>} </span></p>
<p><span>stodo = (todo &lt;= SSIZE_MAX) ? todo : SSIZE_MAX; </span></p>
<p><span>if ((rv = <strong>__WRITE</strong>(stream, (char *) buf, stodo)) &gt;= 0) { //</span>写到标准输出中</p>
<p><span>#ifdef __UCLIBC_MJN3_ONLY__ </span></p>
<p><span>#warning TODO: Make custom stream write return check optional. </span></p>
<p><span>#endif </span></p>
<p><span>#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ </span></p>
<p><span>assert(rv &lt;= stodo); </span></p>
<p><span>if (rv &gt; stodo) {       /* Wrote more than stodo! */ </span></p>
<p><span>/*                              abort(); */ </span></p>
<p><span>} </span></p>
<p><span>#endif </span></p>
<p><span>todo -= rv; </span></p>
<p><span>buf += rv; </span></p>
<p><span>} else </span></p>
<p><span>#ifdef __UCLIBC_MJN3_ONLY__ </span></p>
<p><span>#warning EINTR? </span></p>
<p><span>#endif </span></p>
<p><span>/*              if (errno != EINTR) */ </span></p>
<p><span>{ </span></p>
<p><span>__STDIO_STREAM_SET_ERROR(stream); </span></p>
<p><span> </span></p>
<p><span>#ifdef __STDIO_BUFFERS </span></p>
<p><span>if ((stodo = __STDIO_STREAM_BUFFER_SIZE(stream)) != 0) { </span></p>
<p><span>unsigned char *s; </span></p>
<p><span> </span></p>
<p><span>if (stodo &gt; todo) { </span></p>
<p><span>stodo = todo; </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>s  = stream-&gt;__bufstart; </span></p>
<p><span> </span></p>
<p><span>do { </span></p>
<p><span>if (((*s = *buf) == &#8216;\n&#8217;) </span></p>
<p><span>&amp;&amp; __STDIO_STREAM_IS_LBF(stream) </span></p>
<p><span>) { </span></p>
<p><span>break; </span></p>
<p><span>} </span></p>
<p><span>++s; </span></p>
<p><span>++buf; </span></p>
<p><span>} while (&#8211;stodo); </span></p>
<p><span> </span></p>
<p><span>stream-&gt;__bufpos = s; </span></p>
<p><span> </span></p>
<p><span>todo -= (s &#8211; stream-&gt;__bufstart); </span></p>
<p><span>} </span></p>
<p><span>#endif /* __STDIO_BUFFERS */ </span></p>
<p><span> </span></p>
<p><span>__STDIO_STREAM_VALIDATE(stream); </span></p>
<p><span>return bufsize &#8211; todo; </span></p>
<p><span>} </span></p>
<p><span>} while (1); </span></p>
<p><span>} </span></p>
<p><span>libc/stdio/_stdio.h</span></p>
<p><span>#define __WRITE(STREAMPTR,BUF,SIZE) \ </span></p>
<p><span>((((STREAMPTR)-&gt;__gcs.write) == NULL) ? -1 : \ </span></p>
<p><span>(((STREAMPTR)-&gt;__gcs.write)((STREAMPTR)-&gt;__cookie,(BUF),(SIZE)))) </span></p>
<p>这个<span>write</span>到底怎么实现呢？</p>
<p>看：</p>
<p><span>libc/stdio/_fopen.c</span>：</p>
<p><span>__STDIO_STREAM_RESET_GCS(stream); </span></p>
<p><span>libc/stdio/_stdio.h</span>：</p>
<p><span>#define __STDIO_STREAM_RESET_GCS(S) \ </span></p>
<p><span>(S)-&gt;__cookie = &amp;((S)-&gt;__filedes); \ </span></p>
<p><span>(S)-&gt;__gcs.read = _cs_read; \ </span></p>
<p><span>(S)-&gt;__gcs.write = _cs_write; \ </span></p>
<p><span>(S)-&gt;__gcs.seek = _cs_seek; \ </span></p>
<p><span>(S)-&gt;__gcs.close = _cs_close </span></p>
<p><span>libc/stdio/_cs_funcs.c</span>：</p>
<p><span>ssize_t attribute_hidden _cs_write(void *cookie, const char *buf, size_t bufsize) </span></p>
<p><span>{ </span></p>
<p><span>return write(*((int *) cookie), (char *) buf, bufsize); </span></p>
<p><span>} </span></p>
<p>这个<span>write</span>是怎么实现呢？通过系统调用，不过，这个<span>write</span>的<span>API</span>在哪里实现呢？</p>
<p>在内核中，我还没有找到，不过，从<span>Linux</span>的<span>API</span>列表中，看到了。具体方法是：</p>
<p><span>#:man 2 syscalls</span></p>
<p>系统调用服务例程：</p>
<p><span>asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t count) </span></p>
<p><span>{ </span></p>
<p><span>struct file *file; </span></p>
<p><span>ssize_t ret = -EBADF; </span></p>
<p><span>int fput_needed; </span></p>
<p><span> </span></p>
<p><span><strong>file</strong> = fget_light(fd, &amp;fput_needed); </span></p>
<p><span>if (file) { </span></p>
<p><span>loff_t pos = file_pos_read(file); </span></p>
<p><span>ret = vfs_write(file, buf, count, &amp;pos); </span></p>
<p><span>file_pos_write(file, pos); </span></p>
<p><span>fput_light(file, fput_needed); </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>return ret; </span></p>
<p><span>} </span></p>
<p><span>struct file *fget_light(unsigned int fd, int *fput_needed) </span></p>
<p><span>{ </span></p>
<p><span>struct file *file; </span></p>
<p><span>struct files_struct *files = current-&gt;files; </span></p>
<p><span> </span></p>
<p><span>*fput_needed = 0; </span></p>
<p><span>if (likely((atomic_read(&amp;files-&gt;count) == 1))) { </span></p>
<p><span>file = fcheck_files(files, fd); </span></p>
<p><span>} else { </span></p>
<p><span>rcu_read_lock(); </span></p>
<p><span>file = fcheck_files(files, fd); </span></p>
<p><span>if (file) { </span></p>
<p><span>if (atomic_inc_not_zero(&amp;file-&gt;f_count)) </span></p>
<p><span>*fput_needed = 1; </span></p>
<p><span>else </span></p>
<p><span>/* Didn&#8217;t get the reference, someone&#8217;s freed */ </span></p>
<p><span>file = NULL; </span></p>
<p><span>} </span></p>
<p><span>rcu_read_unlock(); </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>return file; </span></p>
<p><span>} </span></p>
<p><span>fs/read_write.c</span></p>
<p><span>ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) </span></p>
<p><span>{ </span></p>
<p><span>ssize_t ret; </span></p>
<p><span> </span></p>
<p><span>if (!(file-&gt;f_mode &amp; FMODE_WRITE)) </span></p>
<p><span>return -EBADF; </span></p>
<p><span>if (!file-&gt;f_op || (!file-&gt;f_op-&gt;write &amp;&amp; !file-&gt;f_op-&gt;aio_write)) </span></p>
<p><span>return -EINVAL; </span></p>
<p><span>if (unlikely(!access_ok(VERIFY_READ, buf, count))) </span></p>
<p><span>return -EFAULT; </span></p>
<p><span> </span></p>
<p><span>ret = rw_verify_area(WRITE, file, pos, count); </span></p>
<p><span>if (ret &gt;= 0) { </span></p>
<p><span>count = ret; </span></p>
<p><span>if (file-&gt;f_op-&gt;write) </span></p>
<p><span>ret = file-&gt;f_op-&gt;<strong>write</strong>(file, buf, count, pos); </span></p>
<p><span>else </span></p>
<p><span>ret = do_sync_write(file, buf, count, pos); </span></p>
<p><span>if (ret &gt; 0) { </span></p>
<p><span>fsnotify_modify(file-&gt;f_path.dentry); </span></p>
<p><span>add_wchar(current, ret); </span></p>
<p><span>} </span></p>
<p><span>inc_syscw(current); </span></p>
<p><span>} </span></p>
<p><span> </span></p>
<p><span>return ret; </span></p>
<p><span>} </span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/10/12/printf%e4%bb%8e%e5%87%bd%e6%95%b0%e5%ba%93%e5%88%b0os%e8%b7%9f%e8%b8%aa%e6%b5%81%e7%a8%8b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A Skills List for Developing Embedded Software</title>
		<link>http://www.tek-life.org/2010/10/09/a-skills-list-for-developing-embedded-software/</link>
		<comments>http://www.tek-life.org/2010/10/09/a-skills-list-for-developing-embedded-software/#comments</comments>
		<pubDate>Sat, 09 Oct 2010 03:00:38 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[Linux Kernel]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10453</guid>
		<description><![CDATA[A Skills List for Developing Embedded Software   Dale Word Dept. of Electrical and Computer Engineering California State University, Chico 1. What’s Unique About Embedded Development The very nature of embedded systems dictates that the development of software for them &#8230; <a href="http://www.tek-life.org/2010/10/09/a-skills-list-for-developing-embedded-software/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div>
<p><em><span style="font-family: verdana; font-size: x-large;">A Skills List for Developing Embedded Software</span></em><br />
 </p>
<p>Dale Word<br />
Dept. of Electrical and Computer Engineering<br />
California State University, Chico</p>
</div>
<div>
<h2><a name="TOC-1.-What-s-Unique-About-Embedded-Dev"></a>1. What’s Unique About Embedded Development</h2>
<p><span style="font-size: x-small;">The very nature of embedded systems dictates that the development of software for them is different than software for general purpose computers.</span></p>
<h3><a name="TOC-a.-Background"></a><span style="font-size: x-small;">a. Background</span></h3>
<p><span style="font-size: x-small;">The key factors that separate embedded computer systems from other type of computer based systems are as follows:</span></p>
<p><span style="font-size: x-small;">· Dedicated Function – Embedded systems are designed to perform one specific set of functions, typically as part of a larger device or system. This is in contrast with a typical desktop system that is a general purpose computing platform, designed to support a variety of applications, each performing a different set of functions.</span></p>
<p><span style="font-size: x-small;">· Limited Resources – Due to the fact that embedded systems are typically a component of a larger system, rather than a standalone computing device, they commonly are designed with limited computing resources. This can be the result a desire to reduce production costs, or as a result of the operating environment of the end system. These limitations include the lack of graphical user interfaces, limited storage devices, limited processing power, etc.</span></p>
<p><span style="font-size: x-small;">· Hardware Interaction – In general purpose computing systems, the details of the operating environment a typically abstracted by different operating system layers, to enhance portability and interoperability. The limited resources and specific nature of embedded systems prevent the use of those same layers, requiring the embedded software to interface to the operating environment much more directly.</span></p>
<p><span style="font-size: x-small;">· Real-Time Processing – The high level of integration with additional devices and equipment in embedded systems frequently dictate that they perform their processing in a fixed time interval. The “realtime” aspect of the application adds additional complexity to all phases of the development process, from design to testing.</span></p>
<p><span style="font-size: x-small;">Defining the characteristics of embedded systems is very inexact, because embedded systems vary widely in their configurations and capabilities. There is a full spectrum of systems that are considered embedded systems, from the smallest 8 bit microcontroller system, to the highest end, high performance multiprocessor based multitasking system.</span></p>
<h3><a name="TOC-b.-Differentiating-Factors-Developm"></a><span style="font-size: x-small;">b. Differentiating Factors – Development</span></h3>
<p><span style="font-size: x-small;">The unique set of characteristics that define embedded systems result in an equally unique set of development considerations.</span></p>
<p><span style="font-size: x-small;">· Resource Limitations – Developing code in a resource limited environment requires developers to consider not only the functional aspects of their code, but also its resource usage. This includes memory usage, processor loading, data representation, etc.</span></p>
<p><span style="font-size: x-small;">· Hardware Interaction – The low level, hardware intensive code that is required in many embedded systems requires an intimate knowledge of the hardware components being used, and the techniques required to access them directly.</span></p>
<p><span style="font-size: x-small;">· Real-Time – The addition of temporal requirements to any set of software can multiply the complexity of the implementation. The need to consider execution time and behavior patterns significantly changes every aspect of the implementation process.</span></p>
<p><span style="font-size: x-small;">· Development Cycle &#8211; In software development efforts for general purpose computers, the target hardware platform is well-known, fixed, and well defined. In embedded systems this is frequently not the case. The target hardware can vary from a sophisticated, well defined, off-the-shelf platform to a completely untested prototype design. In the cases where hardware development is parallel to the software development, the potential for hardware problems requires that embedded developers consider a much more complex set of possibilities when problem solving. If the software development cycle precedes the hardware development, the software set may need to be developed on a prototype platform, and ported to the actual target when it becomes available.</span></p>
<p><span style="font-size: x-small;"> </span></p>
<h2><a name="TOC-2.-Historical-Perspective-on-Softwa"></a>2. Historical Perspective on Software Development</h2>
<p><span style="font-size: x-small;">Software development, in years past, required many of the skills that embedded development continues to require today. Historically, operating systems and development environments were not sophisticated enough to eliminate the need for the low level skills of an embedded developer. As the technologies have improved, there has been a movement toward more and more abstraction of the underlying hardware and operating system. This increasing abstraction of the target hardware, combined with increasingly sophisticated development tools, has given rise to a growing number of software developers that do not possess the skills required by embedded development. Graphically based, integrated development toolsets have only exacerbated the problem, allowing “point and click” development cycles, that require little or no understanding of what is going on below the application level. In fairness, it should be stated that this trend toward abstraction and tool sophistication has yielded great gains in productivity for general software development. Unfortunately, these gains have come at the cost of losing the skill base that applies directly to embedded development.</span></p>
<p><span style="font-size: x-small;"> </span></p>
<h2><a name="TOC-3.-Detailed-Skills-List"></a>3. Detailed Skills List</h2>
<p><span style="font-size: x-small;">The development of embedded software applications requires a skill set that<br />
focuses on these unique characteristics of embedded systems.</span></p>
<h3><a name="TOC-a.-Architecture-Concepts"></a><span style="font-size: x-small;">a. Architecture Concepts</span></h3>
<p><span style="font-size: x-small;">One of the key areas of knowledge that is required for embedded development is an understanding and awareness of the underlying computer architecture of the target system.</span></p>
<p><span style="font-size: x-small;">· Memory Map View &#8211; Embedded developers need to have a very “physical” view of their applications. This includes an understanding of the overall memory layout of the target system, and where each component of their application resides in that memory. This view can be obtained by a quick study of the memory map (generated during the link process). It should include an understanding of where code and data segments exist, where stack, heap and constant storage is allocated. This “physical” view, like many of the other skills described in this section, is more critical in systems that have limited memory resources, and less critical in systems with more memory and more sophisticated operating systems.</span></p>
<p><span style="font-size: x-small;">· Stack Operations &#8211; One of the best ways to gather information about the current state of execution in a multitasking embedded system is to take a snapshot of the stack, and use it to determine the sequence of calls that have led to the current state. In order to do this, a developer must have a good understanding of the way exception processing and context switching affect the contents of the stack, and the structure of the stack frames and context sets that are pushed onto the stack.</span></p>
<p><span style="font-size: x-small;">· Instruction Set Architecture &#8211; An understanding of the target processor details is another important part of an embedded developer’s skill set. This should include things like: Register Sets, Processor Modes, Addressing Modes, Processor Status Registers, etc. This set of skills may not be required in most development cycles, but can be crucial in resolving subtle problems during debugging, or when implementing advanced features that directly rely on some unique aspect of the processor.</span></p>
<p><span style="font-size: x-small;">· Exception Handling &#8211; A basic understanding of exception and interrupt handling is important in dealing with external events or data sources, and error conditions that result in a processor exception. This level of processing is typically handled for, and hidden from the developer in more sophisticated operating system and toolset systems.</span></p>
<h3><a name="TOC-b.-Programming-Skills:"></a><span style="font-size: x-small;">b. Programming Skills:</span></h3>
<p><span style="font-size: x-small;">The range of programming skills and the different areas of focus can be viewed as a spectrum, from the most abstract, high level programming environments to the lowest level, most hardware specific environment. While development for general purpose machines typically focuses on more abstract, high level programming concepts, embedded developers must also focus on low level, more hardware specific details. Figure 1 depicts this relationship graphically.</span></p>
<p><span style="font-size: x-small;"><a href="http://www.tek-life.org/wp-content/uploads/2010/10/f1-large.jpg"><img class="aligncenter size-full wp-image-10454" title="f1-large" src="http://www.tek-life.org/wp-content/uploads/2010/10/f1-large.jpg" alt="" width="420" height="278" /></a><br />
</span></p>
<p><a href="http://sites.google.com/site/caocliff/f1.jpg"></a></p>
<p><span style="font-size: x-small;">· Assembly Language Programming &#8211; Assembly language code historically has been an important part of many embedded systems, providing better performance and a smaller footprint for critical sections of code. With the advances in processor speed and memory size relative to cost, this is becoming less and less necessary. A basic knowledge of the assembly language of your target processor is important, but more of a background topic than a central development skill as it was in the past.</span></p>
<p><span style="font-size: x-small;">· Low Level C Programming &#8211; One of the key characteristics of embedded software is its low level, direct interaction with hardware. To implement these functions in C, there are a few specific aspects of the language that need to be understood:<br />
     o Direct memory access using type casting of constant values<br />
          *(int *)0xFFF0000 = some_value;<br />
          status = *(int *)0xFFF00000;<br />
     o Bit level operations, including masking and shifting<br />
         *(int *)UART_STAT_REG = uart_status &amp; TX_EN_BIT &amp; RX_EN_BIT;</span></p>
<p><span style="font-size: x-small;">While these are not new concepts to experienced C programmers, they are commonly misunderstood or overlooked by developers coming from a higher level, more abstract programming background.</span></p>
<h3><a name="TOC-c.-Tools-Environment"></a><span style="font-size: x-small;">c. Tools/Environment</span></h3>
<p><span style="font-size: x-small;">The environment used to develop embedded software is sometimes the thing that is most different from other types of software development.</span></p>
<p><span style="font-size: x-small;">· Headless Targets &#8211; The very nature of an embedded device that doesn’t have any kind of external display or interfaces can make getting debugging information difficult. If you have sophisticated tools, this is usually not a problem. For the rare case where you don’t have support form tools, some cleverness and creativity may be required to gain visibility into the embedded application. This may involve using hardware that was not intended to communicate data to provide rudimentary information access.</span></p>
<p><span style="font-size: x-small;">· Cross Development &#8211; One of the key aspects that helps define embedded development is the pattern of cross development – creating code on a machine of one type, to run on a machine of a different type. Once this “foreign” executable has been created, it must be loaded onto the target hardware. This involves several concepts that are important for a developer to understand. First, a developer needs to understand what the boot sequence of the target device really is. Typically the device will boot to a monitor program in ROM, initialize some communications interface, and then wait to receive an executable code image from the development machine. Understanding this sequence, and where each of these components resides in the overall memory map is essential for a developer to have a clear picture of the target board’s behavior.</span></p>
<p><span style="font-size: x-small;">· Hardware Knowledge &#8211; The close interaction with hardware in embedded systems requires that developers understand the characteristics and capabilities of each external (to the CPU) hardware component being accessed. This usually means studying databooks and reference manuals for the specific chips involved, and developing an understanding of the configuration and operation of each of them. This is an area in which example code sets can be of great value in helping speed up the learning process. Looking at an existing driver for the chip you’re using can often yield great insights into its use.</span></p>
<h3><a name="TOC-d.-Problem-Solving"></a><span style="font-size: x-small;">d. Problem Solving</span></h3>
<p><span style="font-size: x-small;">One of the things that separates embedded development most from general purpose systems development is the unique set of problem solving skills it requires.</span></p>
<p><span style="font-size: x-small;">· Real-Time Debugging &#8211; The addition of real time constraints to an application not only complicates the design and development process, but can make the debugging process extremely complex. The key difference is the need to maintain an intuitive model of the temporal aspects of the application. This includes recognizing the different behaviors that result from timing dependencies, resource conflicts, interrupt processing, etc.</span></p>
<p><span style="font-size: x-small;">· Understand Your Toolset &#8211; Starting out with any new embedded development tool set can be a trying process. Most modern tools sets provide a wealth of information and features, but they require an investment of time to learn the details of how to make use of these capabilities. The time spent learning a toolset almost always pays off later in time saved during debugging. Besides learning what a toolset CAN do, it is also crucial to understand its limitations. Examples of this include understanding the effects of remote debug tasks running on your target systems, or pipeline behavior and its affect on emulator performance.</span></p>
<p><span style="font-size: x-small;">· Holistic Problem Solving &#8211; The term holsitic is defined as “ views in which the individual elements of a system are determined by their relations to all other elements of that system”. A holistic approach to problem solving involves looking at all the system components, and objectively analyzing the data to determine the root causes of a problem. The holistic nature of this approach means that the interactions of all components (hardware, RTOS, application software, etc. ) are considered together to identify the causes of unwanted behaviors.</span></p>
<p><a href="http://www.tek-life.org/wp-content/uploads/2010/10/f2-large.jpg"><img class="aligncenter size-full wp-image-10455" title="f2-large" src="http://www.tek-life.org/wp-content/uploads/2010/10/f2-large.jpg" alt="" width="253" height="420" /></a></p>
<p><a href="http://sites.google.com/site/caocliff/f2.jpg"></a><span style="font-size: x-small;">· Vertical vs Horizontal Problem Space &#8211; When considering the components that make up any computer system, one way to view the overall picture is to look at it as a hierarchy, layered from the most concrete (hardware) to the most abstract (user interface). Given this view as a backdrop, it is useful to look at the number of layers that are incorporated into a typical training or problem solving exercise. For most formal academic programs, the focus is on doing very detailed work on one or two layers, taking a “horizontal slice” of the overall problem space. This approach, while giving a great deal of exposure to one specific layer of the problem space, fails to model the way difficult problems are solved in real commercial environments.</span></p>
<p><span style="font-size: x-small;">· Given a difficult or subtle problem, it is often required to take a top to bottom view of the problem, or a “vertical slice” of the problem space. This ability to seek solutions across functional boundaries, and “drill down” through the layers of a system is characteristic of embedded systems development.</span></p>
<p><span style="font-size: x-small;">· Problem Solving Methodology &#8211; Arthur Clarke, the famous science fiction author and futurist, once said: “Any sufficiently advanced technology is indistinguishable from magic.” I would propose that there is an embedded corollary that says: “advanced technology produces problems that ARE indistinguishable from magic (at first)”. The following is a list of problem solving steps:</span></p>
<p><span style="font-size: x-small;">     1. Find Clues – Like NTSB sifting through the wreckage of a plane crash – examine each small piece may have some significance. Look at stack traces, buffer contents, etc., identifying anything that indicates unexpected behavior.</span></p>
<p><span style="font-size: x-small;">     2. Gather as much information as possible – The solution often lies not in some complex implementation detail, but rather in taking a simple (and somehow different) view of what is occurring.</span></p>
<p><span style="font-size: x-small;">     3. </span><span style="font-size: x-small;">Pursue Theories –Like walking a maze, pursue each path until you’re sure it has been exhausted. When that path has been exhausted, backtrack and start pursuing another one.</span></p>
<p><span style="font-size: x-small;">     4. Find the answer – Don’t assume that making some fairly arbitrary change that makes the problem go away is an adequate solution. Find the root cause, so you can be sure it is completely eradicated from the system.</span></p>
<p><span style="font-size: x-small;">     5. Logic is your main tool – No matter how much pressure you’re under, hang on to a logical, methodical approach. Be careful to distinguish between what you know (and have proven), versus what you’re assuming.</span></p>
<p><span style="font-size: x-small;"> </span></p>
<h2><a name="TOC-4.-Transitioning-to-Embedded-Develo"></a>4. Transitioning to Embedded Development</h2>
<p><span style="font-size: x-small;">For any organization transitioning to embedded development for the first time, the first, most critical resource is the development staff. Creating an embedded development team means either hiring the necessary talent or providing specific training for current developers who may lack the specific skills required for embedded development.</span></p>
<h3><a name="TOC-a.-Hiring---Degree-programs"></a><span style="font-size: x-small;">a. Hiring &#8211; Degree programs</span></h3>
<p><span style="font-size: x-small;">Obviously, if the situation permits, hiring experienced embedded developers is preferable. While evaluating the industry experience of a candidate and its applicability to any development effort is too specific to discuss in general terms, the applicability of different engineering degree types and their applicability to embedded development can be briefly summarized.</span></p>
<p><span style="font-size: x-small;">· Computer Science &#8211; In the past 20 years or so, the focus of Computer Science degree programs has changed. In the early days of the computer industry, Computer Science programs tended to teach the fundamental concepts associated with software theory, development and design – without addressing specific application areas as major topics. As the industry and technologies have matured, these application areas have become recognized sub-disciplines of Computer Science. This has led to more concentration on these more abstract areas of study, with a reduction in the amount of time spent addressing low level, concrete details. For embedded development, this means that some Computer Science graduates may lack adequate exposure to things like assembly language programming, interrupt and driver level code, etc. The positive aspect of this trend toward abstraction is that many of these same graduates may be more highly skilled in design approaches, leading to higher quality code once they overcome any low level shortcomings they may have.</span></p>
<p><span style="font-size: x-small;">· Electrical Engineering &#8211; On the opposite end of the spectrum is the hardware-focused discipline of Electrical Engineering. Just as in Computer Science, the evolution of the industry has led to more diversity and specialization in this discipline also. This trend requires an increased focus on these Electrical Engineering specific topics, and less on software related concepts. One development that may help prepare Electrical Engineering students for a transition to more software oriented tasks is the growth in the use of high level logic design languages. Experience with these tools, even in the context of circuit design, provides some fundamental knowledge that can ease the transition into code development later.</span></p>
<p><span style="font-size: x-small;">· Computer Engineering &#8211; Given the constant growth in the amount of information that must be included in Computer Science and Electrical Engineering programs, it follows that these programs would need to spend more time addressing those topics firmly within their disciplines, leaving less time to address topics in the other discipline. As these programs migrate toward their ends of the spectrum, the growing void is filled by the somewhat younger discipline of Computer Engineering. Focusing on both hardware and software concepts, it clearly addresses the issues associated with embedded development.</span></p>
<p><span style="font-size: x-small;">· Finding the Right Fit &#8211; Figure 3 shows a graphical view of this degree program spectrum.</span></p>
<p><span style="font-size: x-small;"><a href="http://www.tek-life.org/wp-content/uploads/2010/10/f3-large.jpg"><img class="aligncenter size-full wp-image-10456" title="f3-large" src="http://www.tek-life.org/wp-content/uploads/2010/10/f3-large.jpg" alt="" width="420" height="289" /></a><br />
</span></p>
<p><a href="http://sites.google.com/site/caocliff/f3.jpg"></a></p>
<p><span style="font-size: x-small;">The key to finding the best candidates for a given organization is to identify where in this spectrum of knowledge skills are needed, and finding candidates whose backgrounds match up.</span></p>
<h3><a name="TOC-b.-Professional-Training"></a><span style="font-size: x-small;">b. Professional Training</span></h3>
<p><span style="font-size: x-small;">For existing employees without a background in embedded development, or for those needing a refresher course, some professional training may be required.</span></p>
<p><span style="font-size: x-small;">· Extension/Remote Classroom Programs &#8211; With the growth in remote classes being offered via the internet, many options exist for acquiring embedded development education in this manner. The following is a brief list of some of these programs:<br />
     Embedded development certificate programs:<br />
          University of California, San Diego<br />
          University of Colorado at Boulder<br />
          University of Washington<br />
    Embedded development courses offered:<br />
          University of California, Berkeley<br />
          University of California, Irvine<br />
          Iowa State University<br />
     (See the references at the end of this document for web links to each of these programs)</span></p>
<p><span style="font-size: x-small;">· Mentoring &#8211; A more informal means of providing training is to establish a mentoring program, using more experienced developers to train their less experienced peers. One way to accomplish this is to have the trainee “shadow” the mentor through a full development cycle. If there are no experienced mentors available in-house, it may be beneficial to hire outside consultants to lead an early development cycle, while acting as mentors to the permanent development staff.</span></p>
<p><span style="font-size: x-small;">· Self Teaching &#8211; A final option, if no other resources are available, is to explore selfteaching exercises. The most common way to do this is with the aid of a reference text. This text should address general embedded development concepts, and provide a set of examples to work through, on some form of target hardware. This kind of exercise can provide a good initial hands-on learning experience. The problems with this approach are 1) help can be difficult to get when you get stuck, and 2) they’re usually based on fairly simplistic, low cost target hardware, restricting the complexity of the exercises. (See the book references at the end of this document for some title suggestions)</span></p>
<p><span style="font-size: x-small;"> </span></p>
<h2><a name="TOC-5.-Tips-Hints"></a>5. Tips/Hints</h2>
<p><span style="font-size: x-small;">The following is a list of informal tips for getting started with, and getting through your first embedded development effort:</span></p>
<p><span style="font-size: x-small;">· Start with reliable target hardware – The last thing you need during your first embedded project is to have more problems and variables introduced by the hardware. If your final target is a custom board, start with a similar evaluation board until you get past the early stages of the software development process.</span></p>
<p><span style="font-size: x-small;">· Do not overestimate the value of demo code – Getting someone else’s code to run, no matter how great of a demo it is, is not the same as generating your own working code from scratch. There may be several issues to be resolved in the process of getting your code to actually run on the board.</span></p>
<p><span style="font-size: x-small;">· Start simple – Get it to do something simple first. Take small steps early, to verify your basic assumptions, before you start adding complexity.</span></p>
<p><span style="font-size: x-small;">· DO NOT expect vendors to design your product – Most vendors of embedded development products provide great support, often with very experienced embedded developers. Be careful not to ask them to design your product for you, 1) it’s not their job – they support their product, not everything that can be produced with it, and 2) you’ll probably get a design that looks more like their latest application note, rather than a good solution for your product.</span></p>
<p><span style="font-size: x-small;">· DO expect vendors to solve their own problems – When you encounter problems with a tool, and the answer is not obvious in the documentation, use the vendor’s support services. You may be struggling with a problem they’ve seen before. Be prepared to isolate the problem section of your code by generating a “cut-down” version of the code – this will greatly simplify the process of identifying the true cause of the problem.</span></p>
<p><span style="font-size: x-small;">· Keep a test/development log – Embedded systems, particularly real-time embedded systems, can exhibit complex and subtle behavior patterns. Over the span of a lengthy development cycle, it is difficult to track the cause-effect relationship between changes to the code and the resulting effect on behavior. A simple way to maintain a persistent record of this is to keep a log, with entries for every set of code changes, the date, and the test results generated. This does not have to be anything rigid or formal, but rather a set of notes, tailored to each developer’s personal style. Using some form of version control software to manage the code base is also very helpful, yielding a precise record of every change to the software, in chronological order.</span></p>
<p><span style="font-size: x-small;">· No matter how mysterious a problem seems, be methodical – Resist the temptation to make arbitrary changes to see if you can make an illusive problem go away. This will probably just cover up a problem that will come back to haunt you later. [Also: No matter how mysterious or bizarre the problem seems, don’t start sacrificing chickens or making offerings to the digital gods – it won’t help, and it looks vaguely unprofessional.]</span></p>
<p><span style="font-size: x-small;"> </span></p>
<h2><a name="TOC-6.-Conclusion---Defining-Your-Needs"></a>6. Conclusion &#8211; Defining Your Needs</h2>
<p><span style="font-size: x-small;">There is no single definition of the skill set required to begin embedded software development. The answer varies with each different development configuration. Factors like the complexity of the target system, the time and budget constraints involved, and the background of the existing development staff all contribute to the unique character of each development effort. The skills needed to develop embedded code for a simple, 8-bit microcontroller based product are distinctly different than those required for a high-end real-time system with multiple 32-bit processors. The key is to identify what your specific needs are for your development, and ensure that you satisfy those needs with the appropriate staff and equipment.</span></p>
<p><span style="font-size: x-small;"> </span></p>
<h2><a name="TOC-7.-References"></a>7. References</h2>
<h3><a name="TOC-a.-Books"></a><span style="font-size: x-small;">a. Books</span></h3>
<p><span style="font-size: x-small;">“Programming Embedded Systems in C and C++”, Michael Barr, O’Reilly</span></p>
<p><span style="font-size: x-small;">“Fundamentals of Embedded Software: Where C and Assembly Meet”,<br />
Daniel Lewis, Prentice Hall</span></p>
<p><span style="font-size: x-small;">“MicroC/OS-II, the Real Time Kernel”, Jean Labrosse, CMP<br />
</span></p>
<h3><a name="TOC-b.-Websites"></a><span style="font-size: x-small;">b. Websites</span></h3>
<p><span style="font-size: x-small;">Embedded development certificate programs:</span></p>
<p><span style="font-size: x-small;">University of California, San Diego<br />
</span><a rel="nofollow" href="http://www.extension.ucsd.edu/Programs/certificate_directory_su00.html"><span style="font-size: x-small;">http://www.extension.ucsd.edu/Programs/certificate_directory_su00.html</span></a><br />
<span style="font-size: x-small;">University of Colorado at Boulder<br />
</span><a rel="nofollow" href="http://ece-www.colorado.edu/embedded.html"><span style="font-size: x-small;">http://ece-www.colorado.edu/embedded.html</span></a><br />
<span style="font-size: x-small;">University of Washington<br />
</span><a rel="nofollow" href="http://www.outreach.washington.edu/extinfo/certprog/emb/emb_main.asp"><span style="font-size: x-small;">http://www.outreach.washington.edu/extinfo/certprog/emb/emb_main.asp</span></a></p>
<p><span style="font-size: x-small;">Embedded development courses:</span></p>
<p><span style="font-size: x-small;">University of California, Berkeley<br />
</span><a rel="nofollow" href="http://www.unex.berkeley.edu/eng/#8"><span style="font-size: x-small;">http://www.unex.berkeley.edu/eng/#8</span></a><br />
<span style="font-size: x-small;">University of California, Irvine<br />
</span><a rel="nofollow" href="http://www.unex.uci.edu/cgi-bin/order/scan/sf=catno/se=214"><span style="font-size: x-small;">http://www.unex.uci.edu/cgi-bin/order/scan/sf=catno/se=214</span></a><br />
<span style="font-size: x-small;">Iowa State University<br />
</span><a rel="nofollow" href="http://www.lifelearner.iastate.edu/courses/disciplinesp03.htm"><span style="font-size: x-small;">http://www.lifelearner.iastate.edu/courses/disciplinesp03.htm</span></a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/10/09/a-skills-list-for-developing-embedded-software/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[转] platform设备驱动全透析</title>
		<link>http://www.tek-life.org/2010/09/26/%e8%bd%ac-platform%e8%ae%be%e5%a4%87%e9%a9%b1%e5%8a%a8%e5%85%a8%e9%80%8f%e6%9e%90/</link>
		<comments>http://www.tek-life.org/2010/09/26/%e8%bd%ac-platform%e8%ae%be%e5%a4%87%e9%a9%b1%e5%8a%a8%e5%85%a8%e9%80%8f%e6%9e%90/#comments</comments>
		<pubDate>Sun, 26 Sep 2010 07:34:09 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Device driver]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/09/26/%e8%bd%ac-platform%e8%ae%be%e5%a4%87%e9%a9%b1%e5%8a%a8%e5%85%a8%e9%80%8f%e6%9e%90/</guid>
		<description><![CDATA[转自：宋宝华博客http://blog.donews.com/21cnbao/archive/2010/07/14/1581997.aspx 宋宝华，其人：畅销书《linux设备驱动开发详解》作者。不多介绍。赶紧转入正题。 1.1 platform总线、设备与驱动 在Linux2.6的设备驱动模型中，关心总线、设备和驱动这3个实体，总线将设备和驱动绑定。在系统每注册一个设备的时候，会寻找与之匹配的驱动；相反的，在系统每注册一个驱动的时候，会寻找与之匹配的设备，而匹配由总线完成。 一个现实的Linux设备和驱动通常都需要挂接在一种总线上，对于本身依附于PCI、USB、I2C、SPI等的设备而言，这自然不是问题，但是在嵌入式系统里面，SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设等确不依附于此类总线。基于这一背景，Linux发明了一种虚拟的总线，称为platform总线，相应的设备称为platform_device，而驱动成为platform_driver。 注意，所谓的platform_device并不是与字符设备、块设备和网络设备并列的概念，而是Linux系统提供的一种附加手段，例如，在S3C6410处理器中，把内部集成的I2C、RTC、SPI、LCD、看门狗等控制器都归纳为platform_device，而它们本身就是字符设备。platform_device结构体的 定义如代码清单1所示。 代码清单1 platform_device结构体 1struct platform_device { 2const char * name;/* 设备名 */ 3 u32 id; 4 struct device dev; 5 u32  num_resources;/* 设备所使用各类资源数量 */ 6  struct resource * resource;/* 资源 */ 7 }; &#8230; <a href="http://www.tek-life.org/2010/09/26/%e8%bd%ac-platform%e8%ae%be%e5%a4%87%e9%a9%b1%e5%8a%a8%e5%85%a8%e9%80%8f%e6%9e%90/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><!-- pre.cjk { font-family: "DejaVu Sans",monospace; }p { margin-bottom: 0.21cm; }h5 { margin-bottom: 0.21cm; }h5.ctl { font-family: "Lohit Hindi"; } --></p>
<h5 class="cjk">转自：宋宝华博客http://blog.donews.com/21cnbao/archive/2010/07/14/1581997.aspx</h5>
<p>宋宝华，其人：畅销书《linux设备驱动开发详解》作者。不多介绍。赶紧转入正题。</p>
<h5 class="cjk"><big><big><big><span>1.1 platform</span>总线、设备与驱动</big></big></big></h5>
<p style="margin-bottom: 0cm;">在<span>Linux2.6</span>的设备驱动模型中，关心总线、设备和驱动这<span>3</span>个实体，总线将设备和驱动绑定。在系统每注册一个设备的时候，会寻找与之匹配的驱动；相反的，在系统每注册一个驱动的时候，会寻找与之匹配的设备，而匹配由总线完成。</p>
<p style="margin-bottom: 0cm;">一个现实的<span>Linux</span>设备和驱动通常都需要挂接在一种总线上，对于本身依附于<span>PCI</span>、<span>USB</span>、<span>I<sup>2</sup>C</span>、<span>SPI</span>等的设备而言，这自然不是问题，但是在嵌入式系统里面，<span>SoC</span>系统中集成的独立的外设控制器、挂接在<span>SoC</span>内存空间的外设等确不依附于此类总线。基于这一背景，<span>Linux</span>发明了一种虚拟的总线，称为<span>platform</span>总线，相应的设备称为<span>platform_device</span>，而驱动成为<span>platform_driver</span>。</p>
<p style="margin-bottom: 0cm;">注意，所谓的<span>platform_device</span>并不是与字符设备、块设备和网络设备并列的概念，而是<span>Linux</span>系统提供的一种附加手段，例如，在<span>S3C6410</span>处理器中，把内部集成的<span>I<sup>2</sup>C</span>、<span>RTC</span>、<span>SPI</span>、<span>LCD</span>、看门狗等控制器都归纳为<span>platform_device</span>，而它们本身就是字符设备。<span>platform_device</span>结构体的<br />
定义如代码清单<span>1</span>所示。</p>
<p style="margin-bottom: 0cm;">代码清单<span>1<br />
</span></p>
<p style="margin-bottom: 0cm;"><span>platform_device</span>结构体</p>
<p style="margin-bottom: 0cm;"><span>1struct platform_device {</span></p>
<p style="margin-bottom: 0cm;"><span>2const char * name;/* </span>设备名 <span>*/</span></p>
<p style="margin-bottom: 0cm;"><span>3 u32 id;</span></p>
<p style="margin-bottom: 0cm;"><span>4 struct device dev;</span></p>
<p style="margin-bottom: 0cm;"><span>5 u32  num_resources;/* </span>设备所使用各类资源数量 <span>*/</span></p>
<p style="margin-bottom: 0cm;"><span>6  struct resource * resource;/* </span>资源 <span>*/</span></p>
<p style="margin-bottom: 0cm;"><span>7 };</span></p>
<p style="margin-bottom: 0cm;"><span>platform_driver</span>这个结构体中包含<span>probe()</span>、<span>remove()</span>、<span>shutdown()</span>、<span>suspend()</span>、<br />
<span>resume()</span>函数，通常也需要由驱动实现，如代码清单<span>2</span>。</p>
<p style="margin-bottom: 0cm;">代码清单<span>2<br />
</span></p>
<p style="margin-bottom: 0cm;"><span>platform_driver</span>结构体</p>
<p style="margin-bottom: 0cm;"><span>1 struct platform_driver {</span></p>
<p style="margin-bottom: 0cm;"><span>2 int (*probe)(struct platform_device *);</span></p>
<p style="margin-bottom: 0cm;"><span>3 int (*remove)(struct platform_device *);</span></p>
<p style="margin-bottom: 0cm;"><span>4 void (*shutdown)(struct platform_device *);</span></p>
<p style="margin-bottom: 0cm;"><span>5 int (*suspend)(struct platform_device *, pm_message_t state);</span></p>
<p style="margin-bottom: 0cm;"><span>6 int (*suspend_late)(struct platform_device *, pm_message_t state);</span></p>
<p style="margin-bottom: 0cm;"><span>7 int (*resume_early)(struct platform_device *);</span></p>
<p style="margin-bottom: 0cm;"><span>8 int (*resume)(struct platform_device *);</span></p>
<p style="margin-bottom: 0cm;"><span>9 struct pm_ext_ops *pm;</span></p>
<p style="margin-bottom: 0cm;"><span>10 struct device_driver driver;</span></p>
<p style="margin-bottom: 0cm;"><span>11};</span></p>
<p style="margin-bottom: 0cm;">系统中为<span>platform</span>总线定义了一个<span>bus_type</span>的实例<span>platform_bus_type</span>，其定义如代码清单<span>.3</span>。</p>
<p style="margin-bottom: 0cm;">代码清单<span>.3<br />
platform</span>总线的<span>bus_type<br />
</span>实例<span>platform_bus_type</span></p>
<p style="margin-bottom: 0cm;"><span>1 struct bus_type platform_bus_type = {</span></p>
<p style="margin-bottom: 0cm;"><span>2 .name = “platform”,</span></p>
<p style="margin-bottom: 0cm;"><span>3 .dev_attrs = platform_dev_attrs,</span></p>
<p style="margin-bottom: 0cm;"><span>4.match = platform_match,</span></p>
<p style="margin-bottom: 0cm;"><span>5.uevent = platform_uevent,</span></p>
<p style="margin-bottom: 0cm;"><span>6 .pm= PLATFORM_PM_OPS_PTR,</span></p>
<p style="margin-bottom: 0cm;"><span>7 };</span></p>
<p style="margin-bottom: 0cm;"><span>8<br />
EXPORT_SYMBOL_GPL(platform_bus_type);</span></p>
<p style="margin-bottom: 0cm;">这里要重点关注其<span>match()</span>成员函数，正是此成员表明了<span>platform_device</span>和<span>platform_driver</span>之间如何匹配，如代码清单<span>4</span>所示。</p>
<p style="margin-bottom: 0cm;">代码清单<span>4<br />
platform_bus_type</span>的<span>match()</span>成员函数</p>
<p style="margin-bottom: 0cm;"><span>1 static int platform_match(struct device *dev, struct device_driver  *drv)</span></p>
<p style="margin-bottom: 0cm;"><span>2 {</span></p>
<p style="margin-bottom: 0cm;"><span>3 struct platform_device *pdev;</span></p>
<p style="margin-bottom: 0cm;"><span>4</span></p>
<p style="margin-bottom: 0cm;"><span>5 pdev = container_of(dev, struct platform_device, dev);</span></p>
<p style="margin-bottom: 0cm;"><span>6 return (strncmp(pdev-&gt;name, drv-&gt;name, BUS_ID_SIZE) == 0);</span></p>
<p style="margin-bottom: 0cm;"><span>7 }</span></p>
<p style="margin-bottom: 0cm;">从代码清单<span>4</span>的第<span>6</span>行可以看出，匹配<span>platform_device</span>和<span>platform_driver</span>主要看二者的<span>name</span>字段是否相同。</p>
<p style="margin-bottom: 0cm;">对<span>platform_device</span>的定义通常在<span>BSP</span>的板文件中实现，在板文件中，将<span>platform_device</span>归纳为一个数组，最终通过<span>platform_add_devices()</span>函数统一注册。<span>platform_add_devices()</span>函数可以将平台设备添加到系统中，这个函数的原型为：</p>
<p style="margin-bottom: 0cm;"><span>int  platform_add_devices(struct platform_device **devs, int num);</span></p>
<p>该函数的第一个参数为平台设备数组的指针，第二个参数为平台设备的数量，它内部调用了<span>platform_device_register()</span>函数用于注册单个的平台设备。</p>
<h5 class="cjk"><big><big><big><span>1.2</span> 将<span>globalfifo</span>作为<span>platform</span>设备</big></big></big></h5>
<p style="margin-bottom: 0cm;">现在我们将前面章节的<span>globalfifo</span>驱动挂接到<span>platform</span>总线上，要完成<span>2</span>个工作<span>:</span></p>
<p style="margin-bottom: 0cm;"><span>1.</span> 将<span>globalfifo</span>移植为<span>platform</span>驱动。</p>
<p style="margin-bottom: 0cm;"><span>2.</span>在板文件中添加<span>globalfifo</span>这个<span>platform</span>设备。</p>
<p style="margin-bottom: 0cm;">为完成将<span>globalfifo</span>移植到<span>platform</span>驱动的工作，需要在原始的<span>globalfifo</span>字符设备驱动中套一层<span>platform_driver</span>的外壳，如代码清单<span>5</span>。注意进行这一工作后，并没有改变<span>globalfifo</span>是字符设备的本质，只是将其挂接到了<span>platform</span>总线。</p>
<p style="margin-bottom: 0cm;">代码清单<span>5</span> 为<span>globalfifo</span>添加<span>platform_driver</span></p>
<p style="margin-bottom: 0cm;"><span>1 static int __devinit globalfifo_probe(struct platform_device *pdev)</span></p>
<p style="margin-bottom: 0cm;"><span>2 {</span></p>
<p style="margin-bottom: 0cm;"><span>3 int ret;</span></p>
<p style="margin-bottom: 0cm;"><span>4 dev_t devno = MKDEV(globalfifo_major, 0);</span></p>
<p style="margin-bottom: 0cm;"><span>5</span></p>
<p style="margin-bottom: 0cm;"><span>6  /*</span>申请设备号<span>*/</span></p>
<p style="margin-bottom: 0cm;"><span>7 if (globalfifo_major)</span></p>
<p style="margin-bottom: 0cm;"><span>8 ret = register_chrdev_region(devno, 1, “globalfifo”);</span></p>
<p style="margin-bottom: 0cm;"><span>9 else<br />
{ /* </span>动态申请设备号 <span>*/</span></p>
<p style="margin-bottom: 0cm;"><span>10 ret = alloc_chrdev_region(&amp;devno, 0, 1, “globalfifo”);</span></p>
<p style="margin-bottom: 0cm;"><span>11 globalfifo_major = MAJOR(devno);</span></p>
<p style="margin-bottom: 0cm;"><span>12 }</span></p>
<p style="margin-bottom: 0cm;"><span>13 if (ret &lt; 0)</span></p>
<p style="margin-bottom: 0cm;"><span>14 return ret;</span></p>
<p style="margin-bottom: 0cm;"><span>15 /*</span>动态申请设备结构体的内存<span>*/</span></p>
<p style="margin-bottom: 0cm;"><span>16 globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);</span></p>
<p style="margin-bottom: 0cm;"><span>17 if(!globalfifo_devp) { /*</span>申请失败<span>*/</span></p>
<p style="margin-bottom: 0cm;"><span>18 ret = – ENOMEM;</span></p>
<p style="margin-bottom: 0cm;"><span>19 goto fail_malloc;</span></p>
<p style="margin-bottom: 0cm;"><span>20 }</span></p>
<p style="margin-bottom: 0cm;"><span>21</span></p>
<p style="margin-bottom: 0cm;"><span>22 memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));</span></p>
<p style="margin-bottom: 0cm;"><span>23</span></p>
<p style="margin-bottom: 0cm;"><span>24 globalfifo_setup_cdev(globalfifo_devp, 0);</span></p>
<p style="margin-bottom: 0cm;"><span>25</span></p>
<p style="margin-bottom: 0cm;"><span>26 init_MUTEX(&amp;globalfifo_devp-&gt;sem); /*</span>初始化信号量<span>*/</span></p>
<p style="margin-bottom: 0cm;"><span>27 init_waitqueue_head(&amp;globalfifo_devp-&gt;r_wait); /*</span>初始化读等待队列头<span>*/</span></p>
<p style="margin-bottom: 0cm;"><span>28 init_waitqueue_head(&amp;globalfifo_devp-&gt;w_wait); /*</span>初始化写等待队列头<span>*/</span></p>
<p style="margin-bottom: 0cm;"><span>29</span></p>
<p style="margin-bottom: 0cm;"><span>30 return 0;</span></p>
<p style="margin-bottom: 0cm;"><span>31</span></p>
<p style="margin-bottom: 0cm;"><span>32 fail_malloc: unregister_chrdev_region(devno, 1);</span></p>
<p style="margin-bottom: 0cm;"><span>33 return ret;</span></p>
<p style="margin-bottom: 0cm;"><span>34 }</span></p>
<p style="margin-bottom: 0cm;"><span>35</span></p>
<p style="margin-bottom: 0cm;"><span>36 static int __devexit globalfifo_remove(struct platform_device *pdev)</span></p>
<p style="margin-bottom: 0cm;"><span>37 {</span></p>
<p style="margin-bottom: 0cm;"><span>38 cdev_del(&amp;globalfifo_devp-&gt;cdev); /*</span>注销<span>cdev*/</span></p>
<p style="margin-bottom: 0cm;"><span>39 kfree(globalfifo_devp); /*</span>释放设备结构体内存<span>*/</span></p>
<p style="margin-bottom: 0cm;"><span>40 unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*</span>释放设备号<span>*/</span></p>
<p style="margin-bottom: 0cm;"><span>41 return 0;</span></p>
<p style="margin-bottom: 0cm;"><span>42 }</span></p>
<p style="margin-bottom: 0cm;"><span>43</span></p>
<p style="margin-bottom: 0cm;"><span>44  static struct platform_driver globalfifo_device_driver = {</span></p>
<p style="margin-bottom: 0cm;"><span>45 .probe = globalfifo_probe,</span></p>
<p style="margin-bottom: 0cm;"><span>46 .remove = __devexit_p(globalfifo_remove),</span></p>
<p style="margin-bottom: 0cm;"><span>47 .driver = {</span></p>
<p style="margin-bottom: 0cm;"><span><strong>48 .name = “globalfifo”,</strong></span></p>
<p style="margin-bottom: 0cm;"><span>49 .owner = THIS_MODULE,</span></p>
<p style="margin-bottom: 0cm;"><span>50 }</span></p>
<p style="margin-bottom: 0cm;"><span>51 };</span></p>
<p style="margin-bottom: 0cm;"><span>52</span></p>
<p style="margin-bottom: 0cm;"><span>53 static int __init globalfifo_init(void)</span></p>
<p style="margin-bottom: 0cm;"><span>54 {</span></p>
<p style="margin-bottom: 0cm;"><span><strong>55 return platform_driver_register(&amp;globalfifo_device_driver);</strong></span></p>
<p style="margin-bottom: 0cm;"><span>56 }</span></p>
<p style="margin-bottom: 0cm;"><span>57</span></p>
<p style="margin-bottom: 0cm;"><span>58 static void __exit globalfifo_exit(void)</span></p>
<p style="margin-bottom: 0cm;"><span>59 {</span></p>
<p style="margin-bottom: 0cm;"><span><strong>60 platform_driver_unregister(&amp;globalfifo_device_driver);</strong></span></p>
<p style="margin-bottom: 0cm;"><span>61 }</span></p>
<p style="margin-bottom: 0cm;"><span>62</span></p>
<p style="margin-bottom: 0cm;"><span>63 module_init(globalfifo_init);</span></p>
<p style="margin-bottom: 0cm;"><span>64 module_exit(globalfifo_exit);</span></p>
<p style="margin-bottom: 0cm;">在代码清单<span>5</span>中，模块加载和卸载函数仅仅通过<span>platform_driver_register()</span>、<span>platform_driver_unregister()</span>函数进行<span>platform_driver</span>的注册与注销，而原先注册和注销字符设备的工作已经被移交到<span>platform_driver</span>的<span>probe()</span>和<span>remove()</span>成员函数中。</p>
<p style="margin-bottom: 0cm;">代码清单<span>5</span>未列出的部分与原始的<span>globalfifo</span>驱动相同，都是实现作为字符设备驱动核心的<span>file_operations</span>的成员函数。</p>
<p style="margin-bottom: 0cm;">为了完成在板文件中添加<span>globalfifo</span>这个<span>platform</span>设备的工作，需要在板文件（对于<span>LDD6410</span>而言，为<span>arch/arm/mach-s3c6410/ mach-ldd6410.c</span>）中添加相应的代码，如代码清单<span>6</span>。</p>
<p style="margin-bottom: 0cm;">代码清单<span>6<br />
globalfifo</span>对应的<span>platform_device</span></p>
<p style="margin-bottom: 0cm;"><span>1 static struct platform_device globalfifo_device = {</span></p>
<p style="margin-bottom: 0cm;"><span><strong>2 .name = “globalfifo”,</strong></span></p>
<p style="margin-bottom: 0cm;"><span>3 .id = -1,</span></p>
<p style="margin-bottom: 0cm;"><span>4 };</span></p>
<p style="margin-bottom: 0cm;">对于<span>LDD6410</span>开发板而言，为了完成上述<span>globalfifo_device</span>这一<span>platform_device</span>的注册，只需要将其地址放<br />
入 <span>arch/arm/mach-s3c6410/mach-ldd6410.c</span>中定义的<span>ldd6410_devices</span>数组，如：</p>
<p style="margin-bottom: 0cm;"><span>static struct platform_device *ldd6410_devices[] __initdata = {</span></p>
<p style="margin-bottom: 0cm;"><span>+ &amp;globalfifo_device,</span></p>
<p style="margin-bottom: 0cm;"><span>#ifdef<br />
CONFIG_FB_S3C_V2</span></p>
<p style="margin-bottom: 0cm;"><span>&amp;s3c_device_fb,</span></p>
<p style="margin-bottom: 0cm;"><span>#endif</span></p>
<p style="margin-bottom: 0cm;"><span>&amp;s3c_device_hsmmc0,</span></p>
<p style="margin-bottom: 0cm;">…</p>
<p style="margin-bottom: 0cm;"><span>}</span></p>
<p style="margin-bottom: 0cm;">在加载<span>LDD6410</span>驱动后，在<span>sysfs</span>中会发现如下结点：</p>
<p style="margin-bottom: 0cm;"><span>/sys/bus/platform/devices/globalfifo/</span></p>
<p style="margin-bottom: 0cm;"><span>/sys/devices/platform/globalfifo/</span></p>
<p>留意一下代码清单<span>5</span>的第<span>48</span>行和代码清单<span>6</span>的第<span>2</span>行，<span>platform_device</span>和<span>platform_driver</span>的<span>name</span>一致，这是二者得以匹配的前提。</p>
<h5 class="cjk"><big><big><big><span>1.3 platform</span>设备资源和数据</big></big></big></h5>
<p style="margin-bottom: 0cm;">留意一下代码清单<span>1</span>中<span>platform_device</span>结构体定义的第<span>5~6</span>行，描述了<span>platform_device</span>的资源，资源本身由<span>resource</span>结构体描述，其定义如代码清单<span>7</span>。</p>
<p style="margin-bottom: 0cm;">代码清单<span>7<br />
resouce</span>结构体定义</p>
<p style="margin-bottom: 0cm;"><span>1 struct resource {</span></p>
<p style="margin-bottom: 0cm;"><span>2 resource_size_t start;</span></p>
<p style="margin-bottom: 0cm;"><span>3 resource_size_t end;</span></p>
<p style="margin-bottom: 0cm;"><span>4 const char *name;</span></p>
<p style="margin-bottom: 0cm;"><span>5 unsigned long flags;</span></p>
<p style="margin-bottom: 0cm;"><span>6 struct resource *parent, *sibling, *child;</span></p>
<p style="margin-bottom: 0cm;"><span>7 };</span></p>
<p style="margin-bottom: 0cm;">我们通常关心<span>start</span>、<span>end</span>和<span>flags</span>这<span>3</span>个字段，分别标明资源的开始值、结束值和类型，<span>flags</span>可以为<span>IORESOURCE_IO</span>、<span>IORESOURCE_MEM</span>、<span>IORESOURCE_IRQ</span>、<span>IORESOURCE_DMA</span>等。<span>start</span>、<span>end</span>的含义会随着<span>flags</span>而变更，如当<span>flags</span>为<span>IORESOURCE_MEM</span>时，<span>start</span>、<span>end</span>分别表示该<span>platform_device</span>占据的内存的开始地址和结束地址；当<span>flags</span>为<span>IORESOURCE_IRQ</span>时，<span>start</span>、<span>end</span>分别表示该<span>platform_device</span>使用的中断号的开始值和结束值，如果只使用了<br />
<span>1</span>个中断号，开始和结束值相同。对于同种类型的资源而言，可以有多份，譬如说某设备占据了<span>2</span>个内存区域，则可以定义<span>2</span>个<span>IORESOURCE_MEM</span>资源。</p>
<p style="margin-bottom: 0cm;">对<span>resource</span>的定义也通常在<span>BSP</span>的板文件中进行，而在具体的设备驱动中透过<span>platform_get_resource()</span>这样的<span>API</span>来获取，此<span>API</span>的原型为：</p>
<p style="margin-bottom: 0cm;"><span>struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);</span></p>
<p style="margin-bottom: 0cm;">譬如在<span>LDD6410</span>开发板的板文件中为<span>DM9000</span>网卡定义了如下<span>resouce</span>：</p>
<p style="margin-bottom: 0cm;"><span>static struct resource ldd6410_dm9000_resource[] = {</span></p>
<p style="margin-bottom: 0cm;"><span>[0] = {</span></p>
<p style="margin-bottom: 0cm;"><span>.start = 0×18000000,</span></p>
<p style="margin-bottom: 0cm;"><span>.end = 0×18000000 + 3,</span></p>
<p style="margin-bottom: 0cm;"><span>.flags = IORESOURCE_MEM</span></p>
<p style="margin-bottom: 0cm;"><span>},</span></p>
<p style="margin-bottom: 0cm;"><span>[1] = {</span></p>
<p style="margin-bottom: 0cm;"><span>.start = 0×18000000 + 0×4,</span></p>
<p style="margin-bottom: 0cm;"><span>.end = 0×18000000 + 0×7,</span></p>
<p style="margin-bottom: 0cm;"><span>.flags = IORESOURCE_MEM</span></p>
<p style="margin-bottom: 0cm;"><span>},</span></p>
<p style="margin-bottom: 0cm;"><span>[2] ={</span></p>
<p style="margin-bottom: 0cm;"><span>.start = IRQ_EINT(7),</span></p>
<p style="margin-bottom: 0cm;"><span>.end = IRQ_EINT(7),</span></p>
<p style="margin-bottom: 0cm;"><span>.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,</span></p>
<p style="margin-bottom: 0cm;"><span>}</span></p>
<p style="margin-bottom: 0cm;"><span>};</span></p>
<p style="margin-bottom: 0cm;">在<span>DM9000</span>网卡的驱动中则是通过如下办法拿到这<span>3</span>份资源：</p>
<p style="margin-bottom: 0cm;"><span>db-&gt;addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);</span></p>
<p style="margin-bottom: 0cm;"><span>db-&gt;data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);</span></p>
<p style="margin-bottom: 0cm;"><span>db-&gt;irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);</span></p>
<p style="margin-bottom: 0cm;">对于<span>IRQ</span>而言，<span>platform_get_resource()</span>还有一个进行了封装的变体<span>platform_get_irq()</span>，其原型为：</p>
<p style="margin-bottom: 0cm;"><span>int platform_get_irq(struct platform_device *dev, unsigned int num);</span></p>
<p style="margin-bottom: 0cm;">它实际上调用了“<span>platform_get_resource(dev,IORESOURCE_IRQ, num);”</span>。</p>
<p style="margin-bottom: 0cm;">设备除了可以在<span>BSP</span>中定义资源以外，还可以附加一些数据信息，因为对设备的硬件描述除了中断、内存、<span>DMA</span>通道以外，可能还会有一些配置信息，而这些配置信息也依赖于板，不适宜直接放置在设备驱动本身，因此，<span>platform</span>也提供了<span>platform_data</span>的支持。<span>platform_data</span>的形式是自定义的，如对于<span>DM9000</span>网卡而言，<span>platform_data</span>为一个<span>dm9000_plat_data</span>结构体，我们就可以将<span>MAC</span>地址、总线宽度、有无<span>EEPROM</span>信息放入<span>platform_data</span>：</p>
<p style="margin-bottom: 0cm;"><span><strong>static struct dm9000_plat_data ldd6410_dm9000_platdata = {</strong></span></p>
<p style="margin-bottom: 0cm;"><span><strong>.flags = DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM,</strong></span></p>
<p style="margin-bottom: 0cm;"><span><strong>.dev_addr = { 0×0, 0×16, 0xd4, 0×9f, 0xed, 0xa4 },</strong></span></p>
<p style="margin-bottom: 0cm;"><span><strong>};</strong></span></p>
<p style="margin-bottom: 0cm;"><span>static struct platform_device ldd6410_dm9000 = {</span></p>
<p style="margin-bottom: 0cm;"><span>.name = “dm9000″,</span></p>
<p style="margin-bottom: 0cm;"><span>.id =0,</span></p>
<p style="margin-bottom: 0cm;"><span>.num_resources= ARRAY_SIZE(ldd6410_dm9000_resource),</span></p>
<p style="margin-bottom: 0cm;"><span>.resource= ldd6410_dm9000_resource,</span></p>
<p style="margin-bottom: 0cm;"><span>.dev ={</span></p>
<p style="margin-bottom: 0cm;"><span><strong>.platform_data= &amp;ldd6410_dm9000_platdata,</strong></span></p>
<p style="margin-bottom: 0cm;"><span>}</span></p>
<p style="margin-bottom: 0cm;"><span>};</span></p>
<p style="margin-bottom: 0cm;">而在<span>DM9000</span>网卡的驱动中，通过如下方式就拿到了<span>platform_data</span>：</p>
<p style="margin-bottom: 0cm;"><span>struct dm9000_plat_data *pdata = pdev-&gt;dev.platform_data;</span>其中，<span>pdev</span>为<span>platform_device</span>的指针。</p>
<p style="margin-bottom: 0cm;">由以上分析可知，设备驱动中引入<span>platform</span>的概念至少有如下<span>2</span>大好处：</p>
<p style="margin-bottom: 0cm;"><span>1.</span> 使得设备被挂接在一个总线上，因此，符合<span>Linux2.6</span>的设备模型。其结果是，配套的<span>sysfs</span>结点、设备电源管理都成为可能。</p>
<p style="margin-bottom: 0cm;"><span>2.</span>隔离<span>BSP</span>和驱动。在<span>BSP</span>中定义<span>platform</span>设备和设备使用的资源、设备的具体配置信息，而在驱动中，只需要通过通用<span>API</span>去获取资源和数据，做到了板相关代码和驱动代码的分离，使得驱动具有更好的可扩展性和跨平台性。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/09/26/%e8%bd%ac-platform%e8%ae%be%e5%a4%87%e9%a9%b1%e5%8a%a8%e5%85%a8%e9%80%8f%e6%9e%90/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ubuntu10.04安装java1.5</title>
		<link>http://www.tek-life.org/2010/09/23/ubuntu10-04%e5%ae%89%e8%a3%85java1-5/</link>
		<comments>http://www.tek-life.org/2010/09/23/ubuntu10-04%e5%ae%89%e8%a3%85java1-5/#comments</comments>
		<pubDate>Thu, 23 Sep 2010 08:17:08 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10446</guid>
		<description><![CDATA[因为要运行android emulator要求必须要是1.5版本的java sdk. 但是默认的sources.list里面都是1.6的。寻找安装方法，网上通用的方法是： Add the following to /etc/apt/sources.list: deb http://us.archive.ubuntu.com/ubuntu/ jaunty multiverse deb http://us.archive.ubuntu.com/ubuntu/ jaunty-updates multiverse 这样sudo apt-get update 速度太慢了。等了好久，还没有更新好数据库。 鄙人性急，受不了这样的速度。直接把ubuntu 9.04的源拿过来。 sudo apt-get update后，然后apt-cache search java5.竟然能找到了。挺好！ 立马install.速度200+k/s,刚刚的。 安装结束后，再将原来的sources.list还原。 附一个哈工大的9.04源： deb http://run.hit.edu.cn/ubuntu/ jaunty main restricted universe multiverse deb-src http://run.hit.edu.cn/ubuntu/ &#8230; <a href="http://www.tek-life.org/2010/09/23/ubuntu10-04%e5%ae%89%e8%a3%85java1-5/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>因为要运行android emulator要求必须要是1.5版本的java sdk.</p>
<p>但是默认的sources.list里面都是1.6的。寻找安装方法，网上通用的方法是：</p>
<p>Add the following to /etc/apt/sources.list:</p>
<div>deb http://us.archive.ubuntu.com/ubuntu/ jaunty multiverse</div>
<div>deb http://us.archive.ubuntu.com/ubuntu/ jaunty-updates multiverse</div>
<div></div>
<div>这样sudo apt-get update 速度太慢了。等了好久，还没有更新好数据库。</div>
<div>鄙人性急，受不了这样的速度。直接把ubuntu 9.04的源拿过来。</div>
<div>sudo apt-get update后，然后apt-cache search java5.竟然能找到了。挺好！</div>
<div>立马install.速度200+k/s,刚刚的。</div>
<div>安装结束后，再将原来的sources.list还原。</div>
<div></div>
<div>附一个哈工大的9.04源：</div>
<div>deb http://run.hit.edu.cn/ubuntu/ jaunty main restricted universe multiverse</p>
<p>deb-src http://run.hit.edu.cn/ubuntu/ jaunty main restricted universe multiverse</p>
<p>deb http://run.hit.edu.cn/ubuntu/ jaunty-updates main restricted universe multiverse</p>
<p>deb-src http://run.hit.edu.cn/ubuntu/ jaunty-updates main restricted universe multiverse</p>
<p>deb http://run.hit.edu.cn/ubuntu/ jaunty-backports main restricted universe multiverse</p>
<p>deb-src http://run.hit.edu.cn/ubuntu/ jaunty-backports main restricted universe multiverse</p>
<p>deb http://run.hit.edu.cn/ubuntu/ jaunty-security main restricted universe multiverse</p>
<p>deb-src http://run.hit.edu.cn/ubuntu/ jaunty-security main restricted universe multiverse</p>
</div>
<div></div>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/09/23/ubuntu10-04%e5%ae%89%e8%a3%85java1-5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>认识FAT32</title>
		<link>http://www.tek-life.org/2010/09/21/%e8%ae%a4%e8%af%86fat32/</link>
		<comments>http://www.tek-life.org/2010/09/21/%e8%ae%a4%e8%af%86fat32/#comments</comments>
		<pubDate>Tue, 21 Sep 2010 04:05:56 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[File System]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/09/21/%e8%ae%a4%e8%af%86fat32/</guid>
		<description><![CDATA[版权所有，禁止转载。 FAT32文件系统是一个比较重要的文件系统，并且，设计思路比较简单。在windows操作系统，以及嵌入式平台上有广泛的用途。对于一个没有安装操作系统的普通FAT32分区的磁盘，主要的结构有：DBR，FAT表，备份FAT表，以及数据区。注意与FAT16和FAT12区分，FAT32是没有单独的根目录区的，因为单独的根目录区，对根目录下面的文件数量是有限制的。对于一个划分了FAT32分区的磁盘来说，在DBR里面包含有重要的磁盘分区信息。操作系统读取磁盘的时候，按照DBR和FAT表来读取和寻找文件的。 在DBR中，最重要的数据结构是BPB块，BPB表里面包含了很多有用的信息。 Q：BPB表保存在哪里？ A：保存在DBR区中，而DBR区，在FAT32格式的磁盘分区中一般位于0磁头1柱面1扇区的位置。0磁头0柱面留给MBR以及保留扇区。如果没有安装操作系统，那么就在0磁头0柱面上第1个扇区。本文的实验没有安装操作系统的SDCARD。 知道了BPB保存在哪里后，要掌握FAT32分区情况，就需要知道一些关键的信息： 1。DBR后面有多少的保留扇区？ 2。FAT表保存在哪里？ 3。一个FAT占多少扇区？ 4。在FAT32里面已经没有了根目录区的概念了，那么根目录在哪个扇区？ 了解了这些以后，我们就能够大概跟踪到各个文件了。 DBR截图 对照以上截图，以及FAT手册，我们就可以回答上面的这几个问题。 Q：DBR后面有多少保留扇区？ A：0X0E 2个字节。0020个扇区，也就是加上DBR一共32个扇区 Q：FAT表保存在哪里？ A：第一个柱面DBR＋保留扇区之后。第32个扇区之后，保存着FAT表 Q：1个FAT表占用多少扇区？ A：0X24 4个字节。01fc个扇区 Q：根目录在哪个扇区？ A：位于的簇号记录于DBR 0X2C 4个字节。不过，一般是在FAT表之后DBR＋2＊FAT表扇区之后。0X0002扇区 Q：FAT表是以簇为单位来管理的，一个簇有多少扇区？ A：DBR里面偏移0x0D一个字节。01个扇区（32MB） Q：根目录数据在哪个扇区？ A：保留扇区＋FAT＊2扇区看一下 32＋0x01fc*2=32+1016=1048扇区数.1048*512=536576B 由于在FAT32分区格式中，根目录区的特殊性已经取消了。因此保留扇区（保留扇区包括DBR）＋FAT1＋FAT2后，便是根目录数据区的位置。根目录本身的目录项是没有的，在数据区开始位置放置的就是根目录的内容（也就是根目录的数据）。所以，如果我们去查找根目录下的某个文件，或者目录，直接异步到数据区的开始处，就可以查找了。我们可能会有疑问，那如果根目录区内的文件名很多，于是根目录的数据大小超过了一个簇的大小，咋办哩？当然有办法了，想想FAT表的作用吧，呵呵。FAT表便是一个数据区的链表，按照FAT表来索冀，我们就能够得到一个大小超过一个簇的文件了。 用一个例子来说明吧。 首先，用android下面的工具：mksdcard来制作一个FAT32的img镜像。 看到了，已经生成了一个32MB的MY_SD的fat32镜像。 接着，我们把MY_SD挂在到/mnt/t分区，并且往上面放两个文件： 然后，经过前面我们对FAT32的分析，第一个分区是DBR分区，然后是31个保留分区，接下来是FAT分区。根据DBR分区中BPB相关字段，FAT分区一共占0x01FC(508)个扇区，再加上备份FAT表，一共是1016个FAT分区。这样子，数据区就在1016+31个分区之后的分区。我们先查找river.txt的文件分布吧： 根目录的数据区域包含了根目录下的文件及目录的相关信息。因此我们要在1048个扇区将根目录的数据读出来，从中，识别river.txt的目录名 和 数据所在的簇号： 从中我们可以看到river.txt数据区所在的位置是00000004簇，文件大小是8个字节(“huanghe”一共是7个字节，再加上\0，一共是8个字节)。另外，我们也可以看到mountain.txt所在簇号00000005，文件大小是9个字节(“ximalaya”，在加上\0)。 &#8230; <a href="http://www.tek-life.org/2010/09/21/%e8%ae%a4%e8%af%86fat32/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><!-- p { margin-bottom: 0.21cm; } --></p>
<p style="margin-bottom: 0cm;">版权所有，禁止转载。</p>
<p style="margin-bottom: 0cm;"><span>FAT32</span>文件系统是一个比较重要的文件系统，并且，设计思路比较简单。在<span>windows</span>操作系统，以及嵌入式平台上有广泛的用途。对于一个没有安装操作系统的普通<span>FAT32</span>分区的磁盘，主要的结构有：<span>DBR</span>，<span>FAT</span>表，备份<span>FAT</span>表，以及数据区。注意与<span>FAT16</span>和<span>FAT12</span>区分，<span>FAT32</span>是没有单独的根目录区的，因为单独的根目录区，对根目录下面的文件数量是有限制的。对于一个划分了<span>FAT32</span>分区的磁盘来说，在<span>DBR</span>里面包含有重要的磁盘分区信息。操作系统读取磁盘的时候，按照<span>DBR</span>和<span>FAT</span>表来读取和寻找文件的。</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">在<span>DBR</span>中，最重要的数据结构是<span>BPB</span>块，<span>BPB</span>表里面包含了很多有用的信息。</p>
<p style="margin-bottom: 0cm;"><span> Q</span>：<span>BPB</span>表保存在哪里？</p>
<p style="margin-bottom: 0cm;"><span> A</span>：保存在<span>DBR</span>区中，而<span>DBR</span>区，在<span>FAT32</span>格式的磁盘分区中一般位于<span>0</span>磁头<span>1</span>柱面<span>1</span>扇区的位置。<span>0</span>磁头<span>0</span>柱面留给<span>MBR</span>以及保留扇区。如果没有安装操作系统，那么就在<span>0</span>磁头<span>0</span>柱面上第<span>1</span>个扇区。本文的实验没有安装操作系统的<span>SDCARD</span>。</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">知道了<span>BPB</span>保存在哪里后，要掌握<span>FAT32</span>分区情况，就需要知道一些关键的信息：</p>
<p style="margin-bottom: 0cm;">
<p style="margin-left: 1.25cm; margin-bottom: 0cm;"><span>1</span>。<span>DBR</span>后面有多少的保留扇区？</p>
<p style="margin-left: 1.25cm; margin-bottom: 0cm;"><span>2</span>。<span>FAT</span>表保存在哪里？</p>
<p style="margin-left: 1.25cm; margin-bottom: 0cm;"><span>3</span>。一个<span>FAT</span>占多少扇区？</p>
<p style="margin-left: 1.25cm; margin-bottom: 0cm;"><span>4</span>。在<span>FAT32</span>里面已经没有了根目录区的概念了，那么根目录在哪个扇区？</p>
<p style="margin-bottom: 0cm;">了解了这些以后，我们就能够大概跟踪到各个文件了。</p>
<p style="margin-bottom: 0cm;"><img style="max-width: 800px;" src="http://www.tek-life.org/wp-content/uploads/2010/09/sendpix1.jpg" alt="" /></p>
<p style="margin-bottom: 0cm;"><span> DBR</span>截图</p>
<p style="margin-bottom: 0cm;">对照以上截图，以及<span>FAT</span>手册，我们就可以回答上面的这几个问题。</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><span>Q</span>：<span>DBR</span>后面有多少保留扇区？</p>
<p style="margin-bottom: 0cm;"><span>A</span>：<span>0X0E<br />
2</span>个字节。<span>0020</span>个扇区，也就是加上<span>DBR</span>一共<span>32</span>个扇区</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><span>Q</span>：<span>FAT</span>表保存在哪里？</p>
<p style="margin-bottom: 0cm;"><span>A</span>：第一个柱面<span>DBR</span>＋保留扇区之后。第<span>32</span>个扇区之后，保存着<span>FAT</span>表</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><span>Q</span>：<span>1</span>个<span>FAT</span>表占用多少扇区？</p>
<p style="margin-bottom: 0cm;"><span>A</span>：<span>0X24<br />
4</span>个字节。<span>01fc</span>个扇区</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><span>Q</span>：根目录在哪个扇区？</p>
<p style="margin-bottom: 0cm;"><span>A</span>：位于的簇号记录于<span>DBR<br />
0X2C 4</span>个字节。不过，一般是在<span>FAT</span>表之后<span>DBR</span>＋<span>2</span>＊<span>FAT</span>表扇区之后。<span>0X0002</span>扇区</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><span>Q</span>：<span>FAT</span>表是以簇为单位来管理的，一个簇有多少扇区？</p>
<p style="margin-bottom: 0cm;"><span>A</span>：<span>DBR</span>里面偏移<span>0x0D</span>一个字节。<span>01</span>个扇区（<span>32MB</span>）</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><span>Q</span>：根目录数据在哪个扇区？</p>
<p style="margin-bottom: 0cm;"><span>A</span>：保留扇区＋<span>FAT</span>＊<span>2</span>扇区看一下<br />
<span>32</span>＋<span>0x01fc*2=32+1016=1048</span>扇区数.<span>1048*512=536576B</span></p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">由于在<span>FAT32</span>分区格式中，根目录区的特殊性已经取消了。因此保留扇区（保留扇区包括<span>DBR</span>）＋<span>FAT1</span>＋<span>FAT2</span>后，便是根目录数据区的位置。根目录本身的目录项是没有的，在数据区开始位置放置的就是根目录的内容（也就是根目录的数据）。所以，如果我们去查找根目录下的某个文件，或者目录，直接异步到数据区的开始处，就可以查找了。我们可能会有疑问，那如果根目录区内的文件名很多，于是根目录的数据大小超过了一个簇的大小，咋办哩？当然有办法了，想想<span>FAT</span>表的作用吧，呵呵。<span>FAT</span>表便是一个数据区的链表，按照<span>FAT</span>表来索冀，我们就能够得到一个大小超过一个簇的文件了。</p>
<p style="margin-bottom: 0cm;">用一个例子来说明吧。</p>
<p style="margin-bottom: 0cm;">首先，用<span>android</span>下面的工具：<span>mksdcard</span>来制作一个<span>FAT32</span>的<span>img</span>镜像。</p>
<p style="margin-bottom: 0cm;"><img style="max-width: 800px;" src="http://www.tek-life.org/wp-content/uploads/2010/09/sendpix4.jpg" alt="" /></p>
<p style="margin-bottom: 0cm;">看到了，已经生成了一个<span>32MB</span>的<span>MY_SD</span>的<span>fat32</span>镜像。</p>
<p style="margin-bottom: 0cm;">接着，我们把<span>MY_SD</span>挂在到<span>/mnt/t</span>分区，并且往上面放两个文件：</p>
<p style="margin-bottom: 0cm;"><img style="max-width: 800px;" src="http://www.tek-life.org/wp-content/uploads/2010/09/sendpix5.jpg" alt="" /></p>
<p style="margin-bottom: 0cm;"><img style="max-width: 800px;" src="http://www.tek-life.org/wp-content/uploads/2010/09/sendpix6.jpg" alt="" /></p>
<p style="margin-bottom: 0cm;">然后，经过前面我们对<span>FAT32</span>的分析，第一个分区是<span>DBR</span>分区，然后是<span>31</span>个保留分区，接下来是<span>FAT</span>分区。根据<span>DBR</span>分区中<span>BPB</span>相关字段，<span>FAT</span>分区一共占<span>0x01FC(508)</span>个扇区，再加上备份<span>FAT</span>表，一共是<span>1016</span>个<span>FAT</span>分区。这样子，数据区就在<span>1016+31</span>个分区之后的分区。我们先查找<span>river.txt</span>的文件分布吧：</p>
<p style="margin-bottom: 0cm;">根目录的数据区域包含了根目录下的文件及目录的相关信息。因此我们要在<span>1048</span>个扇区将根目录的数据读出来，从中，识别<span>river.txt</span>的目录名<br />
和 数据所在的簇号：</p>
<p style="margin-bottom: 0cm;"><img style="max-width: 800px;" src="http://www.tek-life.org/wp-content/uploads/2010/09/sendpix7.jpg" alt="" /></p>
<p style="margin-bottom: 0cm;">从中我们可以看到<span>river.txt</span>数据区所在的位置是<span>00000004</span>簇，文件大小是<span>8</span>个字节<span>(“huanghe”</span>一共是<span>7</span>个字节，再加上<span>\0</span>，一共是<span>8</span>个字节<span>)</span>。另外，我们也可以看到<span>mountain.txt</span>所在簇号<span>00000005</span>，文件大小是<span>9</span>个字节<span>(“ximalaya”</span>，在加上<span>\0)</span>。</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">由于<span>river.txt</span>数据是在第<span>4</span>簇号放着，而<span>FAT</span>规定簇号的起始记号是从<span>2</span>开始，因此，实际上，要找的话，就从数据区的第<span>2</span>个簇号里面放着，在<span>32MB</span>的<span>fat32</span>中，每一个簇占<span>1</span>个扇区，因此<span>river.txt</span>所在的数据扇区的位置：<span>1048</span>＋<span>2</span>＝<span>1050</span>个扇区，<span>1050</span>＊<span>512B</span>＝<span>537600B</span>。我们找一下：</p>
<p style="margin-bottom: 0cm;"><img style="max-width: 800px;" src="http://www.tek-life.org/wp-content/uploads/2010/09/sendpix8.jpg" alt="" /></p>
<p style="margin-bottom: 0cm;">看到了吧。强化一下，我们继续找<span>mountain.txt.<br />
1048+3=1051</span>扇区，<span>1051</span>＊<span>512B</span>＝<span>538112B</span>。我们来找一下：</p>
<p style="margin-bottom: 0cm;"><img style="max-width: 800px;" src="http://www.tek-life.org/wp-content/uploads/2010/09/sendpix91.jpg" alt="" /></p>
<p style="margin-bottom: 0cm;">呵呵，找到了。以上就是对<span>FAT32</span>的文件系统的认识。</p>
<p style="margin-bottom: 0cm;">讨论地址:http://linux.chinaunix.net/bbs/viewthread.php?tid=1171438&amp;page=1&amp;extra=#pid7301790</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/09/21/%e8%ae%a4%e8%af%86fat32/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>用git管理源码</title>
		<link>http://www.tek-life.org/2010/09/17/%e7%94%a8git%e7%ae%a1%e7%90%86%e6%ba%90%e7%a0%81/</link>
		<comments>http://www.tek-life.org/2010/09/17/%e7%94%a8git%e7%ae%a1%e7%90%86%e6%ba%90%e7%a0%81/#comments</comments>
		<pubDate>Fri, 17 Sep 2010 02:54:13 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10422</guid>
		<description><![CDATA[git管理源码，好处自不必说。学习git参考的是《pro git》。 该书的豆瓣链接：http://book.douban.com/subject/3420144/ 关于文件的修改： 如果，自己想添加新的文件。最好是，自己建立一个分支： git branch new-branch    //建立新的分支 git checkout new-branch //切换到新的分支下工作 这时候，可以添加或者修改文件。 注意，这个时候，该分支下新添加的文件与master分支是没有一点关系的。 另外，说一下git revert ，git reset &#8211;hard和 &#8211;soft的区别 git reset &#8211;mixed id ，是将git的HEAD变了（也就是提交记录变了），但文件并没有改变，（也就是working tree并没有改变）。 git reset &#8211;soft id. 实际上，是git reset &#8211;mixed id 后，又做了一次git add git reset &#8211;herd &#8230; <a href="http://www.tek-life.org/2010/09/17/%e7%94%a8git%e7%ae%a1%e7%90%86%e6%ba%90%e7%a0%81/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>git管理源码，好处自不必说。学习git参考的是《pro git》。<br />
该书的豆瓣链接：http://book.douban.com/subject/3420144/</p>
<p><strong><span style="color: #ff0000;">关于文件的修改：</span></strong></p>
<p>如果，自己想添加新的文件。最好是，自己建立一个分支：<br />
git branch new-branch    //建立新的分支<br />
git checkout new-branch //切换到新的分支下工作<br />
这时候，可以添加或者修改文件。</p>
<p>注意，这个时候，该分支下新添加的文件与master分支是没有一点关系的。</p>
<p>另外，说一下git revert ，git reset &#8211;hard和 &#8211;soft的区别</p>
<p>git reset &#8211;mixed id ，是将git的HEAD变了（也就是提交记录变了），但文件并没有改变，（也就是working tree并没有改变）。<br />
git reset &#8211;soft id. 实际上，是git reset &#8211;mixed id 后，又做了一次git add<br />
git reset &#8211;herd id.是将git的HEAD变了，文件也变了。</p>
<p>git revert与git reset最大的不同是，git revert 仅仅是撤销<strong>某次</strong>提交。<br />
比如git revert HEAD~1  ,那么会撤销倒数第二次的提交结果。而倒数第一次的提交记录，仍然在。<br />
如果git reset &#8211;hard HEAD~1,那么，commit退回到倒数第三次的状态中。<br />
总体来讲，还是git revert 好啊，雁过留声嘛。</p>
<p>其实，通过git reset &#8211;soft id的方法，可以将原来多次的git提交记录合并为一个。</p>
<p>两个分支的合并：（分支也就是版本的意思）<br />
git pull . 分支1,  将分支1的变化提交到分支2。<br />
在分支合并部分，不要用git merge 和 git push。容易出错。<br />
另外，还有一种分支合并的方法是rebase。这种方法，更符合思维。<br />
git checkout A1<br />
git rebase master<br />
过程是，找到将A1和master的共同commit点。A1 把自分叉以来的所有补丁都达到master上。相当于：</p>
<p>git checkout master<br />
git pull . A1<br />
&#8212;-or&#8212;-<br />
git checkout master<br />
git merge A1</p>
<p><span style="color: #ff0000;"><strong>关于查看git 记录。</strong></span></p>
<p>git whatchanged 可以查看最近两次提交记录，以及文件的变更。<br />
git log 可以查看所有的git提交记录。另外，一些参数，也是很有用，比如 -p -N -stat<br />
git diff 版本1 版本2 可以查看两个分支的差异。</p>
<p>主要，就是这些了。以后再有什么新的需求，一并写过来。<br />
&#8212;<br />
git add -i 竟然还有交互模式。很方便，不过自己仅仅使用了其中的一条。</p>
<p><strong><span style="color: #ff0000;">本地－服务器查看：</span></strong><br />
omycle@omycle-desktop:/home/sda4/work/neo$ git remote show origin<br />
* remote origin<br />
Fetch URL: git://211.69.198.249/neo.git<br />
Push  URL: git://211.69.198.249/neo.git<br />
HEAD branch: master<br />
Remote branch:<br />
master tracked<br />
Local branch configured for &#8216;git pull&#8217;:               //如果git pull 缺省状态是用master分支<br />
master merges with remote master<br />
Local ref configured for &#8216;git push&#8217;:                  //如果git push 缺省状态是到master分支<br />
master pushes to master (up to date)<br />
如果距离上一次clone 有一定时间了，那么服务器一定是做过了不少更新工作，怎样将新的更新同时同步到本地呢？<br />
git fetch origin<br />
为啥用origin呢，因为，我们在clone的时候，建立默认的远程仓库名的别名 origin/master，就表示远程仓库上的master分支。因此，就用git fetch origin.意思就是，让存放在本地的origin/master分支与服务器同步。</p>
<p><strong><span style="color: #ff0000;">关于diff:</span></strong></p>
<p>git diff    要查看尚未暂存的文件（还没有git add 之前）更新了哪些部分,不加参数直接输入 。<br />
git diff -cached  已经暂存文件更新了那些部分，也就是git add 之后，git commit之前。</p>
<p>其实，要是看文件具体更改详情的话，用git log，也可以。<br />
git log -p -n，n是看最近的n次记录。</p>
<p><span style="color: #ff0000;"><strong>建立git服务器：</strong></span></p>
<p>建立git服务器，其实很简单，特别是对于项目成员比较少的情况下。<br />
比如，在本地有一个test目录，里面是要共享的项目。那么：<br />
git clone &#8211;bare test.git test<br />
这样，就建立了test.git了。其他成员就可以共享了。<br />
例如：<br />
对于我来说，用omycle@192.168.227.101就可以通过SSH的方式远程git clone 项目了。<br />
git clone omycle@192.168.227.101:~/test.git</p>
<p>步骤真是简单啊。</p>
<p>当然，麻烦的步骤是，在一个新的目录里面git初始化后，运行：<br />
git remote add origit omycle@192.168.227.101:~/test.git<br />
git fetch origin<br />
git pull origin master<br />
这样，就可以了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/09/17/%e7%94%a8git%e7%ae%a1%e7%90%86%e6%ba%90%e7%a0%81/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>last.fm客户端编译错误解决过程</title>
		<link>http://www.tek-life.org/2010/09/15/last-fm/</link>
		<comments>http://www.tek-life.org/2010/09/15/last-fm/#comments</comments>
		<pubDate>Wed, 15 Sep 2010 08:19:17 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10417</guid>
		<description><![CDATA[last.fm网站做的挺不错，并且提供了一个c++写的客户端。在ubuntu10.04下编译，竟然缺少了几个头文件。 在网上看到一个碰到的错误和我的一样，因此把他写的贴出来： From:http://wildebeestplain.blogspot.com/2010/05/lastfm-on-ubuntu-lucid-lynx-1004.html Having recently got a nice shiny new netbook (an HP/Compaq 311c) I was trying to get last.fm to compile on it. My netbook was running a reasonably fresh install of Ubuntu Lucid Lynx (10.04). So I downloaded &#8230; <a href="http://www.tek-life.org/2010/09/15/last-fm/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>last.fm网站做的挺不错，并且提供了一个c++写的客户端。在ubuntu10.04下编译，竟然缺少了几个头文件。</p>
<p>在网上看到一个碰到的错误和我的一样，因此把他写的贴出来：</p>
<p>From:http://wildebeestplain.blogspot.com/2010/05/lastfm-on-ubuntu-lucid-lynx-1004.html</p>
<blockquote><p>Having recently got a nice shiny new netbook (an HP/Compaq 311c) I was trying to get <a href="http://last.fm/">last.fm</a> to compile on it. My netbook was running a reasonably fresh install of <a href="https://wiki.ubuntu.com/LucidLynx">Ubuntu Lucid Lynx (10.04)</a>.</p>
<p>So I downloaded the source for last.fm-1.4.2.58240 and unzipped it into  my source directory. Knowing that it needed qt4 I then ran:<br />
<tt>sudo apt-get install libqt4-dev</tt></p>
<p>With that all installed I tried running <tt>./configure</tt> in the source directory and it said everything was fine, so I went ahead and typed <tt>make</tt>.</p>
<p>After waiting while it compiled I was then greeted by the following error:<br />
<tt>make[1]: Entering directory `/home/marvin/src/last.fm-1.4.2.58240/src/libFingerprint/fplib/pro_qmake'<br />
g++ -c -pipe -O2 -w -fPIC -DNBREAKPAD -DLINUX -DNDEBUG  -I/usr/share/qt4/mkspecs/linux-g++ -I. -I../../../../src  -I../../../../build -I../../../libMoose -I../../../libUnicorn  -I../include -I../src -I../../libs/fftw/src/api  -I../../../../res/libsamplerate -o  ../../../../build/fplib/release/FingerprintExtractor.o  ../src/FingerprintExtractor.cpp<br />
../src/FingerprintExtractor.cpp: In member function ‘bool  fingerprint::FingerprintExtractor::process(const short int*, size_t,  bool)’:<br />
../src/FingerprintExtractor.cpp:445: error: ‘memcpy’ was not declared in this scope<br />
make[1]: *** [../../../../build/fplib/release/FingerprintExtractor.o] Error 1<br />
make[1]: Leaving directory `/home/marvin/src/last.fm-1.4.2.58240/src/libFingerprint/fplib/pro_qmake'<br />
make: *** [sub-src-libFingerprint-fplib-pro_qmake-fplib-pro-make_default-ordered] Error 2<br />
marvin@Anjie:~/src/last.fm-1.4.2.58240$<br />
</tt></p>
<p>Knowing that <a href="http://www.cplusplus.com/reference/clibrary/cstring/memcpy/">memcpy</a> is part of the standard C library I thought this seemed a little strange. So I opened up <tt>last.fm-1.4.2.58240/src/libFingerprint/fplib/src/FingerprintExtractor.cpp</tt> and added the following line in bold:<br />
<tt>#include &lt;cmath&gt;<br />
<strong>#include &lt;string.h&gt;</strong><br />
</tt><br />
Then recompiled.</p>
<p>The next error was the following:<br />
<tt>g++ -c -pipe -O2 -w -fPIC -DNBREAKPAD -DLINUX -DNDEBUG  -I/usr/share/qt4/mkspecs/linux-g++ -I. -I../../../../src  -I../../../../build -I../../../libMoose -I../../../libUnicorn  -I../include -I../src -I../../libs/fftw/src/api  -I../../../../res/libsamplerate -o  ../../../../build/fplib/release/OptFFT.o ../src/OptFFT.cpp<br />
../src/OptFFT.cpp: In constructor ‘fingerprint::OptFFT::OptFFT(size_t)’:<br />
../src/OptFFT.cpp:262: error: ‘exit’ was not declared in this scope<br />
make[1]: *** [../../../../build/fplib/release/OptFFT.o] Error 1<br />
make[1]: Leaving directory `/home/marvin/src/last.fm-1.4.2.58240/src/libFingerprint/fplib/pro_qmake'<br />
make: *** [sub-src-libFingerprint-fplib-pro_qmake-fplib-pro-make_default-ordered] Error 2<br />
marvin@Anjie:~/src/last.fm-1.4.2.58240$<br />
</tt></p>
<p>Which I fixed by editing <tt>last.fm-1.4.2.58240/src/libFingerprint/fplib/src/OptFFT.cpp</tt> to include the line in bold:<br />
<tt>#include &lt;iostream&gt;<br />
#include &lt;memory.h&gt; // for memcopy<br />
<strong>#include &lt;stdlib.h&gt;</strong><br />
</tt><br />
And then once again ran make.</p>
<p>The next error was:<br />
<tt>g++ -c -pipe -O2 -w -D_REENTRANT -fPIC -DNBREAKPAD -DLINUX  -DQT_NO_DEBUG -DQT_SQL_LIB -DQT_XML_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB  -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I.  -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtNetwork  -I/usr/include/qt4/QtGui -I/usr/include/qt4/QtXml  -I/usr/include/qt4/QtSql -I/usr/include/qt4 -I../../src -I../../build  -I../libMoose -I../libUnicorn -Ifplib/include -I../src/ -I../../res/mad  -I../../build/LastFmFingerprint/release -o  ../../build/LastFmFingerprint/release/MP3_Source_Qt.o MP3_Source_Qt.cpp<br />
MP3_Source_Qt.cpp: In function ‘short int f2s(mad_fixed_t)’:<br />
MP3_Source_Qt.cpp:70: error: ‘SHRT_MAX’ was not declared in this scope<br />
MP3_Source_Qt.cpp:72: error: ‘SHRT_MAX’ was not declared in this scope<br />
MP3_Source_Qt.cpp: In member function ‘virtual void MP3_Source::skipSilence(double)’:<br />
MP3_Source_Qt.cpp:379: error: ‘abs’ was not declared in this scope<br />
MP3_Source_Qt.cpp:385: error: ‘abs’ was not declared in this scope<br />
make[1]: *** [../../build/LastFmFingerprint/release/MP3_Source_Qt.o] Error 1<br />
make[1]: Leaving directory `/home/marvin/src/last.fm-1.4.2.58240/src/libFingerprint'<br />
make: *** [sub-src-libFingerprint-make_default-ordered] Error 2<br />
</tt></p>
<p>So I opened <tt>last.fm-1.4.2.58240/src/libFingerprint/MP3_Source_Qt.cpp</tt> and added the lines in bold:<br />
<tt>#include &lt;cassert&gt;<br />
#include &lt;stdexcept&gt;<br />
<strong>#include &lt;stdlib.h&gt;</strong><br />
<strong>#include &lt;limits.h&gt;</strong><br />
</tt></p>
<p>And ran make once more.</p>
<p>This time it was a linking error:<br />
<tt>g++ -Wl,-O1 -shared -Wl,-soname,libLastFmFingerprint.so.1 -o  libLastFmFingerprint.so.1.0.0  ../../build/LastFmFingerprint/release/Sha256File.o  ../../build/LastFmFingerprint/release/Sha256.o  ../../build/LastFmFingerprint/release/MP3_Source_Qt.o  ../../build/LastFmFingerprint/release/Fingerprinter2.o  ../../build/LastFmFingerprint/release/FingerprintCollector.o  ../../build/LastFmFingerprint/release/FingerprintQueryer.o  ../../build/LastFmFingerprint/release/moc_Fingerprinter2.o  ../../build/LastFmFingerprint/release/moc_FingerprintCollector.o  ../../build/LastFmFingerprint/release/moc_FingerprintQueryer.o    -L/home/marvin/src/last.fm-1.4.2.58240/bin  -L/home/marvin/src/last.fm-1.4.2.58240/build/LastFmFingerprint/../fplib  -L/usr/lib -lMoose -L/home/marvin/src/last.fm-1.4.2.58240/bin  -lLastFmTools  /home/marvin/src/last.fm-1.4.2.58240/build/fplib/libfplib.a -lsamplerate  -lfftw3f -lQtSql -lQtXml -lQtGui -lQtNetwork -lQtCore -lpthread<br />
/usr/bin/ld: cannot find -lsamplerate<br />
collect2: ld returned 1 exit status<br />
make[1]: *** [../../bin/libLastFmFingerprint.so.1.0.0] Error 1<br />
make[1]: Leaving directory `/home/marvin/src/last.fm-1.4.2.58240/src/libFingerprint'<br />
make: *** [sub-src-libFingerprint-make_default-ordered] Error 2<br />
marvin@Anjie:~/src/last.fm-1.4.2.58240$<br />
</tt></p>
<p>So I ran <tt>sudo apt-get install libsamplerate0 libsamplerate0-dev  libfftw3-dev libfftw3-3 libmad0-dev libmad0 libgpod-dev libgpod4  libasound2-dev</tt> and then ran make again.</p>
<p>If you&#8217;re wondering how I came up with all the libraries above, it was  just from running make several times and then using apt-cache search to  search for the missing libraries. I didn&#8217;t include these steps as  they&#8217;re a bit tedious.</p>
<p>Having done all that it&#8217;s possible to run last.fm with <tt>bin/last.fm.sh</tt>.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/09/15/last-fm/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>通过内核获取config文件</title>
		<link>http://www.tek-life.org/2010/07/28/%e9%80%9a%e8%bf%87%e5%86%85%e6%a0%b8%e8%8e%b7%e5%8f%96config%e6%96%87%e4%bb%b6/</link>
		<comments>http://www.tek-life.org/2010/07/28/%e9%80%9a%e8%bf%87%e5%86%85%e6%a0%b8%e8%8e%b7%e5%8f%96config%e6%96%87%e4%bb%b6/#comments</comments>
		<pubDate>Wed, 28 Jul 2010 03:52:27 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10413</guid>
		<description><![CDATA[有两种方法，但前提是需要配置了，才能在编译好的内核中再获取config文件。貌似是一个蛋生鸡，鸡生蛋的纠结问题。 但，有时候，我们需要配置内核运行。因此，一般是需要获得一个既有的能跑的config文件。下面介绍的这两种获取config的方法就是用于此目的的。 首先，说一下方法： 法1.在运行的内核中的/proc/config.gz 保存的便是配置文件了 法2.通过/usr/src/linux/scripts/extract-ikconfig 脚本获得一个已经编译好的内核。 这两种方法的前提是，获取内核config的内核在编译的时候，下面的两项被选中了。 CONFIG_IKCONFIG This option enables the complete Linux kernel &#8220;.config&#8221; file contents to be saved in the kernel. It provides documentation of which kernel options are used in a running kernel or in &#8230; <a href="http://www.tek-life.org/2010/07/28/%e9%80%9a%e8%bf%87%e5%86%85%e6%a0%b8%e8%8e%b7%e5%8f%96config%e6%96%87%e4%bb%b6/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>有两种方法，但前提是需要配置了，才能在编译好的内核中再获取config文件。貌似是一个蛋生鸡，鸡生蛋的纠结问题。</p>
<p>但，有时候，我们需要配置内核运行。因此，一般是需要获得一个既有的能跑的config文件。下面介绍的这两种获取config的方法就是用于此目的的。</p>
<p>首先，说一下方法：<br />
法1.在运行的内核中的/proc/config.gz 保存的便是配置文件了<br />
法2.通过/usr/src/linux/scripts/extract-ikconfig 脚本获得一个已经编译好的内核。<br />
这两种方法的前提是，获取内核config的内核在编译的时候，下面的两项被选中了。</p>
<p>CONFIG_IKCONFIG<br />
      This option enables the complete Linux kernel &#8220;.config&#8221; file<br />
      contents to be saved in the kernel. It provides documentation<br />
      of which kernel options are used in a running kernel or in an<br />
      on-disk kernel.  This information can be extracted from the kernel<br />
      image file with the script scripts/extract-ikconfig and used as<br />
      input to rebuild the current kernel or to build another kernel.<br />
      It can also be extracted from a running kernel by reading<br />
      /proc/config.gz if enabled (below).<br />
如果这一项配置了，那么通过脚本extract-ikconfig获得config文件</p>
<p>CONFIG_IKCONFIG_PROC</p>
<p>      This option enables access to the kernel configuration file<br />
      through /proc/config.gz.<br />
如果这一项配置了，那么通过运行内核获得 /proc/config.gz</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/07/28/%e9%80%9a%e8%bf%87%e5%86%85%e6%a0%b8%e8%8e%b7%e5%8f%96config%e6%96%87%e4%bb%b6/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>shell脚本后台执行的若干总结</title>
		<link>http://www.tek-life.org/2010/07/24/shell%e8%84%9a%e6%9c%ac%e5%90%8e%e5%8f%b0%e6%89%a7%e8%a1%8c%e7%9a%84%e8%8b%a5%e5%b9%b2%e6%80%bb%e7%bb%93/</link>
		<comments>http://www.tek-life.org/2010/07/24/shell%e8%84%9a%e6%9c%ac%e5%90%8e%e5%8f%b0%e6%89%a7%e8%a1%8c%e7%9a%84%e8%8b%a5%e5%b9%b2%e6%80%bb%e7%bb%93/#comments</comments>
		<pubDate>Sat, 24 Jul 2010 04:35:53 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10407</guid>
		<description><![CDATA[命令后面直接加上&#038;的幼儿园方法不讨论，因为解决不了问题。 shell关闭了,但仍然让脚本执行的方法： 1. nohup 就是忽略掉了父进程hungup信号，继续执行。默认是将脚本所有的输出，输出到了/dev/null或者 nohup.out. 2.用setsid的方法，让脚本的父进程设置为init，这样，关闭shell，脚本照样执行。但对于有输出的脚本程序就不行了，那么用什么方法捏？可以指定其标准输出和错误输出都到/dev/null中。 setsid python /opt/proxy/localproxy/proxy.py > /dev/null 2>&#038;1 另外，搞清楚： command >/devnull 2>&#038;1 与 command > /dev/null 2>/dev/null 其区别不解释. 对于xterm远程登录，可能掉线的情况，推荐方法是用screen.这样可以续上原来的session. screen的具体用法不解释。]]></description>
			<content:encoded><![CDATA[<p>命令后面直接加上&#038;的幼儿园方法不讨论，因为解决不了问题。</p>
<p>shell关闭了,但仍然让脚本执行的方法：<br />
1. nohup 就是忽略掉了父进程hungup信号，继续执行。默认是将脚本所有的输出，输出到了/dev/null或者 nohup.out.<br />
2.用setsid的方法，让脚本的父进程设置为init，这样，关闭shell，脚本照样执行。但对于有输出的脚本程序就不行了，那么用什么方法捏？可以指定其标准输出和错误输出都到/dev/null中。<br />
setsid python /opt/proxy/localproxy/proxy.py > /dev/null 2>&#038;1</p>
<p>另外，搞清楚：<br />
command >/devnull 2>&#038;1 与 command > /dev/null 2>/dev/null<br />
其区别不解释.</p>
<p>对于xterm远程登录，可能掉线的情况，推荐方法是用screen.这样可以续上原来的session.<br />
screen的具体用法不解释。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/07/24/shell%e8%84%9a%e6%9c%ac%e5%90%8e%e5%8f%b0%e6%89%a7%e8%a1%8c%e7%9a%84%e8%8b%a5%e5%b9%b2%e6%80%bb%e7%bb%93/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux设备注册</title>
		<link>http://www.tek-life.org/2010/07/22/linux%e8%ae%be%e5%a4%87%e6%b3%a8%e5%86%8c/</link>
		<comments>http://www.tek-life.org/2010/07/22/linux%e8%ae%be%e5%a4%87%e6%b3%a8%e5%86%8c/#comments</comments>
		<pubDate>Thu, 22 Jul 2010 08:57:16 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Kernel]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10404</guid>
		<description><![CDATA[在加载驱动之前，设备最先被加载。其中最主要的就是资源的注册了。本文简要介绍一些注册的一些过程。 最初，是要准备设备的资源，然后被初始化。 拿imx的MMC为例，其他的与之相同。 arch/arm/mach-imx/gener.c static struct resource imx_mmc_resources[] = { [0] = { .start = 0&#215;00214000, .end = 0x002140FF, .flags = IORESOURCE_MEM, }, [1] = { .start = (SDHC_INT), .end = (SDHC_INT), .flags = IORESOURCE_IRQ, }, }; Coment:设备的资源定义一般都位于arch/arm/mach-机器类型目录下面的文件中。 static struct &#8230; <a href="http://www.tek-life.org/2010/07/22/linux%e8%ae%be%e5%a4%87%e6%b3%a8%e5%86%8c/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>在加载驱动之前，设备最先被加载。其中最主要的就是资源的注册了。本文简要介绍一些注册的一些过程。<br />
最初，是要准备设备的资源，然后被初始化。<br />
拿imx的MMC为例，其他的与之相同。<br />
arch/arm/mach-imx/gener.c<br />
static struct resource imx_mmc_resources[] = {<br />
    [0] = {<br />
        .start    = 0&#215;00214000,<br />
        .end    = 0x002140FF,<br />
        .flags    = IORESOURCE_MEM,<br />
    },<br />
    [1] = {<br />
        .start    = (SDHC_INT),<br />
        .end    = (SDHC_INT),<br />
        .flags    = IORESOURCE_IRQ,<br />
    },<br />
};<br />
Coment:设备的资源定义一般都位于arch/arm/mach-机器类型目录下面的文件中。</p>
<p>static struct platform_device imx_mmc_device = {<br />
    .name        = &#8220;imx-mmc&#8221;,<br />
    .id        = 0,<br />
    .num_resources    = ARRAY_SIZE(imx_mmc_resources),<br />
    .resource    = imx_mmc_resources,<br />
};</p>
<p>最初的设备定义仅此而已。</p>
<p>然后，看一下注册是经过怎样的流程：</p>
<p>void __init imx_map_io(void)<br />
{<br />
    iotable_init(imx_io_desc, ARRAY_SIZE(imx_io_desc));<br />
}</p>
<p>static struct platform_device *devices[] __initdata = {<br />
    &#038;imx_mmc_device,<br />
    &#038;imxfb_device,<br />
    &#038;imx_uart1_device,<br />
    &#038;imx_uart2_device,<br />
};</p>
<p>static int __init imx_init(void)<br />
{<br />
    return platform_add_devices(devices, ARRAY_SIZE(devices));<br />
}</p>
<p>subsys_initcall(imx_init);</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
以上调用imx_init，通过platform_add_devices来注册设备。我们继续跟进：</p>
<p>drivers/base/platform.c</p>
<p>int platform_add_devices(struct platform_device **devs, int num)<br />
{<br />
    int i, ret = 0;</p>
<p>    for (i = 0; i < num; i++) {<br />
        ret = platform_device_register(devs[i]);<br />
        if (ret) {<br />
            while (--i >= 0)<br />
                platform_device_unregister(devs[i]);<br />
            break;<br />
        }<br />
    }</p>
<p>    return ret;<br />
}<br />
EXPORT_SYMBOL_GPL(platform_add_devices);</p>
<p>int platform_device_register(struct platform_device * pdev)<br />
{<br />
    device_initialize(&#038;pdev->dev);<br />
    return platform_device_add(pdev);<br />
}<br />
EXPORT_SYMBOL_GPL(platform_device_register);</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
添加设备实际上也是注册设备，从其名字即可以看出来。<br />
我们看看，platform_device_register里面调用了初始化和add两个函数。<br />
初始化什么呢？因为在最初定义设备的时候，我们仅仅只定义了资源，设备成员变量dev没有被舒适化，那么这里 device_initialize(&#038;pdev->dev);便是初始化这个设备了。<br />
然后我们跟进 platform_device_add()<br />
drivers/base/platform.c<br />
int platform_device_add(struct platform_device *pdev)<br />
{<br />
    int i, ret = 0;</p>
<p>    if (!pdev)<br />
        return -EINVAL;</p>
<p>    if (!pdev->dev.parent)<br />
        pdev->dev.parent = &#038;platform_bus;</p>
<p>    pdev->dev.bus = &#038;platform_bus_type;</p>
<p>    if (pdev->id != -1)<br />
        snprintf(pdev->dev.bus_id, BUS_ID_SIZE, &#8220;%s.%u&#8221;, pdev->name, pdev->id);<br />
    else<br />
        strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);</p>
<p>    for (i = 0; i < pdev->num_resources; i++) {<br />
        struct resource *p, *r = &#038;pdev->resource[i];</p>
<p>        if (r->name == NULL)<br />
            r->name = pdev->dev.bus_id;</p>
<p>        p = r->parent;//如果资源还没有插入到树中的话，那么就到下面这个if语句块中<br />
        if (!p) {<br />
            if (r->flags &#038; IORESOURCE_MEM)<br />
                p = &#038;iomem_resource;<br />
            else if (r->flags &#038; IORESOURCE_IO)<br />
                p = &#038;ioport_resource;<br />
        }</p>
<p>        if (p &#038;&#038; insert_resource(p, r)) {<br />
            printk(KERN_ERR<br />
                   &#8220;%s: failed to claim resource %d\n&#8221;,<br />
                   pdev->dev.bus_id, i);<br />
            ret = -EBUSY;<br />
            goto failed;<br />
        }<br />
    }</p>
<p>    pr_debug(&#8220;Registering platform device &#8216;%s&#8217;. Parent at %s\n&#8221;,<br />
         pdev->dev.bus_id, pdev->dev.parent->bus_id);</p>
<p>    ret = device_add(&#038;pdev->dev);<br />
    if (ret == 0)<br />
        return ret;</p>
<p> failed:<br />
    while (&#8211;i >= 0)<br />
        if (pdev->resource[i].flags &#038; (IORESOURCE_MEM|IORESOURCE_IO))<br />
            release_resource(&#038;pdev->resource[i]);<br />
    return ret;<br />
}<br />
EXPORT_SYMBOL_GPL(platform_device_add);</p>
<p>这里最主要是两个函数，一个是insert_resource,一个是device_add.前一个把资源插入父设备的资源树中，关于Linux资源管理，网上有一篇很好的文章：“Linux对I/O端口资源的管理&#8211;詹荣开”。我摘录了其中对于insert_resource的描述：</p>
<p>　　Linux是以一种倒置的树形结构来管理每一类I/O资源（如：I/O端口、外设内存、DMA和IRQ）的。每一类I/O资源都对应有一颗倒置的资源树，树中的每一个节点都是一个resource结构，而树的根结点root则描述了该类资源的整个资源空间。</p>
<p>insert_resource调用__request_resource()来注册相应的资源：<br />
/* Return the conflict entry if you can&#8217;t request it */<br />
static struct resource * __request_resource(struct resource *root, struct resource *new)<br />
{<br />
    resource_size_t start = new->start;<br />
    resource_size_t end = new->end;<br />
    struct resource *tmp, **p;</p>
<p>    if (end < start)<br />
        return root;<br />
    if (start < root->start)<br />
        return root;<br />
    if (end > root->end)<br />
        return root;<br />
    p = &#038;root->child;<br />
    for (;;) {<br />
        tmp = *p;<br />
        if (!tmp || tmp->start > end) {<br />
            new->sibling = tmp;<br />
            *p = new;<br />
            new->parent = root;<br />
            return NULL;<br />
        }<br />
        p = &#038;tmp->sibling;<br />
        if (tmp->end < start)<br />
            continue;<br />
        return tmp;<br />
    }<br />
}<br />
算法描述：</p>
<p>　　①前三个if语句判断new所描述的资源范围是否被包含在root内，以及是否是一段有效的资源（因为end必须大于start）。否则就返回root指针，表示与根结点相冲突。<br />
　　②接下来用一个for循环遍历根节点root的child链表，以便检查是否有资源冲突，并将new插入到child链表中的合适位置（child链表是以I/O资源物理地址从低到高的顺序排列的）。为此，它用tmp指针指向当前正被扫描的resource结构，用指针p指向前一个resource结构的sibling指针成员变量，p的初始值为指向root－>sibling。For循环体的执行步骤如下：<br />
　　l 让tmp指向当前正被扫描的resource结构（tmp＝*p）。<br />
　　l 判断tmp指针是否为空（tmp指针为空说明已经遍历完整个child链表），或者当前被扫描节点的起始位置start是否比new的结束位置end还要大。只要这两个条件之一成立的话，就说明没有资源冲突，于是就可以把new链入child链表中：①设置new的sibling指针指向当前正被扫描的节点tmp（new->sibling=tmp）；②当前节点tmp的前一个兄弟节点的sibling指针被修改为指向new这个节点（*p=new）；③将new的parent指针设置为指向root。然后函数就可以返回了（返回值NULL表示没有资源冲突）。<br />
　　l 如果上述两个条件都不成立，这说明当前被扫描节点的资源域有可能与new相冲突（实际上就是两个闭区间有交集），因此需要进一步判断。为此它首先修改指针p，让它指向tmp->sibling，以便于继续扫描child链表。然后，判断tmp->end是否小于new->start，如果小于，则说明当前节点tmp和new没有资源冲突，因此执行continue语句，继续向下扫描child链表。否则，如果tmp->end大于或等于new->start，则说明tmp->[start,end]和new->[start,end]之间有交集。所以返回当前节点的指针tmp，表示发生资源冲突。</p>
<p>另外，看一下device_add()<br />
drivers/base/core.c</p>
<p>/**<br />
 *    device_add &#8211; add device to device hierarchy.<br />
 *    @dev:    device.<br />
 *<br />
 *    This is part 2 of device_register(), though may be called<br />
 *    separately _iff_ device_initialize() has been called separately.<br />
 *<br />
 *    This adds it to the kobject hierarchy via kobject_add(), adds it<br />
 *    to the global and sibling lists for the device, then<br />
 *    adds it to the other relevant subsystems of the driver model.<br />
 */<br />
int device_add(struct device *dev)<br />
{<br />
    struct device *parent = NULL;<br />
    char *class_name = NULL;<br />
    struct class_interface *class_intf;<br />
    int error = -EINVAL;</p>
<p>    dev = get_device(dev);<br />
    if (!dev || !strlen(dev->bus_id))<br />
        goto Error;</p>
<p>    pr_debug(&#8220;DEV: registering device: ID = &#8216;%s&#8217;\n&#8221;, dev->bus_id);</p>
<p>    parent = get_device(dev->parent);</p>
<p>    error = setup_parent(dev, parent);<br />
    if (error)<br />
        goto Error;</p>
<p>    /* first, register with generic layer. */<br />
    kobject_set_name(&#038;dev->kobj, &#8220;%s&#8221;, dev->bus_id);<br />
    error = kobject_add(&#038;dev->kobj);<br />
    if (error)<br />
        goto Error;</p>
<p>    /* notify platform of device entry */<br />
    if (platform_notify)<br />
        platform_notify(dev);</p>
<p>    /* notify clients of device entry (new way) */<br />
    if (dev->bus)<br />
        blocking_notifier_call_chain(&#038;dev->bus->bus_notifier,<br />
                         BUS_NOTIFY_ADD_DEVICE, dev);</p>
<p>    dev->uevent_attr.attr.name = &#8220;uevent&#8221;;<br />
    dev->uevent_attr.attr.mode = S_IWUSR;<br />
    if (dev->driver)<br />
        dev->uevent_attr.attr.owner = dev->driver->owner;<br />
    dev->uevent_attr.store = store_uevent;<br />
    error = device_create_file(dev, &#038;dev->uevent_attr);<br />
    if (error)<br />
        goto attrError;</p>
<p>    if (MAJOR(dev->devt)) {<br />
        struct device_attribute *attr;<br />
        attr = kzalloc(sizeof(*attr), GFP_KERNEL);<br />
        if (!attr) {<br />
            error = -ENOMEM;<br />
            goto ueventattrError;<br />
        }<br />
        attr->attr.name = &#8220;dev&#8221;;<br />
        attr->attr.mode = S_IRUGO;<br />
        if (dev->driver)<br />
            attr->attr.owner = dev->driver->owner;<br />
        attr->show = show_dev;<br />
        error = device_create_file(dev, attr);<br />
        if (error) {<br />
            kfree(attr);<br />
            goto ueventattrError;<br />
        }</p>
<p>        dev->devt_attr = attr;<br />
    }</p>
<p>    if (dev->class) {<br />
        sysfs_create_link(&#038;dev->kobj, &#038;dev->class->subsys.kset.kobj,<br />
                  &#8220;subsystem&#8221;);<br />
        /* If this is not a &#8220;fake&#8221; compatible device, then create the<br />
         * symlink from the class to the device. */<br />
        if (dev->kobj.parent != &#038;dev->class->subsys.kset.kobj)<br />
            sysfs_create_link(&#038;dev->class->subsys.kset.kobj,<br />
                      &#038;dev->kobj, dev->bus_id);<br />
        if (parent) {<br />
            sysfs_create_link(&#038;dev->kobj, &#038;dev->parent->kobj,<br />
                            &#8220;device&#8221;);<br />
#ifdef CONFIG_SYSFS_DEPRECATED<br />
            class_name = make_class_name(dev->class->name,<br />
                            &#038;dev->kobj);<br />
            if (class_name)<br />
                sysfs_create_link(&#038;dev->parent->kobj,<br />
                          &#038;dev->kobj, class_name);<br />
#endif<br />
        }<br />
    }</p>
<p>    if ((error = device_add_attrs(dev)))<br />
        goto AttrsError;<br />
    if ((error = device_add_groups(dev)))<br />
        goto GroupError;<br />
    if ((error = device_pm_add(dev)))<br />
        goto PMError;<br />
    if ((error = bus_add_device(dev)))<br />
        goto BusError;<br />
    if (!dev->uevent_suppress)<br />
        kobject_uevent(&#038;dev->kobj, KOBJ_ADD);<br />
    if ((error = bus_attach_device(dev)))<br />
        goto AttachError;<br />
    if (parent)<br />
        klist_add_tail(&#038;dev->knode_parent, &#038;parent->klist_children);</p>
<p>    if (dev->class) {<br />
        down(&#038;dev->class->sem);<br />
        /* tie the class to the device */<br />
        list_add_tail(&#038;dev->node, &#038;dev->class->devices);</p>
<p>        /* notify any interfaces that the device is here */<br />
        list_for_each_entry(class_intf, &#038;dev->class->interfaces, node)<br />
            if (class_intf->add_dev)<br />
                class_intf->add_dev(dev, class_intf);<br />
        up(&#038;dev->class->sem);<br />
    }<br />
 Done:<br />
     kfree(class_name);<br />
    put_device(dev);<br />
    return error;<br />
    &#8230;&#8230;</p>
<p>}</p>
<p>上面的这个语句块主要的工作是在设备模型的工作。从注释中，我们可以看到其主要思路。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/07/22/linux%e8%ae%be%e5%a4%87%e6%b3%a8%e5%86%8c/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ubuntu 10.04 编译低版本gcc3.4.6出错</title>
		<link>http://www.tek-life.org/2010/07/21/ubuntu-10-04-%e7%bc%96%e8%af%91%e4%bd%8e%e7%89%88%e6%9c%acgcc3-4-6%e5%87%ba%e9%94%99/</link>
		<comments>http://www.tek-life.org/2010/07/21/ubuntu-10-04-%e7%bc%96%e8%af%91%e4%bd%8e%e7%89%88%e6%9c%acgcc3-4-6%e5%87%ba%e9%94%99/#comments</comments>
		<pubDate>Wed, 21 Jul 2010 13:12:57 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10402</guid>
		<description><![CDATA[./configure &#8211;disable-checking make 出错： error: call to &#8216;__open_missing_mode&#8217; declared with attribute error: open with O_CREAT in second argument needs 3 arguments 更改 gcc/collect2.c 1537行，添加地三个参数 edir_handle = open (redir, O_WRONLY &#124; O_TRUNC &#124; O_CREAT, 0777);]]></description>
			<content:encoded><![CDATA[<p>./configure &#8211;disable-checking<br />
make<br />
出错：<br />
 error:  call to &#8216;__open_missing_mode&#8217;  declared with attribute error: open with O_CREAT in second argument needs 3 arguments<br />
更改<br />
gcc/collect2.c 1537行，添加地三个参数<br />
edir_handle = open (redir, O_WRONLY | O_TRUNC |  O_CREAT, 0777);</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/07/21/ubuntu-10-04-%e7%bc%96%e8%af%91%e4%bd%8e%e7%89%88%e6%9c%acgcc3-4-6%e5%87%ba%e9%94%99/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于位域</title>
		<link>http://www.tek-life.org/2010/07/13/%e5%85%b3%e4%ba%8e%e4%bd%8d%e5%9f%9f/</link>
		<comments>http://www.tek-life.org/2010/07/13/%e5%85%b3%e4%ba%8e%e4%bd%8d%e5%9f%9f/#comments</comments>
		<pubDate>Tue, 13 Jul 2010 09:24:10 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[C]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10389</guid>
		<description><![CDATA[位域，就是在某些结构的成员变量后面加了冒号后跟一个十进制的数子，比如： struct str { int a:1; }类似的定义在kernel中经常碰到。先看一个例子： #include typedef struct test { unsigned int a:1; int b; }Test; Test tt; void main() { tt.a=1,tt.b=100; printf("tt.a:%d,tt.b:%d\n",tt.a,tt.b); tt.a=0,tt.b=100; printf("tt.a:%d,tt.b:%d\n",tt.a,tt.b); tt.a=2,tt.b=100; printf("tt.a:%d,tt.b:%d\n",tt.a,tt.b); tt.a=3,tt.b=100; printf("tt.a:%d,tt.b:%d\n",tt.a,tt.b); } 运行结果： omycle@omycle-desktop:~$ ./a tt.a:1,tt.b:100 tt.a:0,tt.b:100 tt.a:0,tt.b:100 tt.a:1,tt.b:100 可以看到，结构体Test成员变量a被定义为unsigned &#8230; <a href="http://www.tek-life.org/2010/07/13/%e5%85%b3%e4%ba%8e%e4%bd%8d%e5%9f%9f/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>位域，就是在某些结构的成员变量后面加了冒号后跟一个十进制的数子，比如：<br />
struct str<br />
{<br />
int a:1;<br />
}类似的定义在kernel中经常碰到。先看一个例子：</p>
<pre class="brush:c">
#include <stdio.h>
typedef struct test
{
unsigned int a:1;
int b;
}Test;
Test tt;
void main()
{
tt.a=1,tt.b=100;
printf("tt.a:%d,tt.b:%d\n",tt.a,tt.b);
tt.a=0,tt.b=100;
printf("tt.a:%d,tt.b:%d\n",tt.a,tt.b);
tt.a=2,tt.b=100;
printf("tt.a:%d,tt.b:%d\n",tt.a,tt.b);
tt.a=3,tt.b=100;
printf("tt.a:%d,tt.b:%d\n",tt.a,tt.b);
}</pre>
<p>运行结果：<br />
omycle@omycle-desktop:~$ ./a<br />
tt.a:1,tt.b:100<br />
tt.a:0,tt.b:100<br />
tt.a:0,tt.b:100<br />
tt.a:1,tt.b:100<br />
可以看到，结构体Test成员变量a被定义为unsigned int 中的最低位&#8211;LSB，这就决定了非0即1.我们在实验中也验证了确实是这样子的。<br />
那么位域有什么好处呢？</p>
<blockquote><p>有些信息在存储时，并不需要占用一个完整的字节， 而只需占几个或一个二进制位。例如在存放一个开关量时，只有0和1 两种状态， 用一位二进位即可。为了节省存储空间，并使处理简便，C语言又提供了一种数据结构，称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域，并说明每个区域的位数。每个域有一个域名，允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。</p></blockquote>
<p><strong>说明</strong>：<br />
1. 一个位域必须存储在同一个字节中，不能跨两个字节。如一个字节所剩空间不够存放另一位域时，应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如：<br />
struct bs<br />
{<br />
unsigned a:4<br />
unsigned :0 /*空域*/<br />
unsigned b:4 /*从下一单元开始存放*/<br />
unsigned c:4<br />
}<br />
这个位域定义中，a占第一字节的4位，后4位填0表示不使用，b从第二字节开始，占用4位，c占用4位。<br />
2. 位域可以无位域名，这时它只用来作填充或调整位置。无名的位域是不能使用的。例如：<br />
struct k<br />
{<br />
int a:1<br />
int :2 /*该2位不能使用*/<br />
int b:3<br />
int c:2<br />
}; </p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/07/13/%e5%85%b3%e4%ba%8e%e4%bd%8d%e5%9f%9f/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>函数指针与typedef</title>
		<link>http://www.tek-life.org/2010/07/13/%e5%87%bd%e6%95%b0%e6%8c%87%e9%92%88%e4%b8%8etypedef/</link>
		<comments>http://www.tek-life.org/2010/07/13/%e5%87%bd%e6%95%b0%e6%8c%87%e9%92%88%e4%b8%8etypedef/#comments</comments>
		<pubDate>Tue, 13 Jul 2010 01:20:13 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[C]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10385</guid>
		<description><![CDATA[在阅读内核的时候，会遇到不少函数指针的情况。整理了一下，另外，也有c++函数指针的情况。不过清楚了c的函数指针，读c++的函数指针就不困难了。 另外，也要注意弱类型与强类型。 （一）简单的函数指针的应用。 //形式1：返回类型(*函数名)(参数表) char  (*pFun) (int); //声明 char glFun(int a) { return;}         第一行定义了一个指针变量pFun。首先我们根据前面提到的“形式1”认识到它是一个指向某种函数的指针，这种函数参数是一个int型，返回值是 char类型。只有第一句我们还无法使用这个指针，因为我们还未对它进行赋值。         第二行定义了一个函数glFun()。该函数正好是一个以int为参数返回char的函数。我们要从指针的层次上理解函数——函数的函数名实际上就是一个指针，函数名指向该函数的代码在内存中的首地址。         然后就是可爱的main()函数了，它的第一句您应该看得懂了——它将函数glFun的地址赋值给变量pFun。main()函数的第二句中 “*pFun”显然是取pFun所指向地址的内容，当然也就是取出了函数glFun()的内容，然后给定参数为2。这里，我们可能会稍微疑惑一下，对于变量赋值给指针的话是这样子的 int i; int * ptr; ptr=&#38;i 而这里对函数的赋值，直接是ptr=fun().实际上，函数名就是一个地址。在内核的代码中，我们经常能见到在汇编中调用c函数，是直接call fun，来进行的。因此，疑惑终结。 void main() {     pFun = glFun;//赋值     (*pFun)(2); } &#8230; <a href="http://www.tek-life.org/2010/07/13/%e5%87%bd%e6%95%b0%e6%8c%87%e9%92%88%e4%b8%8etypedef/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><strong><span style="font-size: medium;">在阅读内核的时候，会遇到不少函数指针的情况。整理了一下，另外，也有c++函数指针的情况。不过清楚了c的函数指针，读c++的函数指针就不困难了。</span></strong></p>
<p><strong><span style="font-size: medium;">另外，也要注意弱类型与强类型。</span></strong></p>
<p><strong><span style="font-size: medium;">（一）简单的函数指针的应用。</span></strong><br />
<span style="color: #00ff00;">//形式1：返回类型(*函数名)(参数表)</span><br />
<strong>char  (*pFun) (int); //声明</strong></p>
<p><strong>char glFun(int a)<br />
</strong><strong>{ return;}</strong><br />
        第一行定义了一个指针变量pFun。首先我们根据前面提到的“形式1”认识到它是一个指向某种函数的指针，这种函数参数是一个int型，返回值是 char类型。只有第一句我们还无法使用这个指针，因为我们还未对它进行赋值。<br />
        第二行定义了一个函数glFun()。该函数正好是一个以int为参数返回char的函数。我们要从指针的层次上理解函数——函数的函数名实际上就是一个指针，函数名指向该函数的代码在内存中的首地址。<br />
        然后就是可爱的main()函数了，它的第一句您应该看得懂了——它将函数glFun的地址赋值给变量pFun。main()函数的第二句中 “*pFun”显然是取pFun所指向地址的内容，当然也就是取出了函数glFun()的内容，然后给定参数为2。这里，我们可能会稍微疑惑一下，对于变量赋值给指针的话是这样子的<br />
int i;<br />
int * ptr;<br />
ptr=&amp;i<br />
而这里对函数的赋值，直接是ptr=fun().实际上，函数名就是一个地址。在内核的代码中，我们经常能见到在汇编中调用c函数，是直接call fun，来进行的。因此，疑惑终结。</p>
<p>void main()<br />
{<br />
    pFun = glFun;//赋值<br />
    (*pFun)(2);<br />
}</p>
<p><span style="font-size: medium;"><strong> </strong></span></p>
<p><span style="font-size: medium;"><strong>（二）使用typedef更直观更方便。</strong></span><br />
<span style="color: #00ff00;">//形式2：typedef 返回类型(*新类型)(参数表)</span><br />
<strong>typedef char (*PTRFUN)(int);</strong><br />
        typedef的功能是定义新的类型。第一句就是定义了一种PTRFUN的类型，并定义这种类型为指向某种函数的指针，这种函数以一个int为参数并返回 char类型。后面就可以像使用int,char一样使用PTRFUN了。<br />
        第二行的代码便使用这个新类型定义了变量pFun，此时就可以像使用形式1一样使用这个变量了。</p>
<p>PTRFUN pFun; //声明</p>
<p>char glFun(int a)<br />
{ return;}</p>
<p>void main()<br />
{<br />
    pFun = glFun; //赋值<br />
    (*pFun)(2);<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/07/13/%e5%87%bd%e6%95%b0%e6%8c%87%e9%92%88%e4%b8%8etypedef/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>EmbeddedXENVIRQ发送流程</title>
		<link>http://www.tek-life.org/2010/07/02/hypervisor%e5%8f%91%e9%80%81v/</link>
		<comments>http://www.tek-life.org/2010/07/02/hypervisor%e5%8f%91%e9%80%81v/#comments</comments>
		<pubDate>Fri, 02 Jul 2010 01:41:22 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[EmbeddedXEN]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10378</guid>
		<description><![CDATA[Hypervisor发送virq的函数有两个： 一个是send_geust_vcpu_virq,另一个是send_guest_global_virq void send_guest_vcpu_virq(struct vcpu *v, int virq) {     int port;     ASSERT(!virq_is_global(virq));     port = v-&#62;virq_to_evtchn[virq];     if ( unlikely(port == 0) ) {         return;     }     evtchn_set_pending(v, port); } void send_guest_global_virq(struct domain *d, int &#8230; <a href="http://www.tek-life.org/2010/07/02/hypervisor%e5%8f%91%e9%80%81v/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div>Hypervisor发送virq的函数有两个：</div>
<div>一个是send_geust_vcpu_virq,另一个是send_guest_global_virq</div>
<div>
<table id="wqaf" border="1" cellspacing="0" cellpadding="3" width="100%" bordercolor="#000000">
<tbody>
<tr>
<td width="50%" valign="top">
<div>void send_guest_vcpu_virq(struct vcpu *v, int virq)</div>
<div>{</div>
<div>    int port;</div>
<div>    ASSERT(!virq_is_global(virq));</div>
<div>    port = v-&gt;virq_to_evtchn[virq];</div>
<div>    if ( unlikely(port == 0) ) {</div>
<div>        return;</div>
<div>    }</div>
<div>  <strong>  evtchn_set_pending</strong>(v, port);</div>
<div>}</div>
</td>
<td width="50%">
<div>void send_guest_global_virq(struct domain *d, int virq)</div>
<div>{</div>
<div>    int port;</div>
<div>    struct vcpu *v;</div>
<div>    struct evtchn *chn;</div>
<div>    ASSERT(virq_is_global(virq));</div>
<div>    if ( unlikely(d == NULL) )</div>
<div>        return;</div>
<div>    v = d-&gt;vcpu[0];</div>
<div>    if ( unlikely(v == NULL) )</div>
<div>        return;</div>
<div>    port = v-&gt;virq_to_evtchn[virq];</div>
<div>    if ( unlikely(port == 0) )</div>
<div>        return;</div>
<div>    chn = evtchn_from_port(d, port);</div>
<div>   <strong> evtchn_set_pending</strong>(d-&gt;vcpu[chn-&gt;notify_vcpu_id], port);</div>
<div>}</div>
</td>
</tr>
</tbody>
</table>
</div>
<div>send_guest_vcpu_virq的调用关系是这样的：</div>
<div>图中的两个定时器是在分配vcpu的时候安装的。</div>
<div><a href="http://www.tek-life.org/wp-content/uploads/2010/07/virq-.jpg"></a><a href="http://www.tek-life.org/wp-content/uploads/2010/07/virq-.jpg"><img class="aligncenter size-full wp-image-10379" title="virq---" src="http://www.tek-life.org/wp-content/uploads/2010/07/virq-.jpg" alt="" width="800" height="600" /></a></div>
<p>send_guest_global_virq的调用关系比较复杂，都是发给DOM0的</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/07/02/hypervisor%e5%8f%91%e9%80%81v/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>EmbeddedXEN VIRQ申请</title>
		<link>http://www.tek-life.org/2010/07/01/embeddedxen-virq%e7%94%b3%e8%af%b7/</link>
		<comments>http://www.tek-life.org/2010/07/01/embeddedxen-virq%e7%94%b3%e8%af%b7/#comments</comments>
		<pubDate>Thu, 01 Jul 2010 13:48:57 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[EmbeddedXEN]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10363</guid>
		<description><![CDATA[为了说明VIRQ，拿时间中断的注册为例来说明Virq的申请过程，明白了VIRQ申请过程后，我们简单的看一下DOMU console的注册过程。 arch/arm/mach-pxa/time-xen.c MACHINE_START(MAINSTONE, &#8220;Intel HCDDBBVA0 Development Platform (aka Mainstone) / EmbeddedXEN (xen-pxa270-qemu)&#8221;) /* Maintainer: MontaVista Software Inc. */ .phys_io = 0&#215;40000000, .boot_params = 0xa0000100, /* BLOB boot parameter setting */ .io_pg_offst = (io_p2v(0&#215;40000000) &#62;&#62; 18) &#38; 0xfffc, .timer = &#8230; <a href="http://www.tek-life.org/2010/07/01/embeddedxen-virq%e7%94%b3%e8%af%b7/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div>为了说明VIRQ，拿时间中断的注册为例来说明Virq的申请过程，明白了VIRQ申请过程后，我们简单的看一下DOMU console的注册过程。</p>
<div>
arch/arm/mach-pxa/time-xen.c</p>
<div>MACHINE_START(<strong>MAINSTONE</strong>, &#8220;Intel HCDDBBVA0 Development Platform (aka Mainstone) / EmbeddedXEN (xen-pxa270-qemu)&#8221;)</div>
<div>/* Maintainer: MontaVista Software Inc. */</div>
<div>.phys_io = 0&#215;40000000,</div>
<div>.boot_params = 0xa0000100, /* BLOB boot parameter setting */</div>
<div>.io_pg_offst = (io_p2v(0&#215;40000000) &gt;&gt; 18) &amp; 0xfffc,</div>
<div>.timer = &amp;<strong>pxa_timer</strong>,</div>
<div>.init_machine = mainstone_init</div>
<div>MACHINE_END</div>
<div>struct sys_timer <strong>pxa_timer</strong> = {</div>
<div>.init =<strong> pxa_timer_init</strong>,</div>
<div>/*</div>
<div>.suspend = pxa_timer_suspend,</div>
<div>.resume = pxa_timer_resume,</div>
<div>*/</div>
<div>.offset = pxa_gettimeoffset,</div>
<div>};</div>
<div>&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;好了，上面是数据结构，以及定义，现在看一下怎么用这些数据的&#8212;&#8211;</div>
<div>init/main-xen.c</div>
<p>asmlinkage void  __init start_kernel_lx0(void)</p>
<div>{</div>
<div>    &#8230;&#8230;</div>
<div>    <strong>time_init();</strong> /* (DRE) xen guest */</div>
<p>    &#8230;&#8230;</p>
<div>}</p>
</div>
<div>arch/arm/kernel/time.c</div>
<div>void __init time_init(void)</div>
<div>{</div>
<div>if (system_timer-&gt;offset == NULL)</div>
<div>system_timer-&gt;offset = dummy_gettimeoffset;</div>
<div><strong>system_timer-&gt;init()</strong>;</div>
<div>#ifdef CONFIG_NO_IDLE_HZ</div>
<div>if (system_timer-&gt;dyn_tick)</div>
<div>system_timer-&gt;dyn_tick-&gt;lock = SPIN_LOCK_UNLOCKED;</div>
<div>#endif</div>
<div>}</div>
<p>&#8212;&#8212;&#8212;就这样pxa_timer_init登上舞台了&#8212;&#8212;&#8212;</p></div>
<p>arch/arm/mach-pxa/time-xen.c</p>
<div>static void __init pxa_timer_init(void)</div>
<div>{</div>
<div>   <strong>bind_virq_to_irqhandler</strong>(</div>
<div>                        VIRQ_TIMER, //VIRQ_TIMER:0</div>
<div>                        0,</div>
<div>                        <strong>xen_virtual_timer_interrupt</strong>,</div>
<div>                        SA_INTERRUPT,</div>
<div>                        &#8221;timer0&#8243;,</div>
<div>/*                        &#8221;ost0&#8243;,*/</div>
<div>                        NULL);</div>
<div>}</div>
<p>xen_guest/core/evtchn.c</p>
<div>int <strong>bind_virq_to_irqhandler</strong>(unsigned int virq,unsigned int cpu,irq_handler_t handler,unsigned long irqflags,const char *devname,void *dev_id)</div>
<div>{</div>
<div>unsigned int irq;</div>
<div>int retval;</div>
<div>irq = bind_virq_to_irq(virq, cpu);</div>
<div>retval = <strong>request_irq</strong>(irq, handler, irqflags, devname, dev_id);</div>
<div>if (retval != 0) {</div>
<div>unbind_from_irq(irq);</div>
<div>return retval;</div>
<div>}</div>
<div>return irq;</div>
<div> }</div>
<p>&#8212;&#8212;&#8211;看一下如何将virq绑定到irq上的@！&#8212;&#8212;&#8212;&#8211;</p>
<blockquote>
<blockquote>
<blockquote></blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote><p>文件路径同上<br />
static int <strong>bind_virq_to_irq</strong>(unsigned int virq, unsigned int cpu)<br />
{<br />
//evtchn_op_t op = { .cmd = EVTCHNOP_bind_virq };<br />
evtchn_bind_virq_t op;<br />
int evtchn, irq;</p>
<p>spin_lock(&amp;irq_mapping_update_lock);</p>
<p>if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1) {//说明，这个virq对应的irq还没有被分配，可以使用<br />
op.virq = virq;<br />
op.vcpu = cpu;</p>
<p>BUG_ON(<strong>HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &amp;op)</strong> != 0);//virq与evtchn相联系了。下面要让evtchn和irq相联系,这样，才能在DOMU这一层处理<br />
evtchn = op.port;<br />
irq = <strong>find_unbound_irq</strong>();<br />
evtchn_to_irq[evtchn] = irq;<br />
irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn);</p>
<p>per_cpu(virq_to_irq, cpu)[virq] = irq;</p>
<p>bind_evtchn_to_cpu(evtchn, cpu);//在DOM中将其绑定的！@<br />
}</p>
<p><strong>irq_bindcount[irq]++;//找到一个被绑定的Irq以后，要将数组中的该元素加一的，这样以后再找Unbound_irq的时候，就不用找它了。</strong></p>
<p>return irq;<br />
}</p></blockquote>
<div>文件路径同上，在这里，也可以看出，DOMU里面同样也有Irq的概念。只不过描述符不同<br />
//找到一个没有被绑定的的irq<br />
static int find_unbound_irq(void)<br />
{<br />
int irq;</p>
<p>for (irq = 0; irq &lt; NR_IRQS; irq++)<br />
if (irq_bindcount[irq] == 0)//找到了<br />
break;</p>
<p>if (irq == NR_IRQS)<br />
panic(&#8220;No available IRQ to bind to: increase NR_IRQS!\n&#8221;);</p>
<p>return irq;<br />
}</p></div>
<blockquote><p><strong>HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &amp;op)会根据CMD去调用evtchn_bind_virq<br />
</strong>xen/common/event_channel.c<br />
static long evtchn_bind_virq(evtchn_bind_virq_t *bind)<br />
{<br />
    struct evtchn *chn;<br />
    struct vcpu   *v;<br />
    struct domain *d = current-&gt;domain;<br />
    int            port, virq = bind-&gt;virq, vcpu = bind-&gt;vcpu;<br />
    long           rc = 0;</p>
<p>    if ( (virq &lt; 0) || (virq &gt;= ARRAY_SIZE(v-&gt;virq_to_evtchn)) )<br />
        return -EINVAL;</p>
<p>    if ( virq_is_global(virq) &amp;&amp; (vcpu != 0) )<br />
        return -EINVAL;</p>
<p>    if ( (vcpu &lt; 0) || (vcpu &gt;= ARRAY_SIZE(d-&gt;vcpu)) ||<br />
         ((v = d-&gt;vcpu[vcpu]) == NULL) )<br />
        return -ENOENT;</p>
<p>    spin_lock(&amp;d-&gt;evtchn_lock);</p>
<p>    if ( v-&gt;virq_to_evtchn[virq] != 0 )<br />
        ERROR_EXIT(-EEXIST);</p>
<p>    if ( (port = get_free_port(d)) &lt; 0 )<br />
        ERROR_EXIT(port);<br />
//分配evtchn号<br />
    chn = evtchn_from_port(d, port);<br />
    chn-&gt;state          = ECS_VIRQ;<br />
    chn-&gt;notify_vcpu_id = vcpu;<br />
    chn-&gt;u.virq         = virq;</p>
<p>    v-&gt;virq_to_evtchn[virq] = bind-&gt;port = port;</p>
<p> out:<br />
    spin_unlock(&amp;d-&gt;evtchn_lock);</p>
<p>    return rc;<br />
}</p></blockquote>
</blockquote>
<p>&#8212;&#8212;&#8212;-</p>
<div><span style="color: #ff0000;">将xen_virtual_timer_interrupt送到irq线上</span></div>
<p>arch/arm/mach-pxa/time-xen.c</p>
<div>/* (GCD) From Samsung. Added regs argument */</div>
<div>static irqreturn_t <strong>xen_virtual_timer_interrupt</strong>(int irq, void *dev_id, struct pt_regs *regs)//简单的就是更新墙上时间</div>
<div>{</div>
<div>write_seqlock(&amp;xtime_lock);</div>
<div>//if (displayme)</div>
<div>//  printk(&#8220;### timer_tick\n&#8221;);</div>
<div>timer_tick(regs);</div>
<div>write_sequnlock(&amp;xtime_lock);</div>
<div>return IRQ_HANDLED;</div>
<div>}</div>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote><p>
 </p></blockquote>
</blockquote>
</blockquote>
<div><span>对比一下，看看hypervisor层的时间中断处理函数：<br />
static irqreturn_t pxa_ost0_interrupt(int irq, void *dev_id, struct pt_regs *regs)<br />
{<br />
//struct clock_event_device *c = dev_id;<br />
    //static int cnt = 0;</p>
<p>/* Disarm the compare/match, signal the event. */<br />
OIER &amp;= ~OIER_E0;<br />
OSSR = OSSR_M0;</p>
<p></span><strong><span>timer_interrupt</span></strong><span>(irq, dev_id, regs);</p>
<p>return IRQ_HANDLED;<br />
}<br />
void </span><strong><span>timer_interrupt</span></strong><span>(int irq, void *dev_id, struct xen_cpu_user_regs *regs) {<br />
ASSERT(local_irq_is_enabled());</p>
<p>/* Update jiffies counter. */<br />
(*(volatile unsigned long *) &amp;jiffies_64)++;<br />
<strong>raise_softirq(TIMER_SOFTIRQ)</strong>;</p>
<p>if (&#8211;plt_overflow_jiffies == 0)<br />
plt_overflow();<br />
}</p>
<p></span> </div>
<blockquote>
<blockquote></blockquote>
</blockquote>
</blockquote>
</blockquote>
<div>arch/arm/kernel/time.c</div>
<p>/*<br />
 * Kernel system timer support.<br />
 */<br />
void timer_tick(struct pt_regs *regs)<br />
{<br />
profile_tick(CPU_PROFILING, regs);<br />
do_leds();<br />
do_set_rtc();<br />
do_timer(regs);<br />
#ifndef CONFIG_SMP<br />
update_process_times(user_mode(regs));<br />
#endif<br />
}<br />
以后，当virq中断来了以后，就直接执行<strong>xen_virtual_timer_interrupt</strong>了。</p>
<div>除了timer的virq申请之外，还有console的申请。</div>
<p>在console模块注册的时候，申请了virq中断。我们可以在xen_guest/core/console.c里面找到</p>
<div>static int __init xencons_init(void)</div>
<div>{</div>
<div>int rc;</div>
<div>if (xen_init() &lt; 0)</div>
<div>return -ENODEV;</div>
<div>if (xc_mode == XC_OFF)</div>
<div>return 0;</div>
<div>if (!(xen_start_info-&gt;flags &amp; SIF_INITDOMAIN)) {</div>
<div>rc=xencons_ring_init();</div>
<div>if(rc)</div>
<div>return rc;</div>
<div>}</div>
<div>xencons_driver = alloc_tty_driver((xc_mode == XC_SERIAL) ?</div>
<div> 1 : MAX_NR_CONSOLES);</div>
<div>if (xencons_driver == NULL)</div>
<div>return -ENOMEM;</div>
<div>DRV(xencons_driver)-&gt;name            = &#8220;xencons&#8221;;</div>
<div>DRV(xencons_driver)-&gt;major           = TTY_MAJOR;</div>
<div>DRV(xencons_driver)-&gt;type            = TTY_DRIVER_TYPE_SERIAL;</div>
<div>DRV(xencons_driver)-&gt;subtype         = SERIAL_TYPE_NORMAL;</div>
<div>DRV(xencons_driver)-&gt;init_termios    = tty_std_termios;</div>
<div>DRV(xencons_driver)-&gt;flags           =</div>
<div>TTY_DRIVER_REAL_RAW |</div>
<div>TTY_DRIVER_RESET_TERMIOS;</div>
<div>DRV(xencons_driver)-&gt;termios         = xencons_termios;</div>
<div>DRV(xencons_driver)-&gt;termios_locked  = xencons_termios_locked;</div>
<div>if (xc_mode == XC_SERIAL) {</div>
<div>DRV(xencons_driver)-&gt;name        = &#8220;ttyS&#8221;;</div>
<div>DRV(xencons_driver)-&gt;minor_start = 64 + xc_num;</div>
<div>DRV(xencons_driver)-&gt;name_base   = 0 + xc_num;</div>
<div>} else {</div>
<div>DRV(xencons_driver)-&gt;name        = &#8220;tty&#8221;;</div>
<div>DRV(xencons_driver)-&gt;minor_start = xc_num;</div>
<div>DRV(xencons_driver)-&gt;name_base   = xc_num;</div>
<div>}</div>
<div>tty_set_operations(xencons_driver, &amp;xencons_ops);</div>
<div>if ((rc = tty_register_driver(DRV(xencons_driver))) != 0) {</div>
<div>printk(&#8220;WARNING: Failed to register Xen virtual &#8220;</div>
<div>      &#8220;console driver as &#8216;%s%d&#8217;\n&#8221;,</div>
<div>      DRV(xencons_driver)-&gt;name,</div>
<div>      DRV(xencons_driver)-&gt;name_base);</div>
<div>put_tty_driver(xencons_driver);</div>
<div>xencons_driver = NULL;</div>
<div>return rc;</div>
<div>}</div>
<div>// tty_register_device(xencons_driver, 0, NULL);</div>
<div>if (xen_start_info-&gt;flags &amp; SIF_INITDOMAIN) {</div>
<div>xencons_priv_irq = <strong>bind_virq_to_irqhandler</strong>(</div>
<div>VIRQ_CONSOLE,</div>
<div>0,</div>
<div>xencons_priv_interrupt,</div>
<div>0,</div>
<div>&#8220;console&#8221;,</div>
<div>NULL);</div>
<div>BUG_ON(xencons_priv_irq &lt; 0);</div>
<div>}</div>
<div>printk(&#8220;Xen virtual console successfully installed as %s%d\n&#8221;,</div>
<div>      DRV(xencons_driver)-&gt;name,</div>
<div>      DRV(xencons_driver)-&gt;name_base );</div>
<div>return 0;</div>
<div>}</div>
<div>module_init(xencons_init);</div>
<div>好了到此为止。</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/07/01/embeddedxen-virq%e7%94%b3%e8%af%b7/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>超级调用swi 82的最初流程</title>
		<link>http://www.tek-life.org/2010/06/30/%e8%b6%85%e7%ba%a7%e8%b0%83%e7%94%a8swi-82%e7%9a%84%e6%9c%80%e5%88%9d%e6%b5%81%e7%a8%8b/</link>
		<comments>http://www.tek-life.org/2010/06/30/%e8%b6%85%e7%ba%a7%e8%b0%83%e7%94%a8swi-82%e7%9a%84%e6%9c%80%e5%88%9d%e6%b5%81%e7%a8%8b/#comments</comments>
		<pubDate>Wed, 30 Jun 2010 07:32:23 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[EmbeddedXEN]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10352</guid>
		<description><![CDATA[xen/arch/arm/kernel/entry-armv.S __vectors_start: swi SYS_ERROR0 /* Reset */ b vector_undefined_instruction + stubs_offset ldr pc, .LCvswi + stubs_offset b vector_prefetch_abort + stubs_offset b vector_data_abort + stubs_offset b vector_addrexcptn + stubs_offset b vector_irq + stubs_offset b vector_fiq + stubs_offset .globl __vectors_end __vectors_end: /*  * We &#8230; <a href="http://www.tek-life.org/2010/06/30/%e8%b6%85%e7%ba%a7%e8%b0%83%e7%94%a8swi-82%e7%9a%84%e6%9c%80%e5%88%9d%e6%b5%81%e7%a8%8b/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><span class="Apple-style-span" style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; font: medium Simsun; white-space: normal; orphans: 2; letter-spacing: normal; color: #000000; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;"></p>
<div style="line-height: normal; background-color: #ffffff; margin: 6px; min-height: 1100px; font-family: Verdana; counter-reset: __goog_page__ 0; color: #000000; font-size: 10pt; padding: 0px;">
<div style="margin-top: 0px; margin-bottom: 0px;">xen/arch/arm/kernel/entry-armv.S</div>
<div style="margin-top: 0px; margin-bottom: 0px;">__vectors_start:</div>
<div style="margin-top: 0px; margin-bottom: 0px;">swi SYS_ERROR0 /* Reset */</div>
<div style="margin-top: 0px; margin-bottom: 0px;">b vector_undefined_instruction + stubs_offset</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr pc,<span class="Apple-converted-space"> </span><strong>.LCvswi</strong><span class="Apple-converted-space"> </span>+ stubs_offset</div>
<div style="margin-top: 0px; margin-bottom: 0px;">b vector_prefetch_abort + stubs_offset</div>
<div style="margin-top: 0px; margin-bottom: 0px;">b vector_data_abort + stubs_offset</div>
<div style="margin-top: 0px; margin-bottom: 0px;">b vector_addrexcptn + stubs_offset</div>
<div style="margin-top: 0px; margin-bottom: 0px;">b vector_irq + stubs_offset</div>
<div style="margin-top: 0px; margin-bottom: 0px;">b vector_fiq + stubs_offset</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.globl __vectors_end</div>
<div style="margin-top: 0px; margin-bottom: 0px;">__vectors_end:</div>
<div style="margin-top: 0px; margin-bottom: 0px;">/*</div>
<div style="margin-top: 0px; margin-bottom: 0px;"> * We group all the following data together to optimise</div>
<div style="margin-top: 0px; margin-bottom: 0px;"> * for CPUs with separate I &amp; D caches.</div>
<div style="margin-top: 0px; margin-bottom: 0px;"> */</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.align 5</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.LCvswi:</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.word<span class="Apple-converted-space"> </span><strong>vector_swi</strong></div>
<p>&#8212;&#8212;&#8212;&#8212;&#8211;<br />
xen/arch/arm/hypervisor/hypervisor.S</p>
<div style="margin-top: 0px; margin-bottom: 0px;">ENTRY(<strong>vector_swi</strong>)</div>
<div style="margin-top: 0px; margin-bottom: 0px;">sub sp, sp, #S_FRAME_SIZE</div>
<div style="margin-top: 0px; margin-bottom: 0px;">stmia sp, {r0 &#8211; r12}</div>
<div style="margin-top: 0px; margin-bottom: 0px;">add r8, sp, #S_PC</div>
<div style="margin-top: 0px; margin-bottom: 0px;">stmdb r8, {sp, lr}^</div>
<div style="margin-top: 0px; margin-bottom: 0px;">mrs r8, spsr</div>
<div style="margin-top: 0px; margin-bottom: 0px;">str lr, [sp, #S_PC]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">str r8, [sp, #S_PSR]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">vcpu r8</div>
<div style="margin-top: 0px; margin-bottom: 0px;">add r8, r8, #(OFFSET_ARCH_VCPU + OFFSET_GUEST_CONTEXT)</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr r9, [r8, #(OFFSET_SYS_REGS+OFFSET_VPSR)]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr r10, [sp, #S_SP]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">cmp r9, #PSR_MODE_USR</div>
<div style="margin-top: 0px; margin-bottom: 0px;">streq   r10, [r8, #(OFFSET_SYS_REGS+OFFSET_VUSP)]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">strne   r10, [r8, #(OFFSET_SYS_REGS+OFFSET_VKSP)]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr     r8, [sp, #S_PSR]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">bic     r8, r8, #PSR_MODE_MASK</div>
<div style="margin-top: 0px; margin-bottom: 0px;">orr     r8, r8, r9</div>
<div style="margin-top: 0px; margin-bottom: 0px;">str     r8, [sp, #S_PSR]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr r10, [lr, #-4]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">bic r10, r10, #0xff000000</div>
<div style="margin-top: 0px; margin-bottom: 0px;">cmp r10, #HYPERCALL_VECTOR_NO</div>
<div style="margin-top: 0px; margin-bottom: 0px;">moveq r12, #0&#215;18</div>
<div style="margin-top: 0px; margin-bottom: 0px;">movne r12, #0&#215;08</div>
<div style="margin-top: 0px; margin-bottom: 0px;">str r12, [sp, #S_CONTEXT]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">beq<span class="Apple-converted-space"> </span><strong>process_hypercalls  //BEQ  也就是条件跳转 当r10与 82比较，相等的时候，就跳转到process_hypercalls标号处</strong></div>
<div style="margin-top: 0px; margin-bottom: 0px;">b<span class="Apple-converted-space"> </span><strong>do_upcall                     //这个do_upcall和evtchn_do_upcall好相似，实际上也是的。</strong></div>
<div style="margin-top: 0px; margin-bottom: 0px;"><strong><span class="Apple-style-span" style="color: #ff0000;">process_hypercalls</span></strong>:</div>
<div style="margin-top: 0px; margin-bottom: 0px;">enable_irq</div>
<div style="margin-top: 0px; margin-bottom: 0px;">adr lr, ret_from_hypercall</div>
<div style="margin-top: 0px; margin-bottom: 0px;">adr tbl,<span class="Apple-converted-space"> </span><strong>hypercall_table</strong></div>
<div style="margin-top: 0px; margin-bottom: 0px;">add tbl, tbl, scno, lsl #2</div>
<div style="margin-top: 0px; margin-bottom: 0px;"><span class="Apple-style-span" style="color: #ff0000;"><strong>ldr pc, [tbl]</strong></span></div>
<div style="margin-top: 0px; margin-bottom: 0px;">ENTRY(<strong>hypercall_table</strong>)</div>
<div style="margin-top: 0px; margin-bottom: 0px;">__hypercall_start:</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_set_trap_table     /*  0 */</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_mmu_update</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_ni_hypercall</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_stack_switch</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_set_callbacks</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_fpu_taskswitch     /*  5 */</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_sched_op_compat</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_domctl</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_set_debugreg</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_get_debugreg</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_update_descriptor  /* 10 */</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_ni_hypercall</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_memory_op</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_multicall</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_update_va_mapping</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long do_set_timer_op       /* 15 */</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long<span class="Apple-converted-space"> </span><strong>do_event_channel_op</strong></div>
<p>。。。。。。<br />
&#8212;&#8212;&#8212;&#8212;-</p>
<div style="margin-top: 0px; margin-bottom: 0px;">看一下do_upcall的定义：</div>
<div style="margin-top: 0px; margin-bottom: 0px;">xen/arch/arm/kernel/entry-common.S</div>
<div style="margin-top: 0px; margin-bottom: 0px;">/*</div>
<div style="margin-top: 0px; margin-bottom: 0px;"> *<span class="Apple-converted-space"> </span><strong>Send event to guest domain</strong></div>
<div style="margin-top: 0px; margin-bottom: 0px;"> */</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ENTRY(do_upcall)</div>
<div style="margin-top: 0px; margin-bottom: 0px;">vcpu    r10</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr r11, [r10, #OFFSET_VCPU_INFO]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">add r10, r10, #(OFFSET_ARCH_VCPU + OFFSET_GUEST_CONTEXT)</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr lr, [r10, #OFFSET_HYPERVISOR_CALLBACK]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">cmp lr, #0</div>
<div style="margin-top: 0px; margin-bottom: 0px;">beq restore</div>
<div style="margin-top: 0px; margin-bottom: 0px;">@DRE_start</div>
<div style="margin-top: 0px; margin-bottom: 0px;">@ldrb r9, [r11, #OFFSET_EVTCHN_UPCALL_MASK]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">@cmp r9, #1</div>
<div style="margin-top: 0px; margin-bottom: 0px;">@beq restore</div>
<div style="margin-top: 0px; margin-bottom: 0px;">@DRE_end</div>
<div style="margin-top: 0px; margin-bottom: 0px;">mov r9, #0&#215;01</div>
<div style="margin-top: 0px; margin-bottom: 0px;">strb r9, [r11, #OFFSET_EVTCHN_UPCALL_MASK]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">mov r4, #PSR_MODE_SVC</div>
<div style="margin-top: 0px; margin-bottom: 0px;">str r4, [r10, #(OFFSET_SYS_REGS + OFFSET_VPSR)]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">@ Load virtual kernel stack pointer</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr r11, [r10, #(OFFSET_SYS_REGS + OFFSET_VKSP)]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">@ Align VKSP in 8-byte boundary</div>
<div style="margin-top: 0px; margin-bottom: 0px;">sub r12, r11, #S_FRAME_SIZE</div>
<div style="margin-top: 0px; margin-bottom: 0px;">tst r12, #4</div>
<div style="margin-top: 0px; margin-bottom: 0px;">bicne r12, r12, #4</div>
<div style="margin-top: 0px; margin-bottom: 0px;">tst r12, #4</div>
<div style="margin-top: 0px; margin-bottom: 0px;">1:</div>
<div style="margin-top: 0px; margin-bottom: 0px;">bne 1b</div>
<div style="margin-top: 0px; margin-bottom: 0px;">@ Update effective virtual kernel stack pointer.</div>
<div style="margin-top: 0px; margin-bottom: 0px;">str r12, [r10, #(OFFSET_SYS_REGS + OFFSET_VKSP)]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">@ Create bounce frame in guest stack</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldmia sp!, {r0-r8}</div>
<div style="margin-top: 0px; margin-bottom: 0px;">stmia r12!, {r0-r8}</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldmia sp!, {r0-r8}</div>
<div style="margin-top: 0px; margin-bottom: 0px;">stmia r12!, {r0-r8}</div>
<div style="margin-top: 0px; margin-bottom: 0px;">sub r12, r12, #S_FRAME_SIZE</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr r0, =.LCupcall</div>
<div style="margin-top: 0px; margin-bottom: 0px;">stmia r0, {r12,lr}</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldmia r0, {sp, lr}^</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr r0, [r12, #S_CONTEXT]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr r1, [r12, #S_PSR]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr r2, [r10, #(OFFSET_SYS_REGS + OFFSET_VFAR)]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr r3, [r10, #(OFFSET_SYS_REGS + OFFSET_VFSR)]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">mov r7, #PSR_MODE_USR</div>
<div style="margin-top: 0px; margin-bottom: 0px;">msr spsr, r7</div>
<div style="margin-top: 0px; margin-bottom: 0px;">ldr r5, =DOMAIN_KERNEL_VALUE</div>
<div style="margin-top: 0px; margin-bottom: 0px;">mcr     p15, 0, r5, c3, c0, 0   @ Load DAC</div>
<div style="margin-top: 0px; margin-bottom: 0px;">movs pc, lr</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.LCupcall:</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long 0</div>
<div style="margin-top: 0px; margin-bottom: 0px;">.long 0</div>
</div>
<p></span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/06/30/%e8%b6%85%e7%ba%a7%e8%b0%83%e7%94%a8swi-82%e7%9a%84%e6%9c%80%e5%88%9d%e6%b5%81%e7%a8%8b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>An example of second rank pointer struct data</title>
		<link>http://www.tek-life.org/2010/06/29/an-example-of-second-rank-pointer-struct-data/</link>
		<comments>http://www.tek-life.org/2010/06/29/an-example-of-second-rank-pointer-struct-data/#comments</comments>
		<pubDate>Tue, 29 Jun 2010 01:04:51 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[C]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10349</guid>
		<description><![CDATA[1 #include &#60;stdio.h&#62; 2 3 void main() 4 { 5     struct cc{ 6     int a; 7     char *ap; 8     }; 9     struct cc c; 10     c.a=100; 11     c.ap=&#8221;abcdefg\n&#8221;; 12 13     printf(&#8220;%d;%s&#8221;,c.a,c.ap); 14 15     struct cc * cp=&#38;c; 16     printf(&#8220;%d;%s&#8221;,cp-&#62;a,cp-&#62;ap); 17 &#8230; <a href="http://www.tek-life.org/2010/06/29/an-example-of-second-rank-pointer-struct-data/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>1 #include &lt;stdio.h&gt;<br />
2<br />
3 void main()<br />
4 {<br />
5     struct cc{<br />
6     int a;<br />
7     char *ap;<br />
8     };<br />
9     struct cc c;<br />
10     c.a=100;<br />
11     c.ap=&#8221;abcdefg\n&#8221;;<br />
12<br />
13     printf(&#8220;%d;%s&#8221;,c.a,c.ap);<br />
14<br />
15     struct cc * cp=&amp;c;<br />
16     printf(&#8220;%d;%s&#8221;,cp-&gt;a,cp-&gt;ap);<br />
17<br />
18     struct cc ** cpp=&amp;cp;<br />
19     printf(&#8220;%d,%s&#8221;,(**cpp).a,(**cpp).ap);<br />
20     printf(&#8220;%d,%s&#8221;,(*cpp)-&gt;a,(*cpp)-&gt;ap);<br />
21 }</p>
<p>结果：<br />
omycle@omycle-desktop:~/network$ ./test<br />
100;abcdefg<br />
100;abcdefg<br />
100,abcdefg<br />
100,abcdefg</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/06/29/an-example-of-second-rank-pointer-struct-data/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Embeddedxen中断初始化</title>
		<link>http://www.tek-life.org/2010/06/28/embeddedxen%e4%b8%ad%e6%96%ad%e5%88%9d%e5%a7%8b%e5%8c%96/</link>
		<comments>http://www.tek-life.org/2010/06/28/embeddedxen%e4%b8%ad%e6%96%ad%e5%88%9d%e5%a7%8b%e5%8c%96/#comments</comments>
		<pubDate>Mon, 28 Jun 2010 03:40:39 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[EmbeddedXEN]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10343</guid>
		<description><![CDATA[xen中的中断初始化&#8211;和Linux的差不太多 typedef struct irqdesc {  char   *type;  irq_handler_t  handle;  struct irqchip  *chip;  struct irqaction  *action;  unsigned int  flags;  unsigned int  status;  spinlock_t  lock;  void   *chipdata;  void   *data;  unsigned int  disable_depth;  /* Is this one of HID interrupts? */  unsigned int  &#8230; <a href="http://www.tek-life.org/2010/06/28/embeddedxen%e4%b8%ad%e6%96%ad%e5%88%9d%e5%a7%8b%e5%8c%96/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div>
<div>xen中的中断初始化&#8211;和Linux的差不太多</div>
<blockquote><p>typedef struct irqdesc {</p>
<div> char   *type;</div>
<div> irq_handler_t  handle;</div>
<div> struct irqchip  *chip;</div>
<div> struct irqaction  *action;</div>
<div> unsigned int  flags;</div>
<div> unsigned int  status;</div>
<div> spinlock_t  lock;</div>
<div> void   *chipdata;</div>
<div> void   *data;</div>
<div> unsigned int  disable_depth;</div>
<div> /* Is this one of HID interrupts? */</div>
<div> unsigned int  isHIDirq;</div>
<div>}irqdesc_t __cacheline_aligned; </div>
</blockquote>
<div>xen/arch/arm/hypervisor/irq.c</div>
<div> </div>
<blockquote dir="ltr">
<div>struct irqdesc irq_desc[NR_IRQS]; //128</div>
</blockquote>
<div> </div>
<div> </div>
<blockquote>
<div>void __init xen_init_IRQ(void)</div>
<div>{</div>
<div> int irq;</div>
<div> for (irq = 0; irq &lt; NR_IRQS; irq++)</p>
<div>  irq_desc[irq].status |= IRQ_NO_REQUEST | IRQ_NO_PROBE;</div>
</div>
<div>#ifdef CONFIG_SMP</p>
<div> bad_irq_desc.affinity = CPU_MASK_ALL;</div>
<div> bad_irq_desc.cpu = smp_processor_id();</div>
<div>#endif</div>
</div>
<div> xen_init_arch_irq();</div>
<div>}</div>
</blockquote>
<p>xen/arch/arm/mach-pxa/mainstone.c</p>
<blockquote>
<div> </div>
<div>void __init xen_init_arch_irq(void)</div>
<div>{</div>
<div> int irq;</div>
<div> printk(&#8220;Initing arch-IRQ&#8230;\n&#8221;);</p>
<div> <strong>pxa_init_irq();</strong></div>
<div> /* setup extra Mainstone irqs */</div>
<div> for(irq = MAINSTONE_IRQ(0); irq &lt;= MAINSTONE_IRQ(15); irq++) {/*160&#8211;175*/</div>
</div>
<div>  set_irq_chip(irq, &amp;mainstone_irq_chip);</p>
<div>  set_irq_handler(irq, level_irq_handler);</div>
<div>  if (irq == MAINSTONE_IRQ(10) || irq == MAINSTONE_IRQ(14))</div>
<div>   set_irq_flags(irq, IRQF_VALID | IRQF_TRIGGER_PROBE ); //| IRQF_TRIGGER_NO_AUTO_ENABLE);</div>
<div>  else</div>
<div>   set_irq_flags(irq, IRQF_VALID | IRQF_TRIGGER_PROBE);</div>
<div> }</div>
<div> set_irq_flags(MAINSTONE_IRQ(8), 0);</div>
<div> set_irq_flags(MAINSTONE_IRQ(12), 0);</div>
</div>
<div> MST_INTMSKENA = 0;</p>
<div> MST_INTSETCLR = 0;</div>
</div>
<div> set_irq_chained_handler(IRQ_GPIO(0), mainstone_irq_handler);</p>
<div> set_irq_type(IRQ_GPIO(0), IRQT_RISING);</div>
</div>
<div>}</div>
</blockquote>
<div> xen/arch/arm/mach-pxa/irq.c</div>
<blockquote>
<div><strong>void __init pxa_init_irq(void)</strong></div>
<div>{</div>
<div> int irq;</div>
<p> /* disable all IRQs */</p>
<div> ICMR = 0;</div>
<p> /* all IRQs are IRQ, not FIQ */</p>
<div> ICLR = 0;</div>
<p> /* clear all GPIO edge detects */</p>
<div> GFER0 = 0;</div>
<div> GFER1 = 0;</div>
<div> GFER2 = 0;</div>
<div> GRER0 = 0;</div>
<div> GRER1 = 0;</div>
<div> GRER2 = 0;</div>
<div> GEDR0 = GEDR0;</div>
<div> GEDR1 = GEDR1;</div>
<div> GEDR2 = GEDR2;</div>
<p>#ifdef CONFIG_PXA27x</p>
<div> /* And similarly for the extra regs on the PXA27x */</div>
<div> ICMR2 = 0;</div>
<div> ICLR2 = 0;</div>
<div> GFER3 = 0;</div>
<div> GRER3 = 0;</div>
<div> GEDR3 = GEDR3;</div>
<div>#endif</div>
<p> /* only unmasked interrupts kick us out of idle */</p>
<div> ICCR = 1;</div>
<p> /* GPIO 0 and 1 must have their mask bit always set */</p>
<div> GPIO_IRQ_mask[0] = 3;</div>
<p> //NR_IRQS=512</p>
<div> //PXA_IRQ_SKIP=0</div>
<div> for (irq = PXA_IRQ(PXA_IRQ_SKIP); irq &lt;= PXA_IRQ(31); irq++) {</div>
<div>  set_irq_chip(irq, &amp;pxa_internal_chip_low);</div>
<div>  set_irq_handler(irq, level_irq_handler);</div>
<div>  set_irq_flags(irq, IRQF_VALID);</div>
<div> }</div>
<p>#if PXA_INTERNAL_IRQS &gt; 32</p>
<div> for (irq = PXA_IRQ(32); irq &lt; PXA_IRQ(PXA_INTERNAL_IRQS); irq++) {</div>
<div>  set_irq_chip(irq, &amp;pxa_internal_chip_high);</div>
<div>  set_irq_handler(irq, level_irq_handler);</div>
<div>  set_irq_flags(irq, IRQF_VALID);</div>
<div> }</div>
<div>#endif</div>
<p> for (irq = IRQ_GPIO0; irq &lt;= IRQ_GPIO1; irq++) {</p>
<div>  set_irq_chip(irq, &amp;pxa_low_gpio_chip);</div>
<div>  set_irq_handler(irq, edge_irq_handler);</div>
<div>  set_irq_flags(irq, IRQF_VALID | IRQF_TRIGGER_PROBE);</div>
<div> }</div>
<p> for (irq = IRQ_GPIO(2); irq &lt;= IRQ_GPIO(PXA_LAST_GPIO); irq++) {</p>
<div>  set_irq_chip(irq, &amp;pxa_muxed_gpio_chip);</div>
<div>  set_irq_handler(irq, edge_irq_handler);</div>
<div>  set_irq_flags(irq, IRQF_VALID | IRQF_TRIGGER_PROBE);</div>
<div> }</div>
<p> /* Install handler for GPIO&gt;=2 edge detect interrupts */</p>
<div> set_irq_chip(IRQ_GPIO_2_x, &amp;pxa_internal_chip_low);</div>
<div> set_irq_chained_handler(IRQ_GPIO_2_x, pxa_gpio_demux_handler);</div>
<div>}</div>
</blockquote>
<div> 以上是xen中的中断初始化分析。下面看一下DOM的中断初始化。<strong>在DOM中，实际上没有了中断的概念，全部被evtchn_desc_t所替代了</strong>。</div>
<div> </div>
<div>DOM</div>
<div> </div>
<div>arch/arm/kernel/traps-xen.c</div>
<blockquote><p>void __init trap_init(void)</p>
<div>{</div>
<p> //unsigned long vectors = CONFIG_VECTORS_BASE;</p>
<div> unsigned long vectors = (unsigned long) <strong>__guestvectors</strong>;//.extern __guestvectors  arch/arm/kernel/entry-armv-xen.S</div>
<div> extern char __stubs_start[], __stubs_end[];</div>
<div> extern char __vectors_start[], __vectors_end[];</div>
<div> extern char __kuser_helper_start[], __kuser_helper_end[];</div>
<div> int kuser_sz = __kuser_helper_end &#8211; __kuser_helper_start;</div>
<p> /*</p>
<div>  * Copy the vectors, stubs and kuser helpers (in entry-armv.S)</div>
<div>  * into the vector page, mapped at 0xffff0000, and ensure these</div>
<div>  * are visible to the instruction stream.</div>
<div>  */</div>
<div> memcpy((void *)vectors, __vectors_start, __vectors_end &#8211; __vectors_start);</div>
<div> memcpy((void *)vectors + 0&#215;200, __stubs_start, __stubs_end &#8211; __stubs_start);</div>
<div> memcpy((void *)vectors + 0&#215;1000 &#8211; kuser_sz, __kuser_helper_start, kuser_sz);</div>
<p> /*</p>
<div>  * Copy signal return handlers into the vector page, and</div>
<div>  * set sigreturn to be a pointer to these.</div>
<div>  */</div>
<div> memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,</div>
<div>        sizeof(sigreturn_codes));</div>
<p>/* (GCD) From Samsung */</p>
<div>#if 0</div>
<div> flush_icache_range(vectors, vectors + PAGE_SIZE);</div>
<div> modify_domain(DOMAIN_USER, DOMAIN_CLIENT);</div>
<div>#endif /* 0 */</div>
<p>}</p></blockquote>
<blockquote>
<div>static evtchn_desc_t evtchn_desc[NR_IRQS];//512</div>
<div> </div>
<div> </div>
<div>void __init init_IRQ(void)</div>
<div>{</div>
<div> int i;</div>
<div> int cpu;</div>
<p> spin_lock_init(&amp;irq_mapping_update_lock);</p>
<p> init_evtchn_cpu_bindings();</p>
<p> /* No VIRQ or IPI bindings. */</p>
<div> for (cpu = 0; cpu &lt; NR_CPUS; cpu++) {</div>
<div>  for (i = 0; i &lt; NR_VIRQS; i++)</div>
<div>   per_cpu(virq_to_irq, cpu)[i] = -1;</div>
<div>  for (i = 0; i &lt; NR_IPIS; i++)</div>
<div>   per_cpu(ipi_to_irq, cpu)[i] = -1;</div>
<div> }</div>
<p> /* No event-channel -&gt; IRQ mappings. */</p>
<div> for (i = 0; i &lt; NR_EVENT_CHANNELS; i++) {</div>
<div>  evtchn_to_irq[i] = -1;</div>
<div>  mask_evtchn(i); /* No event channels are &#8216;live&#8217; right now. */</div>
<div> }</div>
<p> /* No IRQ -&gt; event-channel mappings. */</p>
<div> for (i = 0; i &lt; NR_IRQS; i++)</div>
<div>  irq_info[i] = IRQ_UNBOUND;</div>
<p> /* Dynamic IRQ space is currently unbound. Zero the refcnts. */</p>
<div> for (i = 0; i &lt; NR_DYNIRQS; i++) {</div>
<div>  irq_bindcount[dynirq_to_irq(i)] = 0;</div>
<p> <strong> evtchn_desc</strong>[dynirq_to_irq(i)].status  = IRQ_DISABLED;</p>
<div>  evtchn_desc[dynirq_to_irq(i)].action  = NULL;</div>
<div>  evtchn_desc[dynirq_to_irq(i)].depth   = 1;</div>
<div>  evtchn_desc[dynirq_to_irq(i)].handler = &amp;<strong>dynirq_type</strong>;</div>
<div>  evtchn_desc[dynirq_to_irq(i)].lock = __SPIN_LOCK_UNLOCKED(evtchn_desc-&gt;lock);</div>
<div> }</div>
<p> /* Phys IRQ space is statically bound (1:1 mapping). Nail refcnts. */</p>
<div> for (i = 0; i &lt; NR_PIRQS; i++) {</div>
<div>  irq_bindcount[pirq_to_irq(i)] = 1;</div>
<p>  evtchn_desc[pirq_to_irq(i)].status  = IRQ_DISABLED;</p>
<div>  evtchn_desc[pirq_to_irq(i)].action  = NULL;</div>
<div>  evtchn_desc[pirq_to_irq(i)].depth   = 1;</div>
<div>  evtchn_desc[pirq_to_irq(i)].handler = &amp;<strong>pirq_type</strong>;</div>
<div>  evtchn_desc[pirq_to_irq(i)].lock = __SPIN_LOCK_UNLOCKED(evtchn_desc-&gt;lock);</div>
<div> }</div>
<div>}</div>
</blockquote>
<div> </div>
<blockquote>
<div>typedef struct <strong>hw_interrupt_type</strong> hw_irq_controller;</div>
<div> </div>
<div>struct <strong>hw_interrupt_type</strong> {</p>
<div> const char *typename;</div>
<div> unsigned int (*<strong>startup</strong>)(unsigned int irq);</div>
<div> void (*shutdown)(unsigned int irq);</div>
<div> void (*enable)(unsigned int irq);</div>
<div> void (*disable)(unsigned int irq);</div>
<div> void (*ack)(unsigned int irq);</div>
<div> void (*end)(unsigned int irq);</div>
<div> void (*set_affinity)(unsigned int irq, cpumask_t dest);</div>
<div> int (*set_type)(unsigned int irq, unsigned int type);</div>
<div> void (*release)(unsigned int irq, void *dev_id);</div>
<div>};</div>
<div> </div>
</div>
<p>typedef struct evtchn_desc {</p>
<div> <strong>hw_irq_controller</strong> *handler;</div>
<div> void *handler_data;</div>
<div> struct irqaction *action; /* IRQ action list */</div>
<div> unsigned int status; /* IRQ status */</div>
<div> unsigned int depth; /* nested irq disables */</div>
<div> unsigned int irq_count; /* For detecting broken interrupts */</div>
<div> unsigned int irqs_unhandled;</div>
<div> spinlock_t lock;</div>
<div>#if defined (CONFIG_GENERIC_PENDING_IRQ) || defined (CONFIG_IRQBALANCE)</div>
<div>unsigned int move_irq; /* Flag need to re-target intr dest*/</div>
<div>#endif</div>
<div>} evtchn_desc_t;</div>
</blockquote>
<div> </div>
<div> </div>
<div> </div>
<div> </div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/06/28/embeddedxen%e4%b8%ad%e6%96%ad%e5%88%9d%e5%a7%8b%e5%8c%96/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>xenarm中断原理</title>
		<link>http://www.tek-life.org/2010/06/28/xenarm%e4%b8%ad%e6%96%ad%e5%8e%9f%e7%90%86/</link>
		<comments>http://www.tek-life.org/2010/06/28/xenarm%e4%b8%ad%e6%96%ad%e5%8e%9f%e7%90%86/#comments</comments>
		<pubDate>Mon, 28 Jun 2010 03:29:41 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[XENARM]]></category>
		<category><![CDATA[xenarm xenolinux]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/06/28/xenarm%e4%b8%ad%e6%96%ad%e5%8e%9f%e7%90%86/</guid>
		<description><![CDATA[xenarm中断原理： xenarm项目有两部分构成，一部分是xen,一部分是xenolinux 本文有三部分构成 1.中断初始化 2.中断申请 3.中断响应 &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; 中断初始化 xen的irq初始化: arch/arm/arch-imx21/start.S .b start_xen &#160;&#160;&#160; &#124; start_xen() //arch/arm/xen/xensetup.c &#160;&#160;&#160; &#124; platform_setup()&#8212;-DECLARE_PLATFORM_OP(platform_setup, imx21ads_platform_setup);//arch/arm/arch-imx21/platform.c imx21ads_platform_setup() &#160;&#160;&#160; &#124; imx21_irq_init() void imx21_irq_init(void) { unsigned int irq; /* Mask all interrupts initially */ IMR(0) = 0; IMR(1) &#8230; <a href="http://www.tek-life.org/2010/06/28/xenarm%e4%b8%ad%e6%96%ad%e5%8e%9f%e7%90%86/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>xenarm中断原理：</p>
<p>xenarm项目有两部分构成，一部分是xen,一部分是xenolinux</p>
<p>本文有三部分构成</p>
<p>1.中断初始化</p>
<p>2.中断申请</p>
<p>3.中断响应</p>
<p><b>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</b></p>
<p><b>中断初始化</b></p>
<p><b>xen的irq初始化</b>:</p>
<p>arch/arm/arch-imx21/start.S </p>
<p>.b start_xen</p>
<p>&#160;&#160;&#160; |</p>
<p>start_xen() //arch/arm/xen/xensetup.c</p>
<p>&#160;&#160;&#160; |</p>
<p>platform_setup()&#8212;-DECLARE_PLATFORM_OP(platform_setup, imx21ads_platform_setup);//arch/arm/arch-imx21/platform.c</p>
<p>imx21ads_platform_setup() </p>
<p>&#160;&#160;&#160; |</p>
<p>imx21_irq_init()</p>
<p>void imx21_irq_init(void)</p>
<p>{</p>
<p>unsigned int irq;</p>
<p>/* Mask all interrupts initially */</p>
<p>IMR(0) = 0;</p>
<p>IMR(1) = 0;</p>
<p>IMR(2) = 0;</p>
<p>IMR(3) = 0;</p>
<p>IMR(4) = 0;</p>
<p>IMR(5) = 0;</p>
<p>for (irq = 0; irq &lt; IMX_IRQS; irq++) {</p>
<p>set_irq_chip(irq, &amp;imx21_internal_chip);</p>
<p>set_irq_handler(irq, level_irq_handler);</p>
<p>set_irq_flags(irq, IRQF_VALID);</p>
<p>}</p>
<p>for (irq = IRQ_GPIOA(0); irq &lt; IRQ_GPIOF(32); irq++) {</p>
<p>set_irq_chip(irq, &amp;imx21_gpio_chip);</p>
<p>set_irq_handler(irq, edge_irq_handler);</p>
<p>set_irq_flags(irq, IRQF_VALID | IRQF_TRIGGER_PROBE);</p>
<p>}</p>
<p>set_irq_chained_handler(INT_GPIO, imx21_gpio_handler);</p>
<p>/* Disable all interrupts initially. */</p>
<p>/* In IMX21 this is done in the bootloader. */</p>
<p>}</p>
<p><b>DOMU的初始化</b></p>
<p>b start_kernel arch/arm/kernel/head-xen.S</p>
<p>&#160;&#160;&#160; |    <br />start_kernel() </p>
<p>&#160;&#160;&#160; |</p>
<p>init_IRQ()&#160;&#160;&#160;&#160; //drivers/xen/core/evtchn.c</p>
<blockquote><p>void __init init_IRQ(void)    <br />{     <br />int i;     <br />int cpu;     <br />spin_lock_init(&amp;irq_mapping_update_lock);     <br />init_evtchn_cpu_bindings();     <br />/* No VIRQ or IPI bindings. */     <br />for (cpu = 0; cpu &lt; NR_CPUS; cpu++) {     <br />for (i = 0; i &lt; NR_VIRQS; i++)     <br />per_cpu(virq_to_irq, cpu)[i] = -1;     <br />for (i = 0; i &lt; NR_IPIS; i++)     <br />per_cpu(ipi_to_irq, cpu)[i] = -1;     <br />}     <br />/* No event-channel -&gt; IRQ mappings. */     <br />for (i = 0; i &lt; NR_EVENT_CHANNELS; i++) {     <br />evtchn_to_irq[i] = -1;     <br />mask_evtchn(i); /* No event channels are &#8216;live&#8217; right now. */     <br />}     <br />/* No IRQ -&gt; event-channel mappings. */     <br />for (i = 0; i &lt; NR_IRQS; i++)     <br />irq_info[i] = IRQ_UNBOUND;     <br />/* Dynamic IRQ space is currently unbound. Zero the refcnts. */     <br />for (i = 0; i &lt; NR_DYNIRQS; i++) {     <br />irq_bindcount[dynirq_to_irq(i)] = 0;     <br />evtchn_desc[dynirq_to_irq(i)].status&#160; = IRQ_DISABLED;     <br />evtchn_desc[dynirq_to_irq(i)].action&#160; = NULL;     <br />evtchn_desc[dynirq_to_irq(i)].depth&#160;&#160; = 1;     <br />evtchn_desc[dynirq_to_irq(i)].handler = &amp;dynirq_type;     <br />evtchn_desc[dynirq_to_irq(i)].lock = __SPIN_LOCK_UNLOCKED(evtchn_desc-&gt;lock);     <br />}     <br />/* Phys IRQ space is statically bound (1:1 mapping). Nail refcnts. */     <br />for (i = 0; i &lt; NR_PIRQS; i++) {     <br />irq_bindcount[pirq_to_irq(i)] = 1;     <br />evtchn_desc[pirq_to_irq(i)].status&#160; = IRQ_DISABLED;     <br />evtchn_desc[pirq_to_irq(i)].action&#160; = NULL;     <br />evtchn_desc[pirq_to_irq(i)].depth&#160;&#160; = 1;     <br /><b>evtchn_desc</b>[pirq_to_irq(i)].handler = &amp;pirq_type;     <br />evtchn_desc[pirq_to_irq(i)].lock = __SPIN_LOCK_UNLOCKED(evtchn_desc-&gt;lock);     <br />}     <br />}</p></blockquote>
<p><b>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</b>   <br /><b>DOMU的中断申请</b>：
<p>drivers/xen/core/evtchn.c</p>
<p>int request_irq(unsigned int irq,</p>
<p>irq_handler_t handler,</p>
<p>unsigned long irqflags, const char * devname, void *dev_id)</p>
<p>{</p>
<p>struct irqaction * action;</p>
<p>int retval;</p>
<p>/*</p>
<p>* Sanity-check: shared interrupts must pass in a real dev-ID,</p>
<p>* otherwise we&#8217;ll have trouble later trying to figure out</p>
<p>* which interrupt is which (messes up the interrupt freeing</p>
<p>* logic etc).</p>
<p>*/</p>
<p>if ((irqflags &amp; SA_SHIRQ) &amp;&amp; !dev_id)</p>
<p>return -EINVAL;</p>
<p>if (irq &gt;= NR_IRQS)</p>
<p>return -EINVAL;</p>
<p>if (!handler)</p>
<p>return -EINVAL;</p>
<p>action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);</p>
<p>if (!action)</p>
<p>return -ENOMEM;</p>
<p>action-&gt;handler = handler;</p>
<p>action-&gt;flags = irqflags;</p>
<p>cpus_clear(action-&gt;mask);</p>
<p>action-&gt;name = devname;</p>
<p>action-&gt;next = NULL;</p>
<p>action-&gt;dev_id = dev_id;</p>
<p>retval = <b>setup_irq</b>(irq, action);</p>
<p>if (retval)</p>
<p>kfree(action);</p>
<p>return retval;</p>
<p>}</p>
<p>int<b> setup_irq</b>(unsigned int irq, struct irqaction * new)</p>
<p>{</p>
<p>struct evtchn_desc *desc = evtchn_desc + irq;</p>
<p>struct irqaction *old, **p;</p>
<p>unsigned long flags;</p>
<p>int shared = 0;</p>
<p>// the following should be commented: jyhwang 2007 July 15</p>
<p>// printk(&quot;irq=%d, NR_IRQS=%d\n&quot;, irq, NR_IRQS);</p>
<p>if (irq &gt;= NR_IRQS)</p>
<p>return -EINVAL;</p>
<p>if (desc-&gt;handler == NULL)</p>
<p>return -ENOSYS;</p>
<p>/*</p>
<p>* Some drivers like serial.c use request_irq() heavily,</p>
<p>* so we have to be careful not to interfere with a</p>
<p>* running system.</p>
<p>*/</p>
<p>if (new-&gt;flags &amp; SA_SAMPLE_RANDOM) {</p>
<p>/*</p>
<p>* This function might sleep, we want to call it first,</p>
<p>* outside of the atomic block.</p>
<p>* Yes, this might clear the entropy pool if the wrong</p>
<p>* driver is attempted to be loaded, without actually</p>
<p>* installing a new handler, but is this really a problem,</p>
<p>* only the sysadmin is able to do this.</p>
<p>*/</p>
<p>rand_initialize_irq(irq);</p>
<p>}</p>
<p>/*</p>
<p>* The following block of code has to be executed atomically</p>
<p>*/</p>
<p>spin_lock_irqsave(&amp;desc-&gt;lock,flags);</p>
<p>p = &amp;desc-&gt;action;</p>
<p>if ((old = *p) != NULL) {</p>
<p>/* Can&#8217;t share interrupts unless both agree to */</p>
<p>if (!(old-&gt;flags &amp; new-&gt;flags &amp; SA_SHIRQ)) {</p>
<p>spin_unlock_irqrestore(&amp;desc-&gt;lock,flags);</p>
<p>return -EBUSY;</p>
<p>}</p>
<p>/* add new interrupt at end of irq queue */</p>
<p>do {</p>
<p>p = &amp;old-&gt;next;</p>
<p>old = *p;</p>
<p>} while (old);</p>
<p>shared = 1;</p>
<p>}</p>
<p>*p = new;//action赋值</p>
<p>if (!shared) {</p>
<p>desc-&gt;depth = 0;</p>
<p>desc-&gt;status &amp;= ~(IRQ_DISABLED | IRQ_AUTODETECT |</p>
<p>IRQ_WAITING | IRQ_INPROGRESS);</p>
<p>if (desc-&gt;handler-&gt;startup)</p>
<p>desc-&gt;handler-&gt;<b>startup</b>(irq);//<b>priq_startup()</b></p>
<p>else</p>
<p>desc-&gt;handler-&gt;enable(irq);</p>
<p>}</p>
<p>spin_unlock_irqrestore(&amp;desc-&gt;lock,flags);</p>
<p>new-&gt;irq = irq;</p>
<p>new-&gt;dir = NULL;</p>
<p>return 0;</p>
<p>}</p>
<p>static unsigned int <b>startup_pirq</b>(unsigned int irq)</p>
<p>{</p>
<p>evtchn_op_t op = { .cmd = EVTCHNOP_bind_pirq };</p>
<p>int evtchn = evtchn_from_irq(irq);</p>
<p>if (VALID_EVTCHN(evtchn)) {</p>
<p>goto out;</p>
<p>}</p>
<p>op.u.bind_pirq.pirq&#160; = irq;</p>
<p>/* NB. We are happy to share unless we are probing. */</p>
<p>op.u.bind_pirq.flags = probing_irq(irq) ? 0 : BIND_PIRQ__WILL_SHARE;</p>
<p>if (HYPERVISOR_event_channel_op(&amp;op) != 0) {</p>
<p>if (!probing_irq(irq))</p>
<p>printk(KERN_INFO &quot;Failed to obtain physical IRQ %d\n&quot;,</p>
<p>&#160;&#160;&#160;&#160;&#160; irq);</p>
<p>return 0;</p>
<p>}</p>
<p>evtchn = op.u.bind_pirq.port;</p>
<p>pirq_query_unmask(irq_to_pirq(irq));</p>
<p>bind_evtchn_to_cpu(evtchn, 0);</p>
<p>evtchn_to_irq[evtchn] = irq;</p>
<p>irq_info[irq] = mk_irq_info(IRQT_PIRQ, irq, evtchn);</p>
<p>out:</p>
<p>unmask_evtchn(evtchn);</p>
<p>pirq_unmask_notify(irq_to_pirq(irq));</p>
<p>return 0;</p>
<p>}</p>
<p><b>在xenarm中</b>：common/event_channel.c</p>
<p>long do_event_channel_op(int cmd, XEN_GUEST_HANDLE(void) arg)</p>
<p>{</p>
<p>&#160;&#160;&#160; long rc;</p>
<p>&#160;&#160;&#160; switch ( cmd )</p>
<p>&#160;&#160;&#160; {</p>
<p>&#160;&#160;&#160; case EVTCHNOP_alloc_unbound: {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct evtchn_alloc_unbound alloc_unbound;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( copy_from_guest(&amp;alloc_unbound, arg, 1) != 0 )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return -EFAULT;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = evtchn_alloc_unbound(&amp;alloc_unbound);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( (rc == 0) &amp;&amp; (copy_to_guest(arg, &amp;alloc_unbound, 1) != 0) )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = -EFAULT; /* Cleaning up here would be a mess! */</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
<p>&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; case EVTCHNOP_bind_interdomain: {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct evtchn_bind_interdomain bind_interdomain;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( copy_from_guest(&amp;bind_interdomain, arg, 1) != 0 )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return -EFAULT;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = evtchn_bind_interdomain(&amp;bind_interdomain);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( (rc == 0) &amp;&amp; (copy_to_guest(arg, &amp;bind_interdomain, 1) != 0) )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = -EFAULT; /* Cleaning up here would be a mess! */</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
<p>&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; case EVTCHNOP_bind_virq: {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct evtchn_bind_virq bind_virq;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( copy_from_guest(&amp;bind_virq, arg, 1) != 0 )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return -EFAULT;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = evtchn_bind_virq(&amp;bind_virq);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( (rc == 0) &amp;&amp; (copy_to_guest(arg, &amp;bind_virq, 1) != 0) )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = -EFAULT; /* Cleaning up here would be a mess! */</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
<p>&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; case EVTCHNOP_bind_ipi: {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct evtchn_bind_ipi bind_ipi;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( copy_from_guest(&amp;bind_ipi, arg, 1) != 0 )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return -EFAULT;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = evtchn_bind_ipi(&amp;bind_ipi);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( (rc == 0) &amp;&amp; (copy_to_guest(arg, &amp;bind_ipi, 1) != 0) )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = -EFAULT; /* Cleaning up here would be a mess! */</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
<p>&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; case EVTCHNOP_bind_pirq: {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct evtchn_bind_pirq bind_pirq;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( copy_from_guest(&amp;bind_pirq, arg, 1) != 0 )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return -EFAULT;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc =<b> evtchn_bind_pirq</b>(&amp;bind_pirq);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( (rc == 0) &amp;&amp; (copy_to_guest(arg, &amp;bind_pirq, 1) != 0) )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = -EFAULT; /* Cleaning up here would be a mess! */</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
<p>&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; case EVTCHNOP_close: {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct evtchn_close close;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( copy_from_guest(&amp;close, arg, 1) != 0 )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return -EFAULT;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = evtchn_close(&amp;close);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
<p>&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; case EVTCHNOP_send: {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct evtchn_send send;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( copy_from_guest(&amp;send, arg, 1) != 0 )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return -EFAULT;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = evtchn_send(send.port);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
<p>&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; case EVTCHNOP_status: {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct evtchn_status status;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( copy_from_guest(&amp;status, arg, 1) != 0 )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return -EFAULT;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = evtchn_status(&amp;status);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( (rc == 0) &amp;&amp; (copy_to_guest(arg, &amp;status, 1) != 0) )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = -EFAULT;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
<p>&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; case EVTCHNOP_bind_vcpu: {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct evtchn_bind_vcpu bind_vcpu;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( copy_from_guest(&amp;bind_vcpu, arg, 1) != 0 )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return -EFAULT;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = evtchn_bind_vcpu(bind_vcpu.port, bind_vcpu.vcpu);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
<p>&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; case EVTCHNOP_unmask: {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct evtchn_unmask unmask;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( copy_from_guest(&amp;unmask, arg, 1) != 0 )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return -EFAULT;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = evtchn_unmask(&amp;unmask);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
<p>&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; case EVTCHNOP_reset: {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct evtchn_reset reset;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ( copy_from_guest(&amp;reset, arg, 1) != 0 )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return -EFAULT;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = evtchn_reset(&amp;reset);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
<p>&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; default:</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = -ENOSYS;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
<p>&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; return rc;</p>
<p>}</p>
<p>static long <b>evtchn_bind_pirq</b>(evtchn_bind_pirq_t *bind) //common/event_channel.c</p>
<p>{</p>
<p>&#160;&#160;&#160; struct evtchn *chn;</p>
<p>&#160;&#160;&#160; struct domain *d = current-&gt;domain;</p>
<p>&#160;&#160;&#160; int&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; port, pirq = bind-&gt;pirq;</p>
<p>&#160;&#160;&#160; long&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc;</p>
<p>&#160;&#160;&#160; if ( (pirq &lt; 0) || (pirq &gt;= ARRAY_SIZE(d-&gt;pirq_to_evtchn)) )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; return -EINVAL;</p>
<p>&#160;&#160;&#160; if ( !irq_access_permitted(d, pirq) )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; return -EPERM;</p>
<p>&#160;&#160;&#160; spin_lock(&amp;d-&gt;evtchn_lock);</p>
<p>&#160;&#160;&#160; if ( d-&gt;pirq_to_evtchn[pirq] != 0 )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; ERROR_EXIT(-EEXIST);</p>
<p>&#160;&#160;&#160; if ( (<b>port = get_free_port</b>(d)) &lt; 0 )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; ERROR_EXIT(port);</p>
<p>&#160;&#160;&#160; chn = evtchn_from_port(d, port);</p>
<p>&#160;&#160;&#160; d-&gt;pirq_to_evtchn[pirq] = port;</p>
<p>&#160;&#160;&#160; rc = <b>pirq_guest_bind</b>(d-&gt;vcpu[0], pirq, !!(bind-&gt;flags &amp; BIND_PIRQ__WILL_SHARE));</p>
<p>&#160;&#160;&#160; if ( rc != 0 )</p>
<p>&#160;&#160;&#160; {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; d-&gt;pirq_to_evtchn[pirq] = 0;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; goto out;</p>
<p>&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160; chn-&gt;state&#160; = ECS_PIRQ;</p>
<p>&#160;&#160;&#160; chn-&gt;u.pirq = pirq;</p>
<p>&#160;&#160;&#160; bind-&gt;port = port;</p>
<p>out:</p>
<p>&#160;&#160;&#160; spin_unlock(&amp;d-&gt;evtchn_lock);</p>
<p>&#160;&#160;&#160; return rc;</p>
<p>}</p>
<p>int pirq_guest_bind(struct vcpu *v, int irq, int will_share) //arch/arm/xen/irq.c</p>
<p>{</p>
<p>struct irqdesc&#160;&#160;&#160;&#160;&#160; *desc;</p>
<p>irq_guest_action_t&#160; *action;</p>
<p>unsigned long&#160;&#160;&#160;&#160;&#160;&#160; flags;</p>
<p>int&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; rc = 0;</p>
<p>if ( (irq &lt; 0) || (irq &gt;= NR_IRQS) )</p>
<p>return -EINVAL;</p>
<p>desc = get_irq_descriptor(irq);</p>
<p>spin_lock_irqsave(&amp;desc-&gt;lock, flags);</p>
<p><b>action = (irq_guest_action_t *)desc-&gt;action;</b></p>
<p>if (!(desc-&gt;flags &amp; IRQF_GUEST_BOUND)) {</p>
<p>if (desc-&gt;action != NULL ) {</p>
<p>DPRINTK(3,&quot;Cannot bind IRQ %d to guest. In use by %s.\n&quot;,(int)irq, desc-&gt;action-&gt;name);</p>
<p>rc = -EBUSY;</p>
<p>goto out;</p>
<p>}</p>
<p>action = xmalloc(irq_guest_action_t);</p>
<p>if ((desc-&gt;action = (struct irqaction *)action) == NULL ) {</p>
<p>DPRINTK(3,&quot;Cannot bind IRQ %d to guest. Out of memory.\n&quot;, irq);</p>
<p>rc = -ENOMEM;</p>
<p>goto out;</p>
<p>}</p>
<p>action-&gt;shareable = 1;</p>
<p>action-&gt;nr_guests = 0;</p>
<p>action-&gt;in_flight = 0;</p>
<p>action-&gt;ack_type&#160; = pirq_ack_type(irq);</p>
<p>desc-&gt;disable_depth = 0;</p>
<p>desc-&gt;flags |= IRQF_GUEST_BOUND;</p>
<p>if(will_share) {</p>
<p>desc-&gt;flags |= IRQF_SHARABLE;</p>
<p>}</p>
<p>desc-&gt;chip-&gt;unmask(irq);</p>
<p>} else if ( !will_share || !action-&gt;shareable ) {</p>
<p>DPRINTK(3,&quot;Cannot bind IRQ %d to guest. Will not share with others.\n&quot;, irq);</p>
<p>rc = -EBUSY;</p>
<p>goto out;</p>
<p>}</p>
<p>if ( action-&gt;nr_guests == IRQ_MAX_GUESTS ) {</p>
<p>DPRINTK(3,&quot;Cannot bind IRQ %d to guest. Already at max share.\n&quot;, irq);</p>
<p>rc = -EBUSY;</p>
<p>goto out;</p>
<p>}</p>
<p><b>action-&gt;guest[action-&gt;nr_guests++] = v-&gt;domain;</b></p>
<p>out:</p>
<p>spin_unlock_irqrestore(&amp;desc-&gt;lock, flags);</p>
<p>return rc;</p>
<p>}</p>
<p>至此，中断申请结束！</p>
<p><b>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</b></p>
<p><b>中断响应</b>：</p>
<p><b>Hypervisor中断响应：</b></p>
<p>arch/arm/xen/entry.S</p>
<blockquote><p>.align 5    <br />__irq_svc:     <br />save_svc_context     <br />#ifdef CONFIG_MACHINE_VERSATILE     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; get_irqnr_preamble r5, lr @ minsung     <br />#endif     <br />1: get_irqnr_and_base r0, r6, r5, lr     <br />movne r1, sp&#160;&#160; <br />@     <br />@ routine called with r0 = irq number, r1 = struct pt_regs *     <br />@     <br />adrne lr, 1b     <br />bne <b>asm_do_IRQ</b>&#8212;在DOMU中是<b>irq_handler</b>     <br />&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;     <br />__irq_usr:     <br />save_usr_context     <br />vcpu r0     <br />add r0, r0, #(OFFSET_ARCH_VCPU + OFFSET_GUEST_CONTEXT)     <br />ldr r1, [r0, #(OFFSET_SYS_REGS + OFFSET_VPSR)]     <br />ldr r2, [sp, #S_SP]     <br />cmp r1, #PSR_MODE_USR     <br />streq r2, [r0, #(OFFSET_SYS_REGS + OFFSET_VUSP)]     <br />strne r2, [r0, #(OFFSET_SYS_REGS + OFFSET_VKSP)]     <br />ldr&#160;&#160;&#160;&#160; r3, [sp, #S_PSR]     <br />bic&#160;&#160;&#160;&#160; r3, r3, #PSR_MODE_MASK     <br />orr&#160;&#160;&#160;&#160; r3, r3, r1     <br />str&#160;&#160;&#160;&#160; r3, [sp, #S_PSR]     <br />#ifdef CONFIG_MACHINE_VERSATILE     <br />get_irqnr_preamble r5, lr @ minsung     <br />#endif     <br />1: get_irqnr_and_base r0, r6, r5, lr     <br />movne&#160;&#160; r1, sp     <br />adrne lr, 1b     <br />bne&#160;&#160;&#160;&#160; asm_do_IRQ</p></blockquote>
<p>asm_do_IRQ</p>
<p>asmlinkage void asm_do_IRQ(unsigned int irq, struct cpu_user_regs *regs)</p>
<p>{</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; struct irqdesc *desc;</p>
<p>if (irq &gt;= NR_IRQS) {</p>
<p>printk(&quot;Bad IRQ = %d\n&quot;, irq);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
<p>desc = get_irq_descriptor(irq);</p>
<p>desc-&gt;handle(irq, desc, regs);</p>
<p>}</p>
<p>void level_irq_handler(unsigned int irq, struct irqdesc *desc, struct cpu_user_regs *regs)</p>
<p>{</p>
<p>irqreturn_t ret;</p>
<p>irqaction_t *action;</p>
<p>spin_lock(&amp;desc-&gt;lock);</p>
<p>desc-&gt;chip-&gt;ack(irq);</p>
<p>if(desc-&gt;flags &amp; IRQF_GUEST_BOUND) {</p>
<p><b>handle_guest_bound_irq</b>(irq);</p>
<p>goto out_unlock;</p>
<p>}</p>
<p>if (unlikely(desc-&gt;status &amp; IRQ_IN_PROGRESS))</p>
<p>goto out_unlock;</p>
<p>desc-&gt;status &amp;= ~(IRQ_REPLAY | IRQ_WAITING);</p>
<p>action = desc-&gt;action;</p>
<p>if (unlikely(!action || (desc-&gt;status &amp; IRQ_DISABLED))) {</p>
<p>desc-&gt;status |= IRQ_PENDING;</p>
<p>goto out_unlock;</p>
<p>}</p>
<p>desc-&gt;status |= IRQ_IN_PROGRESS;</p>
<p>desc-&gt;status &amp;= ~IRQ_PENDING;</p>
<p>spin_unlock(&amp;desc-&gt;lock);</p>
<p>ret = handle_event(irq, action, regs);</p>
<p>if(!ret) {</p>
<p>printk(&quot;Action return = %d\n&quot;, ret);</p>
<p>}</p>
<p>spin_lock(&amp;desc-&gt;lock);</p>
<p>desc-&gt;status &amp;= ~IRQ_IN_PROGRESS;</p>
<p>if (!(desc-&gt;status &amp; IRQ_DISABLED) &amp;&amp; desc-&gt;chip-&gt;unmask)</p>
<p>desc-&gt;chip-&gt;unmask(irq);</p>
<p>out_unlock:</p>
<p>spin_unlock(&amp;desc-&gt;lock);</p>
<p>}</p>
<p>static void <b>handle_guest_bound_irq</b>(unsigned int irq)</p>
<p>{</p>
<p>int i;</p>
<p>struct domain&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; *d;</p>
<p>struct irqdesc&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; *desc;</p>
<p>irq_guest_action_t&#160;&#160;&#160;&#160;&#160; *action;</p>
<p>desc = get_irq_descriptor(irq);</p>
<p>action = (<b>irq_guest_action_t</b> *)desc-&gt;action;</p>
<p>for ( i = 0; i &lt; action-&gt;nr_guests; i++ ) {</p>
<p>d = action-&gt;guest[i];</p>
<p>// send HID irqs to only the foreground domain.</p>
<p>if (desc-&gt;isHIDirq &amp;&amp; d-&gt;domain_id != (domid_t)foreground_domain) {</p>
<p>continue;</p>
<p>}</p>
<p>if(action-&gt;ack_type == ACK_TYPE_UNMASK) {</p>
<p>if(!test_and_set_bit(irq, (volatile unsigned long *)&amp;d-&gt;pirq_mask)) {//测试，是否被mask了。</p>
<p>action-&gt;in_flight++;</p>
<p>}</p>
<p>}</p>
<p><b>send_guest_pirq</b>(d, irq);</p>
<p>}</p>
<p>}</p>
<p>void send_guest_pirq(struct domain *d, int pirq)</p>
<p>{</p>
<p>&#160;&#160;&#160; int port = d-&gt;pirq_to_evtchn[pirq];</p>
<p>&#160;&#160;&#160; struct evtchn *chn;</p>
<p>&#160;&#160;&#160; ASSERT(port != 0);</p>
<p>&#160;&#160;&#160; if(!acm_send_guest_pirq(d, pirq))</p>
<p>return;</p>
<p>&#160;&#160;&#160; chn = evtchn_from_port(d, port);</p>
<p>&#160;&#160;&#160; evtchn_set_pending(d-&gt;vcpu[chn-&gt;notify_vcpu_id], port);</p>
<p>}</p>
<p><b>DOMU的中断响应：</b>     <br />arch/arm/kernel/entry-armv-xen.S </p>
<blockquote><p>__irq_svc:</p>
<p>&#160;&#160;&#160; svc_entry</p>
<p>#ifndef CONFIG_XENOLINUX</p>
<p>#ifdef CONFIG_TRACE_IRQFLAGS</p>
<p>&#160;&#160;&#160; bl&#160; trace_hardirqs_off</p>
<p>#endif</p>
<p>#ifdef CONFIG_PREEMPT</p>
<p>&#160;&#160;&#160; get_thread_info tsk</p>
<p>&#160;&#160;&#160; ldr r8, [tsk, #TI_PREEMPT]&#160;&#160;&#160;&#160;&#160; @ get preempt count</p>
<p>&#160;&#160;&#160; add r7, r8, #1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; @ increment it</p>
<p>&#160;&#160;&#160; str r7, [tsk, #TI_PREEMPT]</p>
<p>#endif</p>
<p>&#160;&#160;&#160; irq_handler</p>
<p>&#160;&#160; .macro&#160; irq_handler      <br />&#160;&#160;&#160; mov r0, sp       <br />&#160;&#160;&#160; bl&#160; evtchn_do_upcall       <br />&#160;&#160;&#160; .endm</p>
</blockquote>
<blockquote><p>* NB. Interrupts are disabled on entry. */    <br />asmlinkage void <b>evtchn_do_upcall</b>(struct pt_regs *regs)     <br />{     <br />unsigned long&#160; l1, l2;     <br />unsigned int&#160;&#160; l1i, l2i, port;     <br />int&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; irq, cpu = smp_processor_id();     <br />shared_info_t *s = HYPERVISOR_shared_info;     <br />vcpu_info_t&#160;&#160; *vcpu_info = &amp;s-&gt;vcpu_info[cpu];     <br />retry:     <br />xchg(&amp;vcpu_info-&gt;evtchn_upcall_pending,0);     <br />l1 = xchg(&amp;vcpu_info-&gt;evtchn_pending_sel, 0);     <br />while (l1 != 0) {     <br />l1i = __ffs(l1);     <br />l1 &amp;= ~(1UL &lt;&lt; l1i);     <br />while ((l2 = active_evtchns(cpu, s, l1i)) != 0) {     <br />l2i = __ffs(l2);     <br />port = (l1i * BITS_PER_LONG) + l2i;     <br />if ((irq = evtchn_to_irq[port]) != -1) {     <br />irq_enter();     <br /><b>do_IRQ</b>(irq, regs);     <br />irq_exit();     <br />}&#160; else {     <br />evtchn_device_upcall(port);     <br />}     <br />}     <br />}     <br />if(vcpu_info-&gt;evtchn_upcall_pending) {     <br />goto retry;     <br />}     <br />}
<p>fastcall unsigned int <b>do_IRQ</b>(unsigned int irq, struct pt_regs *regs)</p>
<p>{</p>
<p>struct pt_regs *old_regs = set_irq_regs(regs);</p>
<p><b>evtchn_desc_t </b>*desc = evtchn_desc + irq;</p>
<p>struct irqaction * action;</p>
<p>unsigned int status = 0;</p>
<p>kstat_this_cpu.irqs[irq]++;</p>
<p>spin_lock(&amp;desc-&gt;lock);</p>
<p>if (desc-&gt;handler-&gt;ack)</p>
<p>desc-&gt;handler-&gt;ack(irq);</p>
<p>status = desc-&gt;status &amp; ~(IRQ_REPLAY | IRQ_WAITING);</p>
<p>status |= IRQ_PENDING; /* we _want_ to handle it */</p>
<p>action = NULL;</p>
<p>if (likely(!(status &amp; (IRQ_DISABLED | IRQ_INPROGRESS)))) {</p>
<p>action = desc-&gt;action;</p>
<p>status &amp;= ~IRQ_PENDING; /* we commit to handling */</p>
<p>status |= IRQ_INPROGRESS; /* we are handling it */</p>
<p>}</p>
<p>desc-&gt;status = status;</p>
<p>if (unlikely(!action))</p>
<p>goto out;</p>
<p>/*</p>
<p>* Edge triggered interrupts need to remember</p>
<p>* pending events.</p>
<p>* This applies to any hw interrupts that allow a second</p>
<p>* instance of the same irq to arrive while we are in do_IRQ</p>
<p>* or in the handler. But the code here only handles the _second_</p>
<p>* instance of the irq, not the third or fourth. So it is mostly</p>
<p>* useful for irq hardware that does not mask cleanly in an</p>
<p>* SMP environment.</p>
<p>*/</p>
<p>for (;;) {</p>
<p>irqreturn_t ret;</p>
<p>spin_unlock(&amp;desc-&gt;lock);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (!(action-&gt;flags &amp; SA_INTERRUPT))</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; local_irq_enable();</p>
<p>do {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ret = action-&gt;<b>handler</b>(irq, action-&gt;dev_id);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (ret == IRQ_HANDLED)</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; status |= action-&gt;flags;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; action = action-&gt;next;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } while (action);</p>
<p>if (status &amp; SA_SAMPLE_RANDOM)</p>
<p>add_interrupt_randomness(irq);</p>
<p>local_irq_disable();</p>
<p>spin_lock(&amp;desc-&gt;lock);</p>
<p>if (likely(!(desc-&gt;status &amp; IRQ_PENDING)))</p>
<p>break;</p>
<p>desc-&gt;status &amp;= ~IRQ_PENDING;</p>
<p>}</p>
<p>desc-&gt;status &amp;= ~IRQ_INPROGRESS;</p>
<p>out:</p>
<p>/*</p>
<p>* The -&gt;end() handler has to deal with interrupts which got</p>
<p>* disabled while the handler was running.</p>
<p>*/</p>
<p>desc-&gt;handler-&gt;end(irq);</p>
<p>spin_unlock(&amp;desc-&gt;lock);</p>
<p>set_irq_regs(old_regs);</p>
<p>return 1;</p>
<p>}</p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/06/28/xenarm%e4%b8%ad%e6%96%ad%e5%8e%9f%e7%90%86/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Embeddedxen中断申请</title>
		<link>http://www.tek-life.org/2010/06/25/xenarm%e4%b8%ad%e6%96%ad%e7%94%b3%e8%af%b7/</link>
		<comments>http://www.tek-life.org/2010/06/25/xenarm%e4%b8%ad%e6%96%ad%e7%94%b3%e8%af%b7/#comments</comments>
		<pubDate>Fri, 25 Jun 2010 05:53:11 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[EmbeddedXEN]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10339</guid>
		<description><![CDATA[先看xen的中断申请 申请中断离不开request_irq和setup_irq xen只申请了两个irq一个是timer的,另一个是console. xen/arch/arm/mach-pxa/time.c void __init xen_pxa_timer_init(void) { unsigned long clock_tick_rate; OIER = 0; OSSR = OSSR_M0 &#124; OSSR_M1 &#124; OSSR_M2 &#124; OSSR_M3; if (cpu_is_pxa25x()) clock_tick_rate = 3686400; else if (machine_is_mainstone()) clock_tick_rate = 3249600; else clock_tick_rate = 3250000; set_oscr2ns_scale(clock_tick_rate); ckevt_pxa_osmr0.mult &#8230; <a href="http://www.tek-life.org/2010/06/25/xenarm%e4%b8%ad%e6%96%ad%e7%94%b3%e8%af%b7/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div>先看xen的中断申请</div>
<div>申请中断离不开request_irq和setup_irq</div>
<p>xen只申请了两个irq一个是timer的,另一个是console.</p>
<div>xen/arch/arm/mach-pxa/time.c</div>
<blockquote><p>void __init xen_pxa_timer_init(void)<br />
{<br />
unsigned long clock_tick_rate;</p>
<p>OIER = 0;<br />
OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3;</p>
<p>if (cpu_is_pxa25x())<br />
clock_tick_rate = 3686400;<br />
else if (machine_is_mainstone())<br />
clock_tick_rate = 3249600;<br />
else<br />
clock_tick_rate = 3250000;</p>
<p>set_oscr2ns_scale(clock_tick_rate);</p>
<p>ckevt_pxa_osmr0.mult =<br />
div_sc(clock_tick_rate, NSEC_PER_SEC, ckevt_pxa_osmr0.shift);</p>
<p>ckevt_pxa_osmr0.max_delta_ns =<br />
clockevent_delta2ns(0x7fffffff, &amp;ckevt_pxa_osmr0);<br />
ckevt_pxa_osmr0.min_delta_ns =<br />
clockevent_delta2ns(MIN_OSCR_DELTA * 2, &amp;ckevt_pxa_osmr0) + 1;</p>
<p>cksrc_pxa_oscr0.mult =<br />
clocksource_hz2mult(clock_tick_rate, cksrc_pxa_oscr0.shift);</p>
<p><strong>setup_irq</strong>(IRQ_OST0, &amp;<strong>pxa_ost0_irq</strong>);</p>
<p>/* timebase_freq = CLOCK_TICK_RATE: source clock rate in Hz */<br />
//timebase_freq = 1000000LL;<br />
timebase_freq = CLOCK_TICK_RATE;<br />
system_timer_clocksource = &amp;cksrc_pxa_oscr0;<br />
system_timer_clockevent = &amp;ckevt_pxa_osmr0;</p>
<p>    ckevt_pxa_osmr0.set_mode(CLOCK_EVT_MODE_PERIODIC, &amp;ckevt_pxa_osmr0);</p>
<p>}</p>
<p>所安装的irqaction是pxa_ost0_irq,xen/arch/arm/mach-pxa/time.c</p>
<p>static struct irqaction <strong>pxa_ost0_irq</strong> = {<br />
.name = &#8220;ost0&#8243;,<br />
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,<br />
.handler = pxa_ost0_interrupt,<br />
.dev_id = &amp;ckevt_pxa_osmr0,<br />
};</p></blockquote>
<blockquote><p>xen/arch/arm/hypervisor/irq.c<br />
int <strong>setup_irq</strong>(unsigned int irq, struct irqaction *new)<br />
{<br />
int rc = 0;<br />
        int shared = 0;<br />
        struct irqaction *old, **p;<br />
        unsigned long flags;<br />
        struct irqdesc *desc;</p>
<p>if(irq &gt;= NR_IRQS) {<br />
printk(&#8220;BAD IRQ = %d\n&#8221;, irq);<br />
while(1);<br />
}</p>
<p>        desc = get_irq_descriptor(irq);</p>
<p>        spin_lock_irqsave(&amp;desc-&gt;lock, flags);</p>
<p>        p = &amp;desc-&gt;action;</p>
<p>        if ((old = *p) != NULL) {<br />
                /* Can&#8217;t share interrupts unless both agree to */<br />
                if (!(old-&gt;flags &amp; new-&gt;flags &amp; IRQF_SHARABLE) ||<br />
   ((old-&gt;flags ^ new-&gt;flags) &amp; IRQF_TRIGGER_MASK)) {<br />
    printk(&#8220;IRQ sharing failed.\n&#8221;);</p>
<p>rc = -EBUSY;<br />
goto out;<br />
                }</p>
<p>                /* add new interrupt at end of irq queue */<br />
                do {<br />
                        p = &amp;old-&gt;next;<br />
                        old = *p;<br />
                } while (old);<br />
                shared = 1;<br />
        }</p>
<p>        *p = new;</p>
<p>        if (!shared) {<br />
if(new-&gt;flags &amp; IRQF_TRIGGER_MASK) {<br />
if(desc-&gt;chip-&gt;set_type) {<br />
desc-&gt;chip-&gt;set_type(irq, new-&gt;flags &amp; IRQF_TRIGGER_MASK);<br />
} else {<br />
printk(&#8220;No set_irq_type function for irq %d\n&#8221;, irq);<br />
rc = -1;<br />
goto out;<br />
}<br />
}</p>
<p>desc-&gt;status &amp;= ~(IRQ_AUTO_DETECT | IRQ_WAITING | IRQ_IN_PROGRESS);</p>
<p>if(!(desc-&gt;status &amp; IRQ_NO_AUTO_ENABLE)) {<br />
desc-&gt;disable_depth = 0;<br />
desc-&gt;status &amp;= ~IRQ_DISABLED;<br />
desc-&gt;chip-&gt;unmask(irq);<br />
} else {<br />
desc-&gt;disable_depth = 1;<br />
}</p>
<p>        }</p>
<p>desc-&gt;flags &amp;= ~IRQF_GUEST_BOUND;</p>
<p>out:<br />
        spin_unlock_irqrestore(&amp;desc-&gt;lock, flags);</p>
<p>        return rc;<br />
}</p></blockquote>
<p>setup_irq timer后，就开始setup_irq console了。</p>
<div>console的设置稍微复杂了一些：</div>
<div>在__start_xen中：xen/arch/arm/hypersivor/setup.c</div>
<blockquote><p>pxaserial.baud = 115200;<br />
pxaserial.data_bits = 8;<br />
pxaserial.parity = &#8216;n&#8217;;<br />
pxaserial.stop_bits = 1;<br />
pxaserial.io_base = 1;<br />
pxaserial.irq = IRQ_FFUART;</p>
<p>//pxaserial.io_base = 0x3f8;<br />
<strong>pxaserial_init</strong>(0, &amp;pxaserial);</p>
<p>pxaserial的结构体：xen/drivers/char/pxa.c<br />
static struct pxaserial {</p>
<div><strong>int baud, data_bits, parity, stop_bits, irq;</strong></div>
<div>unsigned long<strong> io_base</strong>; /* I/O port or memory-mapped I/O address. */</div>
<div>char *remapped_io_base; /* Remapped virtual address of mmap I/O.  */</div>
<div>/* UART with IRQ line: interrupt-driven I/O. */</div>
<div>struct irqaction irqaction;</div>
<div>/* UART with no IRQ line: periodically-polled I/O. */</div>
<div>struct timer timer;</div>
<div>unsigned int timeout_ms;</div>
<div>} pxaserial_com[2] = { { 0 }, { 0 } };</div>
<p><strong>pxaserial_init  </strong>xen/drivers/char/pxa.c</p>
<div>void __init pxaserial_init(int index, struct pxaserial_defaults *defaults)</div>
<div>{</div>
<div>struct pxaserial *uart = &amp;pxaserial_com[index];</div>
<div>if ( (index &lt; 0) || (index&gt; 1) )</div>
<div>return;</div>
<div>if ( defaults != NULL )</div>
<div>{</div>
<div>uart-&gt;baud = defaults-&gt;baud;</div>
<div>uart-&gt;data_bits = defaults-&gt;data_bits;</div>
<div>uart-&gt;parity = parse_parity_char(defaults-&gt;parity);</div>
<div>uart-&gt;stop_bits = defaults-&gt;stop_bits;</div>
<div>uart-&gt;irq = defaults-&gt;irq;</div>
<div>uart-&gt;io_base = defaults-&gt;io_base;</div>
<div>}</div>
<div><strong>pxaserial_parse_port_config</strong>(uart, index);</div>
<div>}</div>
<p>&#8212;</p>
<div><strong>pxaserial_parse_port_config </strong>xen/drivers/char/pxa.c</div>
<div>static void __init pxaserial_parse_port_config(struct pxaserial *uart, int index)</div>
<div>{</div>
<div>int baud;</div>
<div>const char *conf = ((index == 0) ? opt_com1 : opt_com2);</div>
<div>/* (LSU) Defined in xen/lib/vsprintf.c */</div>
<div>extern unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base);</div>
<div>/* No user-specified configuration? */</div>
<div>if ( (conf == NULL) || (*conf == &#8216;\0&#8242;) )</div>
<div>{</div>
<div>/* Some platforms may automatically probe the UART configuartion. */</div>
<div>if ( uart-&gt;baud != 0 )</div>
<div>goto config_parsed;</div>
<div>return;</div>
<div>}</div>
<div>if ( strncmp(conf, &#8220;auto&#8221;, 4) == 0 )</div>
<div>{</div>
<div>uart-&gt;baud = BAUD_AUTO;</div>
<div>conf += 4;</div>
<div>}</div>
<div>else if ( (baud = simple_strtoul(conf, (char **) &amp;conf, 10)) != 0 )</div>
<div>uart-&gt;baud = baud;</div>
<div>if ( *conf != &#8216;,&#8217; )</div>
<div>goto config_parsed;</div>
<div>conf++;</div>
<div>uart-&gt;data_bits = simple_strtoul(conf, (char **) &amp;conf, 10);</div>
<div>uart-&gt;parity = parse_parity_char(*conf);</div>
<div>conf++;</div>
<div>uart-&gt;stop_bits = simple_strtoul(conf,(char **)  &amp;conf, 10);</div>
<div>if ( *conf == &#8216;,&#8217; )</div>
<div>{</div>
<div>conf++;</div>
<div>uart-&gt;io_base = simple_strtoul(conf, (char **) &amp;conf, 0);</div>
<div>if ( *conf == &#8216;,&#8217; )</div>
<div>{</div>
<div>conf++;</div>
<div>uart-&gt;irq = simple_strtoul(conf, (char **) &amp;conf, 10);</div>
<div>}</div>
<div>}</div>
<div>config_parsed:</div>
<div>/* Sanity checks. */</div>
<div>if ( (uart-&gt;baud != BAUD_AUTO) &amp;&amp; ((uart-&gt;baud &lt; 1200) || (uart-&gt;baud&gt; 115200)) )</div>
<div>PARSE_ERR(&#8220;Baud rate %d outside supported range.&#8221;, uart-&gt;baud);</div>
<div>if ( (uart-&gt;data_bits &lt; 5) || (uart-&gt;data_bits&gt; <img src='http://www.tek-life.org/wp-includes/images/smilies/icon_cool.gif' alt='8)' class='wp-smiley' /> )</div>
<div>PARSE_ERR(&#8220;%d data bits are unsupported.&#8221;, uart-&gt;data_bits);</div>
<div>if ( (uart-&gt;stop_bits &lt; 1) || (uart-&gt;stop_bits&gt; 2) )</div>
<div>PARSE_ERR(&#8220;%d stop bits are unsupported.&#8221;, uart-&gt;stop_bits);</div>
<div>if ( uart-&gt;io_base == 0 )</div>
<div>PARSE_ERR(&#8220;I/O base address must be specified.&#8221;);</div>
<div>/* Register with generic serial driver. */</div>
<div><strong>serial_register_uart</strong>(0, &amp;pxaserial_driver, uart);</div>
<div>}</div>
<p>上面的这个pxaserial_parse_port_config函数有点长，不过，除了最后一句有用外，其他的都在扯淡。特别是中间那一大段完全没有被执行，所以不要有压力哈。</p>
<p><strong>serial_register_uart </strong>xen/drivers/char/serial.c</p>
<div>void __init serial_register_uart(int idx, struct uart_driver *driver, void *uart)</div>
<div>{</div>
<div>    /* Store UART-specific info. */</div>
<div>    com[idx].driver = driver;</div>
<div>    com[idx].uart   = uart;</div>
<div>    /* Default is no transmit FIFO. */</div>
<div>    com[idx].tx_fifo_size = 1;</div>
<div>}</div>
<p>其实这个函数不再是重点了，重点的是其中的值：<br />
我们看一下，com[0]的类型是：</p>
<div>struct serial_port {</div>
<div>    /* Uart-driver parameters. */</div>
<div>    struct <strong>uart_driver</strong> *driver;</div>
<div>    void               *<strong>uart</strong>;</div>
<div>    /* Number of characters the port can hold for transmit. */</div>
<div>    int                <strong> tx_fifo_size</strong>;</div>
<div>    /* Transmit data buffer (interrupt-driven uart). */</div>
<div>    char               *txbuf;</div>
<div>    unsigned int        txbufp, txbufc;</div>
<div>    /* Force synchronous transmit. */</div>
<div>    int                 sync;</div>
<div>    /* Receiver callback functions (asynchronous receivers). */</div>
<div>    serial_rx_fn        <strong>rx_lo</strong>, <strong>rx_hi</strong>,<strong> rx</strong>;</div>
<div>    /* Receive data buffer (polling receivers). */</div>
<div>    char                rxbuf[SERIAL_RXBUFSZ];</div>
<div>    unsigned int        rxbufp, rxbufc;</div>
<div>    /* Serial I/O is concurrency-safe. */</div>
<div>    spinlock_t          rx_lock, tx_lock;</div>
<div>};</div>
<p>也就是说，设置了com[0]的两个成员变量，一个是driver,另一个是uart.我们在回头看一下pxaserial_parse_port_config(struct pxaserial *uart, int index),其实这个函数调用<strong>serial_register_uart</strong>(0, &amp;pxaserial_driver, uart)的并不优雅，它将原本是index确定为了0.不过对于本项目显然是没有问题的。可以看看linux是怎样实现的。<br />
在函数serial_register_uart里面讲3个成员变量定以后，就剩下其他的小东西了。这些东西紧接着就定义了，不要急。我们先来看一下struct uart_driver *driver=&amp;pxaserial_driver.</p>
<p>改变定义在xen/drivers/char/pxa.c文件中</p>
<div>static struct uart_driver pxaserial_driver = {</div>
<div>.init_preirq = pxa_initpreirq,</div>
<div>.putc = pxaserial_putc,</div>
<div>.getc = pxaserial_getc,</div>
<div>.irq = pxaserial_irq,</div>
<div>.init_postirq = pxaserial_init_postirq,</div>
<div>.endboot = NULL,</div>
<div>.tx_empty = serial_pxa_tx_empty,</div>
<div>};</div>
<p>以上分别定义了struct pxaserial类型的pxaserial，定义了struct uart_driver类型的pxaserial_driver.然后将它们赋值给了struct serial_port类型的com[0].</p>
<p>我们回过头来看看，实际上，并没有定义struct pxaserial pxaserial的全部成员，比如irqaction，比如timer.也许是以后还会有。我们需要耐心。<br />
好，这里已经定义好了serial_port的3个重要的成员变量了，革命尚未成功，同志仍需努力！</p>
<p>返回到__start_xen的主程序，接下来开始执行</p>
<div>serial_init_preirq(void);</div>
<div>void __init serial_init_preirq(void)</div>
<div>{</div>
<div>    int i;</div>
<div>    for ( i = 0; i &lt; ARRAY_SIZE(com); i++ )</div>
<div>        if ( com[i].driver &amp;&amp; com[i].driver-&gt;init_preirq )</div>
<div>            com[i].driver-&gt;init_preirq(&amp;com[i]);</div>
<div>}</div>
<div>可以看到，实际上执行的是pxa_initpreirq(&amp;com[0]),com[1]没有初始化，因此com[1]并没有被执行。</div>
<div>/* from startup&#8230;*/</div>
<div>/* (LSU) Applied awaited signature */</div>
<div>//static int pxa_initpreirq(struct serial_port *port) {</div>
<div>static void pxa_initpreirq(struct serial_port *port) {</div>
<div>struct uart_pxa_port *up = &amp;<strong>serial_pxa_port</strong>;</div>
<div>unsigned long flags;</div>
<div>// int retval;</div>
<div>if (up-&gt;line == 3) /* HWUART */</div>
<div>up-&gt;mcr |= UART_MCR_AFE;</div>
<div>else</div>
<div>up-&gt;mcr = 0;</div>
<div>/*</div>
<div>* Clear the FIFO buffers and disable them.</div>
<div>* (they will be reenabled in set_termios())</div>
<div>*/</div>
<div>serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);</div>
<div>serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR</div>
<div>| UART_FCR_CLEAR_XMIT);</div>
<div>serial_out(up, UART_FCR, 0);</div>
<div>/*</div>
<div>* Clear the interrupt registers.</div>
<div>*/</div>
<div>(void) serial_in(up, UART_LSR);</div>
<div>(void) serial_in(up, UART_RX);</div>
<div>(void) serial_in(up, UART_IIR);</div>
<div>(void) serial_in(up, UART_MSR);</div>
<div>/*</div>
<div>* Now, initialize the UART</div>
<div>*/</div>
<div>serial_out(up, UART_LCR, UART_LCR_WLEN8);</div>
<div>spin_lock_irqsave(&amp;up-&gt;lock, flags);</div>
<div>up-&gt;mctrl |= TIOCM_OUT2;</div>
<div><strong>serial_pxa_set_mctrl(port, up-&gt;mctrl);</strong></div>
<div>spin_unlock_irqrestore(&amp;up-&gt;lock, flags);</div>
<div>//return 0;</div>
<div>}</div>
<p>只里面又包含了已经被定义好的东东    xen/drivers/char/pxa.c</p>
<div>static struct uart_pxa_port serial_pxa_port = {</div>
<div>.name = &#8220;FFUART&#8221;, </div>
<div>.cken = CKEN6_FFUART,</div>
<div>.membase = (void *)&amp;FFUART, </div>
<div>.mapbase = __PREG(FFUART),</div>
<div> .irq = IRQ_FFUART,</div>
<div> .uartclk = 921600 * 16,</div>
<div> .line = 0, </div>
<div>};</div>
<div>struct uart_pxa_port {</div>
<div>spinlock_t lock;</div>
<div>unsigned char ier;</div>
<div>unsigned char lcr;</div>
<div>unsigned char mcr;</div>
<div>unsigned int mctrl;</div>
<div>unsigned int lsr_break_flag;</div>
<div>unsigned int cken;</div>
<div>int line;</div>
<div>char *name;</div>
<div>unsigned long iobase; /* io base address */</div>
<div>void __iomem *membase; /* ioremap cookie or NULL */</div>
<div>unsigned long mapbase; /* resource base */</div>
<div>unsigned int irq; /* interrupt number */</div>
<div>unsigned int uartclk; /* UART clock rate */</div>
<div>unsigned char regshift; /* register shift */</div>
<div>unsigned char iotype; /* UPIO_* */</div>
<div>unsigned char hub6;</div>
<div>upf_t flags; /* UPF_* flags */</div>
<div>};</div>
<p>从上面看来，应该是一些硬件参数信息了。不过和pxaserial那个结构体似乎有某种联系。</p>
<p>还是看那个函数，有用的只有这么一行：<strong>serial_pxa_set_mctrl(port, up-&gt;mctrl);<br />
</strong>我们进去看看：</p>
<div>static void serial_pxa_set_mctrl(struct serial_port *port, unsigned int mctrl) {</div>
<div><strong>struct uart_pxa_port *up = &amp;serial_pxa_port</strong>;</div>
<div>unsigned char mcr = 0;</div>
<div>if (mctrl &amp; TIOCM_RTS)</div>
<div>mcr |= UART_MCR_RTS;</div>
<div>if (mctrl &amp; TIOCM_DTR)</div>
<div>mcr |= UART_MCR_DTR;</div>
<div>if (mctrl &amp; TIOCM_OUT1)</div>
<div>mcr |= UART_MCR_OUT1;</div>
<div>if (mctrl &amp; TIOCM_OUT2)</div>
<div>mcr |= UART_MCR_OUT2;</div>
<div>if (mctrl &amp; TIOCM_LOOP)</div>
<div>mcr |= UART_MCR_LOOP;</div>
<div>mcr |= up-&gt;mcr;</div>
<div>serial_out(up, UART_MCR, mcr);</div>
<div>}</div>
<p>晕倒啊，将port传进来有意义么？根本就没有用到port,还是用的原来的serial_pxa_port<br />
然后就是serial_out(up,UART_MCR,mcr).</p>
<div>static inline void serial_out(struct uart_pxa_port *up, int offset, int value)</div>
<div>{</div>
<div>offset &lt;&lt;= 2;</div>
<div>writel(value, up-&gt;port.membase + offset);</div>
<div>}</div>
<p>drivers/serial/pxa.c</p>
<div>struct uart_pxa_port {</div>
<div>struct uart_port        port;</div>
<div>unsigned char           ier;</div>
<div>unsigned char           lcr;</div>
<div>unsigned char           mcr;</div>
<div>unsigned int            lsr_break_flag;</div>
<div>unsigned int cken;</div>
<div>char *name;</div>
<div>};</div>
<p>这里的uart_port</p>
<div>struct uart_port {</div>
<div>spinlock_t lock; /* port lock */</div>
<div>unsigned int iobase; /* in/out[bwl] */</div>
<div>unsigned char __iomem *membase; /* read/write[bwl] */</div>
<div>unsigned int irq; /* irq number */</div>
<div>unsigned int uartclk; /* base uart clock */</div>
<div>unsigned int fifosize; /* tx fifo size */</div>
<div>unsigned char x_char; /* xon/xoff char */</div>
<div>unsigned char regshift; /* reg offset shift */</div>
<div>unsigned char iotype; /* io access style */</div>
<div>unsigned char unused1;</div>
<div>#define UPIO_PORT (0)</div>
<div>#define UPIO_HUB6 (1)</div>
<div>#define UPIO_MEM (2)</div>
<div>#define UPIO_MEM32 (3)</div>
<div>#define UPIO_AU (4) /* Au1x00 type IO */</div>
<div>#define UPIO_TSI (5) /* Tsi108/109 type IO */</div>
<div>unsigned int read_status_mask; /* driver specific */</div>
<div>unsigned int ignore_status_mask; /* driver specific */</div>
<div>struct uart_info *info; /* pointer to parent info */</div>
<div>struct uart_icount icount; /* statistics */</div>
<div>struct console *cons; /* struct console, if any */</div>
<div>#ifdef CONFIG_SERIAL_CORE_CONSOLE</div>
<div>unsigned long sysrq; /* sysrq timeout */</div>
<div>#endif</div>
<div>upf_t flags;</div>
<div>#define UPF_FOURPORT ((__force upf_t) (1 &lt;&lt; 1))</div>
<div>#define UPF_SAK ((__force upf_t) (1 &lt;&lt; 2))</div>
<div>#define UPF_SPD_MASK ((__force upf_t) (0&#215;1030))</div>
<div>#define UPF_SPD_HI ((__force upf_t) (0&#215;0010))</div>
<div>#define UPF_SPD_VHI ((__force upf_t) (0&#215;0020))</div>
<div>#define UPF_SPD_CUST ((__force upf_t) (0&#215;0030))</div>
<div>#define UPF_SPD_SHI ((__force upf_t) (0&#215;1000))</div>
<div>#define UPF_SPD_WARP ((__force upf_t) (0&#215;1010))</div>
<div>#define UPF_SKIP_TEST ((__force upf_t) (1 &lt;&lt; 6))</div>
<div>#define UPF_AUTO_IRQ ((__force upf_t) (1 &lt;&lt; 7))</div>
<div>#define UPF_HARDPPS_CD ((__force upf_t) (1 &lt;&lt; 11))</div>
<div>#define UPF_LOW_LATENCY ((__force upf_t) (1 &lt;&lt; 13))</div>
<div>#define UPF_BUGGY_UART ((__force upf_t) (1 &lt;&lt; 14))</div>
<div>#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 &lt;&lt; 16))</div>
<div>#define UPF_CONS_FLOW ((__force upf_t) (1 &lt;&lt; 23))</div>
<div>#define UPF_SHARE_IRQ ((__force upf_t) (1 &lt;&lt; 24))</div>
<div>#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 &lt;&lt; 28))</div>
<div>#define UPF_DEAD ((__force upf_t) (1 &lt;&lt; 30))</div>
<div>#define UPF_IOREMAP ((__force upf_t) (1 &lt;&lt; 31))</div>
<div>#define UPF_CHANGE_MASK ((__force upf_t) (0x17fff))</div>
<div>#define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))</div>
<div>unsigned int mctrl; /* current modem ctrl settings */</div>
<div>unsigned int timeout; /* character-based timeout */</div>
<div>unsigned int type; /* port type */</div>
<div>const struct uart_ops *ops;</div>
<div>unsigned int custom_divisor;</div>
<div>unsigned int line; /* port index */</div>
<div>unsigned long mapbase; /* for ioremap */</div>
<div>struct device *dev; /* parent device */</div>
<div>unsigned char hub6; /* this should be in the 8250 driver */</div>
<div>unsigned char unused[3];</div>
<div>};</div>
<p>有这么长。。。<br />
好了，搞这这么久，实际上根本与Port屁事不关。我擦～～～</p>
<p>回到__start_xen的主程序，继续我们未完成的千秋大业。<br />
init_console();</p>
<div>void __init init_console(void)</div>
<div>{</div>
<div>extern void add_taint(unsigned flag);</div>
<div>    char *p;</div>
<div>    /* Where should console output go? */</div>
<div>    for ( p = opt_console; p != NULL; p = strchr(p, &#8216;,&#8217;) )</div>
<div>    {</div>
<div>        if ( *p == &#8216;,&#8217; )</div>
<div>            p++;</div>
<div>        if ( strncmp(p, &#8220;com&#8221;, 3) == 0 )</div>
<div>            sercon_handle = serial_parse_handle(p);</div>
<div>        else if ( strncmp(p, &#8220;vga&#8221;, 3) == 0 )</div>
<div>            vga_init();</div>
<div>    }</div>
<div>    <strong>serial_set_rx_handler</strong>(sercon_handle, serial_rx);</div>
<div>    /* HELLO WORLD &#8212; start-of-day banner text. */</div>
<div>    printk(xen_banner());</div>
<div>    printk(&#8221; http://www.cl.cam.ac.uk/netos/xen\n&#8221;);</div>
<div>    printk(&#8221; University of Cambridge Computer Laboratory\n\n&#8221;);</div>
<div>    printk(&#8221; Xen version %d.%d%s (%s@%s) (%s) %s\n&#8221;,</div>
<div>           xen_major_version(), xen_minor_version(), xen_extra_version(),</div>
<div>           xen_compile_by(), xen_compile_domain(),</div>
<div>           xen_compiler(), xen_compile_date());</div>
<div>    printk(&#8221; EmbeddedXEN/PENAR Project &#8211; Reconfigurable Embedded Digital System (REDS) Institute from HEIG-VD/Switzerland\n&#8221;);</div>
<div>    printk(&#8221; http://reds.heig-vd.ch\n\n&#8221;);</div>
<div>    printk(&#8221; Latest ChangeSet: %s\n\n&#8221;, xen_changeset());</div>
<div>    set_printk_prefix(&#8220;(XEN) &#8220;);</div>
<div>    if ( opt_sync_console )</div>
<div>    {</div>
<div>        serial_start_sync(sercon_handle);</div>
<div>        add_taint(TAINT_SYNC_CONSOLE);</div>
<div>        printk(&#8220;Console output is synchronous.\n&#8221;);</div>
<div>    }</div>
<div>}</div>
<p>这里面有一句挺重要：<br />
<strong>serial_set_rx_handler</strong>(sercon_handle, serial_rx);</p>
<div>void __init serial_set_rx_handler(int handle, serial_rx_fn fn)</div>
<div>{</div>
<div>    struct serial_port *port = &amp;com[handle &amp; SERHND_IDX];</div>
<div>    unsigned long flags;</div>
<div>    if ( handle == -1 )</div>
<div>        return;</div>
<div>    spin_lock_irqsave(&amp;port-&gt;rx_lock, flags);</div>
<div>    if ( port-&gt;rx != NULL )</div>
<div>        goto fail;</div>
<div>    if ( handle &amp; SERHND_LO )</div>
<div>    {</div>
<div>        if ( port-&gt;rx_lo != NULL )</div>
<div>            goto fail;</div>
<div>        port-&gt;rx_lo = fn;</div>
<div>    }</div>
<div>    else if ( handle &amp; SERHND_HI )</div>
<div>    {</div>
<div>        if ( port-&gt;rx_hi != NULL )</div>
<div>            goto fail;</div>
<div>        port-&gt;rx_hi = fn;</div>
<div>    }</div>
<div>    else</div>
<div>    {</div>
<div>        if ( (port-&gt;rx_hi != NULL) || (port-&gt;rx_lo != NULL) )</div>
<div>            goto fail;</div>
<div>        port-&gt;rx = fn;</div>
<div>    }</div>
<div>    spin_unlock_irqrestore(&amp;port-&gt;rx_lock, flags);</div>
<div>    return;</div>
<div> fail:</div>
<div>    spin_unlock_irqrestore(&amp;port-&gt;rx_lock, flags);</div>
<div>    printk(&#8220;ERROR: Conflicting receive handlers for COM%d\n&#8221;,</div>
<div>           handle &amp; SERHND_IDX);</div>
<div>}</div>
<p>这里将port的成员变量rx_lo,rx_hi,rx_rx全部定位了fn，也就是serial_rx</p>
<p>然后在start_of_day()中，com实际上才开始注册了。在start_of_day中<br />
serial_init_postirq();</p>
<div>void __init serial_init_postirq(void)</div>
<div>{</div>
<div>    int i;</div>
<div>    for ( i = 0; i &lt; ARRAY_SIZE(com); i++ )</div>
<div>        if ( com[i].driver &amp;&amp; com[i].driver-&gt;init_postirq )</div>
<div>            com[i].driver-&gt;init_postirq(&amp;com[i]);</div>
<div>}</div>
<p>实际上是pxaserial_init_postirq.&#8211;这在前面赋值的时候，已经被赋值了。xen/drivers/char/pxa.c</p>
<div>static void __init pxaserial_init_postirq(struct serial_port *port)</div>
<div>{</div>
<div>struct pxaserial *uart = port-&gt;uart;</div>
<div>struct uart_pxa_port *up = &amp;serial_pxa_port;</div>
<div>int rc;</div>
<div>//int bits;</div>
<div>if ( uart-&gt;irq &lt; 0 )</div>
<div>return;</div>
<div>serial_async_transmit(port);</div>
<div>/* We do not consider polling mode (DRE) */</div>
<div><strong>uart-&gt;irqaction.handler = pxaserial_interrupt;</strong></div>
<div><strong>uart-&gt;irqaction.name = &#8220;pxaserial&#8221;;</strong></div>
<div><strong>uart-&gt;irqaction.dev_id = port;</strong></div>
<div>if ( (rc = <strong>setup_irq</strong>(uart-&gt;irq, &amp;uart-&gt;irqaction)) != 0 )</div>
<div>printk(&#8220;ERROR: Failed to allocate ns16550 IRQ %d\n&#8221;, uart-&gt;irq);</div>
<div>/*</div>
<div>* Finally, enable interrupts.  Note: Modem status interrupts</div>
<div>* are set via set_termios(), which will be occurring imminently</div>
<div>* anyway, so we don&#8217;t enable them here.</div>
<div>*/</div>
<div>up-&gt;ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;</div>
<div>serial_out(up, UART_IER, up-&gt;ier);</div>
<div>/*</div>
<div>* And clear the interrupt registers again for luck.</div>
<div>*/</div>
<div>(void) serial_in(up, UART_LSR);</div>
<div>(void) serial_in(up, UART_RX);</div>
<div>(void) serial_in(up, UART_IIR);</div>
<div>(void) serial_in(up, UART_MSR);</div>
<div>}</div>
<p>看到<strong>uart-&gt;irqaction</strong>了吧，被赋值了，呵呵，终于被赋值了，不过timer还没有被赋值，没关系，我们以后再看，至此，本文基本上也就完成一半了。</p></blockquote>
<div>
<blockquote>
<div>现在我们来看一下DOM方面的中断申请，不例外，肯定是用reqeust_irq和setup_irq.我们先来看一下request_irq:</div>
<blockquote dir="ltr">
<div> xen_guest/core/evtchn.c</div>
</blockquote>
</blockquote>
<blockquote><p>int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char * devname, void *dev_id)</p>
<div>{</div>
<div> struct irqaction * action;</div>
<div> int retval;</div>
<p> /*</p>
<div>  * Sanity-check: shared interrupts must pass in a real dev-ID,</div>
<div>  * otherwise we&#8217;ll have trouble later trying to figure out</div>
<div>  * which interrupt is which (messes up the interrupt freeing</div>
<div>  * logic etc).</div>
<div>  */</div>
<div> if ((irqflags &amp; SA_SHIRQ) &amp;&amp; !dev_id)</div>
<div>  return -EINVAL;</div>
<div> if (irq &gt;= NR_IRQS)</div>
<div>  return -EINVAL;</div>
<div> if (!handler)</div>
<div>  return -EINVAL;</div>
<p> action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);</p>
<div> if (!action)</div>
<div>  return -ENOMEM;</div>
<p> action-&gt;handler = handler;</p>
<div> action-&gt;flags = irqflags;</div>
<div> cpus_clear(action-&gt;mask);</div>
<div> action-&gt;name = devname;</div>
<div> action-&gt;next = NULL;</div>
<div> action-&gt;dev_id = dev_id;</div>
<p> retval = <strong>setup_irq</strong>(irq, action);</p>
<p> if (retval)</p>
<div>  kfree(action);</div>
<p> return retval;</p>
<div>}</div>
</blockquote>
<blockquote><p>setup_irq()</p>
<blockquote dir="ltr"><p>xen_guest/core/evtchn.c</p></blockquote>
</blockquote>
<blockquote>
<div>int <strong>setup_irq</strong>(unsigned int irq, struct irqaction * new)</div>
<p>{</p>
<p> struct evtchn_desc *desc = evtchn_desc + irq;</p>
<p> struct irqaction *old, **p;</p>
<p> unsigned long flags;</p>
<p> int shared = 0;</p>
<p> // the following should be commented: jyhwang 2007 July 15</p>
<p> // printk(&#8220;irq=%d, NR_IRQS=%d\n&#8221;, irq, NR_IRQS);</p>
<p> if (irq &gt;= NR_IRQS)</p>
<p>  return -EINVAL;</p>
<p> if (desc-&gt;handler == NULL)</p>
<p>  return -ENOSYS;</p>
<p> /*</p>
<p>  * Some drivers like serial.c use request_irq() heavily,</p>
<p>  * so we have to be careful not to interfere with a</p>
<p>  * running system.</p>
<p>  */</p>
<p> if (new-&gt;flags &amp; SA_SAMPLE_RANDOM) {</p>
<p>  /*</p>
<p>   * This function might sleep, we want to call it first,</p>
<p>   * outside of the atomic block.</p>
<p>   * Yes, this might clear the entropy pool if the wrong</p>
<p>   * driver is attempted to be loaded, without actually</p>
<p>   * installing a new handler, but is this really a problem,</p>
<p>   * only the sysadmin is able to do this.</p>
<p>   */</p>
<p>  rand_initialize_irq(irq);</p>
<p> }</p>
<p> /*</p>
<p>  * The following block of code has to be executed atomically</p>
<p>  */</p>
<p> spin_lock_irqsave(&amp;desc-&gt;lock,flags);</p>
<p> p = &amp;desc-&gt;action;</p>
<p> if ((old = *p) != NULL) {</p>
<p>  /* Can&#8217;t share interrupts unless both agree to */</p>
<p>  if (!(old-&gt;flags &amp; new-&gt;flags &amp; SA_SHIRQ)) {</p>
<p>   spin_unlock_irqrestore(&amp;desc-&gt;lock,flags);</p>
<p>   return -EBUSY;</p>
<p>  }</p>
<p>  /* add new interrupt at end of irq queue */</p>
<p>  do {</p>
<p>   p = &amp;old-&gt;next;</p>
<p>   old = *p;</p>
<p>  } while (old);</p>
<p>  shared = 1;</p>
<p> }</p>
<p> <strong>*p = new;//action赋值给evtchn_desc</strong></p>
<p> if (!shared) {</p>
<p>  desc-&gt;depth = 0;</p>
<p>  desc-&gt;status &amp;= ~(IRQ_DISABLED | IRQ_AUTODETECT |</p>
<p>      IRQ_WAITING | IRQ_INPROGRESS);</p>
<p>  if (desc-&gt;handler-&gt;startup)</p>
<p>   desc-&gt;handler-&gt;<strong>startup</strong>(irq);</p>
<p>  else</p>
<p>   desc-&gt;handler-&gt;enable(irq);</p>
<p> }</p>
<p> spin_unlock_irqrestore(&amp;desc-&gt;lock,flags);</p>
<p> new-&gt;irq = irq;</p>
<p> new-&gt;dir = NULL;</p>
<p> return 0;</p>
<p>}</p></blockquote>
<p>看到setup_irq，似乎与Linux没有什么太大的不同，可是这也正是移植的妙处，总是要尽量的和原来的系统兼容，对吧。</p>
<div>我们来看看desc-&gt;handler-&gt;startup(irq);这句吧，这才是核心。我们知道在DOM进行初始化的时候，是怎么初始化呢？如果是pirq的话，分配的handler是pirq_type,这个handler的startup是被startup_pirq()实例化的。那么我们就来看看startup_pirq吧：</div>
<blockquote dir="ltr"><p>#define probing_irq(_irq) (evtchn_desc[(_irq)].action == NULL)</p>
<p>static unsigned int startup_pirq(unsigned int irq)</p>
<div>{</div>
<div>// evtchn_op_t op = { .cmd = EVTCHNOP_bind_pirq };</div>
<div> evtchn_bind_pirq_t op;</div>
<div> int evtchn = evtchn_from_irq(irq), ret = 0;</div>
<p> if (VALID_EVTCHN(evtchn)) {</p>
<div>  goto out;</div>
<div> }</div>
<p> op.pirq  = irq;</p>
<div> /* NB. We are happy to share unless we are probing. */</div>
<div> op.flags = probing_irq(irq) ? 0 : BIND_PIRQ__WILL_SHARE;</div>
<p> ret = HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &amp;op);</p>
<div> if (ret != 0) {</div>
<div>  if (!probing_irq(irq))</div>
<div>   printk(KERN_INFO &#8220;Failed to obtain physical IRQ %d\n&#8221;, irq);</div>
<div>  return 0;</div>
<div> }</div>
<div> evtchn = op.port;</div>
<p> pirq_query_unmask(irq_to_pirq(irq));</p>
<p> bind_evtchn_to_cpu(evtchn, 0);</p>
<div> evtchn_to_irq[evtchn] = irq;</div>
<div> irq_info[irq] = mk_irq_info(IRQT_PIRQ, irq, evtchn);</div>
<p> out:</p>
<div> unmask_evtchn(evtchn);</div>
<div> pirq_unmask_notify(irq_to_pirq(irq));</div>
<p> return 0;</p>
<div>}</div>
</blockquote>
<blockquote>
<div>由于我们现在工作于DOMU的内核中，如果在x86架构中的话，是在ring1态，但这是在arm架构中，因此仍然是在ring3。不过,如果要申请进入ring0,也要进行HYPERVISOR_event_channel_op</div>
<div> </div>
<div>xen/common/event_channel.c </div>
<div>long do_event_channel_op(int cmd, XEN_GUEST_HANDLE(void) arg)</div>
<div>{</div>
<div>&#8230;</div>
<div>  case EVTCHNOP_bind_pirq: {</p>
<div>        struct evtchn_bind_pirq bind_pirq;</div>
<div>        if ( copy_from_guest(&amp;bind_pirq, arg, 1) != 0 )</div>
<div>            return -EFAULT;</div>
<div>        rc = evtchn_bind_pirq(&amp;bind_pirq);</div>
<div>        if ( (rc == 0) &amp;&amp; (copy_to_guest(arg, &amp;bind_pirq, 1) != 0) )</div>
<div>            rc = -EFAULT; /* Cleaning up here would be a mess! */</div>
<div>        break;</div>
<div>    }</div>
</div>
<div>&#8230;</div>
<div>}</div>
<div> </div>
<div>evtchn_bind_pirq(&amp;bind_pirq);</div>
<div>xen/common/event_channel.c </div>
<p>static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind)</p>
<div>{</div>
<div>    struct evtchn *chn;</div>
<div>    struct domain *d = current-&gt;domain;</div>
<div>    int            port, pirq = bind-&gt;pirq;</div>
<div>    long           rc;</div>
<p>    if ( (pirq &lt; 0) || (pirq &gt;= ARRAY_SIZE(d-&gt;pirq_to_evtchn)) )</p>
<div>        return -EINVAL;</div>
<p>    if ( !irq_access_permitted(d, pirq) )</p>
<div>        return -EPERM;</div>
<p>    spin_lock(&amp;d-&gt;evtchn_lock);</p>
<p>    if ( d-&gt;pirq_to_evtchn[pirq] != 0 )</p>
<div>        ERROR_EXIT(-EEXIST);</div>
<p>    if ( (port = get_free_port(d)) &lt; 0 )//得到evtchn号</p>
<div>        ERROR_EXIT(port);</div>
<p>    chn = evtchn_from_port(d, port);//从evtchn号得到chn数据结构</p>
<p>    d-&gt;pirq_to_evtchn[pirq] = port;</p>
<div>    rc = pirq_guest_bind(d-&gt;vcpu[0], pirq, !!(bind-&gt;flags &amp; BIND_PIRQ__WILL_SHARE));</div>
<div>    if ( rc != 0 )</div>
<div>    {</div>
<div>        d-&gt;pirq_to_evtchn[pirq] = 0;</div>
<div>        goto out;</div>
<div>    }</div>
<p>    chn-&gt;state  = ECS_PIRQ;//充实这个数据结构</p>
<div>    chn-&gt;u.pirq = pirq;//充实</div>
<p>    bind-&gt;port = port;//充实bind，要带回去的</p>
<p> out:</p>
<div>    spin_unlock(&amp;d-&gt;evtchn_lock);</div>
<div>    return rc;</p>
<div>}</div>
</div>
<div> </div>
<div>port与pirq</div>
<div>port属于DOM的，pirq属于系统的。</div>
<div>当irq来了以后，实际上就是pirq，找到port,然后找到chn，然后就设定vcpu响应的Port了.(这些在中断响应文章中都有叙述)</div>
<div> </div>
<div>也就是说xen-&gt;dom的interface是通过port来进行的,而在申请的时候，是通过结构体evtchn_bind_pirq</div>
</blockquote>
<blockquote>
<blockquote>
<div>void send_guest_pirq(struct domain *d, int pirq)</div>
<div>{</div>
<div>    int port = d-&gt;pirq_to_evtchn[pirq];</div>
<div>    struct evtchn *chn;</div>
<div> </div>
<div>    ASSERT(port != 0);</div>
<div> </div>
<div>    chn = evtchn_from_port(d, port);</div>
<div> </div>
<div>    evtchn_set_pending(d-&gt;vcpu[<strong>chn-&gt;notify_vcpu_id</strong>], port);</div>
<div>}</div>
<div>  然后当DOM执行时，</div>
<div>evtchn_do_upcall</div>
<div> </p>
<div>asmlinkage void evtchn_do_upcall(struct pt_regs *regs)</div>
<div>{</div>
<div>unsigned long  l1, l2;</div>
<div>unsigned int   l1i, l2i, port;</div>
<div>int            irq, cpu = smp_processor_id();</div>
<div>shared_info_t *s = HYPERVISOR_shared_info;</div>
<div>vcpu_info_t   *vcpu_info = &amp;s-&gt;vcpu_info[cpu];</div>
<div>retry:</div>
<div>xchg(&amp;vcpu_info-&gt;evtchn_upcall_pending,0);</div>
<div>l1 = xchg(&amp;vcpu_info-&gt;evtchn_pending_sel, 0);</div>
<div>while (l1 != 0) {</div>
<div>l1i = __ffs(l1);</div>
<div>l1 &amp;= ~(1UL &lt;&lt; l1i);</div>
<div>while ((l2 = active_evtchns(cpu, s, l1i)) != 0) {</div>
<div>l2i = __ffs(l2);</div>
<div><strong>port = (l1i * BITS_PER_LONG) + l2i;</strong></div>
<div>if ((irq = <strong>evtchn_to_irq[port])</strong> != -1) {  //evtchn号转化为irq号</div>
<div>irq_enter();</div>
<div><strong>do_IRQ(irq, regs)</strong>; //guest分发中断函数</div>
<div>irq_exit();</div>
<div>}  else {</div>
<div>evtchn_device_upcall(port);</div>
<div>}</div>
<div>}</div>
<div>}</div>
<div>if(vcpu_info-&gt;evtchn_upcall_pending) {//当有多个中断的时候</div>
<div>goto retry;</div>
<div>}</div>
<div>}</div>
<div>好了，这部分的回顾，就到此打住。</div>
</div>
</blockquote>
</blockquote>
<blockquote><p>int pirq_guest_bind(struct vcpu *v, int irq, int will_share)</p>
<div>{</div>
<div> struct irqdesc      *desc;</div>
<div> irq_guest_action_t  *action;</div>
<div> unsigned long       flags;</div>
<div> int                 rc = 0;</div>
<p> if ( (irq &lt; 0) || (irq &gt;= NR_IRQS) )</p>
<div>  return -EINVAL;</div>
<p> desc = get_irq_descriptor(irq);</p>
<p> spin_lock_irqsave(&amp;desc-&gt;lock, flags);</p>
<p> action = (irq_guest_action_t *)desc-&gt;action;//</p>
<p> if (!(desc-&gt;flags &amp; IRQF_GUEST_BOUND)) {</p>
<div>  if (desc-&gt;action != NULL ) {</div>
<div>   printk(&#8220;Cannot bind IRQ %d to guest. In use by %s.\n&#8221;,(int)irq, desc-&gt;action-&gt;name);</div>
<div>   rc = -EBUSY;</div>
<div>   goto out;</div>
<div>  }//<strong>:</strong></div>
<p>  action = xmalloc(irq_guest_action_t);</p>
<div>  if ((desc-&gt;action = (struct irqaction *)action) == NULL ) {</div>
<div>   printk(&#8220;Cannot bind IRQ %d to guest. Out of memory.\n&#8221;, irq);</div>
<div>   rc = -ENOMEM;</div>
<div>   goto out;</div>
<div>  }</div>
<p>  action-&gt;shareable = 1;</p>
<div>  action-&gt;nr_guests = 0;</div>
<div>  action-&gt;in_flight = 0;</div>
<div>  action-&gt;ack_type  = pirq_ack_type(irq);</div>
<p>  desc-&gt;disable_depth = 0;</p>
<div>  desc-&gt;flags |= IRQF_GUEST_BOUND;</div>
<div>  if(will_share) {</div>
<div>   desc-&gt;flags |= IRQF_SHARABLE;</div>
<div>  }</div>
<p>  desc-&gt;chip-&gt;unmask(irq);</p>
<p> } else if ( !will_share || !action-&gt;shareable ) {</p>
<div>  printk(&#8220;Cannot bind IRQ %d to guest. Will not share with others.\n&#8221;, irq);</div>
<div>  rc = -EBUSY;</div>
<div>  goto out;</div>
<div> }</div>
<p> if ( action-&gt;nr_guests == IRQ_MAX_GUESTS ) {</p>
<div>  printk(&#8220;Cannot bind IRQ %d to guest. Already at max share.\n&#8221;, irq);</div>
<div>  rc = -EBUSY;</div>
<div>  goto out;</div>
<div> }</div>
<p> action-&gt;guest[action-&gt;nr_guests++] = v-&gt;domain;</p>
<p>out:</p>
<p> spin_unlock_irqrestore(&amp;desc-&gt;lock, flags);</p>
<p> return rc;</p>
<div>}</div>
<div> <strong>在上面的这个pirq_guest_bind需要注意的是action的初始化。这里的action和domu的action是两个概念。不是同一回事，一个是irqdesc,一个是evtchn_desc_t。也就是说，一个是中断描述符，另一个是事件通道描述符。当中断由下往上传输的时候，先有irqdesc工作，然后，交给DOMU的时候，evtchn_desc_t开始工作。</strong></div>
<div>我们看到，在request_irq中已经将action实例化了因此，在 pirq_guest_bind(struct vcpu *v, int irq, int will_share)中，直接更改action-&gt;guest[acton-&gt;nr_guests++]=v-&gt;domain,然后返回。</div>
<div>好了，接下来，我们会分析一个实例，来看一下，具体是怎样利用接口request_irq()申请中断.</div>
</blockquote>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/06/25/xenarm%e4%b8%ad%e6%96%ad%e7%94%b3%e8%af%b7/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>中断响应，从Embeddedxen到DOM</title>
		<link>http://www.tek-life.org/2010/06/24/%e4%b8%ad%e6%96%ad%e5%93%8d%e5%ba%94%ef%bc%8c%e4%bb%8exenarm%e5%88%b0dom/</link>
		<comments>http://www.tek-life.org/2010/06/24/%e4%b8%ad%e6%96%ad%e5%93%8d%e5%ba%94%ef%bc%8c%e4%bb%8exenarm%e5%88%b0dom/#comments</comments>
		<pubDate>Thu, 24 Jun 2010 14:04:12 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[EmbeddedXEN]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10332</guid>
		<description><![CDATA[最初的跳转： arch/arm/kernel/entry-armv.S __irq_svc: svc_entry #ifdef CONFIG_PREEMPT get_thread_info tsk ldr r8, [tsk, #TI_PREEMPT] @ get preempt count add r7, r8, #1 @ increment it str r7, [tsk, #TI_PREEMPT] #endif irq_handler &#8230;  __irq_usr: usr_entry get_thread_info tsk #ifdef CONFIG_PREEMPT ldr r8, [tsk, #TI_PREEMPT] &#8230; <a href="http://www.tek-life.org/2010/06/24/%e4%b8%ad%e6%96%ad%e5%93%8d%e5%ba%94%ef%bc%8c%e4%bb%8exenarm%e5%88%b0dom/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div>最初的跳转：<br />
arch/arm/kernel/entry-armv.S</div>
<blockquote><p>__irq_svc:<br />
svc_entry</p>
<p>#ifdef CONFIG_PREEMPT<br />
get_thread_info tsk<br />
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count<br />
add r7, r8, #1 @ increment it<br />
str r7, [tsk, #TI_PREEMPT]<br />
#endif</p>
<p><strong>irq_handler</strong></p></blockquote>
<p>&#8230; </p>
<blockquote><p>__irq_usr:<br />
usr_entry</p>
<p>get_thread_info tsk<br />
#ifdef CONFIG_PREEMPT<br />
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count<br />
add r7, r8, #1 @ increment it<br />
str r7, [tsk, #TI_PREEMPT]<br />
#endif</p>
<p><strong>irq_handler</strong></p></blockquote>
<blockquote><p>.macro irq_handler<br />
1: get_irqnr_and_base r0, r6, r5, lr<br />
movne r1, sp<br />
@<br />
@ routine called with r0 = irq number, r1 = struct pt_regs *<br />
@<br />
adrne lr, 1b<br />
bne <strong>asm_do_IRQ</strong></p></blockquote>
<div>看asm_do_IRQ:</div>
<div>xen/arch/arm/hypervisor/irq.c</div>
<blockquote><p>asmlinkage void asm_do_IRQ(unsigned int irq, struct xen_cpu_user_regs *regs)<br />
{<br />
struct irqdesc *desc;</p>
<p>if (irq &gt;= NR_IRQS) {<br />
printk(&#8220;Bad IRQ = %d\n&#8221;, irq);<br />
}</p>
<p>  /* (GCD) CONFIG_XEN_BENCHMARK<br />
   * GPIO37 irq#69 is decode from interrupt #10 in pxa_gpio_demux_handler() function<br />
   * See file xen/arch/arm/mach-pxa/irq.c<br />
   */</p>
<p>desc = get_irq_descriptor(irq);<br />
desc-&gt;<strong>handle</strong>(irq, desc, regs);<br />
}</p></blockquote>
<div>这里初始化被赋值为: <strong>level_irq_handler</strong>&#8211;&lt;-xen_init_IRQ(void)</div>
<div>void level_irq_handler(unsigned int irq, struct irqdesc *desc, struct xen_cpu_user_regs *regs)</div>
<div>{</div>
<div>irqreturn_t ret;</div>
<div>irqaction_t *action;</div>
<div>spin_lock(&amp;desc-&gt;lock);</div>
<div>desc-&gt;chip-&gt;ack(irq);</div>
<div>if(desc-&gt;flags &amp; <strong>IRQF_GUEST_BOUND</strong>) {</div>
<div><strong>handle_guest_bound_irq(irq);</strong></div>
<div>goto out_unlock;</div>
<div>}</div>
<div>if (unlikely(desc-&gt;status &amp; IRQ_IN_PROGRESS))</div>
<div>goto out_unlock;</div>
<div>desc-&gt;status &amp;= ~(IRQ_REPLAY | IRQ_WAITING);</div>
<div>action = desc-&gt;action;</div>
<div>if (unlikely(!action || (desc-&gt;status &amp; IRQ_DISABLED))) {</div>
<div>desc-&gt;status |= IRQ_PENDING;</div>
<div>goto out_unlock;</div>
<div>}</div>
<div>desc-&gt;status |= IRQ_IN_PROGRESS;</div>
<div>desc-&gt;status &amp;= ~IRQ_PENDING;</div>
<div>spin_unlock(&amp;desc-&gt;lock);</div>
<div>ret = <strong>handle_event</strong>(irq, action, regs);</div>
<div>if(!ret) {</div>
<div>printk(&#8220;Action return = %d\n&#8221;, ret);</div>
<div>}</div>
<div>spin_lock(&amp;desc-&gt;lock);</div>
<div>desc-&gt;status &amp;= ~IRQ_IN_PROGRESS;</div>
<div>if (!(desc-&gt;status &amp; IRQ_DISABLED) &amp;&amp; desc-&gt;chip-&gt;unmask)</div>
<div>desc-&gt;chip-&gt;unmask(irq);</div>
<div>out_unlock:</div>
<div>spin_unlock(&amp;desc-&gt;lock);</div>
<div>}</div>
<p>当xen执行到这里的时候,出现了分叉,如果desc的flags标志位是IRQF_GUEST_BOUND,那么就发往DOM否则，自己处理。</p>
<div>其实在xen中，自己处理的中断只有两处，一个是timer，另一个是console.</div>
<p>由于handle_event的执行流程跟linux中的差不多，就不再多叙。我们就假设，中断具有IRQF_GUEST_BOUND,那么就会执行Handle_guest_bound_irq(irq).</p>
<blockquote><p>static void handle_guest_bound_irq(unsigned int irq)<br />
{<br />
int i;<br />
struct domain           *d;<br />
struct irqdesc          *desc;<br />
irq_guest_action_t      *action;</p>
<p>desc = get_irq_descriptor(irq);</p>
<p>action = (irq_guest_action_t *)desc-&gt;action;</p>
<p>for ( i = 0; i &lt; <strong>action-&gt;nr_guests</strong>; i++ ) {//<strong>每一个申请中断的DOM都发。也就是多个DOM可以申请同一个中断</strong><br />
<strong>                                                                        </strong>//<strong>从中我们看到，XEN是向DOM发送的，而不是VCPU</strong></p>
<p>d = action-&gt;guest[i];</p>
<p>// send HID irqs to only the foreground domain.<br />
if (desc-&gt;isHIDirq &amp;&amp; d-&gt;domain_id != (domid_t)foreground_domain) {<br />
continue;<br />
}</p>
<p>if(action-&gt;ack_type == ACK_TYPE_UNMASK) {<br />
if(!test_and_set_bit(irq, (volatile unsigned long *)&amp;d-&gt;pirq_mask)) {<br />
action-&gt;in_flight++;<br />
}<br />
}</p>
<p><strong>send_guest_pirq(d, irq);</strong><br />
}<br />
}</p>
<p>在xen/common/event_channel.c中</p>
<div>void send_guest_pirq(struct domain *d, int pirq)</div>
<div>{</div>
<div>    int port = d-&gt;pirq_to_evtchn[pirq];</div>
<div>    struct evtchn *chn;</div>
<div>    ASSERT(port != 0);</div>
<div>    chn = evtchn_from_port(d, port);</div>
<div>    evtchn_set_pending(d-&gt;vcpu[<strong>chn-&gt;notify_vcpu_id</strong>], port);</div>
<div>}</div>
<p>在上面的这个send_guest_pirq中，设置Pending是向dom中的该chn的notify_vcpu_id,而不是随便制定的一个vcpu.</p>
<div>void evtchn_set_pending(struct vcpu *v, int port)</div>
<div>{</div>
<div>    struct domain *d = v-&gt;domain;</div>
<div>    shared_info_t *s = d-&gt;shared_info;</div>
<div>    /*</div>
<div>     * The following bit operations must happen in strict order.</div>
<div>     * NB. On x86, the atomic bit operations also act as memory barriers.</div>
<div>     * There is therefore sufficiently strict ordering for this architecture &#8211;</div>
<div>     * others may require explicit memory barriers.</div>
<div>     */</div>
<div>if ( test_and_set_bit(port, __shared_info_addr(d, s, evtchn_pending)) )</div>
<div>      return;</div>
<div>    if ( !test_bit        (port, __shared_info_addr(d, s, evtchn_mask)) &amp;&amp;</div>
<div>         !test_and_set_bit(port / BITS_PER_GUEST_LONG(d),</div>
<div>                           vcpu_info_addr(v, evtchn_pending_sel)) )</div>
<div>    {</div>
<div>       vcpu_mark_events_pending(v);</div>
<div>    }</div>
<div>    /* Check if some VCPU might be polling for this event. */</div>
<div>    if ( unlikely(d-&gt;is_polling) )</div>
<div>    {</div>
<div>        d-&gt;is_polling = 0;</div>
<div>        smp_mb(); /* check vcpu poll-flags /after/ clearing domain poll-flag */</div>
<div>        for_each_vcpu ( d, v )</div>
<div>        {</div>
<div>            if ( !v-&gt;is_polling )</div>
<div>                continue;</div>
<div>            v-&gt;is_polling = 0;</div>
<div>            vcpu_unblock(v);</div>
<div>        }</div>
<div>    }</div>
<div>}</div>
<div>对上面这个函数的认识还有一些问题。</div>
<div><strong>接下来，如果函数被执行了，那么相关的vcpu位被设置，然后什么时候被执行呢？</strong></div>
<div><strong><span style="color: #ff0000;">当一个时间中断来了以后，会触发SCHEDULE_SOFTIRQ.因此schedule()会被执行.这样，相关的vcpu就执行了。</span></strong></div>
<div>
<div><span>当VCPU被调度后，我们看一下DOM的中断执行顺序：</span></div>
<p><span></p>
<div>arch/arm/kernel/entry-armv-xen.S</div>
<div>__irq_usr:</div>
<div>usr_entry</div>
<div>get_thread_info tsk</div>
<div>#ifdef CONFIG_PREEMPT</div>
<div>ldr r8, [tsk, #TI_PREEMPT] @ get preempt count</div>
<div>add r7, r8, #1 @ increment it</div>
<div>str r7, [tsk, #TI_PREEMPT]</div>
<div>#endif</div>
<div><strong>irq_handler</strong></div>
<div>&#8230;</div>
<div>.align 5</div>
<div>__irq_svc:</div>
<div>svc_entry</div>
<div>#ifdef CONFIG_PREEMPT</div>
<div>get_thread_info tsk</div>
<div>ldr r8, [tsk, #TI_PREEMPT] @ get preempt count</div>
<div>add r7, r8, #1 @ increment it</div>
<div>str r7, [tsk, #TI_PREEMPT]</div>
<div>#endif</div>
<div><strong>irq_handler</strong></div>
<p><strong><br />
</strong></p>
<div>.macro irq_handler</div>
<div>mov r0, sp</div>
<div>bl <strong>evtchn_do_upcall</strong></div>
<div>.endm</div>
<p>看一下evtchn_do_upcall</p>
<p>xen_guest/core/evtchn.c</p>
<div>asmlinkage void evtchn_do_upcall(struct pt_regs *regs)</div>
<div>{</div>
<div>unsigned long  l1, l2;</div>
<div>unsigned int   l1i, l2i, port;</div>
<div>int            irq, cpu = smp_processor_id();</div>
<div>shared_info_t *s = HYPERVISOR_shared_info;</div>
<div>vcpu_info_t   *vcpu_info = &amp;s-&gt;vcpu_info[cpu];</div>
<div>retry:</div>
<div>xchg(&amp;vcpu_info-&gt;evtchn_upcall_pending,0);</div>
<div>l1 = xchg(&amp;vcpu_info-&gt;evtchn_pending_sel, 0);</div>
<div>while (l1 != 0) {</div>
<div>l1i = __ffs(l1);</div>
<div>l1 &amp;= ~(1UL &lt;&lt; l1i);</div>
<div>while ((l2 = active_evtchns(cpu, s, l1i)) != 0) {</div>
<div>l2i = __ffs(l2);</div>
<div>port = (l1i * BITS_PER_LONG) + l2i;</div>
<div>if ((irq = evtchn_to_irq[port]) != -1) {  //evtchn号转化为irq号</div>
<div>irq_enter();</div>
<div><strong>do_IRQ(irq, regs)</strong>; //guest分发中断函数</div>
<div>irq_exit();</div>
<div>}  else {</div>
<div>evtchn_device_upcall(port);</div>
<div>}</div>
<div>}</div>
<div>}</div>
<div>if(vcpu_info-&gt;evtchn_upcall_pending) {//当有多个中断的时候</div>
<div>goto retry;</div>
<div>}</div>
<div>}</div>
<p>到这里实际上已经和Linux接上轨了</p>
<div>fastcall unsigned int do_IRQ(unsigned int irq, struct pt_regs *regs)</div>
<div>{</div>
<div>struct pt_regs *old_regs = <strong>set_irq_regs(regs);</strong></div>
<div>evtchn_desc_t *desc = evtchn_desc + irq;</div>
<div>struct irqaction * action;</div>
<div>unsigned int status = 0;</div>
<div>kstat_this_cpu.irqs[irq]++;</div>
<div>spin_lock(&amp;desc-&gt;lock);</div>
<div>if (desc-&gt;handler-&gt;ack)</div>
<div>desc-&gt;handler-&gt;ack(irq);//调用pirq_ack函数&#8212;xen_guest/core/evtchn.c</div>
<div>status = desc-&gt;status &amp; ~(IRQ_REPLAY | IRQ_WAITING);</div>
<div>status |= IRQ_PENDING; /* we _want_ to handle it */</div>
<div>action = NULL;</div>
<div>if (likely(!(status &amp; (IRQ_DISABLED | IRQ_INPROGRESS)))) {</div>
<div>action = desc-&gt;action;</div>
<div>status &amp;= ~IRQ_PENDING; /* we commit to handling */</div>
<div>status |= IRQ_INPROGRESS; /* we are handling it */</div>
<div>}</div>
<div>desc-&gt;status = status;</div>
<div>if (unlikely(!action))</div>
<div>goto out;</div>
<div>/*</div>
<div>* Edge triggered interrupts need to remember</div>
<div>* pending events.</div>
<div>* This applies to any hw interrupts that allow a second</div>
<div>* instance of the same irq to arrive while we are in do_IRQ</div>
<div>* or in the handler. But the code here only handles the _second_</div>
<div>* instance of the irq, not the third or fourth. So it is mostly</div>
<div>* useful for irq hardware that does not mask cleanly in an</div>
<div>* SMP environment.</div>
<div>*/</div>
<div>for (;;) {</div>
<div>irqreturn_t ret;</div>
<div>spin_unlock(&amp;desc-&gt;lock);</div>
<div>         if (!(action-&gt;flags &amp; SA_INTERRUPT))</div>
<div>                 local_irq_enable();</div>
<div>do {</div>
<div>                 ret =<strong> action-&gt;handler(irq, action-&gt;dev_id, regs);</strong></div>
<div>                 if (ret == IRQ_HANDLED)</div>
<div>                         status |= action-&gt;flags;</div>
<div>                 action = action-&gt;next;</div>
<div>         } while (action);</div>
<div>if (status &amp; SA_SAMPLE_RANDOM)</div>
<div>add_interrupt_randomness(irq);</div>
<div>local_irq_disable();</div>
<div>spin_lock(&amp;desc-&gt;lock);</div>
<div>if (likely(!(desc-&gt;status &amp; IRQ_PENDING)))</div>
<div>break;</div>
<div>desc-&gt;status &amp;= ~IRQ_PENDING;</div>
<div>}</div>
<div>desc-&gt;status &amp;= ~IRQ_INPROGRESS;</div>
<div>out:</div>
<div>/*</div>
<div>* The -&gt;end() handler has to deal with interrupts which got</div>
<div>* disabled while the handler was running.</div>
<div>*/</div>
<div>desc-&gt;handler-&gt;end(irq);</div>
<div>spin_unlock(&amp;desc-&gt;lock);</div>
<div><strong>set_irq_regs(old_regs)</strong>;</div>
<div>return 1;</div>
<div>}</div>
<p> </p>
<p></span></p>
<div>贴图一张</div>
<div><a href="http://www.tek-life.org/wp-content/uploads/2010/06/df6sncnm_843dp3bffhk_b.jpg"><img class="alignnone size-medium wp-image-10333" title="df6sncnm_843dp3bffhk_b" src="http://www.tek-life.org/wp-content/uploads/2010/06/df6sncnm_843dp3bffhk_b-275x300.jpg" alt="" width="275" height="300" /></a></div>
</div>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/06/24/%e4%b8%ad%e6%96%ad%e5%93%8d%e5%ba%94%ef%bc%8c%e4%bb%8exenarm%e5%88%b0dom/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>embeddedxen中xen的时间中断分析</title>
		<link>http://www.tek-life.org/2010/06/22/embeddedxen%e4%b8%adxen%e7%9a%84%e6%97%b6%e9%97%b4%e4%b8%ad%e6%96%ad%e5%88%86%e6%9e%90/</link>
		<comments>http://www.tek-life.org/2010/06/22/embeddedxen%e4%b8%adxen%e7%9a%84%e6%97%b6%e9%97%b4%e4%b8%ad%e6%96%ad%e5%88%86%e6%9e%90/#comments</comments>
		<pubDate>Tue, 22 Jun 2010 09:35:39 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[EmbeddedXEN]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10327</guid>
		<description><![CDATA[需要注意的一点是，这里的timer_softirq和DOMU里面的并不一样。本文仅浅析xen里面的timer 执行顺序： irq来了以后-&#62;time_irq_handler-&#62;调用其他函数-&#62;raise_softirq(TIMER_SOFTIRQ)-&#62;timer_softirq_action-&#62;(定时器timer-&#62;fn())-&#62;raise_softirq(SCHEDULE_SOFTIRQ)-&#62;schedule() timer_init();&#8212;&#8212;open_softirq(TIMER_SOFTIRQ, timer_softirq_action); &#124; init_xen_time()//这个函数里面有很多故事 &#124; init_platform_timer(); &#124; xen_pxa_timer_init(); &#124; setup_irq(IRQ_OST0, &#38;pxa_ost0_irq);//IRQ_OST0 是26   也就是安装了IRQ，那么，看一下pxa_ost0_irq static struct irqaction pxa_ost0_irq = { .name = &#8220;ost0&#8243;, .flags = IRQF_DISABLED &#124; IRQF_TIMER &#124; IRQF_IRQPOLL, .handler = pxa_ost0_interrupt, .dev_id = &#38;ckevt_pxa_osmr0, }; 看到它的handler是pxa_ost0_interrupt &#8230; <a href="http://www.tek-life.org/2010/06/22/embeddedxen%e4%b8%adxen%e7%9a%84%e6%97%b6%e9%97%b4%e4%b8%ad%e6%96%ad%e5%88%86%e6%9e%90/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div>需要注意的一点是，这里的timer_softirq和DOMU里面的并不一样。本文仅浅析xen里面的timer</div>
<div>执行顺序：</div>
<div>irq来了以后-&gt;time_irq_handler-&gt;调用其他函数-&gt;raise_softirq(TIMER_SOFTIRQ)-&gt;timer_softirq_action-&gt;(定时器timer-&gt;fn())-&gt;raise_softirq(SCHEDULE_SOFTIRQ)-&gt;schedule()</div>
<div>timer_init();&#8212;&#8212;open_softirq(TIMER_SOFTIRQ, <strong>timer_softirq_action</strong>);</div>
<div>|</div>
<div>init_xen_time()//这个函数里面有很多故事</div>
<div>|</div>
<div>init_platform_timer();</div>
<div>|</div>
<div>xen_pxa_timer_init();</div>
<p>|</p>
<div>setup_irq(IRQ_OST0, &amp;pxa_ost0_irq);//IRQ_OST0 是26</div>
<p> </p>
<div>也就是安装了IRQ，那么，看一下pxa_ost0_irq</div>
<div>
<div>static struct irqaction pxa_ost0_irq = {</div>
<div>.name = &#8220;ost0&#8243;,</div>
<div>.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,</div>
<div>.handler = pxa_ost0_interrupt,</div>
<div>.dev_id = &amp;ckevt_pxa_osmr0,</div>
<div>};</div>
<p>看到它的handler是pxa_ost0_interrupt</p>
</div>
<div>static irqreturn_t</div>
<div>pxa_ost0_interrupt(int irq, void *dev_id, struct pt_regs *regs)</div>
<div>{</div>
<div>//struct clock_event_device *c = dev_id;</div>
<div>    //static int cnt = 0;</div>
<p> </p>
<div>/* Disarm the compare/match, signal the event. */</div>
<div>OIER &amp;= ~OIER_E0;</div>
<div>OSSR = OSSR_M0;</div>
<p> </p>
<div><strong>timer_interrupt(irq, dev_id, regs);</strong></div>
<p> </p>
<div>return IRQ_HANDLED;</div>
<div>}</div>
<div>当发生时间中断的时候，会调用timer_interrupt();</div>
<div>void timer_interrupt(int irq, void *dev_id, struct xen_cpu_user_regs *regs) {</div>
<div>ASSERT(local_irq_is_enabled());</div>
<p> </p>
<div>/* Update jiffies counter. */</div>
<div>(*(volatile unsigned long *) &amp;jiffies_64)++;</div>
<div>raise_softirq(TIMER_SOFTIRQ);</div>
<p> </p>
<div>if (&#8211;plt_overflow_jiffies == 0)</div>
<div>plt_overflow();</div>
<div>}</div>
<div>可以看到，每来一次中断，就raise_softirq();也就是响应 <strong>timer_softirq_action</strong></div>
<div><strong>我看来看一下timer_softirq_action都在做什么事情：</strong></div>
<p><strong><br />
</strong></p>
<div>static void timer_softirq_action(void)</div>
<div>{</div>
<div>    struct timer  *t, **heap;</div>
<div>    struct timers *ts;</div>
<div>    s_time_t       now;</div>
<div>    void         (*fn)(void *);</div>
<div>    void          *data;</div>
<p> </p>
<div>    ts = &amp;this_cpu(timers);</div>
<p> </p>
<div>    spin_lock_irq(&amp;ts-&gt;lock);</div>
<p> </p>
<div>    do {</div>
<div>        heap = ts-&gt;heap;</div>
<div>        now  = NOW();</div>
<p> </p>
<div>        while ( (GET_HEAP_SIZE(heap) != 0) &amp;&amp;</div>
<div>                ((t = heap[1])-&gt;expires &lt; (now + TIMER_SLOP))  )</div>
<div>        {</div>
<div>            remove_entry(heap, t);</div>
<div>            ts-&gt;running = t;</div>
<p> </p>
<div>            fn   = t-&gt;function;</div>
<div>            data = t-&gt;data;</div>
<p> </p>
<div>            spin_unlock_irq(&amp;ts-&gt;lock);</div>
<div>            (*fn)(data);</div>
<div>            spin_lock_irq(&amp;ts-&gt;lock);</div>
<p> </p>
<div>            /* Heap may have grown while the lock was released. */</div>
<div>            heap = ts-&gt;heap;</div>
<div>        }</div>
<p> </p>
<div>        ts-&gt;running = NULL;</div>
<div>    }</div>
<div>    while ( !reprogram_timer(GET_HEAP_SIZE(heap) ? heap[1]-&gt;expires : 0) );</div>
<p> </p>
<div>    spin_unlock_irq(&amp;ts-&gt;lock);</div>
<div>}</div>
<p>在一个时间中断来了以后那个ISR做的事情是(* fn)(data),即是t-&gt;function.这个function到底是哪个函数呢？&#8212;s_time_fn().为什么捏？让我们从长计议：</p>
<blockquote><p>在启动xen的时候，调用了scheduler_init();在scheduler_init()里面做的事情是init_timer<br />
for_each_cpu ( i )<br />
 {<br />
        spin_lock_init(&amp;per_cpu(schedule_data, i).schedule_lock);<br />
        init_timer(&amp;per_cpu(schedule_data, i).s_timer, s_timer_fn, NULL, i);<br />
}<br />
也就是将定时器发生是的函数给确定了，是s_timer_fn.有问题么？我们看一下init_timer的定义就可以啦：<br />
static inline void init_timer(<br />
    struct timer *timer,<br />
    void           (*function)(void *),<br />
    void            *data,<br />
    unsigned int     cpu)<br />
{<br />
    memset(timer, 0, sizeof(*timer));<br />
    timer-&gt;function = function;<br />
    timer-&gt;data     = data;<br />
    timer-&gt;cpu      = cpu;<br />
}<br />
那么s_timer_fn做什么事情了？<br />
/* The scheduler timer: force a run through the scheduler */<br />
static void s_timer_fn(void *unused)<br />
{<br />
  <strong>  raise_softirq(SCHEDULE_SOFTIRQ);</strong><br />
    perfc_incr(sched_irq);<br />
}<br />
晕啊，又将<strong>SCHEDULE_SOFTIRQ</strong>给raise了</p></blockquote>
<p>又一个软中断，让我们继续追踪一下SCHEDULE_SOFTIRQ吧：<br />
记得我们上面说过在xen启动的时候，调用了scheduler_init吧，在scheduler_init中，我们做的工作是初始化了定时器，其实在初始化定时器之前，这个函数还做了另外一件事情：</p>
<div> open_softirq(SCHEDULE_SOFTIRQ, schedule);</div>
<div>是不是有点小晕，好吧，我们把scheduler_init()全部拿出来晒一晒</div>
<div>/* Initialise the data structures. */</div>
<div>void __init scheduler_init(void)</div>
<div>{</div>
<div>    int i;</div>
<p> </p>
<div> <strong>   open_softirq(SCHEDULE_SOFTIRQ, schedule);</strong></div>
<p> </p>
<div>    for_each_cpu ( i )</div>
<div>    {</div>
<div>        spin_lock_init(&amp;per_cpu(schedule_data, i).schedule_lock);</div>
<div>   <strong>     init_timer(&amp;per_cpu(schedule_data, i).s_timer, s_timer_fn, NULL, i);</strong></div>
<div>    }</div>
<p> </p>
<div>    for ( i = 0; schedulers[i] != NULL; i++ )</div>
<div>    {</div>
<div>        ops = *schedulers[i];</div>
<div>        if ( strcmp(ops.opt_name, opt_sched) == 0 )</div>
<div>            break;</div>
<div>    }</div>
<p> </p>
<div>    if ( schedulers[i] == NULL )</div>
<div>        printk(&#8220;Could not find scheduler: %s\n&#8221;, opt_sched);</div>
<p> </p>
<div>    printk(&#8220;Using scheduler: %s (%s)\n&#8221;, ops.name, ops.opt_name);</div>
<div>    SCHED_OP(init);</div>
<div>}</div>
<div>好，现在看一下schedule都做的是啥事情：</div>
<div>/*</div>
<div> * The main function</div>
<div> * &#8211; deschedule the current domain (scheduler independent).</div>
<div> * &#8211; pick a new domain (scheduler dependent).</div>
<div> */</div>
<div>static void schedule(void)</div>
<div>{</div>
<div>    struct vcpu          *prev = current, *next = NULL;</div>
<div>    s_time_t              now = NOW();</div>
<div>    struct schedule_data *sd;</div>
<div>    struct task_slice     next_slice;</div>
<div>    s32                   r_time;     /* time for new dom to run */</div>
<p> </p>
<div>    ASSERT(!in_irq());</div>
<div>    ASSERT(this_cpu(mc_state).flags == 0);</div>
<p> </p>
<div>    perfc_incr(sched_run);</div>
<p> </p>
<div>    sd = &amp;this_cpu(schedule_data);</div>
<p> </p>
<div>    spin_lock_irq(&amp;sd-&gt;schedule_lock);</div>
<p> </p>
<div>    stop_timer(&amp;sd-&gt;s_timer);</div>
<p> </p>
<div>    /* get policy-specific decision on scheduling&#8230; */</div>
<div>    next_slice = ops.do_schedule(now);</div>
<p> </p>
<div>    r_time = next_slice.time;</div>
<div>    next = next_slice.task;</div>
<p> </p>
<div>    sd-&gt;curr = next;</div>
<p> </p>
<div>    set_timer(&amp;sd-&gt;s_timer, now + r_time);</div>
<p> </p>
<div>    if ( unlikely(prev == next) )</div>
<div>    {</div>
<p> </p>
<div>        spin_unlock_irq(&amp;sd-&gt;schedule_lock);</div>
<div>        return continue_running(prev);</div>
<div>    }</div>
<p> </p>
<div>    TRACE_2D(TRC_SCHED_SWITCH_INFPREV,</div>
<div>             prev-&gt;domain-&gt;domain_id,</div>
<div>             now &#8211; prev-&gt;runstate.state_entry_time);</div>
<div>    TRACE_3D(TRC_SCHED_SWITCH_INFNEXT,</div>
<div>             next-&gt;domain-&gt;domain_id,</div>
<div>             (next-&gt;runstate.state == RUNSTATE_runnable) ?</div>
<div>             (now &#8211; next-&gt;runstate.state_entry_time) : 0,</div>
<div>             r_time);</div>
<p> </p>
<div>    ASSERT(prev-&gt;runstate.state == RUNSTATE_running);</div>
<div>    vcpu_runstate_change(</div>
<div>        prev,</div>
<div>        (test_bit(_VPF_blocked, &amp;prev-&gt;pause_flags) ? RUNSTATE_blocked :</div>
<div>         (vcpu_runnable(prev) ? RUNSTATE_runnable : RUNSTATE_offline)),</div>
<div>        now);</div>
<p> </p>
<div>    ASSERT(next-&gt;runstate.state != RUNSTATE_running);</div>
<div>    vcpu_runstate_change(next, RUNSTATE_running, now);</div>
<p> </p>
<div>    ASSERT(!next-&gt;is_running);</div>
<div>    next-&gt;is_running = 1;</div>
<p> </p>
<div>    spin_unlock_irq(&amp;sd-&gt;schedule_lock);</div>
<p> </p>
<div>    perfc_incr(sched_ctx);</div>
<div>    stop_timer(&amp;prev-&gt;periodic_timer);</div>
<p> </p>
<div>    /* Ensure that the domain has an up-to-date time base. */</div>
<div>    update_vcpu_system_time(next);</div>
<p> </p>
<div>    vcpu_periodic_timer_work(next);</div>
<p> </p>
<div>    TRACE_4D(TRC_SCHED_SWITCH,</div>
<div>             prev-&gt;domain-&gt;domain_id, prev-&gt;vcpu_id,</div>
<div>             next-&gt;domain-&gt;domain_id, next-&gt;vcpu_id);</div>
<p> </p>
<div>   context_switch(prev, next);</div>
<div>}</div>
<p>唉，这么多东西，哥也晕了。这个调度是在调度domain还是调度vcpu还是调度一个进程呢？</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/06/22/embeddedxen%e4%b8%adxen%e7%9a%84%e6%97%b6%e9%97%b4%e4%b8%ad%e6%96%ad%e5%88%86%e6%9e%90/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>step by step 设备驱动程序学习笔记</title>
		<link>http://www.tek-life.org/2010/06/12/step-by-step/</link>
		<comments>http://www.tek-life.org/2010/06/12/step-by-step/#comments</comments>
		<pubDate>Sat, 12 Jun 2010 08:45:40 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Reading notes]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/06/12/step-by-step-%e8%ae%be%e5%a4%87%e9%a9%b1%e5%8a%a8%e7%a8%8b%e5%ba%8f%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0/</guid>
		<description><![CDATA[有个参照很好 tekkman.cublog.cn ·Linux设备驱动程序学习（1）-字符设备驱动程序 ·Linux设备驱动程序学习（0）－Hello, world！模块 ·Linux设备驱动程序学习（2）-调试技术 ·Linux设备驱动程序学习（3）-并发和竞态 ·Linux设备驱动程序学习（4）-高级字符驱动程序操作［（1）ioctl and llseek］ ·Linux设备驱动程序学习（5）-高级字符驱动程序操作［（2）阻塞型I/O和休眠］ ·Linux设备驱动程序学习（6）-高级字符驱动程序操作［（3）设备文件的访问控制］ ·Linux设备驱动程序学习（7）-内核的数据类型 ·Linux设备驱动程序学习（9）-与硬件通信 ·Linux设备驱动程序学习（8）-分配内存 ·Linux设备驱动程序学习（10）-时间、延迟及延缓操作 ·Linux设备驱动程序学习（11）-中断处理 ·Linux设备驱动程序学习（3-补）-Linux中的循环缓冲区 ·Linux设备驱动程序学习（12）-Linux设备模型（底层原理简介） ·Linux设备驱动程序学习（13）-Linux设备模型（总线、设备、驱动程序和类） ·Linux设备驱动程序学习（14）-Linux设备模型（各环节的整合） ·Linux设备驱动程序学习（15）-Linux设备模型（热插拔、mdev 与 firmware） ·Linux设备驱动程序学习（16）－USB 驱动程序（一） ·Linux设备驱动程序学习（17）－USB 驱动程序（二） ·Linux设备驱动程序学习（18）－USB 驱动程序（三） ·Linux设备驱动程序学习（19）－USB 驱动程序（四）]]></description>
			<content:encoded><![CDATA[<p>有个参照很好</p>
<p><a href="http://tekkman.cublog.cn">tekkman.cublog.cn</a></p>
<p>·<a href="http://blog.chinaunix.net/showart.php?id=404280">Linux设备驱动程序学习（1）-字符设备驱动程序</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=407202">Linux设备驱动程序学习（0）－Hello, world！模块</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=407383">Linux设备驱动程序学习（2）-调试技术 </a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=408682">Linux设备驱动程序学习（3）-并发和竞态</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=411534">Linux设备驱动程序学习（4）-高级字符驱动程序操作［（1）ioctl and llseek］</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=413183">Linux设备驱动程序学习（5）-高级字符驱动程序操作［（2）阻塞型I/O和休眠］</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=418262">Linux设备驱动程序学习（6）-高级字符驱动程序操作［（3）设备文件的访问控制］</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=418515">Linux设备驱动程序学习（7）-内核的数据类型</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=422428">Linux设备驱动程序学习（9）-与硬件通信 </a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=428673">Linux设备驱动程序学习（8）-分配内存</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=432687">Linux设备驱动程序学习（10）-时间、延迟及延缓操作</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=432711">Linux设备驱动程序学习（11）-中断处理 </a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=439308">Linux设备驱动程序学习（3-补）-Linux中的循环缓冲区</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=445477">Linux设备驱动程序学习（12）-Linux设备模型（底层原理简介）</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=451196">Linux设备驱动程序学习（13）-Linux设备模型（总线、设备、驱动程序和类）</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=454889">Linux设备驱动程序学习（14）-Linux设备模型（各环节的整合）</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=460882">Linux设备驱动程序学习（15）-Linux设备模型（热插拔、mdev 与 firmware）</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=1980426">Linux设备驱动程序学习（16）－USB 驱动程序（一）</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=1995182">Linux设备驱动程序学习（17）－USB 驱动程序（二）</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=1995569">Linux设备驱动程序学习（18）－USB 驱动程序（三）</a><br />
·<a href="http://blog.chinaunix.net/showart.php?id=1997419">Linux设备驱动程序学习（19）－USB 驱动程序（四）</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/06/12/step-by-step/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>内联汇编笔记</title>
		<link>http://www.tek-life.org/2010/06/12/%e5%86%85%e8%81%94%e6%b1%87%e7%bc%96%e7%ac%94%e8%ae%b0/</link>
		<comments>http://www.tek-life.org/2010/06/12/%e5%86%85%e8%81%94%e6%b1%87%e7%bc%96%e7%ac%94%e8%ae%b0/#comments</comments>
		<pubDate>Sat, 12 Jun 2010 04:35:11 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/06/12/%e5%86%85%e8%81%94%e6%b1%87%e7%bc%96%e7%ac%94%e8%ae%b0/</guid>
		<description><![CDATA[重要的一条：其实内联汇编输入部分输出部分相当于函数的参数而已。 __asm__ __volatile__(&#34;Instruction List&#34;:output:input:Modify); 在output 部分 &#34;=register&#34;(a) :&#160;&#160;&#160;&#160;&#160;&#160;&#160; a=register 在input部分 “=register”(a) :&#160;&#160;&#160;&#160;&#160;&#160;&#160; register=a 我觉得这是在output部分和input部分的很大的一个区别了。强调这个是因为很容易被混淆。 另外，无论是Output,还是input是没有执行顺序的。我觉得这就完全相当于一条指令，并且没有优先级的指令，不像函数入栈那样有个先后顺序。我为什么这样觉得呢？因为从output开始从0被编号，第一个变量被定为0，就想enum那样。然后在Instruction List中就可以%0，这样引用了。 在output部分，还有一点需要注意，就是“+register”(a)的情况，那么这样子的话，在Instruction List操作之前，先会有一个赋值的操作，就像Input部分那样 register=a. 另外，在gcc汇编c文件的时候，Instruction List 部分会被 APP &#8230;. NO APP 标识.我觉得，这个也很好，并且可以帮助我们看到__asm__ ()里面的执行顺序。虽然我老是说，他们是没有执行顺序的，但是这是两码事。其中的细节，需要细细探究。 还有一些东西，不想再整理了，一起放到google docs了。 http://docs.google.com/Doc?docid=0ASKC30p2j_DDZGY2c25jbm1fODI1aHI0N3ZyZGc&#38;hl=zh_CN]]></description>
			<content:encoded><![CDATA[<p><strong>重要的一条：其实内联汇编输入部分输出部分相当于函数的参数而已。</strong></p>
<p>__asm__ __volatile__(&quot;Instruction List&quot;:output:input:Modify);   <br />在output 部分    <br />&quot;=register&quot;(a) :&#160;&#160;&#160;&#160;&#160;&#160;&#160; a=register    <br />在input部分    <br />“=register”(a) :&#160;&#160;&#160;&#160;&#160;&#160;&#160; register=a    <br />我觉得这是在output部分和input部分的很大的一个区别了。强调这个是因为很容易被混淆。    <br />另外，无论是Output,还是input是没有执行顺序的。我觉得这就完全相当于一条指令，并且没有优先级的指令，不像函数入栈那样有个先后顺序。我为什么这样觉得呢？因为从output开始从0被编号，第一个变量被定为0，就想enum那样。然后在Instruction List中就可以%0，这样引用了。    <br />在output部分，还有一点需要注意，就是“+register”(a)的情况，那么这样子的话，在Instruction List操作之前，先会有一个赋值的操作，就像Input部分那样 register=a.    <br />另外，在gcc汇编c文件的时候，Instruction List 部分会被    <br />APP    <br />&#8230;.    <br />NO APP    <br />标识.我觉得，这个也很好，并且可以帮助我们看到__asm__ ()里面的执行顺序。虽然我老是说，他们是没有执行顺序的，但是这是两码事。其中的细节，需要细细探究。</p>
<p>还有一些东西，不想再整理了，一起放到google docs了。</p>
<p><a title="http://docs.google.com/Doc?docid=0ASKC30p2j_DDZGY2c25jbm1fODI1aHI0N3ZyZGc&amp;hl=zh_CN" href="http://docs.google.com/Doc?docid=0ASKC30p2j_DDZGY2c25jbm1fODI1aHI0N3ZyZGc&amp;hl=zh_CN">http://docs.google.com/Doc?docid=0ASKC30p2j_DDZGY2c25jbm1fODI1aHI0N3ZyZGc&amp;hl=zh_CN</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/06/12/%e5%86%85%e8%81%94%e6%b1%87%e7%bc%96%e7%ac%94%e8%ae%b0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>设备控制器，设备驱动程序,文件系统</title>
		<link>http://www.tek-life.org/2010/06/01/%e8%ae%be%e5%a4%87%e6%8e%a7%e5%88%b6%e5%99%a8%ef%bc%8c%e8%ae%be%e5%a4%87%e9%a9%b1%e5%8a%a8%e7%a8%8b%e5%ba%8f%e6%96%87%e4%bb%b6%e7%b3%bb%e7%bb%9f/</link>
		<comments>http://www.tek-life.org/2010/06/01/%e8%ae%be%e5%a4%87%e6%8e%a7%e5%88%b6%e5%99%a8%ef%bc%8c%e8%ae%be%e5%a4%87%e9%a9%b1%e5%8a%a8%e7%a8%8b%e5%ba%8f%e6%96%87%e4%bb%b6%e7%b3%bb%e7%bb%9f/#comments</comments>
		<pubDate>Tue, 01 Jun 2010 03:39:43 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Reading notes]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/06/01/%e8%ae%be%e5%a4%87%e6%8e%a7%e5%88%b6%e5%99%a8%ef%bc%8c%e8%ae%be%e5%a4%87%e9%a9%b1%e5%8a%a8%e7%a8%8b%e5%ba%8f%e6%96%87%e4%bb%b6%e7%b3%bb%e7%bb%9f/</guid>
		<description><![CDATA[首先当用户进程发出输入输出时，系统把请求处理的权限放在文件系统，文件系统通过驱动程序提供的接口将任务下放到驱动程序，驱动程序根据需要对设备控制器进行操作，设备控制器再去控制设备本身。 设备控制器对设备本身的控制是电器工程师所关心的事情. 设备驱动程序是一组相关函数的集合。它包含设备服务子程序和中断处理程序。设备服务子程序包含了所有与设备相关的代码，每个设备服务子程序只处理一种设备或者紧密相关的设备。其功能就是从与设备无关的软件中接受抽象的命令并执行之。当执行一条请求时，具体操作是根据控制器对驱动程序提供的接口（指的是控制器中的各种寄存器），并利用中断机制去调用中断服务子程序配合设备来完成这个请求。 设备驱动程序利用结构 file_operations 与文件系统联系起来，即设备的各种操作的入口函数存在file_operation中。对于特定的设备来说有一些操作是不必要的,其入口置为NULL。]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.tek-life.org/wp-content/uploads/2010/06/image002.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image002" border="0" alt="image002" src="http://www.tek-life.org/wp-content/uploads/2010/06/image002_thumb.jpg" width="130" height="244" /></a> </p>
<p>首先当用户进程发出输入输出时，系统把请求处理的权限放在文件系统，文件系统通过驱动程序提供的接口将任务下放到驱动程序，驱动程序根据需要对设备控制器进行操作，设备控制器再去控制设备本身。</p>
<p><strong>设备控制器</strong>对设备本身的控制是电器工程师所关心的事情.</p>
<p><strong>设备驱动程序</strong>是一组相关函数的集合。它包含设备服务子程序和中断处理程序。设备服务子程序包含了所有与设备相关的代码，每个设备服务子程序只处理一种设备或者紧密相关的设备。其功能就是从与设备无关的软件中接受抽象的命令并执行之。当执行一条请求时，具体操作是根据控制器对驱动程序提供的接口（指的是控制器中的各种寄存器），并利用中断机制去调用中断服务子程序配合设备来完成这个请求。</p>
<p>设备驱动程序利用结构 file_operations 与<strong>文件系统</strong>联系起来，即设备的各种操作的入口函数存在file_operation中。对于特定的设备来说有一些操作是不必要的,其入口置为NULL。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/06/01/%e8%ae%be%e5%a4%87%e6%8e%a7%e5%88%b6%e5%99%a8%ef%bc%8c%e8%ae%be%e5%a4%87%e9%a9%b1%e5%8a%a8%e7%a8%8b%e5%ba%8f%e6%96%87%e4%bb%b6%e7%b3%bb%e7%bb%9f/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>About my Grub.cfg</title>
		<link>http://www.tek-life.org/2010/06/01/about-my-grub-cfg/</link>
		<comments>http://www.tek-life.org/2010/06/01/about-my-grub-cfg/#comments</comments>
		<pubDate>Tue, 01 Jun 2010 02:32:06 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>
		<category><![CDATA[grub]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10314</guid>
		<description><![CDATA[装了双系统。直接默认进入Linux。没有显示Grub启动菜单。 检查了以下/boot/grub目录下，是有grub.cfg的. 在grub.cfg文件中，添加了 menuentry &#8220;win7&#8243; { set root=(hd0,1) chainloader +1 } 并且，改了set default=&#8221;5&#8243; 可是仍然进入的是LINUX。 重启后，一直按ESC，看到GRUB的启动界面。 按C，手工输入 set root=(hd0,1) chainloader +1 boot 能够进入WIN 7. 重新启动进入linux。 输入 grub-mkconfig. 看到grub.cfg自动加载了win7 . 终于OK了。 原来有一个grub-update。现在变成了grub-mkconfig.]]></description>
			<content:encoded><![CDATA[<p>装了双系统。直接默认进入Linux。没有显示Grub启动菜单。<br />
检查了以下/boot/grub目录下，是有grub.cfg的.<br />
在grub.cfg文件中，添加了<br />
menuentry &#8220;win7&#8243; {<br />
set root=(hd0,1)<br />
chainloader +1<br />
}</p>
<p>并且，改了set default=&#8221;5&#8243;</p>
<p>可是仍然进入的是LINUX。</p>
<p>重启后，一直按ESC，看到GRUB的启动界面。</p>
<p>按C，手工输入</p>
<p>set root=(hd0,1)</p>
<p>chainloader +1</p>
<p>boot</p>
<p>能够进入WIN 7.</p>
<p>重新启动进入linux。</p>
<p>输入 grub-mkconfig.</p>
<p>看到grub.cfg自动加载了win7 .</p>
<p>终于OK了。</p>
<p>原来有一个grub-update。现在变成了grub-mkconfig.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/06/01/about-my-grub-cfg/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>shell-find备忘</title>
		<link>http://www.tek-life.org/2010/05/31/shell-find%e5%a4%87%e5%bf%98/</link>
		<comments>http://www.tek-life.org/2010/05/31/shell-find%e5%a4%87%e5%bf%98/#comments</comments>
		<pubDate>Mon, 31 May 2010 09:41:12 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>
		<category><![CDATA[shell]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/31/shell-find%e5%a4%87%e5%bf%98/</guid>
		<description><![CDATA[find: 在find中用正则表达式： find –name “[A-Z]*” ;寻找大写字母开头的文件 过滤掉特殊路径 find /&#160; –path “/proc” –prune&#160; -o –name “abc” –print ;/proc下面的不读 find /&#160;&#160; \(–path&#160; /proc –o -path /dev \)&#160; -prune –o –name “abc” &#8211; print; /proc和/dev下面的目录都无视 在这里 –o 是or的意思，-a是and的意思。 另外，还用到的有 –perm –mtime –type –user&#160;&#160; &#8230; <a href="http://www.tek-life.org/2010/05/31/shell-find%e5%a4%87%e5%bf%98/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>find:</p>
<p>在find中用正则表达式：</p>
<blockquote><p>find –name “[A-Z]*” ;寻找大写字母开头的文件</p>
</blockquote>
<p>过滤掉特殊路径</p>
<blockquote><p>find /&#160; –path “/proc” –prune&#160; -o –name “abc” –print ;/proc下面的不读     <br />find /&#160;&#160; \(–path&#160; /proc –o -path /dev \)&#160; -prune –o –name “abc” &#8211; print; /proc和/dev下面的目录都无视</p>
</blockquote>
<blockquote><p>在这里 –o 是or的意思，-a是and的意思。</p>
</blockquote>
<p>另外，还用到的有 –perm –mtime –type –user&#160;&#160; -newer在各个参数前面加个！就是去反的意思。</p>
<blockquote><p><font style="background-color: #ffffff">find . ！-type d&#160; -print&#160; ;输出当前目录下非目录类型文件</font></p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/31/shell-find%e5%a4%87%e5%bf%98/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从外到内，看中断处理</title>
		<link>http://www.tek-life.org/2010/05/30/interrubt-process/</link>
		<comments>http://www.tek-life.org/2010/05/30/interrubt-process/#comments</comments>
		<pubDate>Sun, 30 May 2010 14:33:14 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[Linux Kernel]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10308</guid>
		<description><![CDATA[仍旧以s3c2410来看。中断来了以后,找到一个表项，假如找到了vector_irq。 /home/sda3/linux-2.6.21.1/arch/arm/kernel/entry-armv.S .globl __vectors_start __vectors_start: swi SYS_ERROR0 b vector_und + stubs_offset ldr pc, .LCvswi + stubs_offset b vector_pabt + stubs_offset b vector_dabt + stubs_offset b vector_addrexcptn + stubs_offset b vector_irq + stubs_offset b vector_fiq + stubs_offset .globl __vectors_end __vectors_end: 即，执行了b &#8230; <a href="http://www.tek-life.org/2010/05/30/interrubt-process/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>仍旧以s3c2410来看。中断来了以后,找到一个表项，假如找到了vector_irq。<br />
 /home/sda3/linux-2.6.21.1/arch/arm/kernel/entry-armv.S </p>
<pre class="brush:c">
    .globl  __vectors_start
__vectors_start:
    swi SYS_ERROR0
    b   vector_und + stubs_offset
    ldr pc, .LCvswi + stubs_offset
    b   vector_pabt + stubs_offset
    b   vector_dabt + stubs_offset
    b   vector_addrexcptn + stubs_offset
    b   vector_irq + stubs_offset
    b   vector_fiq + stubs_offset

    .globl  __vectors_end
__vectors_end:
</pre>
<p>即，执行了b vector_irq + sbus_offset<br />
vector_irq是什么东东呢？<br />
我们在该文件中，可以看到一个宏：</p>
<pre class="brush:c">
    .macro  vector_stub, name, mode, correction=0
    .align  5

vector_\name:
    .if \correction
    sub lr, lr, #\correction
    .endif

    @
    @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
    @ (parent CPSR)
    @
    stmia   sp, {r0, lr}        @ save r0, lr
    mrs lr, spsr
    str lr, [sp, #8]        @ save spsr

    @
    @ Prepare for SVC32 mode.  IRQs remain disabled.
    @
    mrs r0, cpsr
    eor r0, r0, #(\mode ^ SVC_MODE)
    msr spsr_cxsf, r0

    @
    @ the branch table must immediately follow this code
    @
    and lr, lr, #0x0f
    mov r0, sp
    ldr lr, [pc, lr, lsl #2]
    movs    pc, lr          @ branch to handler in SVC mode
    .endm
</pre>
<p>即，程序会跳转到这里玩一会儿，然后去哪里呢？我们看到movs pc,lr 也就是通过改变pc的值来进行跳转，那么依据是什么捏？该句上面为ldr lr,[pc,lr,lsl #2]也即是根据lr的值然后逻辑左移2位。lr是什么，lr在上面的语句 and lr,lr,#0x0f与了一下，其实是取其mode.假如，我们得到的值是0，则：</p>
<pre class="brush:c">
    .globl  __stubs_start
__stubs_start:
/*
 * Interrupt dispatcher
 */
    vector_stub irq, IRQ_MODE, 4

    .long   __irq_usr           @  0  (USR_26 / USR_32)
    .long   __irq_invalid           @  1  (FIQ_26 / FIQ_32)
    .long   __irq_invalid           @  2  (IRQ_26 / IRQ_32)
    .long   __irq_svc           @  3  (SVC_26 / SVC_32)
    .long   __irq_invalid           @  4
    .long   __irq_invalid           @  5
    .long   __irq_invalid           @  6
    .long   __irq_invalid           @  7
    .long   __irq_invalid           @  8
    .long   __irq_invalid           @  9
    .long   __irq_invalid           @  a
    .long   __irq_invalid           @  b
    .long   __irq_invalid           @  c
    .long   __irq_invalid           @  d
    .long   __irq_invalid           @  e
    .long   __irq_invalid           @  f
</pre>
<p>我们会到__irq_usr里面去处理。__irq_usr在哪里捏，我们就用cscope的神功去ctrl + ]去寻找。<br />
 /home/sda3/linux-2.6.21.1/arch/arm/kernel/entry-armv.S   </p>
<pre class="brush:c">
__irq_usr:
    usr_entry

#ifdef CONFIG_TRACE_IRQFLAGS
    bl  trace_hardirqs_off
#endif
    get_thread_info tsk
#ifdef CONFIG_PREEMPT
    ldr r8, [tsk, #TI_PREEMPT]      @ get preempt count
    add r7, r8, #1          @ increment it
    str r7, [tsk, #TI_PREEMPT]
#endif

    irq_handler
#ifdef CONFIG_PREEMPT
    ldr r0, [tsk, #TI_PREEMPT]
    str r8, [tsk, #TI_PREEMPT]
    teq r0, r7
    strne   r0, [r0, -r0]
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
    bl  trace_hardirqs_on
#endif

    mov why, #0
    b   ret_to_user

    .ltorg
</pre>
<p>在__irq_usr里面进行一番云雨之后，跳转到了__irq_handler.我们继续追踪：<br />
在同一文件中：</p>
<pre class="brush:c">
    .macro  irq_handler
    get_irqnr_preamble r5, lr
1:  get_irqnr_and_base r0, r6, r5, lr
    movne   r1, sp
    @
    @ routine called with r0 = irq number, r1 = struct pt_regs *
    @
    adrne   lr, 1b
    bne asm_do_IRQ

#ifdef CONFIG_SMP
    /*
     * XXX
     *
     * this macro assumes that irqstat (r6) and base (r5) are
     * preserved from get_irqnr_and_base above
     */
    test_for_ipi r0, r6, r5, lr
    movne   r0, sp
    adrne   lr, 1b
    bne do_IPI

#ifdef CONFIG_LOCAL_TIMERS
    test_for_ltirq r0, r6, r5, lr
    movne   r0, sp
    adrne   lr, 1b
    bne do_local_timer
#endif
#endif

    .endm
</pre>
<p>看到了木有，调用了asm_do_irq.到这里，基本上，我们就可以出一口气了，毕竟arm_do_irq的语义已经很明显了嘛。可是，且慢。让我们看看arm_do_irq到底干了什么事情：<br />
 /home/sda3/linux-2.6.21.1/arch/arm/kernel/irq.c</p>
<pre class="brush:c">
asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
    struct irq_desc *desc = irq_desc + irq;                                                                   

    /*
     * Some hardware gives randomly wrong interrupts.  Rather
     * than crashing, do something sensible.
     */
    if (irq >= NR_IRQS)
        desc = &#038;bad_irq_desc;                                                                                    

    irq_enter();                                                                                                

    desc_handle_irq(irq, desc);                                                                                 

    /* AT91 specific workaround */
    irq_finish(irq);                                                                                              

    irq_exit();
    set_irq_regs(old_regs);
}
</pre>
<p>在上面的这个函数中，我们要注意两个东西:其一,desc_handle_irq(irq,desc).其二，set_irq_regs(old_regs).即处理完成后，要将regs复原。<br />
让我们看看desc_handle_irq吧。</p>
<pre class="brush:c">
static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
    desc->handle_irq(irq, desc);
}
</pre>
<p>这里，我们就不能随便追踪啦。因为要和desc有关系啦，也就是中断描述符有关系。我们先看一下struct irq_desc的结构吧：</p>
<pre class="brush:c">
struct irq_desc {
    irq_flow_handler_t  handle_irq;
    struct irq_chip     *chip;
    struct msi_desc     *msi_desc;
    void            *handler_data;
    void            *chip_data;
    struct irqaction    *action;    /* IRQ action list */
    unsigned int        status;     /* IRQ status */                                                     

    unsigned int        depth;      /* nested irq disables */
    unsigned int        wake_depth; /* nested wake enables */
    unsigned int        irq_count;  /* For detecting broken IRQs */
    unsigned int        irqs_unhandled;
    spinlock_t      lock;
#ifdef CONFIG_SMP
    cpumask_t       affinity;
    unsigned int        cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
    cpumask_t       pending_mask;
#endif
#ifdef CONFIG_PROC_FS
    struct proc_dir_entry   *dir;
#endif
    const char      *name;
} ____cacheline_aligned;
</pre>
<p>从结构体来看，handler是irq的处理函数。还记得初始化irq的时候么？init_IRQ->init_arch_irq 而init_arch_irq因平台不同而不同。在setup_arch()中，<br />
 /home/sda3/linux-2.6.21.1/arch/arm/kernel/setup.c  </p>
<pre class="brush:c">
    /*
     * Set up various architecture-specific pointers
     */
    init_arch_irq = mdesc->init_irq;
    system_timer = mdesc->timer;
    init_machine = mdesc->init_machine;
</pre>
<p>s3c2410平台下，我们姑且看一种init_arch_irq吧：</p>
<pre class="brush:c">
MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch
                    * to SMDK2410 */
    /* Maintainer: Jonas Dietsche */
    .phys_io    = S3C2410_PA_UART,
    .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) &#038; 0xfffc,
    .boot_params    = S3C2410_SDRAM_PA + 0x100,
    .map_io     = smdk2410_map_io,
    .init_irq   = s3c24xx_init_irq,
    .init_machine   = smdk_machine_init,
    .timer      = &#038;s3c24xx_timer,
MACHINE_END
</pre>
<p>也就是其init_irq为s3c24xx_init_irq。我们就去找一下：cs f s s3c24xx_init_irq</p>
<pre class="brush:c">
void __init s3c24xx_init_irq(void)
{
    unsigned long pend;
    unsigned long last;
    int irqno;
    int i;

    irqdbf("s3c2410_init_irq: clearing interrupt status flags\n");

    /* first, clear all interrupts pending... */

    last = 0;
    for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C24XX_EINTPEND);

        if (pend == 0 || pend == last)
            break;

        __raw_writel(pend, S3C24XX_EINTPEND);
        printk("irq: clearing pending ext status %08x\n", (int)pend);
        last = pend;
    }

    last = 0;
    for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C2410_INTPND);

        if (pend == 0 || pend == last)
            break;

        __raw_writel(pend, S3C2410_SRCPND);
        __raw_writel(pend, S3C2410_INTPND);
        printk("irq: clearing pending status %08x\n", (int)pend);
        last = pend;
    }

    last = 0;
    for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C2410_SUBSRCPND);

        if (pend == 0 || pend == last)
            break;
}
    last = 0;
    for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C2410_SUBSRCPND);

        if (pend == 0 || pend == last)
            break;

        printk("irq: clearing subpending status %08x\n", (int)pend);
        __raw_writel(pend, S3C2410_SUBSRCPND);
        last = pend;
    }

    /* register the main interrupts */

    irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n");

    for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {
        /* set all the s3c2410 internal irqs */

        switch (irqno) {
            /* deal with the special IRQs (cascaded) */

        case IRQ_EINT4t7:
        case IRQ_EINT8t23:
        case IRQ_UART0:
        case IRQ_UART1:
        case IRQ_UART2:
        case IRQ_ADCPARENT:
            set_irq_chip(irqno, &#038;s3c_irq_level_chip);
            set_irq_handler(irqno, handle_level_irq);
            break;

        case IRQ_RESERVED6:
        case IRQ_RESERVED24:
            /* no IRQ here */
            break;

        default:
            //irqdbf("registering irq %d (s3c irq)\n", irqno);
            set_irq_chip(irqno, &#038;s3c_irq_chip);
            set_irq_handler(irqno, handle_edge_irq);
            set_irq_flags(irqno, IRQF_VALID);
        }
    }

    /* setup the cascade irq handlers */

    set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
    set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);

    set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
    set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
    set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
    set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);

    /* external interrupts */

    for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
        irqdbf("registering irq %d (ext int)\n", irqno);
        set_irq_chip(irqno, &#038;s3c_irq_eint0t4);
        set_irq_handler(irqno, handle_edge_irq);
        set_irq_flags(irqno, IRQF_VALID);
    }

    for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
        irqdbf("registering irq %d (extended s3c irq)\n", irqno);
        set_irq_chip(irqno, &#038;s3c_irqext_chip);
        set_irq_handler(irqno, handle_edge_irq);
        set_irq_flags(irqno, IRQF_VALID);
    }

    /* register the uart interrupts */

    irqdbf("s3c2410: registering external interrupts\n");

    irqdbf("s3c2410: registering external interrupts\n");

    for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) {
        irqdbf("registering irq %d (s3c uart0 irq)\n", irqno);
        set_irq_chip(irqno, &#038;s3c_irq_uart0);
        set_irq_handler(irqno, handle_level_irq);
        set_irq_flags(irqno, IRQF_VALID);
    }

    for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) {
        irqdbf("registering irq %d (s3c uart1 irq)\n", irqno);
        set_irq_chip(irqno, &#038;s3c_irq_uart1);
        set_irq_handler(irqno, handle_level_irq);
        set_irq_flags(irqno, IRQF_VALID);
    }

    for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) {
        irqdbf("registering irq %d (s3c uart2 irq)\n", irqno);
        set_irq_chip(irqno, &#038;s3c_irq_uart2);
        set_irq_handler(irqno, handle_level_irq);
        set_irq_flags(irqno, IRQF_VALID);
    }

    for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) {
        irqdbf("registering irq %d (s3c adc irq)\n", irqno);
        set_irq_chip(irqno, &#038;s3c_irq_adc);
        set_irq_handler(irqno, handle_edge_irq);
        set_irq_flags(irqno, IRQF_VALID);
    }

    irqdbf("s3c2410: registered interrupt handlers\n");
}
</pre>
<p>在这里我们的默认irq_handler为handler_edge_irq\handle_level_irq.我们姑且看一下handler_edge_irq吧：</p>
<pre class="brush:c">
void fastcall
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
    unsigned int cpu = smp_processor_id();
    struct irqaction *action;
    irqreturn_t action_ret;

    spin_lock(&#038;desc->lock);
    mask_ack_irq(desc, irq);

    if (unlikely(desc->status &#038; IRQ_INPROGRESS))
        goto out_unlock;
    desc->status &#038;= ~(IRQ_REPLAY | IRQ_WAITING);
    kstat_cpu(cpu).irqs[irq]++;

    /*
     * If its disabled or no action available
     * keep it masked and get out of here
     */
    action = desc->action;
    if (unlikely(!action || (desc->status &#038; IRQ_DISABLED))) {
        desc->status |= IRQ_PENDING;
        goto out_unlock;
    }

    desc->status |= IRQ_INPROGRESS;
    desc->status &#038;= ~IRQ_PENDING;
    spin_unlock(&#038;desc->lock);

    action_ret = handle_IRQ_event(irq, action);
    if (!noirqdebug)
        note_interrupt(irq, desc, action_ret);

    spin_lock(&#038;desc->lock);
    desc->status &#038;= ~IRQ_INPROGRESS;
    if (!(desc->status &#038; IRQ_DISABLED) &#038;&#038; desc->chip->unmask)
        desc->chip->unmask(irq);
out_unlock:
    spin_unlock(&#038;desc->lock);
}
</pre>
<p>我们看到，会调用handle_IRQ_event(irq, action);继续追踪之</p>
<pre class="brush:c">
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
    irqreturn_t ret, retval = IRQ_NONE;
    unsigned int status = 0;

    handle_dynamic_tick(action);

    if (!(action->flags &#038; IRQF_DISABLED))
        local_irq_enable_in_hardirq();

    do {
        ret = action->handler(irq, action->dev_id);
        if (ret == IRQ_HANDLED)
            status |= action->flags;
        retval |= ret;
        action = action->next;
    } while (action);

    if (status &#038; IRQF_SAMPLE_RANDOM)
        add_interrupt_randomness(irq);
    local_irq_disable();

    return retval;
}
</pre>
<p>我们看到 handle_IRQ_event会有一个do{}while()循环，依次处理该irq线上的action。好，就是这样啦。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/30/interrubt-process/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从下到上分析中断处理</title>
		<link>http://www.tek-life.org/2010/05/30/%e4%bb%8e%e4%b8%8b%e5%88%b0%e4%b8%8a%e5%88%86%e6%9e%90%e4%b8%ad%e6%96%ad%e5%a4%84%e7%90%86/</link>
		<comments>http://www.tek-life.org/2010/05/30/%e4%bb%8e%e4%b8%8b%e5%88%b0%e4%b8%8a%e5%88%86%e6%9e%90%e4%b8%ad%e6%96%ad%e5%a4%84%e7%90%86/#comments</comments>
		<pubDate>Sun, 30 May 2010 03:34:51 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[Linux Kernel]]></category>
		<category><![CDATA[interrupt]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10300</guid>
		<description><![CDATA[S3C2410中断的定义的中断向量是从16～47.然后外部中断从48~69.70~80.一共有66(80-16)个中断其中70～78 UART是用来与PC进行通信的。 25 #define S3C2410_CPUIRQ_OFFSET (16) 26 27 #define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET) 28 29 /* main cpu interrupts */ 30 #define IRQ_EINT0 S3C2410_IRQ(0) /* 16 */ 31 #define IRQ_EINT1 S3C2410_IRQ(1) 32 #define IRQ_EINT2 S3C2410_IRQ(2) 33 #define IRQ_EINT3 S3C2410_IRQ(3) 34 &#8230; <a href="http://www.tek-life.org/2010/05/30/%e4%bb%8e%e4%b8%8b%e5%88%b0%e4%b8%8a%e5%88%86%e6%9e%90%e4%b8%ad%e6%96%ad%e5%a4%84%e7%90%86/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>S3C2410中断的定义的中断向量是从16～47.然后外部中断从48~69.70~80.一共有66(80-16)个中断其中70～78 UART是用来与PC进行通信的。</p>
<pre class="brush:c">
 25 #define S3C2410_CPUIRQ_OFFSET    (16)
 26
 27 #define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET)
 28
 29 /* main cpu interrupts */
 30 #define IRQ_EINT0      S3C2410_IRQ(0)       /* 16 */
 31 #define IRQ_EINT1      S3C2410_IRQ(1)
 32 #define IRQ_EINT2      S3C2410_IRQ(2)
 33 #define IRQ_EINT3      S3C2410_IRQ(3)
 34 #define IRQ_EINT4t7    S3C2410_IRQ(4)       /* 20 */
 35 #define IRQ_EINT8t23   S3C2410_IRQ(5)
 36 #define IRQ_RESERVED6  S3C2410_IRQ(6)       /* for s3c2410 */
 37 #define IRQ_CAM        S3C2410_IRQ(6)       /* for s3c2440,s3c2443 */
 38 #define IRQ_BATT_FLT   S3C2410_IRQ(7)
 39 #define IRQ_TICK       S3C2410_IRQ(8)       /* 24 */
 40 #define IRQ_WDT        S3C2410_IRQ(9)       /* WDT/AC97 for s3c2443 */
 41 #define IRQ_TIMER0     S3C2410_IRQ(10)
 42 #define IRQ_TIMER1     S3C2410_IRQ(11)
 43 #define IRQ_TIMER2     S3C2410_IRQ(12)
 44 #define IRQ_TIMER3     S3C2410_IRQ(13)
 45 #define IRQ_TIMER4     S3C2410_IRQ(14)
 46 #define IRQ_UART2      S3C2410_IRQ(15)
 47 #define IRQ_LCD        S3C2410_IRQ(16)      /* 32 */
 48 #define IRQ_DMA0       S3C2410_IRQ(17)      /* IRQ_DMA for s3c2443 */
 49 #define IRQ_DMA1       S3C2410_IRQ(18)
 50 #define IRQ_DMA2       S3C2410_IRQ(19)
 51 #define IRQ_DMA3       S3C2410_IRQ(20)
 52 #define IRQ_SDI        S3C2410_IRQ(21)
 53 #define IRQ_SPI0       S3C2410_IRQ(22)
 54 #define IRQ_UART1      S3C2410_IRQ(23)
 55 #define IRQ_RESERVED24 S3C2410_IRQ(24)      /* 40 */
 56 #define IRQ_NFCON      S3C2410_IRQ(24)      /* for s3c2440 */
 57 #define IRQ_USBD       S3C2410_IRQ(25)
 58 #define IRQ_USBH       S3C2410_IRQ(26)
 59 #define IRQ_IIC        S3C2410_IRQ(27)
 60 #define IRQ_UART0      S3C2410_IRQ(28)      /* 44 */
 61 #define IRQ_SPI1       S3C2410_IRQ(29)
 62 #define IRQ_RTC        S3C2410_IRQ(30)
 63 #define IRQ_ADCPARENT  S3C2410_IRQ(31)
</pre>
<pre class="brush:c">
 65 /* interrupts generated from the external interrupts sources */
 66 #define IRQ_EINT4      S3C2410_IRQ(32)     /* 48 */
 67 #define IRQ_EINT5      S3C2410_IRQ(33)
 68 #define IRQ_EINT6      S3C2410_IRQ(34)
 69 #define IRQ_EINT7      S3C2410_IRQ(35)
 70 #define IRQ_EINT8      S3C2410_IRQ(36)
 71 #define IRQ_EINT9      S3C2410_IRQ(37)
 72 #define IRQ_EINT10     S3C2410_IRQ(38)
 73 #define IRQ_EINT11     S3C2410_IRQ(39)
 74 #define IRQ_EINT12     S3C2410_IRQ(40)
 75 #define IRQ_EINT13     S3C2410_IRQ(41)
 76 #define IRQ_EINT14     S3C2410_IRQ(42)
 77 #define IRQ_EINT15     S3C2410_IRQ(43)
 78 #define IRQ_EINT16     S3C2410_IRQ(44)
 79 #define IRQ_EINT17     S3C2410_IRQ(45)
 80 #define IRQ_EINT18     S3C2410_IRQ(46)
 81 #define IRQ_EINT19     S3C2410_IRQ(47)
 82 #define IRQ_EINT20     S3C2410_IRQ(48)     /* 64 */
 83 #define IRQ_EINT21     S3C2410_IRQ(49)
 84 #define IRQ_EINT22     S3C2410_IRQ(50)
 85 #define IRQ_EINT23     S3C2410_IRQ(51)
 86
 87
 88 #define IRQ_EINT(x)    S3C2410_IRQ((x >= 4) ? (IRQ_EINT4 + (x) - 4) : (S3C2410_IRQ(0) + (x)))
 89
 90 #define IRQ_LCD_FIFO   S3C2410_IRQ(52)
 91 #define IRQ_LCD_FRAME  S3C2410_IRQ(53)
 98 #define S3C2410_IRQSUB(x)   S3C2410_IRQ((x)+54)
 99
100 #define IRQ_S3CUART_RX0     S3C2410_IRQSUB(0)   /* 70 */
101 #define IRQ_S3CUART_TX0     S3C2410_IRQSUB(1)
102 #define IRQ_S3CUART_ERR0    S3C2410_IRQSUB(2)
103
104 #define IRQ_S3CUART_RX1     S3C2410_IRQSUB(3)   /* 73 */
105 #define IRQ_S3CUART_TX1     S3C2410_IRQSUB(4)
106 #define IRQ_S3CUART_ERR1    S3C2410_IRQSUB(5)
107
108 #define IRQ_S3CUART_RX2     S3C2410_IRQSUB(6)   /* 76 */
109 #define IRQ_S3CUART_TX2     S3C2410_IRQSUB(7)
110 #define IRQ_S3CUART_ERR2    S3C2410_IRQSUB(8)
111
112 #define IRQ_TC          S3C2410_IRQSUB(9)
113 #define IRQ_ADC         S3C2410_IRQSUB(10)
114
115 /* extra irqs for s3c2440 */
...
</pre>
<p>那么前16就是trap了。<br />
这些中断向量的初始化是由</p>
<pre class="brush:c">
661 void __init s3c24xx_init_irq(void)
662 {
663     unsigned long pend;
664     unsigned long last;
665     int irqno;
666     int i;
667
668     irqdbf("s3c2410_init_irq: clearing interrupt status flags\n");
669
670     /* first, clear all interrupts pending... */
671
672     last = 0;
673     for (i = 0; i < 4; i++) {
674         pend = __raw_readl(S3C24XX_EINTPEND);
675
676         if (pend == 0 || pend == last)
677             break;
678
679         __raw_writel(pend, S3C24XX_EINTPEND);
680         printk("irq: clearing pending ext status %08x\n", (int)pend);
681         last = pend;
682     }
683
684     last = 0;
685     for (i = 0; i < 4; i++) {
686         pend = __raw_readl(S3C2410_INTPND);
687
688         if (pend == 0 || pend == last)
689             break;
690
691         __raw_writel(pend, S3C2410_SRCPND);
692         __raw_writel(pend, S3C2410_INTPND);
693         printk("irq: clearing pending status %08x\n", (int)pend);
694         last = pend;
695     }
696
697     last = 0;
698     for (i = 0; i < 4; i++) {
699         pend = __raw_readl(S3C2410_SUBSRCPND);
700
701         if (pend == 0 || pend == last)
702             break;
703
704         printk("irq: clearing subpending status %08x\n", (int)pend);
705         __raw_writel(pend, S3C2410_SUBSRCPND);
706         last = pend;
707     }
708
709     /* register the main interrupts */
710
711     irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n");
712
713     for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {
714         /* set all the s3c2410 internal irqs */
715
716         switch (irqno) {
717             /* deal with the special IRQs (cascaded) */
718
719         case IRQ_EINT4t7:
720         case IRQ_EINT8t23:
721         case IRQ_UART0:
722         case IRQ_UART1:
723         case IRQ_UART2:
724         case IRQ_ADCPARENT:
725             set_irq_chip(irqno, &#038;s3c_irq_level_chip);
726             set_irq_handler(irqno, handle_level_irq);
727             break;
728
729         case IRQ_RESERVED6:
730         case IRQ_RESERVED24:
731             /* no IRQ here */
732             break;
733
734         default:
735             //irqdbf("registering irq %d (s3c irq)\n", irqno);
736             set_irq_chip(irqno, &#038;s3c_irq_chip);
737             set_irq_handler(irqno, handle_edge_irq);
738             set_irq_flags(irqno, IRQF_VALID);
739         }
740     }
741
742     /* setup the cascade irq handlers */
743
744     set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
745     set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
746
747     set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
748     set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
749     set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
750     set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);
751
752     /* external interrupts */
753
754     for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
755         irqdbf("registering irq %d (ext int)\n", irqno);
756         set_irq_chip(irqno, &#038;s3c_irq_eint0t4);
757         set_irq_handler(irqno, handle_edge_irq);
758         set_irq_flags(irqno, IRQF_VALID);
759     }
760
761     for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
762         irqdbf("registering irq %d (extended s3c irq)\n", irqno);
763         set_irq_chip(irqno, &#038;s3c_irqext_chip);
764         set_irq_handler(irqno, handle_edge_irq);
765         set_irq_flags(irqno, IRQF_VALID);
766     }
767
768     /* register the uart interrupts */
769
770     irqdbf("s3c2410: registering external interrupts\n");
771
772     for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) {
773         irqdbf("registering irq %d (s3c uart0 irq)\n", irqno);
774         set_irq_chip(irqno, &#038;s3c_irq_uart0);
775         set_irq_handler(irqno, handle_level_irq);
776         set_irq_flags(irqno, IRQF_VALID);
777     }
778
779     for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) {
780         irqdbf("registering irq %d (s3c uart1 irq)\n", irqno);
781         set_irq_chip(irqno, &#038;s3c_irq_uart1);
782         set_irq_handler(irqno, handle_level_irq);
783         set_irq_flags(irqno, IRQF_VALID);
784     }
785
786     for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) {
787         irqdbf("registering irq %d (s3c uart2 irq)\n", irqno);
788         set_irq_chip(irqno, &#038;s3c_irq_uart2);
789         set_irq_handler(irqno, handle_level_irq);
790         set_irq_flags(irqno, IRQF_VALID);
791     }
792
793     for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) {
794         irqdbf("registering irq %d (s3c adc irq)\n", irqno);
795         set_irq_chip(irqno, &#038;s3c_irq_adc);
796         set_irq_handler(irqno, handle_edge_irq);
797         set_irq_flags(irqno, IRQF_VALID);
798     }
799
800     irqdbf("s3c2410: registered interrupt handlers\n");
801 }
</pre>
<p>上面便是对中断向量进行赋值了。其中set_irq_chip是设置irq上的中断控制器。set_irq_handle便是设置该中断线上的处理函数了，比如ack mark之类的。set_irq_flags是设置该中断线的标志位，有哪些呢？</p>
<pre class="brush:c">
 49 #define IRQ_INPROGRESS      0x00000100  /* IRQ handler active - do not enter! */
 50 #define IRQ_DISABLED        0x00000200  /* IRQ disabled - do not enter! */
 51 #define IRQ_PENDING     0x00000400  /* IRQ pending - replay on enable */
 52 #define IRQ_REPLAY      0x00000800  /* IRQ has been replayed but not acked yet */
 53 #define IRQ_AUTODETECT      0x00001000  /* IRQ is being autodetected */
 54 #define IRQ_WAITING     0x00002000  /* IRQ not yet seen - for autodetection */
 55 #define IRQ_LEVEL       0x00004000  /* IRQ level triggered */
 56 #define IRQ_MASKED      0x00008000  /* IRQ masked - shouldn't be seen again */
 57 #define IRQ_PER_CPU     0x00010000  /* IRQ is per CPU */
 58 #define IRQ_NOPROBE     0x00020000  /* IRQ is not valid for probing */
 59 #define IRQ_NOREQUEST       0x00040000  /* IRQ cannot be requested */
 60 #define IRQ_NOAUTOEN        0x00080000  /* IRQ will not be enabled on request irq */
 61 #define IRQ_WAKEUP      0x00100000  /* IRQ triggers system wakeup */
 62 #define IRQ_MOVE_PENDING    0x00200000  /* need to re-target IRQ destination */
 63 #define IRQ_NO_BALANCING    0x00400000  /* IRQ is excluded from balancing */
</pre>
<p>另外set_irq_handler我们来看一下：</p>
<pre class="brush:c">
534 void
535 __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
536           const char *name)
537 {
538     struct irq_desc *desc;
539     unsigned long flags;
540
541     if (irq >= NR_IRQS) {
542         printk(KERN_ERR
543                "Trying to install type control for IRQ%d\n", irq);
544         return;
545     }
546
547     desc = irq_desc + irq;
548
549     if (!handle)              //如果形参handle没有被赋值，那么先将之赋值为handle_bad_irq.在后面567行我们会看到，实际上是会执行卸载操作
550         handle = handle_bad_irq;
551     else if (desc->chip == &#038;no_irq_chip) {
552         printk(KERN_WARNING "Trying to install %sinterrupt handler "
553                "for IRQ%d\n", is_chained ? "chained " : "", irq);
554         /*
555          * Some ARM implementations install a handler for really dumb
556          * interrupt hardware without setting an irq_chip. This worked
557          * with the ARM no_irq_chip but the check in setup_irq would
558          * prevent us to setup the interrupt at all. Switch it to
559          * dummy_irq_chip for easy transition.
560          */
561         desc->chip = &#038;dummy_irq_chip;
562     }
563
564     spin_lock_irqsave(&#038;desc->lock, flags);
565
566     /* Uninstall? */
567     if (handle == handle_bad_irq) {
568         if (desc->chip != &#038;no_irq_chip)
569             mask_ack_irq(desc, irq);
570         desc->status |= IRQ_DISABLED;
571         desc->depth = 1;
572     }
573     desc->handle_irq = handle;
574     desc->name = name;
</pre>
<p>另外，现在已经设置好中断向量了，那么这些中断向量在哪里放着呢？因为如果要响应中断的话，必须找到入口啊。<br />
看一下trap_init函数：</p>
<pre class="brush:c">
678 void __init trap_init(void)
679 {
680     unsigned long vectors = CONFIG_VECTORS_BASE;
681     extern char __stubs_start[], __stubs_end[];
682     extern char __vectors_start[], __vectors_end[];
683     extern char __kuser_helper_start[], __kuser_helper_end[];
684     int kuser_sz = __kuser_helper_end - __kuser_helper_start;
685
686     /*
687      * Copy the vectors, stubs and kuser helpers (in entry-armv.S)
688      * into the vector page, mapped at 0xffff0000, and ensure these
689      * are visible to the instruction stream.
690      */
691     memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
692     memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
693     memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
694
695     /*
696      * Copy signal return handlers into the vector page, and
697      * set sigreturn to be a pointer to these.
698      */
699     memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,
700            sizeof(sigreturn_codes));
701
702     flush_icache_range(vectors, vectors + PAGE_SIZE);
703     modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
704 }
</pre>
<p>看到了吧，将__vectors_start~__vectors_end还有stub都移到了vector的位置，而vector的位置既是 CONFIG_VECTORS_BASE，</p>
<pre class="brush:c">
147 #define CONFIG_VECTORS_BASE 0xffff0000
</pre>
<p>那么__vector_start在哪里捏？<br />
 ~/linux-2.6.21.1/arch/arm/kernel/entry-armv.S </p>
<pre class="brush：c">
1062 __vectors_start:
1063     swi SYS_ERROR0
1064     b   vector_und + stubs_offset
1065     ldr pc, .LCvswi + stubs_offset
1066     b   vector_pabt + stubs_offset
1067     b   vector_dabt + stubs_offset
1068     b   vector_addrexcptn + stubs_offset
1069     b   vector_irq + stubs_offset
1070     b   vector_fiq + stubs_offset
1071
1072     .globl  __vectors_end
1073 __vectors_end:
</pre>
<p>在该文件，我们也可以找到__stub_start的变量地址。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/30/%e4%bb%8e%e4%b8%8b%e5%88%b0%e4%b8%8a%e5%88%86%e6%9e%90%e4%b8%ad%e6%96%ad%e5%a4%84%e7%90%86/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>S3C2410中断处理器模块寄存器详解</title>
		<link>http://www.tek-life.org/2010/05/30/s3c2410%e4%b8%ad%e6%96%ad%e5%a4%84%e7%90%86%e5%99%a8%e6%a8%a1%e5%9d%97%e5%af%84%e5%ad%98%e5%99%a8%e8%af%a6%e8%a7%a3/</link>
		<comments>http://www.tek-life.org/2010/05/30/s3c2410%e4%b8%ad%e6%96%ad%e5%a4%84%e7%90%86%e5%99%a8%e6%a8%a1%e5%9d%97%e5%af%84%e5%ad%98%e5%99%a8%e8%af%a6%e8%a7%a3/#comments</comments>
		<pubDate>Sun, 30 May 2010 03:03:59 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[interrupt]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/30/s3c2410%e4%b8%ad%e6%96%ad%e5%a4%84%e7%90%86%e5%99%a8%e6%a8%a1%e5%9d%97%e5%af%84%e5%ad%98%e5%99%a8%e8%af%a6%e8%a7%a3/</guid>
		<description><![CDATA[&#160;&#160;&#160;&#160;&#160; S3C2410的中断异常处理模块有以下寄存器构成： SRCPND， INTMODE， INTMSK， PRIORITY， INTPND， INTOFFSET， SUBSRCPND， INTSUBMSK。 下面具体介绍各个寄存器的功能：&#160; &#160;&#160;&#160;&#160;&#160; SRCPND（Source Pending Register）寄存器有效位32位，可读写，每一位涉及到一个中断源，SRCPND是主中断源引脚寄存器，某个位被置1表示相应的中断被触发，但我们知道在同一时刻内系统可以触发若干个中断，只要中断被触发了，SRCPND的相应位便被置1，也就是说SRCPND 在同一时刻可以有若干位同时被置1，另外，此寄存器不受中断控制器的优先权逻辑的影响。如果此中断没有被INTMSK寄存器屏蔽或者是快中断(FIQ) 的话，它将被进一步处理。通过写数据到这个寄存器能清除SPCPND相应的位。 &#160;&#160;&#160;&#160;&#160; INTMODE（Interrupt Mode Register）寄存器有效位为32位，可读写，每一位与SRCPND中各位相对应，它的作用是指定该位相应的中断源处理模式（IRQ还是FIQ）。若某位为0，则该位相对应的中断按IRQ模式处理，为1则以FIQ模式进行处理，该寄存器初始化值为0&#215;00000000,即所有中断皆以IRQ模式进行处理。此中断控制器中只有一个中断源能用FIQ mode（在紧急中断下使用FIQ mode），因此INTMODE仅有一位能置1。 &#160;&#160;&#160;&#160; INTMSK（Interrupt Mask Register）寄存器有效位为32位，可读写，INTMSK为主中断屏蔽寄存器与SRCPND寄存器对应，它的作用是决定该位相应的中断请求是否被处理。若某位被设置为1，则该位相对应的中断产生后将被忽略（CPU不处理该中断请求），设置为0则CPU对其进行处理。该寄存器初始化值为 0xFFFFFFFF，既默认情况下所有的中断都是被屏蔽的。 &#160;&#160;&#160;&#160;&#160; PRIORITY（IRQ PRIORITY Control Register）寄存器有32位，有效位[20：0]，可读写，此寄存器的作用是如果有几个中断源同时触发，按照下图的流向，假如这几个中断源都没被屏蔽，并且都是IRQ模式，因此就要判定哪个中断源的优先级最高，使其在INTPND寄存器中对应位置1，CPU转向相应的中断服务程序，让中断服务程序来处理相应的中断请求。 &#160;&#160;&#160;&#160;&#160; INTPND（Interrupt Pending Register）寄存器有效位为32位，可读写，看起来和SRCPND寄存器一样，其实他们在功能上有着重大的区别。他在某一时刻只能有1个位被置 1，INTPND 某个位被置1（该位对应的中断在所有已触发的中断里具有最高优先级且该中断没有被屏蔽），则表示CPU即将或已经在对该位相应的中断进行处理。因此 SRCPND寄存器说明有什么中断被触发了，而INTPND寄存器说明CPU即将或已经在对某一个中断进行处理。 &#8230; <a href="http://www.tek-life.org/2010/05/30/s3c2410%e4%b8%ad%e6%96%ad%e5%a4%84%e7%90%86%e5%99%a8%e6%a8%a1%e5%9d%97%e5%af%84%e5%ad%98%e5%99%a8%e8%af%a6%e8%a7%a3/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>&#160;&#160;&#160;&#160;&#160; S3C2410的中断异常处理模块有以下寄存器构成：</p>
<ul>
<li> SRCPND， </li>
<li>INTMODE， </li>
<li>INTMSK， </li>
<li>PRIORITY， </li>
<li>INTPND， </li>
<li>INTOFFSET， </li>
<li>SUBSRCPND， </li>
<li>INTSUBMSK。</li>
</ul>
<p>下面具体介绍各个寄存器的功能：&#160; </p>
<p>&#160;&#160;&#160;&#160;&#160; SRCPND（Source Pending Register）寄存器有效位32位，可读写，每一位涉及到一个中断源，SRCPND是主中断源引脚寄存器，某个位被置1表示相应的中断被触发，但我们知道在同一时刻内系统可以触发若干个中断，只要中断被触发了，SRCPND的相应位便被置1，也就是说SRCPND 在<strong>同一时刻可以有若干位同时被置1</strong>，另外，此寄存器不受中断控制器的优先权逻辑的影响。如果此中断没有被INTMSK寄存器屏蔽或者是快中断(FIQ) 的话，它将被进一步处理。通过写数据到这个寄存器能清除SPCPND相应的位。</p>
<p>&#160;&#160;&#160;&#160;&#160; INTMODE（Interrupt Mode Register）寄存器有效位为32位，可读写，每一位与SRCPND中各位相对应，它的<strong>作用是指定该位相应的中断源处理模式</strong>（IRQ还是FIQ）。若某位为0，则该位相对应的中断按IRQ模式处理，为1则以FIQ模式进行处理，该寄存器初始化值为0&#215;00000000,即所有中断皆以IRQ模式进行处理。此中断控制器中只有一个中断源能用FIQ mode（在紧急中断下使用FIQ mode），因此INTMODE仅有一位能置1。</p>
<p>&#160;&#160;&#160;&#160; INTMSK（Interrupt Mask Register）寄存器有效位为32位，可读写，INTMSK为主中断屏蔽寄存器与SRCPND寄存器对应，它的作用是决定该位相应的中断请求是否被处理。若某位被设置为1，则该位相对应的中断产生后将被忽略（CPU不处理该中断请求），设置为0则CPU对其进行处理。该寄存器初始化值为 0xFFFFFFFF，既默认情况下所有的中断都是被屏蔽的。</p>
<p>&#160;&#160;&#160;&#160;&#160; PRIORITY（IRQ PRIORITY Control Register）寄存器有32位，有效位[20：0]，可读写，此寄存器的作用是如果有几个中断源同时触发，按照下图的流向，假如这几个中断源都没被屏蔽，并且都是IRQ模式，因此就要判定哪个中断源的优先级最高，使其在INTPND寄存器中对应位置1，CPU转向相应的中断服务程序，让中断服务程序来处理相应的中断请求。</p>
<p>&#160;&#160;&#160;&#160;&#160; INTPND（Interrupt Pending Register）寄存器有效位为32位，可读写，看起来和SRCPND寄存器一样，其实他们在功能上有着重大的区别。他在某一时刻只能有1个位被置 1，INTPND 某个位被置1（该位对应的中断在所有已触发的中断里具有最高优先级且该中断没有被屏蔽），则表示CPU即将或已经在对该位相应的中断进行处理。因此 SRCPND寄存器说明有什么中断被触发了，而INTPND寄存器说明CPU即将或已经在对某一个中断进行处理。</p>
<p>&#160;&#160;&#160;&#160;&#160; INTOFFSET（Interrupt Offset Register）寄存器有效位为32位，只读，此寄存器的值显示IRQ mode的哪个中断请求在INTPND寄存器中，通过清SRCPND和INTPND这位能自动清除，FIQ mode中断不会影响INTOFFSET寄存器，因此此寄存器仅对IRQ mode中断有效。</p>
<p>&#160;&#160;&#160;&#160;&#160; SUBSRCPND（Sub Source Pending Register）寄存器有32位，有效位[10：0]，可读写，它们中的每一位分别代表一个中断源，SRCPND是主中断源引脚寄存器，它是副中断源引脚寄存器，情况类似SRCPND。</p>
<p>&#160;&#160;&#160;&#160;&#160; INTSUBMSK（Interrupt Sub Mask Register）寄存器有32位但有效位为11位，可读写，如果mask bit位是0，此中断请求被服务，情况类似INTMSK。</p>
<p><a href="http://www.tek-life.org/wp-content/uploads/2010/05/S3C2410.png"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="S3C2410" border="0" alt="S3C2410" src="http://www.tek-life.org/wp-content/uploads/2010/05/S3C2410_thumb.png" width="465" height="149" /></a></p>
<p>更全的资料：http://www.cnblogs.com/dubingsky/archive/2010/04/11/1709378.html<br />
<iframe width=100% height=560px frameborder=0 src=http://docs.google.com/View?docid=0ASKC30p2j_DDZGY2c25jbm1fODE3Y3RkNWRmZms&#038;pageview=1&#038;hgd=1&#038;embedded=1&#038;hl=zh_CN></iframe></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/30/s3c2410%e4%b8%ad%e6%96%ad%e5%a4%84%e7%90%86%e5%99%a8%e6%a8%a1%e5%9d%97%e5%af%84%e5%ad%98%e5%99%a8%e8%af%a6%e8%a7%a3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>vim + cscope</title>
		<link>http://www.tek-life.org/2010/05/29/vim-cscope/</link>
		<comments>http://www.tek-life.org/2010/05/29/vim-cscope/#comments</comments>
		<pubDate>Sat, 29 May 2010 05:03:52 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>
		<category><![CDATA[cscope]]></category>
		<category><![CDATA[vim]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/29/vim-cscope/</guid>
		<description><![CDATA[配置vim 利用网上现成的vim配置文件 $ mkdir ~/.vim_runtime $ svn co svn://orangoo.com/vim ~/.vim_runtime $ cat ~/.vim_runtime/install.sh $ sh ~/.vim_runtime/install.sh &#60;system&#62; &#60;sytem&#62; can be `mac`, `linux` or `windows` 参考：http://amix.dk/blog/post/19486#The-ultimate-vim-configuration-vimrc 配置cscope: if has(&#34;cscope&#34;) &#160;&#160;&#160; set csprg=/usr/bin/cscope &#160;&#160;&#160; set csto=0 &#160;&#160;&#160; set cst &#160;&#160;&#160; set nocsverb &#8230; <a href="http://www.tek-life.org/2010/05/29/vim-cscope/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p> 配置vim</p>
<blockquote><p>利用网上现成的vim配置文件</p>
</blockquote>
<blockquote><pre>$ mkdir ~/.vim_runtime
$ svn co svn://orangoo.com/vim ~/.vim_runtime
$ cat ~/.vim_runtime/install.sh
$ sh ~/.vim_runtime/install.sh &lt;system&gt;
  &lt;sytem&gt; can be `mac`, `linux` or `windows`</pre>
</blockquote>
<p>参考：<a title="http://amix.dk/blog/post/19486#The-ultimate-vim-configuration-vimrc" href="http://amix.dk/blog/post/19486#The-ultimate-vim-configuration-vimrc">http://amix.dk/blog/post/19486#The-ultimate-vim-configuration-vimrc</a></p>
<p></p>
<p>配置cscope:</p>
<blockquote>
<p>if has(&quot;cscope&quot;)<br />
    <br />&#160;&#160;&#160; set csprg=/usr/bin/cscope</p>
<p>&#160;&#160;&#160; set csto=0</p>
<p>&#160;&#160;&#160; set cst</p>
<p>&#160;&#160;&#160; set nocsverb</p>
<p>&#160;&#160;&#160; &quot; add any database in current directory</p>
<p>&#160;&#160;&#160; if filereadable(&quot;cscope.out&quot;)</p>
<p>&#160;&#160;&#160; cs add cscope.out</p>
<p>&#160;&#160;&#160; &quot;&#160; else add database pointed to by environment</p>
<p>&#160;&#160;&#160; elseif $CSCOPE_DB != &quot;&quot;</p>
<p>&#160;&#160;&#160; cs add $CSCOPE_DB</p>
<p>endif</p>
</blockquote>
<p>&#160;</p>
<p>一些容易忽略的快捷键：</p>
<blockquote>
<p>$<br />
    <br />当前行的末尾</p>
<p>0<br />
    <br />当前行的开头</p>
<p>&#8220;<br />
    <br />这是两个 back quote，在键盘上是ESC下边那个键。</p>
<p>回到光标跳转前的位置.其实引号也可以。</p>
<p>k是向上走一行<br />
    <br />j是向下走一行</p>
<p>h是向左走一列<br />
    <br />l是向右走一列</p>
<p></p>
<p>ctrl + e&#160; 逐行上翻<br />
    <br />ctrl + y 逐行下翻</p>
<p>ctrl + f 往下翻一屏 front<br />
    <br />ctrl + b 往上翻一屏 back</p>
<p>*</p>
<p>查找光标所在的单词</p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/29/vim-cscope/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>一个简单的模块编译例子</title>
		<link>http://www.tek-life.org/2010/05/28/a-simple-module-example/</link>
		<comments>http://www.tek-life.org/2010/05/28/a-simple-module-example/#comments</comments>
		<pubDate>Fri, 28 May 2010 08:45:32 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10296</guid>
		<description><![CDATA[1.测试源码test.c test.c #include #include static __init int test_init(void) { printk("Test module is ready--omycle ...\n"); return 0; } static __exit void test_exit(void) { printk("Test module is exit--omycle ...\n"); return ; } module_init(test_init); module_exit(test_exit); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("omycle"); MODULE_DESCRIPTION("This is a simple module!"); &#8230; <a href="http://www.tek-life.org/2010/05/28/a-simple-module-example/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>1.<strong>测试源码test.c<br />
</strong>
<pre class="brush:c">
 test.c
#include
<linux/init.h>
#include
<linux/module.h>
static __init int test_init(void)
{
    printk("Test module is ready--omycle ...\n");
    return 0;
}
static __exit void test_exit(void)
{
    printk("Test module is exit--omycle ...\n");
    return ;
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("omycle");
MODULE_DESCRIPTION("This is a simple module!");
MODULE_VERSION("Ver 0.0001");
</pre>
<p>2.<br />
模块的编译，需要Makefile文件。当然，如果没有的话，命令写的比较麻烦。</p>
<pre class="brush:c">
Makefile
KERNELDIR ?=/usr/src/linux-headers-2.6.31-21-generic/
PWD :=$(shell pwd)
    obj-m :=test.o
default:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
</pre>
<p>注意Makefile的写法，行首的间隔符要用TAB键<br />
3.编译<br />
在测试根目录下输入：make<br />
我的结果是：<br />
<code>omycle@omycle-desktop:/home/sda3/LKD/B$ make<br />
make -C /usr/src/linux-headers-2.6.31-21-generic/ M=/home/sda3/LKD/B modules<br />
make[1]: Entering directory `/usr/src/linux-headers-2.6.31-21-generic'<br />
/usr/src/linux-headers-2.6.31-21-generic/arch/x86/Makefile:80: stack protector enabled but no compiler support<br />
  Building modules, stage 2.<br />
  MODPOST 1 modules<br />
make[1]: Leaving directory `/usr/src/linux-headers-2.6.31-21-generic'</code><br />
4.<br />
插入模块<br />
 sudo insmod test.ko<br />
删除模块<br />
sudo rmmod test<br />
5.验证：<br />
<code>dmseg | tail -10<br />
[27844.553239] Test module is ready--omycle ...</code></p>
<p>rmmod test后：<br />
<code>[27888.625461] Test module is exit--omycle ...</code><br />
6.扩展<br />
<code>omycle@omycle-desktop:/home/sda3/LKD/B$ modinfo test.ko<br />
filename:       test.ko<br />
license:        Dual BSD/GPL<br />
author:         omycle<br />
description:    This is a simple module!<br />
version:        Ver 0.0001<br />
vermagic:       2.6.31-21-generic SMP mod_unload modversions 586<br />
depends:<br />
srcversion:     8E9BAD26686753AA92DC8D6</code><br />
7.普通应用程序和模块编程的区别：<br />
	C语言应用程序 	内核模块程序<br />
使用函数 	Libc库 	内核函数<br />
运行空间 	用户空间 	内核空间<br />
运行权限 	普通用户 	超级用户<br />
入口函数 	main() 	module_init()<br />
出口函数 	exit() 	module_exit()<br />
编译   	Gcc –c 	Makefile<br />
连接   	Gcc   	insmod<br />
运行   	直接运行 	insmod<br />
调试 	        Gdb 	        kdbug, kdb,kgdb等</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/28/a-simple-module-example/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>arm-linux注册中断</title>
		<link>http://www.tek-life.org/2010/05/27/arm-linux-interrupt-register/</link>
		<comments>http://www.tek-life.org/2010/05/27/arm-linux-interrupt-register/#comments</comments>
		<pubDate>Thu, 27 May 2010 08:31:03 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[Linux Kernel]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10291</guid>
		<description><![CDATA[二 注册中断 这部分我们仍以3sc2410下 的watchdog的中断为例来讲解中断的注册及调用过程。 drivers/char/watchdog/s3c2410_wdt.c: static int s3c2410wdt_probe(struct platform_device *pdev) { struct resource *res; int started = 0; int ret; int size; DBG("%s: probe=%p\n", __FUNCTION__, pdev); /* get the memory region for the watchdog timer */ res = platform_get_resource(pdev, IORESOURCE_MEM, &#8230; <a href="http://www.tek-life.org/2010/05/27/arm-linux-interrupt-register/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>二 注册中断<br />
这部分我们仍以3sc2410下 的watchdog的中断为例来讲解中断的注册及调用过程。<br />
drivers/char/watchdog/s3c2410_wdt.c:</p>
<pre class="brush:c">
static int s3c2410wdt_probe(struct platform_device *pdev)
{
        struct resource *res;
        int started = 0;
        int ret;
        int size;

        DBG("%s: probe=%p\n", __FUNCTION__, pdev);

        /* get the memory region for the watchdog timer */

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (res == NULL) {
                printk(KERN_INFO PFX "failed to get memory region resouce\n");
                return -ENOENT;
        }

        size = (res->end-res->start)+1;
        wdt_mem = request_mem_region(res->start, size, pdev->name);
        if (wdt_mem == NULL) {
                printk(KERN_INFO PFX "failed to get memory region\n");
                ret = -ENOENT;
                goto err_req;
        }

        wdt_base = ioremap(res->start, size);
        if (wdt_base == 0) {
                printk(KERN_INFO PFX "failed to ioremap() region\n");
                ret = -EINVAL;
                goto err_req;
        }

        DBG("probe: mapped wdt_base=%p\n", wdt_base);

        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (res == NULL) {
                printk(KERN_INFO PFX "failed to get irq resource\n");
                ret = -ENOENT;
                goto err_map;
        }

        ret = request_irq(res->start, s3c2410wdt_irq, 0, pdev->name, pdev);
        if (ret != 0) {
                printk(KERN_INFO PFX "failed to install irq (%d)\n", ret);
                goto err_map;
        }

        wdt_clock = clk_get(&#038;pdev->dev, "watchdog");
        if (IS_ERR(wdt_clock)) {
                printk(KERN_INFO PFX "failed to find watchdog clock source\n");
                ret = PTR_ERR(wdt_clock);
                goto err_irq;
        }

        clk_enable(wdt_clock);

        /* see if we can actually set the requested timer margin, and if
         * not, try the default value */

        if (s3c2410wdt_set_heartbeat(tmr_margin)) {
                started = s3c2410wdt_set_heartbeat(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);

                if (started == 0) {
                        printk(KERN_INFO PFX "tmr_margin value out of range, default %d used\n",
                               CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
                } else {
                        printk(KERN_INFO PFX "default timer value is out of range, cannot start\n");
                }
        }

        ret = misc_register(&#038;s3c2410wdt_miscdev);
        if (ret) {
                printk (KERN_ERR PFX "cannot register miscdev on minor=%d (%d)\n",
                        WATCHDOG_MINOR, ret);
                goto err_clk;
        }

        if (tmr_atboot &#038;&#038; started == 0) {
                printk(KERN_INFO PFX "Starting Watchdog Timer\n");
                s3c2410wdt_start();
        } else if (!tmr_atboot) {
                /* if we're not enabling the watchdog, then ensure it is
                 * disabled if it has been left running from the bootloader
                 * or other source */

                s3c2410wdt_stop();
        }

        return 0;

 err_clk:
        clk_disable(wdt_clock);
        clk_put(wdt_clock);

 err_irq:
        free_irq(wdt_irq->start, pdev);

 err_map:
        iounmap(wdt_base);

 err_req:
        release_resource(wdt_mem);
        kfree(wdt_mem);

        return ret;
}
</pre>
<p>在s3c2410wdt_probe函数中为watchdog注册了一个中断，中断号为IRQ_WDT，<br />
#define IRQ_WDT        S3C2410_IRQ(9)<br />
中断处理函数是s3c2410wdt_irq。 我们来看request_irq是 如何实现的：<br />
kernel/irq/Manage.c:</p>
<pre class="brush:c">
/**
 *  request_irq - allocate an interrupt line
 *  @irq: Interrupt line to allocate
 *  @handler: Function to be called when the IRQ occurs
 *  @irqflags: Interrupt type flags
 *  @devname: An ascii name for the claiming device
 *  @dev_id: A cookie passed back to the handler function
 *
 *  This call allocates interrupt resources and enables the
 *  interrupt line and IRQ handling. From the point this
 *  call is made your handler function may be invoked. Since
 *  your handler function must clear any interrupt the board
 *  raises, you must take care both to initialise your hardware
 *  and to set up the interrupt handler in the right order.
 *
 *  Dev_id must be globally unique. Normally the address of the
 *  device data structure is used as the cookie. Since the handler
 *  receives this value it makes sense to use it.
 *
 *  If your interrupt is shared you must pass a non NULL dev_id
 *  as this is required when freeing the interrupt.
 *
 *  Flags:
 *
 *  IRQF_SHARED     Interrupt is shared
 *  IRQF_DISABLED   Disable local interrupts while processing
 *  IRQF_SAMPLE_RANDOM The interrupt can be used for entropy
  */
int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *),  unsigned long irqflags, const char *devname, void *dev_id)
{
    struct irqaction *action;
    int retval;
 #ifdef CONFIG_LOCKDEP
    /*
     * Lockdep wants atomic interrupt handlers:
     */
  irqflags |= IRQF_DISABLED;
#endif
    /*
     * Sanity-check: shared interrupts must pass in a real dev-ID,
     * otherwise we'll have trouble later trying to figure out
     * which interrupt is which (messes up the interrupt freeing
     * logic etc).
     */
    /*允许共享的中断必须要有一个独一无二的dev_id, 看上面函数的解释
    if ((irqflags &#038; IRQF_SHARED) &#038;&#038; !dev_id)
        return -EINVAL;
    if (irq >= NR_IRQS) /*中断号是否合法*/
        return -EINVAL;
    /*这个标记对s3c2410来 说在s3c24xx_init_irq里通过调用set_irq_flags()被 去掉了*/
    if (irq_desc[irq].status &#038; IRQ_NOREQUEST)
        return -EINVAL;
    if (!handler)  /*中断例程*/
        return -EINVAL;

    /*分配一个irqaction对 象，来保存这个中断信息*/
    action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
    if (!action)
        return -ENOMEM;

   /*保存中断例程等信息*/
    action->handler = handler;
    action->flags = irqflags;
    cpus_clear(action->mask);   /*清除相应位*/
    action->name = devname;
    action->next = NULL;
    action->dev_id = dev_id;

    select_smp_affinity(irq);

    retval = setup_irq(irq, action);/*安装中断*/
    if (retval)
        kfree(action);

    return retval;
}
</pre>
<p>该函数的功能及参数含义在函数头有详细的说明，这里就不多介绍了，值得注意的是我们在申请中断之前，必须要去掉该中断的IRQ_NOREQUEST标记(系统初始化的时候赋了这个标记)， 具体方法是调用set_irq_flags()函数。<br />
接着看setup_irq<br />
kernel/irq/Manage.c:</p>
<pre class="brush:c">
/*
 * Internal function to register an irqaction - typically used to
 * allocate special interrupts that are part of the architecture.
 */
int setup_irq(unsigned int irq, struct irqaction *new)
{
    struct irq_desc *desc = irq_desc + irq; /*获取保存该中断的 中断描述符地址*/
    struct irqaction *old, **p;
    unsigned long flags;
    int shared = 0;

    if (irq >= NR_IRQS)
        return -EINVAL;

    /*对于s3c2410的 中断在s3c24xx_init_irq里已经初始化过了*/
    if (desc->chip == &#038;no_irq_chip)
        return -ENOSYS;
    /*
     * Some drivers like serial.c use request_irq() heavily,
     * so we have to be careful not to interfere with a
     * running system.
     */
    if (new->flags &#038; IRQF_SAMPLE_RANDOM) {
        /*
         * This function might sleep, we want to call it first,
         * outside of the atomic block.
         * Yes, this might clear the entropy pool if the wrong
         * driver is attempted to be loaded, without actually
         * installing a new handler, but is this really a problem,
         * only the sysadmin is able to do this.
         */
        rand_initialize_irq(irq);
    }

    /*
     * The following block of code has to be executed atomically
     */
     /*
* 下面if代码段主要是查看该中断是否可以共享，如可以，则把中断例程链入中断例程list中, 至于中断共享的条件有： 1 触发方式相同， 2 都允许中断共享*/
    spin_lock_irqsave(&#038;desc->lock, flags);
    p = &#038;desc->action;
    old = *p;
    if (old) { /*对于IRQ_WDT，这个if不成立，但它已经设置了handle_irq 喔*/
        /*
         * Can't share interrupts unless both agree to and are
         * the same type (level, edge, polarity). So both flag
         * fields must have IRQF_SHARED set and the bits which
         * set the trigger type must match.
         */
        /*判断能否共享中断*/
        if (!((old->flags &#038; new->flags) &#038; IRQF_SHARED) ||
            ((old->flags ^ new->flags) &#038; IRQF_TRIGGER_MASK))
            goto mismatch;

#if defined(CONFIG_IRQ_PER_CPU)
        /* All handlers must agree on per-cpuness */
        if ((old->flags &#038; IRQF_PERCPU) !=
            (new->flags &#038; IRQF_PERCPU))
            goto mismatch;
#endif
         /* add new interrupt at end of irq queue */
        /*把中断例程加入list中*/
        do {
            p = &#038;old->next;
            old = *p;
        } while (old);
        shared = 1;
    }

    *p = new;  /*该行很关键， 它把中断的处理函数添加到该中断描述符的中断例程list里*/
#if defined(CONFIG_IRQ_PER_CPU)
    if (new->flags &#038; IRQF_PERCPU)
        desc->status |= IRQ_PER_CPU;
#endif
    if (!shared) {
/*对于IRQ_WDT来说这步是多余的，初始化 的时候已做过*/
        irq_chip_set_defaults(desc->chip); 

        /* Setup the type (level, edge polarity) if configured: */
/*对于IRQ_WDT来说，没有定义触发方式， 即默认触发方式*/
        if (new->flags &#038; IRQF_TRIGGER_MASK) {
            /*如果要设触发方式，则调用平台特定的函数，因此如果我们的平台要实现该功能则必*须要在irq_chip对象里加入对set_type 函数的支持*/
            if (desc->chip &#038;&#038; desc->chip->set_type)
               desc->chip->set_type(irq, new->flags &#038; IRQF_TRIGGER_MASK);
            else
               /*
                * IRQF_TRIGGER_* but the PIC does not support
                * multiple flow-types?
                 */
               printk(KERN_WARNING "No IRQF_TRIGGER set_type "
                      "function for IRQ %d (%s)\n", irq,
                      desc->chip ? desc->chip->name :
                      "unknown");
        } else
            /*这函数只是简单的检查是否有handle_irq*/
            compat_irq_chip_set_default_handler(desc);
         /*去掉中断的相关状态，让它就绪*/
        desc->status &#038;= ~(IRQ_AUTODETECT | IRQ_WAITING |
                 IRQ_INPROGRESS);
         if (!(desc->status &#038; IRQ_NOAUTOEN)) {
            /*使能该中断*/
            desc->depth = 0;
            desc->status &#038;= ~IRQ_DISABLED;
            if (desc->chip->startup)
               desc->chip->startup(irq); /*平台相关函数*/
            else
               desc->chip->enable(irq);  /*平台相关函数*/
        } else
            /* Undo nested disables: */
            desc->depth = 1;
    }
    spin_unlock_irqrestore(&#038;desc->lock, flags);
     new->irq = irq;
    register_irq_proc(irq);  /*在/proc/irq/下创建该中断的一个文件*/
    new->dir = NULL;
    register_handler_proc(irq, new); /*为/proc/irq/下 创建的文件设置处理函数*/

    return 0;

mismatch:
    spin_unlock_irqrestore(&#038;desc->lock, flags);
    if (!(new->flags &#038; IRQF_PROBE_SHARED)) {
        printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);
        dump_stack();
    }<span id="more-10291"></span>
    return -EBUSY;
}
</pre>
<p>该函数主要是安装了一个中断，并使能它。 我们先看使能函数，它是平台相关的，对于s3c2410的IRQ_WDT来说使用的是默认的使能函数(default_startup(),初始化赋值)，如果我们要使用自己的使能函数，只要在chip对象 里添加就行了。<br />
kernel/irq/Chip.c:</p>
<pre class="brush:c">
static unsigned int default_startup(unsigned int irq)
{
    irq_desc[irq].chip->enable(irq);
     return 0;
}
</pre>
<p>对于IRQ_WDT来 说调用的irq_desc[irq].chip->enable(irq)仍是默认函数：default_enable（）<br />
kernel/irq/chip.c:</p>
<pre class="brush:c">
static void default_enable(unsigned int irq)
{
    struct irq_desc *desc = irq_desc + irq;
     desc->chip->unmask(irq);   /*unmask该中断*/
    desc->status &#038;= ~IRQ_MASKED;
}
</pre>
<p>呵呵，对于IRQ_WDT来 说这次的调用desc->chip->unmask(irq)，实际上是s3c_irq_unmask, 具体可查看前面分析的chip对 象。<br />
arch/arm/mach-s3c2410/Irq.c:</p>
<pre class="brush:c">
static void s3c_irq_unmask(unsigned int irqno)
{
    unsigned long mask;
     if (irqno != IRQ_TIMER4 &#038;&#038; irqno != IRQ_EINT8t23)
        irqdbf2("s3c_irq_unmask %d\n", irqno);
     irqno = IRQ_EINT0;
     mask = __raw_readl(S3C2410_INTMSK);
    mask &#038;= ~(1UL << irqno);
    __raw_writel(mask, S3C2410_INTMSK);
}
</pre>
<p>对着s3c2410的data sheet一看就知道了， 就是打开相应中断。至此中断处理函数安装好了，中断也打开了，系统就可以正确的响应中断了。Ok，到此为止IRQ_WDT的中断注册过程已完 成，此时的中断描述符如下所示：<br />
<img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/aaronychen/EntryImages/20080903/1633560596216552500.JPG" class="alignnone" width="635" height="595" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/27/arm-linux-interrupt-register/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>arm-linux中断初始化分析</title>
		<link>http://www.tek-life.org/2010/05/26/arm-linux-interrupt/</link>
		<comments>http://www.tek-life.org/2010/05/26/arm-linux-interrupt/#comments</comments>
		<pubDate>Wed, 26 May 2010 13:21:07 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[ARM]]></category>
		<category><![CDATA[Linux Kernel]]></category>
		<category><![CDATA[armlinux]]></category>
		<category><![CDATA[interrupt]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10288</guid>
		<description><![CDATA[这篇文档准备简要的分析下arm平台上linux下的中断是如何运行的，本文将先分析初始化时的中断是如何建立的，然后以一个例子来注册一个中断，并详细分析中断触发到调用我们自己的中断例程的整个流程。 本文linux2.6.18的源码和s3c2410的CPU及smdk2410的板子为例来分析代码。 一 中断初始化： 大家都知道arm下规定，在0&#215;00000000或0xffff0000的地址处必须存放一张跳转表, 其格式如下： 上面的这个表我们称之为”异常中断向量表”,表中的IRQ和FIQ位置就是用来存放处理中断函数的地址。至于具体选择在哪一个地址处存放该表，可由CPU的协处理器完成。如s3c2410下由CP15中寄存器1的位13来决定，我们可以通过设置该位来告诉系统我们的向量表在哪。 include/arch/asm-arm/proc-armv/system.h #if __LINUX_ARM_ARCH__ &#62;= 4 //at91rm9200是ARMV4结构 #define vectors_base() ((cr_alignment &#38; CR_V) ? 0xffff0000 : 0) #else #define vectors_base() (0) #endif 可以看到ARMv4以下的版本,该地址固定为0;ARMv4及以上版本，ARM中断向量表的地址由CP15协处理器c1寄存器中V位 (bit[13])控制,V和中断向量表的对应关系如下: V=0    ～    0&#215;00000000~0x0000001C V=1    ～    0xffff0000~0xffff001C 因此，在中断初始化的时候我们要做的就是在IRQ和FIQ的位置处放置我们的中断处理函数地址或跳转语句跳转到我们的中断处理函数。这个过程是在trap_init中完成的，而它由start_kernel()调用。 arch/arm/kernel/traps.c void __init trap_init(void) &#8230; <a href="http://www.tek-life.org/2010/05/26/arm-linux-interrupt/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>这篇文档准备简要的分析下arm平台上linux下的中断是如何运行的，本文将先分析初始化时的中断是如何建立的，然后以一个例子来注册一个中断，并详细分析中断触发到调用我们自己的中断例程的整个流程。<br />
本文linux2.6.18的源码和s3c2410的CPU及smdk2410的板子为例来分析代码。<br />
一 中断初始化：<br />
大家都知道arm下规定，在0&#215;00000000或0xffff0000的地址处必须存放一张跳转表, 其格式如下：<br />
<img class="alignnone" title="中断向量表" src="http://p.blog.csdn.net/images/p_blog_csdn_net/aaronychen/EntryImages/20080903/1.JPG" alt="" width="786" height="348" /></p>
<p>上面的这个表我们称之为”异常中断向量表”,表中的IRQ和FIQ位置就是用来存放处理中断函数的地址。至于具体选择在哪一个地址处存放该表，<strong>可由</strong><strong>CPU的协处理器完成</strong>。如s3c2410下由CP15中寄存器1的位13来决定，我们可以通过设置该位来告诉系统我们的向量表在哪。<br />
include/arch/asm-arm/proc-armv/system.h</p>
<pre class="brush:c">#if __LINUX_ARM_ARCH__ &gt;= 4    //at91rm9200是ARMV4结构
#define vectors_base()    ((cr_alignment &amp; CR_V) ? 0xffff0000 : 0)
#else
#define vectors_base()    (0)
#endif</pre>
<p>可以看到ARMv4以下的版本,该地址固定为0;ARMv4及以上版本，ARM中断向量表的地址由CP15协处理器c1寄存器中V位 (bit[13])控制,V和中断向量表的对应关系如下:</p>
<p>V=0    ～    0&#215;00000000~0x0000001C<br />
V=1    ～    0xffff0000~0xffff001C</p>
<p>因此，在中断初始化的时候我们要做的就是在IRQ和FIQ的位置处放置我们的中断处理函数地址或跳转语句跳转到我们的中断处理函数。这个过程是在trap_init中完成的，而它由start_kernel()调用。<br />
arch/arm/kernel/traps.c</p>
<pre class="brush:c">void __init trap_init(void)
{
    unsigned long vectors = CONFIG_VECTORS_BASE;/*跳转表的存放位置（即上面那表的存放位置）*/
     /*这些都在entry-armv.S下定义
*stub vector kuser_helper具体指什么？
*/
    extern char __stubs_start[], __stubs_end[];
    extern char __vectors_start[], __vectors_end[];
    extern char __kuser_helper_start[], __kuser_helper_end[];
    int kuser_sz = __kuser_helper_end - __kuser_helper_start;
     /*
     * Copy the vectors, stubs and kuser helpers (in entry-armv.S)
     * into the vector page, mapped at 0xffff0000, and ensure these
     * are visible to the instruction stream.
     */
/*复制跳转表内容到指定的位置
* memcpy(dst,src,count)
*/
    memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
    memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
    memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
     /*
     * Copy signal return handlers into the vector page, and
     * set sigreturn to be a pointer to these.
     */
    memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,   sizeof(sigreturn_codes));
     flush_icache_range(vectors, vectors + PAGE_SIZE);
    modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}
</pre>
<p>上面这个函数主要就是在CONFIG_VECTORS_BASE处设置好那张跳转表， CONFIG_VECTORS_BASE在autoconf.h中定义(该文件自动成生)，值为0xffff0000, 而CP15下的r1[13]在系统启动的时候在汇编部分就已经设置好 了。<br />
接下来我们就看下__vectors_start，__vectors_end，__stubs_start，__stubs_end之间的内容。<br />
arch/arm/kernel/entry-armv.S</p>
<pre class="brush:c"> .globl __vectors_start
__vectors_start:
    swi SYS_ERROR0
    b   vector_und + stubs_offset
    ldr pc, .LCvswi + stubs_offset
    b   vector_pabt + stubs_offset
    b   vector_dabt + stubs_offset
    b   vector_addrexcptn + stubs_offset
    b   vector_irq + stubs_offset
    b   vector_fiq + stubs_offset
 .globl  __vectors_end
__vectors_end:
     .data
</pre>
<p>这就是那张跳转表. vector_irq, vector_fiq等函数我们后面在分析，他们就定义在__stubs_start，__stubs_end中.至此经过traps_init后,在0xffff0000处的跳转表就形成了.当产生IRQ时，将调用b  vector_irq + stubs_offset.</p>
<p>在系统初始化的时候除了会调用trap_init外,还会调用init_IRQ函数(也由start_kernel调用)，它初始化了一个全局中断描述符表(该表保存了每个中断的所有属性信息),并调用特定平台的中断初始化函数。<br />
arm/arm/kernel/irq.c</p>
<pre class="brush:c">
void __init init_IRQ(void)
{
    int irq;
    /*初始化中断描述符表*/
for (irq = 0; irq < NR_IRQS; irq++)
      irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_DELAYED_DISABLE | IRQ_NOPROBE;
 #ifdef CONFIG_SMP
    bad_irq_desc.affinity = CPU_MASK_ALL;
    bad_irq_desc.cpu = smp_processor_id();
#endif
    init_arch_irq();  /*特定平台的中断初始化*/
}
</pre>
<p>系统中总共有NR_IRQS个中断，并且每个中断都有一个中断描述符，保存在irq_desc中，该描述符保存了该中断的所有属性信息。对于平台smdk2410来说init_arch_irq()就是s3c24xx_init_irq()函数, 这是在setup_arch()里面赋值的--setup_arch也是在start_kernel里面被调用的。后面的内容我们都以中断号IRQ_WDT为例来讲解：<br />
arch/arm/mach-s3c2410/irq.c:</p>
<pre class="brush:c">
/* s3c24xx_init_irq
 * Initialise S3C2410 IRQ system
*/
void __init s3c24xx_init_irq(void)
{
    unsigned long pend;
    unsigned long last;
    int irqno;
    int i;

    irqdbf("s3c2410_init_irq: clearing interrupt status flags\n");
     /* first, clear all interrupts pending... */
    /*先清掉所有的pending标志位，该位代表是否系统中触发了一个中断*/
    last = 0;
    for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C24XX_EINTPEND);
   if (pend == 0 || pend == last)
            break;
         __raw_writel(pend, S3C24XX_EINTPEND);
        printk("irq: clearing pending ext status %08x\n", (int)pend);
        last = pend;
    }
     last = 0;
    for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C2410_INTPND);
         if (pend == 0 || pend == last)
            break;
         __raw_writel(pend, S3C2410_SRCPND);
        __raw_writel(pend, S3C2410_INTPND);
        printk("irq: clearing pending status %08x\n", (int)pend);
        last = pend;
    }
     last = 0;
    for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C2410_SUBSRCPND);
         if (pend == 0 || pend == last)
            break;
         printk("irq: clearing subpending status %08x\n", (int)pend);
        __raw_writel(pend, S3C2410_SUBSRCPND);
        last = pend;
    }
     /* register the main interrupts */
    /* 注册主要的中断*/
    irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n");
     for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {
        /* set all the s3c2410 internal irqs */
         switch (irqno) {
            /* deal with the special IRQs (cascaded) */
         case IRQ_EINT4t7:
        case IRQ_EINT8t23:
        case IRQ_UART0:
        case IRQ_UART1:
        case IRQ_UART2:
        case IRQ_ADCPARENT:
            set_irq_chip(irqno, &#038;s3c_irq_level_chip);
            set_irq_handler(irqno, do_level_IRQ);
            break;

        case IRQ_RESERVED6:
        case IRQ_RESERVED24:
            /* no IRQ here */
            break;

        default:  /*IRQ_WDT 就是这条通路*/
            //irqdbf("registering irq %d (s3c irq)\n", irqno);
            set_irq_chip(irqno, &#038;s3c_irq_chip); /*为中断号设置chip*/
            set_irq_handler(irqno, do_edge_IRQ); /*设 置中断例程*/
            set_irq_flags(irqno, IRQF_VALID);    /*设置中断ready的标记*/
        }
    }

    /* setup the cascade irq handlers */
     set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint);
    set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint);
     set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
    set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
    set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
    set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);
     /* external interrupts */
     for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
        irqdbf("registering irq %d (ext int)\n", irqno);
        set_irq_chip(irqno, &#038;s3c_irq_eint0t4);
        set_irq_handler(irqno, do_edge_IRQ);
        set_irq_flags(irqno, IRQF_VALID);
    }

    for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
        irqdbf("registering irq %d (extended s3c irq)\n", irqno);
        set_irq_chip(irqno, &#038;s3c_irqext_chip);
        set_irq_handler(irqno, do_edge_IRQ);
        set_irq_flags(irqno, IRQF_VALID);
    }
     /* register the uart interrupts */
     irqdbf("s3c2410: registering external interrupts\n");
     for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) {
        irqdbf("registering irq %d (s3c uart0 irq)\n", irqno);
        set_irq_chip(irqno, &#038;s3c_irq_uart0);
        set_irq_handler(irqno, do_level_IRQ);
        set_irq_flags(irqno, IRQF_VALID);
    }
     for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) {
        irqdbf("registering irq %d (s3c uart1 irq)\n", irqno);
        set_irq_chip(irqno, &#038;s3c_irq_uart1);
        set_irq_handler(irqno, do_level_IRQ);
        set_irq_flags(irqno, IRQF_VALID);
    }
     for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) {
        irqdbf("registering irq %d (s3c uart2 irq)\n", irqno);
        set_irq_chip(irqno, &#038;s3c_irq_uart2);
        set_irq_handler(irqno, do_level_IRQ);
        set_irq_flags(irqno, IRQF_VALID);
    }
     for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) {
        irqdbf("registering irq %d (s3c adc irq)\n", irqno);
        set_irq_chip(irqno, &#038;s3c_irq_adc);
        set_irq_handler(irqno, do_edge_IRQ);
        set_irq_flags(irqno, IRQF_VALID);
    }
     irqdbf("s3c2410: registered interrupt handlers\n");
}
</pre>
<p>上面这个函数结合s3c2410的data sheet很好理解，就是注册各个必要的中断，注意这里为每个中断号注册的中断例程只是个整体的函数，该函数只是处理一些共性的操作如清中断标记位等，他会进一步调用我们注册的中断例程来处理特定 的中断。如何注册中断会在后面分析。这个初始化函数调用了很多与中断相关的函数，我们逐个分析：<br />
kernel/irq/chip.c:</p>
<pre class="brush:c">
/*
 *  set_irq_chip - set the irq chip for an irq
 *  @irq:   irq number
 *  @chip:  pointer to irq chip description structure
 */
/*为某个中断号设置一个chip*/
int set_irq_chip(unsigned int irq, struct irq_chip *chip)
{
    struct irq_desc *desc;
    unsigned long flags;
     if (irq >= NR_IRQS) {
        printk(KERN_ERR "Trying to install chip for IRQ%d\n", irq);
        WARN_ON(1);
        return -EINVAL;
    }
     if (!chip)
        chip = &#038;no_irq_chip;

    desc = irq_desc + irq;  /*获取保存该中断的中断描述符*/
    spin_lock_irqsave(&#038;desc->lock, flags);
    irq_chip_set_defaults(chip); /*为chip设置一些默认的公共的操作函数*/
    desc->chip = chip;  /*为中断保存chip对象*/
    /*
     * For compatibility only:
     */
    desc->chip = chip;
    spin_unlock_irqrestore(&#038;desc->lock, flags);

    return 0;
}
</pre>
<p>为特定中断号初始化好chip对象，表示该中断号由这个chip控制，后面会调用到该中断号所属chip的相关函数，各个中断的chip是不同的,以IRQ_WDT为例，它的chip是s3c_irq_chip,请看：<br />
arch/arm/mach-s3c2410/irq.c:</p>
<pre class="brush:c">
static struct irqchip s3c_irq_chip = {
    .ack       = s3c_irq_ack,
    .mask      = s3c_irq_mask,
    .unmask    = s3c_irq_unmask,
    .set_wake  = s3c_irq_wake
};
</pre>
<p>在set_irq_chip()中,还调用了irq_chip_set_defaults，目的是设置一些公共的开关操作。<br />
kernel/irq/Chip.c:</p>
<pre class="brush:c">
/*
 * Fixup enable/disable function pointers
 */
void irq_chip_set_defaults(struct irq_chip *chip)
{
    if (!chip->enable)
        chip->enable = default_enable;
    if (!chip->disable)
        chip->disable = default_disable;
    if (!chip->startup)
        chip->startup = default_startup;
    if (!chip->shutdown)
        chip->shutdown = chip->disable;
    if (!chip->name)
        chip->name = chip->typename;
}
</pre>
<p>从代码中看，很显然，如果chip没有相应的操作函数，则就给chip赋默认的操作函数。<br />
我们仍然回到s3c24xx_init_irq中. 接着看set_irq_handler()<br />
include/linux/Irq.h:</p>
<pre class="brush:c">
static inline void set_irq_handler(unsigned int irq, void fastcall (*handle)(unsigned int, struct irq_desc *, struct pt_regs *))
{
    __set_irq_handler(irq, handle, 0);
}
</pre>
<p>kernel/irq/chip.c:</p>
<pre class="brush:c">
void __set_irq_handler(unsigned int irq, void fastcall (*handle)(unsigned int, irq_desc_t *, struct pt_regs *),int is_chained)
{
    struct irq_desc *desc;
    unsigned long flags;

    if (irq >= NR_IRQS) { /*参数检查*/
        printk(KERN_ERR
               "Trying to install type control for IRQ%d\n", irq);
        return;
    }

    desc = irq_desc + irq; /*获取中断描述符的存储地址*/

    if (!handle)
        handle = handle_bad_irq;  /*赋默认的中断handle*/

    if (desc->chip == &#038;no_irq_chip) {
        printk(KERN_WARNING "Trying to install %sinterrupt handler "
               "for IRQ%d\n", is_chained ? "chained " : " ", irq);
        /*
         * Some ARM implementations install a handler for really dumb
         * interrupt hardware without setting an irq_chip. This worked
         * with the ARM no_irq_chip but the check in setup_irq would
         * prevent us to setup the interrupt at all. Switch it to
         * dummy_irq_chip for easy transition.
         */
        desc->chip = &#038;dummy_irq_chip;  /*赋默认的chip*/
    }

    spin_lock_irqsave(&#038;desc->lock, flags);
     /* Uninstall? */
    if (handle == handle_bad_irq) {/*没有设置中断例程，这里先将该中断设置为disable，防止在这时以及在这时之前来了此中断，就进行了错误的处理，这样就悲剧了。*/
        if (desc->chip != &#038;no_irq_chip) {
            desc->chip->mask(irq);
            desc->chip->ack(irq);
        }
        desc->status |= IRQ_DISABLED;  /*没有中断例程则disable掉该中断*/
        desc->depth = 1;
    }
    desc->handle_irq = handle;  /*保存中断例程，对于IRQ_WDT来说则是do_edge_IRQ */

    /*由上面的调用可知，is_chained 始终等于0*/
    if (handle != handle_bad_irq &#038;&#038; is_chained) {
        desc->status &#038;= ~IRQ_DISABLED;//这个时候，就将IRQ_ENABLE了
        desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;
        desc->depth = 0;
        desc->chip->unmask(irq);
    }
    spin_unlock_irqrestore(&#038;desc->lock, flags);
}
</pre>
<p>上面这个函数就是为特定的中断设置好一个中断处理例程.<br />
Comment:这里的例程可不是我们request_irq注册的例程.<br />
继续回到那个s3c24xx_init_irq函数，看set_irq_flags<br />
arch/arm/kernel/irq.c:</p>
<pre class="brush:c">
void set_irq_flags(unsigned int irq, unsigned int iflags)
{
    struct irqdesc *desc;
    unsigned long flags;

    if (irq >= NR_IRQS) {
        printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);
        return;
    }

    desc = irq_desc + irq;
    spin_lock_irqsave(&#038;desc->lock, flags);
    desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
    if (iflags &#038; IRQF_VALID)
        desc->status &#038;= ~IRQ_NOREQUEST;  /*清掉IRQ_NOREQUEST 标记*/
    if (iflags &#038; IRQF_PROBE)
        desc->status &#038;= ~IRQ_NOPROBE;
    if (!(iflags &#038; IRQF_NOAUTOEN))
        desc->status &#038;= ~IRQ_NOAUTOEN;
    spin_unlock_irqrestore(&#038;desc->lock, flags);
}
</pre>
<p>该函数主要是为特定的中断设置相应的状态标记, 而这里我们调用它的目的就是清掉IRQ_NOREQUEST标记，告诉系统该中断已经可以被申请使用了，中断在申请的时候会查看是否有IRQ_NOREQUEST标记，如有则表面该中断还不能使用。而初始化的时候所有的中断都有这个标记.<br />
好，告一段落，基本上中断已经初始化差不多了.<br />
参考：http://blog.csdn.net/aaronychen/archive/2008/09/03/2874643.aspx</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/26/arm-linux-interrupt/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ARM中断原理&#8212;ARM9 2410</title>
		<link>http://www.tek-life.org/2010/05/25/arm%e4%b8%ad%e6%96%ad%e5%8e%9f%e7%90%86arm9-2410/</link>
		<comments>http://www.tek-life.org/2010/05/25/arm%e4%b8%ad%e6%96%ad%e5%8e%9f%e7%90%86arm9-2410/#comments</comments>
		<pubDate>Tue, 25 May 2010 03:24:12 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[ARM]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/25/arm%e4%b8%ad%e6%96%ad%e5%8e%9f%e7%90%86arm9-2410/</guid>
		<description><![CDATA[几天前一个学生问我ARM中断嵌套的问题，我才发现原在我心中理所当然的事对学生来说理解实属不易。 &#160;&#160;&#160;&#160; ARM有七种模式，我们这里只讨论SVC、IRQ和FIQ模式。 &#160;&#160;&#160;&#160; 我们可以假设ARM核心有两根中断引脚（实际上是看不见的），一根叫 irq pin, 一根叫fiq pin. &#160;&#160;&#160;&#160; 在ARM的cpsr中，有一个I位和一个F位，分别用来禁止IRQ和FIQ的。 &#160;&#160;&#160;&#160; 先不说中断控制器，只说ARM核心。正常情况下，ARM核都只是机械地随着pc的指示去做事情，当CPSR中的I和F位为1的时候，IRQ和FIQ全部处 于禁止状态。无论你在irq pin和fiq pin上面发什么样的中断信号，ARM是不会理你的，你根本不能打断他，因为他耳聋了，眼也瞎了。 &#160;&#160;&#160;&#160; 在I位和F位为0的时候，当irq pin上有中断信号过来的时候，就会打断arm的当前工作，并且切换到IRQ模式下，并且跳到相应的异常向量表（vector)位置去执行代码。这个过程 是自动的，但是返回到被中断打断的地方就得您亲自动手了。当你跳到异常向量表，处于IRQ的模式的时候，这个时候如果irq pin上面又来中断信号了，这个时候ARM不会理你的，irq pin就跟秘书一样，ARM核心就像老板，老板本来在做事，结果来了一个客户，秘书打断它，让客户进去了。而这个时候再来一个客户，要么秘书不断去敲门 问，要么客户走人。老板第一个客户没有会见完，是不会理你的。 &#160;&#160;&#160;&#160; 但是有一种情况例外，当ARM处在IRQ模式，这个时候fiq pin来了一个中断信号，fiq pin是什么？是快速中断呀，比如是公安局的来查刑事案件，那才不管你老板是不是在会见客户，直接打断，进入到fiq模式下，并且跳到相应的fiq的异常 向量表处去执行代码。那如果当ARM处理FIQ模式，fiq pin又来中断信号，又就是又一批公安来了，那没戏，都是执法人员，你打不断我。那如果这个时候irq pin来了呢？来了也不理呀，正在办案，还敢来妨碍公务。 &#160;&#160;&#160; 所以得出一个结论： IRQ模式只能被FIQ模式打断，FIQ模式下谁也打不断。 &#160;&#160; 在打不断的情况下，irq pin 或 fiq pin随便你怎么发中断信号，都是白发。 &#160;&#160; &#8230; <a href="http://www.tek-life.org/2010/05/25/arm%e4%b8%ad%e6%96%ad%e5%8e%9f%e7%90%86arm9-2410/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<blockquote><p>几天前一个学生问我ARM中断嵌套的问题，我才发现原在我心中理所当然的事对学生来说理解实属不易。</p>
<p>&#160;&#160;&#160;&#160; ARM有七种模式，我们这里只讨论SVC、IRQ和FIQ模式。</p>
<p>&#160;&#160;&#160;&#160; 我们可以假设ARM核心有两根中断引脚（实际上是看不见的），一根叫 irq pin, 一根叫fiq pin.</p>
<p>&#160;&#160;&#160;&#160; 在ARM的cpsr中，有一个I位和一个F位，分别用来禁止IRQ和FIQ的。</p>
<p>&#160;&#160;&#160;&#160; 先不说中断控制器，只说ARM核心。正常情况下，ARM核都只是机械地随着pc的指示去做事情，当CPSR中的I和F位为1的时候，IRQ和FIQ全部处 于禁止状态。无论你在irq pin和fiq pin上面发什么样的中断信号，ARM是不会理你的，你根本不能打断他，因为他耳聋了，眼也瞎了。</p>
<p>&#160;&#160;&#160;&#160; 在I位和F位为0的时候，当irq pin上有中断信号过来的时候，就会打断arm的当前工作，并且切换到IRQ模式下，并且跳到相应的异常向量表（vector)位置去执行代码。这个过程 是自动的，但是返回到被中断打断的地方就得您亲自动手了。当你跳到异常向量表，处于IRQ的模式的时候，这个时候如果irq pin上面又来中断信号了，这个时候ARM不会理你的，irq pin就跟秘书一样，ARM核心就像老板，老板本来在做事，结果来了一个客户，秘书打断它，让客户进去了。而这个时候再来一个客户，要么秘书不断去敲门 问，要么客户走人。老板第一个客户没有会见完，是不会理你的。</p>
<p>&#160;&#160;&#160;&#160; 但是有一种情况例外，当ARM处在IRQ模式，这个时候fiq pin来了一个中断信号，fiq pin是什么？是快速中断呀，比如是公安局的来查刑事案件，那才不管你老板是不是在会见客户，直接打断，进入到fiq模式下，并且跳到相应的fiq的异常 向量表处去执行代码。那如果当ARM处理FIQ模式，fiq pin又来中断信号，又就是又一批公安来了，那没戏，都是执法人员，你打不断我。那如果这个时候irq pin来了呢？来了也不理呀，正在办案，还敢来妨碍公务。</p>
<p>&#160;&#160;&#160; 所以得出一个结论： IRQ模式只能被FIQ模式打断，FIQ模式下谁也打不断。</p>
<p>&#160;&#160; 在打不断的情况下，irq pin 或 fiq pin随便你怎么发中断信号，都是白发。</p>
<p>&#160;&#160; 所以除了fiq能打断irq以外，根本没有所谓中断嵌套的情况。</p>
<p>&#160;&#160; 但是再怎么说irq pin 和fiq pin加起来也就2根引脚，那这么多中断源，怎么办呢？不可能谁来了都直接来敲门吧。</p>
<p>&#160;&#160;&#160; 那么接下来该说谁来给irq pin或者 fiq pin发信号呢？上文中可以看到，是老板的客户，也可能是公安。这个事情就是由中断控制器来管理。</p>
<p>&#160;&#160;&#160; 拿最简单的2410/2440的中断控制器举例，这个中断控制器加一个子中断控制器，还有一个外部中断控制器管理了50多个中断资源，说穿了有50多个 脚。这些脚除了外部中断都是规定了功能的，比如WDT、LCD、DMA等，这个功能不能改，因为2410/2440内部硬件连线决定了。</p>
<p>&#160; 当你WDT和DMA的中断都到来的时候，就会被送到SRCPND寄存器中，两个中断都在里面，那到底把哪一个送给ARM呢？这个时候先看INTMOD,也 就 是模式，哪个是设置成为了快速中断，哪个就被送上去；那如果两个都是设置的快速中断呢？不可能，因为同一时间只能有一个中断可以被设成快速中断。所以，如 果有快速中断，这个时候直接就给fiq pin发中断信号，打断ARM。</p>
<p>&#160;&#160; 那要是没有快速中断呢，这个时候就看INTMSK，看WDT和ＤＭＡ有没有被屏蔽的，如果DMA在INTMSK被屏蔽了，那就只有ＷＤＴ继续向上送了，如 果都没有屏蔽，那么他们两个同进来到了PRIORITY优先级寄存器，在这里，根据优先级的设置，一定会分出一个高，一个低的优先级出来，高的那个就被送 上去，送到了INTPND寄存器，所以ＩＮＴＰＮＤ随时随地有且只有一个一个中断在里面。只要ＩＮＴＰＮＤ里面有中断，irq pin就不会一直不断给ＡＲＭ中断信号，当第一次发的时候，中断了ＡＲＭ，这个时候ＡＲＭ进入相应的异常向量，并处于ＩＲＱ模式，正在这个时 候，ＩＮＴＰＮＤ仍然在不断的通过irq pin向ＡＲＭ发中断信号，但是ＡＲＭ这个时候已经处于ＩＲＱ模式，是不会理睬你的。当你中断处理完了，要退出ＩＲＱ模式了，这个时候小心了，如果你在退 出ＩＲＱ模式之前不清除INTPND里面的中断位，当你刚退出IRQ模式，又被中断了，因为INTPND一直在发中断信号。所以在退出IRQ模式之前要清 除INTPND里面的中断位。但是光清除INTPND里面的位还不行，因为SRCPND里面WDT和DMA的中断还在，当你刚清除完INTPND，结果 SRCPND里面又选了一个出来又送到了INTPND里面。所以正确的处理方法是退出IRQ模式之前，一定要先清除SRCPND里相应的中断位，再清除 INTPND里相应的位。那么请注意，SRCPND里面可能有多位，所以清除你已处理过的中断就行了，而INTPND里面只可能有一位，直接清掉就可以 了。</p>
<p> 附上一张我用＋－号画的简图:</p>
<p>&lt;IMG SRC=”<a title="http://www.upemb.com/uploads/allimg/091019/16394K234-0.png" href="http://www.upemb.com/uploads/allimg/091019/16394K234-0.png">http://www.upemb.com/uploads/allimg/091019/16394K234-0.png</a>”/&gt;</p>
<p>&#160;&#160;&#160;&#160; 再来说说Linux的情况。Linux不用FIQ,只用到了IRQ。但是我们有时候一个中断需要处理很长时间，那我们就需要占用IRQ模式那么长的时间 吗？没有，linux在IRQ模式下只是简单的记录是什么中断，马上就切换回了ＳＶＣ模式，换句话说，Ｌinux的中断处理都是在ＳＶＣ模式下处理的。那 么中断号是怎么来的呢？在ＡＲＭ上固死了，相应的中断号只有一个办法得到：查询irqs.h 。那我先用一个中断号注册一个中断处理程序，当中断发生的时候，Linux怎么知道是我这个中断号发生的中断呢？ 在处理中断的时候，先读取INTPND，根据需要再读取EINTPEND或SUBSRCPND计算出一个中断号，相应的处理算法在 get_irq_nr_base这个宏中。而且irqs.h中的中断号就是根据这个算法把每个中断算一下得来的。</p>
<p>&#160;&#160;&#160;&#160;&#160; 还有子中断、外部中断，电平触发、边沿触发等，以后再谈。</p>
<p>作者介绍：本文由尚观科技老师和同学生（刘勇，孙贺，聂强，聂大鹏 ，牛须乐，孙磊）共同创作</p>
</blockquote>
<p>ARM是RISC架构，不像X86那样的复杂。在X86中，外部中断一共只有15根线由8259A做中断控制器，这样，就会存在设备共享终端线的情况。</p>
<p>ARM没有这样做，因为它的中断控制器就比较复杂。除了中断控制器，还有子中断控制器，外部中断控制器，能控制50多个中断。除此之外，还有INTPND、SRCPND用于中断处理的寄存器。总而言之，ARM在硬件方面做了很多工作，来简化程序设计。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/25/arm%e4%b8%ad%e6%96%ad%e5%8e%9f%e7%90%86arm9-2410/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux源码点滴&#8211;asmlinkage</title>
		<link>http://www.tek-life.org/2010/05/24/linux%e6%ba%90%e7%a0%81%e7%82%b9%e6%bb%b4-asmlinkage/</link>
		<comments>http://www.tek-life.org/2010/05/24/linux%e6%ba%90%e7%a0%81%e7%82%b9%e6%bb%b4-asmlinkage/#comments</comments>
		<pubDate>Mon, 24 May 2010 01:23:01 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/24/linux%e6%ba%90%e7%a0%81%e7%82%b9%e6%bb%b4-asmlinkage/</guid>
		<description><![CDATA[asmlinkage是个宏，使用它是为了保持参数在stack中。因为从汇编语言到C语言代码参数的传递是通过stack的，它也可能从 stack中得到一些不需要的参数。Asmlinkage将要解析那些参数。 &#8220;asmlinkage&#8221; is defined, for example, in the header file &#8220;include/asm-i386/linkage.h&#8221; as __attribute__((regparm(0))) that tells compiler put all function parameters in the stack (i.e. disables call optimization). Definition of &#8220;__init&#8221; is placed in the header &#8220;include/linux/init.h&#8221;. (Paths to headers &#8230; <a href="http://www.tek-life.org/2010/05/24/linux%e6%ba%90%e7%a0%81%e7%82%b9%e6%bb%b4-asmlinkage/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>asmlinkage是个宏，使用它是为了保持参数在stack中。因为从汇编语言到C语言代码参数的传递是通过stack的，它也可能从 stack中得到一些不需要的参数。Asmlinkage将要解析那些参数。</p>
<blockquote><p>&#8220;asmlinkage&#8221; is defined, for example, in the header file<br />
&#8220;include/asm-i386/linkage.h&#8221; as<br />
__attribute__((regparm(0)))<br />
that tells compiler put all function parameters in the stack (i.e.<br />
disables call optimization).</p>
<p>Definition of &#8220;__init&#8221; is placed in the header &#8220;include/linux/init.h&#8221;.<br />
(Paths to headers are valid for version 2.6.9)</p></blockquote>
<p>看一下/usr/include/asm /linkage.h里面的定义：<br />
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))<br />
__attribute__是关键字，是gcc的C语言扩展，regparm(0)表示不从寄存器传递参数</p>
<p>asmlinkage存在的必要性：</p>
<p>这个asmlinkage大都用在系统调用中，系统调用需要在entry.s文件中用汇编语言调用，所以必须要保证它符合C语言的参数传递规则，才能用汇编语言正确调用它。</p>
<p>补充一：</p>
<blockquote><p>__attribute__机制是GNU C的一大特色，它可以设置函数属性、变量属性和类型属性等。可以通过它们向编译器提供更多数据，帮助编译器执行优化等。<br />
__attribute__((regparm(0)))：告诉gcc编译器该函数不需要通过任何寄存器来传递参数，参数只是通过堆栈来传递。<br />
__attribute__((regparm(3)))：告诉gcc编译器这个函数可以通过寄存器传递多达3个的参数，这3个寄存器依次为EAX、 EDX 和 ECX。更多的参数才通过堆栈传递。这样可以减少一些入栈出栈操作，因此调用比较快。</p></blockquote>
<p>补充二：</p>
<p>关于CPP_ASMLINKAGE:</p>
<blockquote><p>6 #ifdef __cplusplus<br />
7 #define CPP_ASMLINKAGE extern &#8220;C&#8221;<br />
8 #else<br />
9 #define CPP_ASMLINKAGE<br />
10 #endif<br />
11<br />
12 #ifndef asmlinkage<br />
13 #define asmlinkage CPP_ASMLINKAGE<br />
14 #endif</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/24/linux%e6%ba%90%e7%a0%81%e7%82%b9%e6%bb%b4-asmlinkage/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux入门进阶方法谈</title>
		<link>http://www.tek-life.org/2010/05/23/linux%e5%85%a5%e9%97%a8%e8%bf%9b%e9%98%b6%e6%96%b9%e6%b3%95%e8%b0%88/</link>
		<comments>http://www.tek-life.org/2010/05/23/linux%e5%85%a5%e9%97%a8%e8%bf%9b%e9%98%b6%e6%96%b9%e6%b3%95%e8%b0%88/#comments</comments>
		<pubDate>Sun, 23 May 2010 03:07:04 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[kernel study]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/23/linux%e5%85%a5%e9%97%a8%e8%bf%9b%e9%98%b6%e6%96%b9%e6%b3%95%e8%b0%88/</guid>
		<description><![CDATA[&#160; 一，入门阶段 target： 知道什么是kernel，大致了解kernel，解除对kernel的恐惧心理 1&#62; Makefile - target： &#160;&#160;&#160;&#160;&#160; Makefile要熟悉，至少对应用程序的Makefile裁减要比较了解，总之对Makefile比较熟悉，这是了解kernel的架构组织的基础。 -建议练习： 交叉编译应用程序，裁减Makefile。 - Status:已经都练习过了。 - Enhance:建议平时学习，多多学习Makefile，可以重点关注kernel代码里面的Makefile的写法，以及如何修改Kconfig文件，如何在kernel源代码中，加入新功能代码，如何添加修改Makefile 2&#62;在普通的PC上升级Linux kernel - target: 熟悉如何编译kernel的源代码，升级kernel后，要配置哪些文件，才能正确的运行kernel - Status: - Enhance: 无 3&#62;掌握如何用KGDB调试kernel的源代码 - Target: 掌握KGDB和kgdb调试器的用法和常用的命令，图形界面可选，但是命令行界面一定要掌握。 二，进阶阶段： Target： 进入到具体的编程阶段，加一些练习代码。熟悉kernel的函数用法 4&#62;熟悉module的写法，这是进入kernel的捷径 - Target： 掌握module的的写法，具体的Makefile怎么样写？怎么编译？掌握和module有关的常用的函数，比如export_symbol() ,MODULE_LICIENCE()等等，总之，ldd3头两章要看的比较熟悉才行。 可以熟练的写一些module，其实这些module都可以看成是一个driver或者一个内核模块的缩写或者简化版本。比如一个helloworld的module。虽然简单，也能反应很多问题。driver就是在上面的基础上控制硬件而已。 -练习： &#8230; <a href="http://www.tek-life.org/2010/05/23/linux%e5%85%a5%e9%97%a8%e8%bf%9b%e9%98%b6%e6%96%b9%e6%b3%95%e8%b0%88/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>&#160;</p>
<p><b>一，入门阶段</b><b></b></p>
<p>target：</p>
<p>知道什么是kernel，大致了解kernel，解除对kernel的恐惧心理</p>
<p>1&gt; Makefile</p>
<p>- target：   <br />&#160;&#160;&#160;&#160;&#160; Makefile要熟悉，至少对应用程序的Makefile裁减要比较了解，总之对Makefile比较熟悉，这是了解kernel的架构组织的基础。</p>
<p>-建议练习：   <br />交叉编译应用程序，裁减Makefile。</p>
<p>- Status:已经都练习过了。</p>
<p>- Enhance:建议平时学习，多多学习Makefile，可以重点关注kernel代码里面的Makefile的写法，以及如何修改Kconfig文件，如何在kernel源代码中，加入新功能代码，如何添加修改Makefile</p>
<p>2&gt;在普通的PC上升级Linux kernel</p>
<p>- target:   <br />熟悉如何编译kernel的源代码，升级kernel后，要配置哪些文件，才能正确的运行kernel</p>
<p>- Status:</p>
<p>- Enhance:   <br />无</p>
<p>3&gt;掌握如何用KGDB调试kernel的源代码</p>
<p>- Target:   <br />掌握KGDB和kgdb调试器的用法和常用的命令，图形界面可选，但是命令行界面一定要掌握。</p>
<p><b>二，进阶阶段：</b><b></b></p>
<p>Target：   <br />进入到具体的编程阶段，加一些练习代码。熟悉kernel的函数用法</p>
<p>4&gt;熟悉module的写法，这是进入kernel的捷径</p>
<p>- Target：   <br />掌握module的的写法，具体的Makefile怎么样写？怎么编译？掌握和module有关的常用的函数，比如export_symbol() ,MODULE_LICIENCE()等等，总之，ldd3头两章要看的比较熟悉才行。</p>
<p>可以熟练的写一些module，其实这些module都可以看成是一个driver或者一个内核模块的缩写或者简化版本。比如一个helloworld的module。虽然简单，也能反应很多问题。driver就是在上面的基础上控制硬件而已。</p>
<p>-练习：</p>
<p>看ldd3上面的22页上面的helloworld小module程序。   <br />参照<a>http://dirac.org/linux/writing/lkmpg/2.6/chinese/lkmpg_chs/</a>来练习module的写法，和实现一些简单的功能，这些和arm和x86都是无关的，是通用的。</p>
<p>5&gt;在x86机器上练习如何给kernel加上一些系统调用。</p>
<p>- target :</p>
<p>了解什么是“系统调用”，系统调用的原理，和glibc里面的c函数有什么区别，如何在kernel代码里面加系统调用，用户程序如何调用这个系统调用。</p>
<p>知道如何在kernel现有的基础上加上一些系统调用。</p>
<p>-参考资料：   <br /><a>http://www.kerneltravel.net/journal/iv/index.htm</a> 陈莉君老师的网站</p>
<p>-练习：   <br />我这里有个例子，比如实现一个系统调用：查找某个进程的pid（当然用ps ax|grep很容易搞定，我们这里只是练习而已）    <br />可以参考我的例子：    <br />&#160;&#160;&#160;&#160;&#160; (See attached file: module_syscall.c)&#160; (See attached file:chardev_test.c) ,供参考，这是我原来自己练习的例子。</p>
<p>6&gt;熟悉kernel一些常用的机制，一些基本函数的用法，</p>
<p>Target：   <br />了解kernel api的用法，    <br />例子：自旋锁，信号量，同步，互斥。    <br />推荐书籍：ldd3（linux device driver 3rd），非常好    <br />必看章节：    <br />第一章设备驱动程序简介，入门。    <br />第二章构造和运行模块，了解什么是模块，怎么编译，大致怎么写？    <br />第三障字符设备， 写一个简单的模块，可以实现read write（）函数，和硬件无关，因为我们不关心driver    <br />第四章debug，掌握printk的用法    <br />选看章节，或者提高章节    <br />第五章并发和竟态 一定要掌握</p>
<p>参考资料：</p>
<p>&#160;&#160;&#160;&#160;&#160; ldd3   <br />下面是我在<a>www.linuxforum.net</a>上的一个讨论关于自旋锁和信号量分析的讨    <br />论：对于了解信号量和spinlock的用法，比较有好处。    <br />&#160;&#160;&#160;&#160;&#160; (See attached file: spinlock讨论帖子.pdf)    <br />大家也可以充分利用<a>www.linuxforum.net</a>边讨论边学习。这种方式进步很快。</p>
<p>练习：   <br />&#160;&#160;&#160;&#160;&#160; -一个module，实现read/write/ioctl()函数。    <br />&#160;&#160;&#160;&#160;&#160; &#8211; enhance上面的module，要实现互斥同步的功能。比如实现一个module    <br />，当一个进程read的时候，另一个进程再去读的时候，要阻塞调用，当第一个进程    <br />释放这个资源的时候，第二个进程要被唤醒。    <br />这是我的例子：参考：(See attached file: chardev_test_waitqueue.c)</p>
<p>关于，更多的互斥的例子，可以参考下面我的自己以前练习的例子：   <br />&#160;&#160;&#160;&#160;&#160; (See attached file: chardev_test_atomic.c)(See attached file:chardev_test_sema.c)(See attached file: chardev_test_waitqueue.c)(See attached file: chardev_test_wait_kmalloc.c)    <br />下面是kernel里面timer的用法，可以参考。</p>
<p>&#160;&#160;&#160;&#160;&#160; (See attached file: chrdev_timer.c)</p>
<p>编译module的时候，需要Makefile，   <br />下面的Makefile可以作为例子：    <br />针对2.4 kernel的例子（仅仅是例子，不要照抄）    <br />&#160;&#160;&#160;&#160;&#160; (See attached file: Makefile)    <br />针对2.6 kernel的例子：    <br />&#160;&#160;&#160;&#160;&#160; (See attached file: Makefile)</p>
<p>6&gt;进阶资料</p>
<p>可以看看<a>www.kerneltravel.net</a>上面的文章，相当不错，    <br />推荐：内核中的调度与同步    <br /><a>http://www.kerneltravel.net/journal/vi/syn.htm</a></p>
<p>中断解析   <br /><a>http://www.kerneltravel.net/journal/viii/index.htm</a></p>
<p>7&gt;根据工作需要去看相应的代码，比如要看jffs2，就要看jffs2的代码，如果看实时， 就要看RT的实现代码。</p>
<p><b>本文摘自：kernelchina.cublog.cn</b></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/23/linux%e5%85%a5%e9%97%a8%e8%bf%9b%e9%98%b6%e6%96%b9%e6%b3%95%e8%b0%88/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ubuntu 9.10 network-server configure</title>
		<link>http://www.tek-life.org/2010/05/22/ubuntu-9-10-network-server-configure/</link>
		<comments>http://www.tek-life.org/2010/05/22/ubuntu-9-10-network-server-configure/#comments</comments>
		<pubDate>Sat, 22 May 2010 04:21:01 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>
		<category><![CDATA[bridge]]></category>
		<category><![CDATA[dhcp]]></category>
		<category><![CDATA[nfs]]></category>
		<category><![CDATA[tftp]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/22/ubuntu-9-10-network-server-configure/</guid>
		<description><![CDATA[删除网桥： ip link set br0 down brctl delbr br0 tftp服务器 sudo vi /etc/inetd.conf tftp dgram udp wait nobody /usr/sbin/tcpd /usr/sbin/in.tftpd /tftpboot &#160; 重新启动服务： sudo /etc/init.d/openbsd-inetd restart 测试： 在/tftpboot 文件夹下新建立一个文件 cd /tftpboot touch test 进入另外一个文件夹 tftp 192.168.227.101 tftp&#62; get test &#160; &#8230; <a href="http://www.tek-life.org/2010/05/22/ubuntu-9-10-network-server-configure/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>删除网桥：   </p>
<blockquote><p>ip link set br0 down     <br />brctl delbr br0</p>
</blockquote>
<p>tftp服务器</p>
<blockquote><p>sudo vi /etc/inetd.conf      <br />tftp dgram udp wait nobody /usr/sbin/tcpd /usr/sbin/in.tftpd /tftpboot </p>
<p>&#160; 重新启动服务：      <br />sudo /etc/init.d/openbsd-inetd restart </p>
<p>测试：</p>
<p>在/tftpboot 文件夹下新建立一个文件      <br />cd /tftpboot       <br />touch test       <br />进入另外一个文件夹       <br />tftp 192.168.227.101       <br />tftp&gt; get test</p>
</blockquote>
<p>&#160;</p>
<p><em><font style="background-color: #f9f9f9">NFS服务器：</font></em></p>
<blockquote><p>1、进行NFS服务器端与客户端的安装：</p>
<p>sudo apt-get install nfs-kernel-server&#160; nfs-common&#160; portmap</p>
<p>安装客户端的作用是可以在本机进行NFS服务的测试。</p>
<p>2、配置portmap</p>
<p>两种方法任选一种就可以：</p>
<p>(1):sudo emacs /etc/default/portmap</p>
<p>去掉 -i 127.0.0.1 </p>
<p>(2)sudo dpkg-reconfigure portmap </p>
<p>运行后选择“否”</p>
<p>3、配置挂载目录和权限</p>
<p>vim /etc/exports</p>
<p>配置如下：</p>
<p>/home/nfsboot *(rw,sync)     <br />解释一下：</p>
<p>/home/nfsboot是NFS的共享目录，*表示任何IP都可以共享这个目录，你可以改为受限的IP，rw表示的是权限，sync是默认的。</p>
<p>4、更新exports文件</p>
<p>只要你更改了/etc/exports, 你不可以通过sudo exportfs -r 来更新 这个文件</p>
<p>5、重启NFS服务</p>
<p> sudo /etc/init.d/nfs-kernel-server restart 重启nfs服务</p>
<p>6、进行测试</p>
<p>尝试一下挂载本地磁盘(我的linux系统IP为202.198.137.18，将/home/nfsboot挂载到/mnt)      <br />$ sudo mount 202.198.137.18:/home/nfsboot /mnt       <br />运行 $ df 看看结果       <br />$ sudo umount /mnt </p>
<p>&#160;</p>
</blockquote>
<p><em><font style="background-color: #f9f9f9">DHCP服务器：</font></em></p>
<blockquote><p>启动服务器      <br />root@ubuntu:/# /etc/init.d/dhcp3-server start       <br />* Starting DHCP server dhcpd3 [ OK ]       <br />root@ubuntu:/#       <br />查看服务是否已经正常监听       <br />root@ubuntu:/# netstat -aunp|grep dhcp       <br />udp 0 0 0.0.0.0:67 0.0.0.0:* 23011/dhcpd3</p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/22/ubuntu-9-10-network-server-configure/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CentOS5.4安装xen</title>
		<link>http://www.tek-life.org/2010/05/20/centos5-4%e5%ae%89%e8%a3%85xen/</link>
		<comments>http://www.tek-life.org/2010/05/20/centos5-4%e5%ae%89%e8%a3%85xen/#comments</comments>
		<pubDate>Thu, 20 May 2010 09:12:43 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Linux Skills]]></category>
		<category><![CDATA[CentOS]]></category>
		<category><![CDATA[Xen]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/20/centos5-4%e5%ae%89%e8%a3%85xen/</guid>
		<description><![CDATA[终于把xen编译好了，只有DOM0,安装的时候，出了很多错误，说实话都没有解决。于是，采用了最简单的方法： 下载: xen3.4.2:http://bits.xensource.com/oss-xen/release/3.4.2/xen-3.4.2.tar.gz linux 2.6.18.8打过xen补丁的：http://bits.xensource.com/oss-xen/release/3.4.2/linux-2.6.18-xen-3.4.2.tar.gz 安装方法： step 1: 分别解压后，将linux-2.6.18.8解压后的内核拷入xen-3.4.2目录内，并重命名为 xen-2.6.18-xen.hg。 为了不再进行同步linux，修改xen-3.4.2/buildconfigs/src.hg-clone文件，将其中的某些行删除，删除后的内容为： # Mercurial HG ?= hg LINUX_SRCDIR ?= linux-$(LINUX_VER)-xen.hg # Repository to clone. XEN_LINUX_HGREPO ?= $$(sh buildconfigs/select-repository $(LINUX_SRCDIR) $(LINUX_SRC_PATH)) # Set XEN_LINUX_HGREV to update to a particlar revision. XEN_LINUX_HGREV&#160; ?= &#8230; <a href="http://www.tek-life.org/2010/05/20/centos5-4%e5%ae%89%e8%a3%85xen/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>终于把xen编译好了，只有DOM0,安装的时候，出了很多错误，说实话都没有解决。于是，采用了最简单的方法：</p>
<p>下载:</p>
<p>xen3.4.2:<a title="http://bits.xensource.com/oss-xen/release/3.4.2/xen-3.4.2.tar.gz" href="http://bits.xensource.com/oss-xen/release/3.4.2/xen-3.4.2.tar.gz">http://bits.xensource.com/oss-xen/release/3.4.2/xen-3.4.2.tar.gz</a></p>
<p>linux 2.6.18.8打过xen补丁的：<a title="http://bits.xensource.com/oss-xen/release/3.4.2/linux-2.6.18-xen-3.4.2.tar.gz" href="http://bits.xensource.com/oss-xen/release/3.4.2/linux-2.6.18-xen-3.4.2.tar.gz">http://bits.xensource.com/oss-xen/release/3.4.2/linux-2.6.18-xen-3.4.2.tar.gz</a></p>
<p>安装方法：</p>
<p>step 1:   <br />分别解压后，将linux-2.6.18.8解压后的内核拷入xen-3.4.2目录内，并重命名为 xen-2.6.18-xen.hg。    <br />为了不再进行同步linux，修改xen-3.4.2/buildconfigs/src.hg-clone文件，将其中的某些行删除，删除后的内容为：</p>
<p># Mercurial   <br />HG ?= hg    <br />LINUX_SRCDIR ?= linux-$(LINUX_VER)-xen.hg    <br /># Repository to clone.    <br />XEN_LINUX_HGREPO ?= $$(sh buildconfigs/select-repository $(LINUX_SRCDIR) $(LINUX_SRC_PATH))    <br /># Set XEN_LINUX_HGREV to update to a particlar revision.    <br />XEN_LINUX_HGREV&#160; ?= tip    <br />$(LINUX_SRCDIR)/.valid-src: $(__XEN_LINUX_UPDATE)    <br />set -e ; \    <br />touch $@</p>
<p>step 2:</p>
<p>安装xen的时候，需要依赖一些库文件，因此，要预先检查一下</p>
<p>进入xen-3.4.2/tools/check目录</p>
<p>分别运行 ./chk build 和 ./chk install</p>
<p>都提示OK，则可，否则，安装之。</p>
<p>step3:</p>
<p>准备xen-3.4.2/studom目录内的相关文件：</p>
<p>grub-0.97.tar.gz&#160;&#160; newlib-1.16.0.tar.gz&#160;&#160;&#160; pciutils-2.2.9.tar.gz   <br />lwip-1.3.0.tar.gz&#160; pciutils-2.2.9.tar.bz2&#160; zlib-1.2.3.tar.gz</p>
<p>注意：pciutils有两个，一个是.tar.bz2,一个是.tar.gz。</p>
<p>将以上的文件下载好后，拷贝到studom目录下</p>
<p>step4:</p>
<p>开始安装了。这里，会发生很多警告，或者错误，如果想将错误输出到文件中，以便分别到网上寻找错误的解决方法，可以在每一条命令后面加上 2&gt; error.txt 这样，就将错误和警告信息输出到文本文件error.txt了。</p>
<p>运行 make dist –j 8 ;这里 –j 的参数是多线程，这里用8线程进行make</p>
<p>然后，运行 make install –j 8;</p>
<p>通过这两条命令，已经在/boot目录下生成了kernel文件和xen.gz了。   </p>
<p>注意，不可运行make world，因为这条命令是几条命令的集合，首先会运行 make clean，这条命令一运行，我们在linux-2.6.18-xen.hg下的目录都被清空了，功夫白费了。</p>
<p>我们还少initrd文件，因此，Continue.</p>
<p>step5:</p>
<p>运行：mkinitrd &#8211;without-dmraid -v -f /boot/initrd-2.6.18.8-xen.img 2.6.18.8-xen</p>
<p>这个—without-dmraid可加可不加，不过，我没有实验过不加的情况，如果你时间充足，都测试一下，完了，留言告诉偶一声，谢谢。</p>
<p>step6:</p>
<p>上面的工作都已经做完了，那么就很轻松了，改一下grub.conf就可以啦。我的grub为：</p>
<p>title CentOS (2.6.18-164.15.1.el5)   <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; root (hd0,0)    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; kernel /vmlinuz-2.6.18-164.15.1.el5 ro root=/dev/VolGroup00/LogVol00 rhgb quiet    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; initrd /initrd-2.6.18-164.15.1.el5.img    <br />title Red Hat Enterprise Linux Server (2.6.18-128.el5)    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; root (hd0,0)    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; kernel /vmlinuz-2.6.18-128.el5 ro root=/dev/VolGroup00/LogVol00 rhgb quiet    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; initrd /initrd-2.6.18-128.el5.img    <br />title xen-3    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; root (hd0,0)    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; kernel /xen-3.4.2.gz    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; module /vmlinuz-2.6.18.8-xen ro&#160; root=/dev/VolGroup00/LogVol00 rhgb quiet    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; module /initrd-2.6.18.8-xen.img</p>
<p>到这里，就可以重启了，如果启动后，出现cann’t find filesystem的错误，检查一下grub.conf里面的root参数，是否赋值正确了。我就在这里摔了好几跤。</p>
<p>OK。</p>
<p>接下来，我会进行DOMU的实验。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/20/centos5-4%e5%ae%89%e8%a3%85xen/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>centos 5.4 安装mercurial</title>
		<link>http://www.tek-life.org/2010/05/19/centos-5-4-%e5%ae%89%e8%a3%85mercurial/</link>
		<comments>http://www.tek-life.org/2010/05/19/centos-5-4-%e5%ae%89%e8%a3%85mercurial/#comments</comments>
		<pubDate>Wed, 19 May 2010 14:16:40 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[mercurial]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/19/centos-5-4-%e5%ae%89%e8%a3%85mercurial/</guid>
		<description><![CDATA[Mercurial和其他软件的源码编译安装不太一样。 除了最基本的./configure ;make ;make install;外，还需要一个hg debuginstall. 安装步骤如下： 1. 下载源码 http://mercurial.selenic.com/release/mercurial-1.4.1.tar.gz 2. 编译安装 a) # make all b) # make install c) # hg debuginstall 会出现abort: couldn&#8217;t find mercurial libraries in… 方法 可以在命令行中export，但范围和时间都很有限，不如找到hg和mercurial的位置，进行如下修改： # mv /usr/local/bin/hg /usr/local/bin/hg.py # cat &#62; /usr/local/bin/hg &#8230; <a href="http://www.tek-life.org/2010/05/19/centos-5-4-%e5%ae%89%e8%a3%85mercurial/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Mercurial和其他软件的源码编译安装不太一样。</p>
<p>除了最基本的./configure ;make ;make install;外，还需要一个hg debuginstall.</p>
<p>安装步骤如下：</p>
<p>1. 下载源码</p>
<p><a href="http://mercurial.selenic.com/release/mercurial-1.4.1.tar.gz">http://mercurial.selenic.com/release/mercurial-1.4.1.tar.gz</a></p>
<p>2. 编译安装</p>
<p>a) # make all</p>
<p>b) # make install</p>
<p>c) # hg debuginstall<br />
会出现abort: couldn&#8217;t find mercurial libraries in…</p>
<p><strong>方法</strong></p>
<p>可以在命令行中export，但范围和时间都很有限，不如找到hg和mercurial的位置，进行如下修改：</p>
<blockquote><p># mv /usr/local/bin/hg /usr/local/bin/hg.py<br />
# cat &gt; /usr/local/bin/hg &lt;&lt;\EOF<br />
#!/bin/sh<br />
PYTHONPATH=/usr/local/lib64/python2.4/site-packages:${PYTHONPATH}<br />
export PYTHONPATH<br />
exec /usr/local/bin/hg.py &#8220;$@&#8221;<br />
EOF</p></blockquote>
<p>别忘了chmod 755 /usr/local/bin/hg</p>
<p><strong>然后，运行 hg debuginstall</strong><br />
仍然会出现错误：</p>
<p>Checking username&#8230;<br />
no username supplied (see &#8220;hg help config&#8221;)<br />
(specify a username in your .hgrc file)<br />
1 problems detected, please check your install!</p>
<p>通过man hgrc会看到一些说明。默认是去一些位置找配置文件的。如果没有，就创建。源码中contrib文件夹下提供了一个sample.hgrc，可以拷贝过来修改</p>
<p># cp contrib/sample.hgrc /root/.hgrc</p>
<p># vim /root/.hgrc</p>
<p>改变如下：</p>
<p>### show changed files and be a bit more verbose if True</p>
<p># verbose = True</p>
<p>### username data to appear in comits</p>
<p>### it usually takes the form: Joe User &lt;joe.user@host.com&gt;</p>
<p>username = Joe Who &lt;j.user@example.com&gt;</p>
<p>verbose = True</p>
<p>### &#8212; Extensions</p>
<p><strong>再运行hg debuginstall</strong> ，出现这个提示就可以了</p>
<p>Checking encoding (UTF-8)&#8230;</p>
<p>Checking extensions&#8230;</p>
<p>Checking templates&#8230;</p>
<p>Checking patch&#8230;</p>
<p>patching file hg-debuginstall-wCOuEs</p>
<p>Checking commit editor&#8230;</p>
<p>Checking username&#8230;</p>
<p>No problems detected</p>
<p>d) # hg</p>
<p>运行hg，出现</p>
<p>分布式软件配置管理工具 &#8211; 水银 (版本 1.4.1)</p>
<p>版权所有 (C) 2005-2009 Matt Mackall &lt;mpm@selenic.com&gt; 和其他人。</p>
<p>这是自由软件，具体参见版权条款。这里没有任何担保，甚至没有适合</p>
<p>特定目的的隐含的担保。</p>
<p>ok!</p>
<p>参考：<a title="http://hi.baidu.com/shuxiaotu/blog/item/4fe50c195a03e40c35fa4192.html" href="http://hi.baidu.com/shuxiaotu/blog/item/4fe50c195a03e40c35fa4192.html">http://hi.baidu.com/shuxiaotu/blog/item/4fe50c195a03e40c35fa4192.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/19/centos-5-4-%e5%ae%89%e8%a3%85mercurial/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&quot;/usr/openwin/bin/xauth:  timeout in locking authority file $HOME/.Xauthority&quot;</title>
		<link>http://www.tek-life.org/2010/05/19/usropenwinbinxauth-timeout-in-locking-authority-file-home-xauthority/</link>
		<comments>http://www.tek-life.org/2010/05/19/usropenwinbinxauth-timeout-in-locking-authority-file-home-xauthority/#comments</comments>
		<pubDate>Wed, 19 May 2010 03:07:05 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[timeout]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/19/usropenwinbinxauth-timeout-in-locking-authority-file-home-xauthority/</guid>
		<description><![CDATA[1. The user&#8217;s $HOME is not writable by the user; $HOME may be owned by an entirely different user. 2. The lock file $HOME/.Xauthority-l already exists; remove this file. Expert these two points: 3.please check the permission.The general method is &#8230; <a href="http://www.tek-life.org/2010/05/19/usropenwinbinxauth-timeout-in-locking-authority-file-home-xauthority/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>1. The user&#8217;s $HOME is not writable by the user; $HOME may be owned by an entirely different user.   <br />2. The lock file $HOME/.Xauthority-l already exists; remove this file.</p>
<p>Expert these two points:</p>
<p>3.please check the permission.The general method is run this command</p>
<p>chmod 777 .</p>
<p>&#8212;&#8212;other problem&#8212;-</p>
<p>Unluckily,we may come across this problem: </p>
<p>we couldn’t modify the $HOME ‘s file or add some things.The problem is that the $HOME is not ours,we should change the owner by root . </p>
<p>switch to root,and run this command: </p>
<p>chown user:user *.*//the user is our id name </p>
<p>and then change the permission: </p>
<p>chmod 555 *.* </p>
<p>Then that is ok.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/19/usropenwinbinxauth-timeout-in-locking-authority-file-home-xauthority/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ubuntu 更改默认桌面路径</title>
		<link>http://www.tek-life.org/2010/05/19/ubuntu-%e6%9b%b4%e6%94%b9%e9%bb%98%e8%ae%a4%e6%a1%8c%e9%9d%a2%e8%b7%af%e5%be%84/</link>
		<comments>http://www.tek-life.org/2010/05/19/ubuntu-%e6%9b%b4%e6%94%b9%e9%bb%98%e8%ae%a4%e6%a1%8c%e9%9d%a2%e8%b7%af%e5%be%84/#comments</comments>
		<pubDate>Wed, 19 May 2010 01:36:50 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/19/ubuntu-%e6%9b%b4%e6%94%b9%e9%bb%98%e8%ae%a4%e6%a1%8c%e9%9d%a2%e8%b7%af%e5%be%84/</guid>
		<description><![CDATA[mv 桌面 desktop gedit ~/.config/user-dirs.dirs 找到 XDG_DESKTOP_DIR=&#34;$HOME/ 桌面&#34; 改成 XDG_DESKTOP_DIR=&#34;$HOME/desktop&#34; 改完重启gnome sudo /etc/init.d/gdb restart]]></description>
			<content:encoded><![CDATA[<ol>
<li>mv 桌面 desktop </li>
<li>gedit ~/.config/user-dirs.dirs </li>
<li>找到<br />
<blockquote>XDG_DESKTOP_DIR=&quot;$HOME/ 桌面&quot;</p></blockquote>
</li>
<li>改成<br />
<blockquote>XDG_DESKTOP_DIR=&quot;$HOME/desktop&quot;</p></blockquote>
</li>
<li>改完重启gnome </li>
<ol>
<ul>
<li>sudo /etc/init.d/gdb restart</li>
</ul>
</ol>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/19/ubuntu-%e6%9b%b4%e6%94%b9%e9%bb%98%e8%ae%a4%e6%a1%8c%e9%9d%a2%e8%b7%af%e5%be%84/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>libstdc++.so.6: no version information available</title>
		<link>http://www.tek-life.org/2010/05/19/libstdc-so-6-no-version-information-available/</link>
		<comments>http://www.tek-life.org/2010/05/19/libstdc-so-6-no-version-information-available/#comments</comments>
		<pubDate>Wed, 19 May 2010 00:54:06 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>
		<category><![CDATA[apt-get]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/19/libstdc-so-6-no-version-information-available/</guid>
		<description><![CDATA[用apt-get install ，总是出现：/usr/local/lib /libstdc++.so.6: no version information available之类的错误。然后用ldconfig检查&#160; ldconfig -p &#124; grep libstd ，结果如下： &#160;&#160;&#160; libstdc++.so.6 (libc6) =&#62; /usr/local/lib/libstdc++.so.6 &#160;&#160;&#160; libstdc++.so.6 (libc6) =&#62; /usr/lib/libstdc++.so.6 &#160;&#160;&#160; libstdc++.so (libc6) =&#62; /usr/local/lib/libstdc++.so 后来发现问题是：/etc/ld.so.conf.d/libc.conf的问题，将其中的/usr/local/lib去掉，然后运行ldconfig,问题就解决了。 必须运行sudo ldconfig，这样才会改变/etc/ld.so.cache的内容，让配置生效。]]></description>
			<content:encoded><![CDATA[<p>用apt-get install ，总是出现：/usr/local/lib /libstdc++.so.6: no version information available之类的错误。然后用ldconfig检查&#160; ldconfig -p | grep libstd ，结果如下：   <br />&#160;&#160;&#160; libstdc++.so.6 (libc6) =&gt; /usr/local/lib/libstdc++.so.6    <br />&#160;&#160;&#160; libstdc++.so.6 (libc6) =&gt; /usr/lib/libstdc++.so.6    <br />&#160;&#160;&#160; libstdc++.so (libc6) =&gt; /usr/local/lib/libstdc++.so    <br />后来发现问题是：/etc/ld.so.conf.d/libc.conf的问题，将其中的/usr/local/lib去掉，然后运行ldconfig,问题就解决了。</p>
<p>必须运行sudo ldconfig，这样才会改变/etc/ld.so.cache的内容，让配置生效。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/19/libstdc-so-6-no-version-information-available/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>apt-get 两种安装软件方法</title>
		<link>http://www.tek-life.org/2010/05/18/apt-get-%e4%b8%a4%e7%a7%8d%e5%ae%89%e8%a3%85%e8%bd%af%e4%bb%b6%e6%96%b9%e6%b3%95/</link>
		<comments>http://www.tek-life.org/2010/05/18/apt-get-%e4%b8%a4%e7%a7%8d%e5%ae%89%e8%a3%85%e8%bd%af%e4%bb%b6%e6%96%b9%e6%b3%95/#comments</comments>
		<pubDate>Tue, 18 May 2010 07:39:39 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[apt-get]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/18/apt-get-%e4%b8%a4%e7%a7%8d%e5%ae%89%e8%a3%85%e8%bd%af%e4%bb%b6%e6%96%b9%e6%b3%95/</guid>
		<description><![CDATA[方法一(直接用二进制包安装）： apt-get install mozilla-firefox 方法二（源码编译安装） apt-get build-dep mozilla-firefox (先安装编译firefox需要依赖的包） apt-get -b source mozilla-firefox (下载firefox的源码包并自动编译生成deb包，但并没有对源码作任何修改） dpkg -i “生成的firefox软件的deb包的名称”(安装firefox软件的deb包）]]></description>
			<content:encoded><![CDATA[<p>方法一(直接用二进制包安装）：    <br />apt-get install mozilla-firefox     <br />方法二（源码编译安装）     <br />apt-get build-dep mozilla-firefox (先安装编译firefox需要依赖的包）     <br />apt-get -b source mozilla-firefox (下载firefox的源码包并自动编译生成deb包，但并没有对源码作任何修改）     <br />dpkg -i “生成的firefox软件的deb包的名称”(安装firefox软件的deb包）</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/18/apt-get-%e4%b8%a4%e7%a7%8d%e5%ae%89%e8%a3%85%e8%bd%af%e4%bb%b6%e6%96%b9%e6%b3%95/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ubuntu 9.10 编译qemu-0.9.1错误&#8212;dirent.h</title>
		<link>http://www.tek-life.org/2010/05/18/ubuntu-9-10-%e7%bc%96%e8%af%91qemu-0-9-1%e9%94%99%e8%af%afdirent-h/</link>
		<comments>http://www.tek-life.org/2010/05/18/ubuntu-9-10-%e7%bc%96%e8%af%91qemu-0-9-1%e9%94%99%e8%af%afdirent-h/#comments</comments>
		<pubDate>Tue, 18 May 2010 07:38:31 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[qemu 0.9.1]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/18/ubuntu-9-10-%e7%bc%96%e8%af%91qemu-0-9-1%e9%94%99%e8%af%afdirent-h/</guid>
		<description><![CDATA[原因是在usr/include/linux/目录下，缺少dirent.h文件。 从2.6.18内核中拷贝即可。]]></description>
			<content:encoded><![CDATA[<p>原因是在usr/include/linux/目录下，缺少dirent.h文件。</p>
<p>从2.6.18内核中拷贝即可。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/18/ubuntu-9-10-%e7%bc%96%e8%af%91qemu-0-9-1%e9%94%99%e8%af%afdirent-h/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>linux-2.6.21.1编译出错&#8211;“sumversion.c”</title>
		<link>http://www.tek-life.org/2010/05/17/linux-2-6-21-1%e7%bc%96%e8%af%91%e5%87%ba%e9%94%99-%e2%80%9csumversion-c%e2%80%9d/</link>
		<comments>http://www.tek-life.org/2010/05/17/linux-2-6-21-1%e7%bc%96%e8%af%91%e5%87%ba%e9%94%99-%e2%80%9csumversion-c%e2%80%9d/#comments</comments>
		<pubDate>Mon, 17 May 2010 03:31:15 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[compile]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10260</guid>
		<description><![CDATA[scripts/mod/sumversion.c: 在函数‘get_src_version’中: scripts/mod/sumversion.c:384: 错误：‘PATH_MAX’未声明(在此函数内第一次使用) scripts/mod/sumversion.c:384: 错误：(即使在一个函数内多次出现，每个未声明的标识符在其 scripts/mod/sumversion.c:384: 错误：所在的函数内也只报告一次。) scripts/mod/sumversion.c:384: 警告：未使用的变量‘filelist’ make[2]: *** [scripts/mod/sumversion.o] 错误 1 make[1]: *** [scripts/mod] 错误 2 make: *** [scripts] 错误 2 解决办法: #vim  /linux-2.6.21.1/scripts/mod/sumversion.c 添加 #include &#60;limits.h&#62;]]></description>
			<content:encoded><![CDATA[<p>scripts/mod/sumversion.c: 在函数‘get_src_version’中:<br />
scripts/mod/sumversion.c:384:  错误：‘PATH_MAX’未声明(在此函数内第一次使用)<br />
scripts/mod/sumversion.c:384:  错误：(即使在一个函数内多次出现，每个未声明的标识符在其<br />
scripts/mod/sumversion.c:384:  错误：所在的函数内也只报告一次。)<br />
scripts/mod/sumversion.c:384: 警告：未使用的变量‘filelist’<br />
make[2]:  *** [scripts/mod/sumversion.o] 错误 1<br />
make[1]: *** [scripts/mod] 错误 2<br />
make:  *** [scripts] 错误 2</p>
<p>解决办法:<br />
#vim  /linux-2.6.21.1/scripts/mod/sumversion.c<br />
添加</p>
<pre>#include &lt;limits.h&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/17/linux-2-6-21-1%e7%bc%96%e8%af%91%e5%87%ba%e9%94%99-%e2%80%9csumversion-c%e2%80%9d/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>pts与tty</title>
		<link>http://www.tek-life.org/2010/05/14/pts%e4%b8%8etty/</link>
		<comments>http://www.tek-life.org/2010/05/14/pts%e4%b8%8etty/#comments</comments>
		<pubDate>Fri, 14 May 2010 09:17:43 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[pts]]></category>
		<category><![CDATA[tty]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/14/pts%e4%b8%8etty/</guid>
		<description><![CDATA[在使用ubuntu的时候，如果输入ps –e ，会出现： 2358 pts/0&#160;&#160;&#160; 00:00:00 su 2368 pts/0&#160;&#160;&#160; 00:00:00 bash 2789 tty1&#160;&#160;&#160;&#160; 00:00:00 bash 2837 ?&#160;&#160;&#160;&#160;&#160;&#160;&#160; 00:00:00 gnome-terminal 2838 ?&#160;&#160;&#160;&#160;&#160;&#160;&#160; 00:00:00 gnome-pty-helpe 2839 pts/1&#160;&#160;&#160; 00:00:00 bash 2873 pts/2&#160;&#160;&#160; 00:00:00 bash 2905 pts/0&#160;&#160;&#160; 00:00:00 ps 之类的信息，其中tty1和pts/0是何东东？ 在计算机的发展史上，关于人机接口，有2个主要概念，一个是终端；另一个是控制台 终端分为 字符哑终端 和 &#8230; <a href="http://www.tek-life.org/2010/05/14/pts%e4%b8%8etty/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>在使用ubuntu的时候，如果输入ps –e ，会出现：</p>
<blockquote><p>2358 pts/0&#160;&#160;&#160; 00:00:00 su     <br />2368 pts/0&#160;&#160;&#160; 00:00:00 bash      <br />2789 tty1&#160;&#160;&#160;&#160; 00:00:00 bash      <br />2837 ?&#160;&#160;&#160;&#160;&#160;&#160;&#160; 00:00:00 gnome-terminal      <br />2838 ?&#160;&#160;&#160;&#160;&#160;&#160;&#160; 00:00:00 gnome-pty-helpe      <br />2839 pts/1&#160;&#160;&#160; 00:00:00 bash      <br />2873 pts/2&#160;&#160;&#160; 00:00:00 bash      <br />2905 pts/0&#160;&#160;&#160; 00:00:00 ps </p>
</blockquote>
<p>之类的信息，其中tty1和pts/0是何东东？</p>
<p>在计算机的发展史上，关于人机接口，有2个主要概念，一个是终端；另一个是控制台</p>
<p>终端分为 字符哑终端 和 图形终端</p>
<p>控制台 是另外一个人机接口，不通过终端与主机相连, 而是通过显示卡-显示器和键盘接口分别与主机相连。</p>
<p>在PC机上，没有终端的概念，仅有控制台，而且是1个。   <br />我们所看到的这些tty1-7和pts/0实际上都是虚拟的终端，由getty来虚拟的。而tty7是由Xorg来管理的，如下：</p>
<blockquote><p>892 tty7&#160;&#160;&#160;&#160; 00:00:10 Xorg     <br />894 ?&#160;&#160;&#160;&#160;&#160;&#160;&#160; 00:00:00 hald-addon-acpi      <br />895 ?&#160;&#160;&#160;&#160;&#160;&#160;&#160; 00:00:00 hald-addon-inpu      <br />944 tty4&#160;&#160;&#160;&#160; 00:00:00 getty      <br />946 tty5&#160;&#160;&#160;&#160; 00:00:00 getty      <br />956 tty2&#160;&#160;&#160;&#160; 00:00:00 getty      <br />957 tty3&#160;&#160;&#160;&#160; 00:00:00 getty      <br />961 tty6&#160;&#160;&#160;&#160; 00:00:00 getty </p>
</blockquote>
<p>另外，扩展一下：</p>
<p>在UNIX系统中，计算机显示器通常被称为控制台终端（Console），关于/dev/console&#160; 应该来说更像一个缓冲结果。实现对内核的打印，比如说内核把要打印的内容装入缓冲区，然后由console来决定打印到哪里吧（比如是tty0还是串口等）。</p>
<p>在kernel中，console这个结构中有个device。 如果，我们来个专门打印内核的设备（比如通过串口），我们把那个串口register_console，那么/dev/console就到这个串口设备了。这时，内核打印就到这个串口设备了，而用户的打印还是和上面的/dev/tty相关，如果/dev/tty对应/dev/tty0,那么用户打印还在窗口中出现。所以说/dev/console是用来外接控制台的。</p>
<p>好，比较明白了吧，在打印时候的路径为：console-&gt;tty-&gt;tty?</p>
<ul>
<li>参考资料：     <br /><a title="http://blog.csdn.net/liaoxinmeng/archive/2009/12/14/5004743.aspx" href="http://blog.csdn.net/liaoxinmeng/archive/2009/12/14/5004743.aspx">http://blog.csdn.net/liaoxinmeng/archive/2009/12/14/5004743.aspx</a>      </li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/14/pts%e4%b8%8etty/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>git备忘</title>
		<link>http://www.tek-life.org/2010/05/11/git%e5%a4%87%e5%bf%98/</link>
		<comments>http://www.tek-life.org/2010/05/11/git%e5%a4%87%e5%bf%98/#comments</comments>
		<pubDate>Tue, 11 May 2010 13:00:37 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Se]]></category>
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/11/git%e5%a4%87%e5%bf%98/</guid>
		<description><![CDATA[git diff :比较索引和本地之间的区别，当本地提交到索引（使用了add命令），则输出为空 而 git diff -cached 应该是比较仓库和索引之间的区别，当索引提交到仓库（使用了commit命令），则输出为空 git-merge后，是会产生双父母的，这种情况如果要查看父母信息，： $git show HEAD^1 //查看HEAD的第一个父母,合并之前的主分支 $git show HEAD^2 //查看HEAD的第二个父母，合并之前的被合并分支 git cat-file –t&#160; … 查看文件类型 git cat-file 类型 id 查看文件内容 git ls-tree id 查看树 Changes to be committed表示在index和commit的区别状况。 而Changed but not updated表示当前目录和index的区别状况。 git &#8230; <a href="http://www.tek-life.org/2010/05/11/git%e5%a4%87%e5%bf%98/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>git diff :比较<strong>索引</strong>和<strong>本地</strong>之间的区别，当本地提交到索引（使用了add命令），则输出为空     <br />而 git diff -cached 应该是比较<strong>仓库</strong>和<strong>索引</strong>之间的区别，当索引提交到仓库（使用了commit命令），则输出为空</p>
<p>git-merge后，是会产生双父母的，这种情况如果要查看父母信息，：    <br />$git show HEAD^1 //查看HEAD的第一个父母,合并之前的主分支     <br />$git show HEAD^2 //查看HEAD的第二个父母，合并之前的被合并分支</p>
<p>git cat-file –t&#160; … 查看文件类型   <br />git cat-file 类型 id 查看文件内容    <br />git ls-tree id 查看树    </p>
<p>Changes to be committed表示在index和commit的区别状况。   <br />而Changed but not updated表示当前目录和index的区别状况。</p>
<p>git commit之后要输入commit信息，加个 –s 不用输入信息</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/11/git%e5%a4%87%e5%bf%98/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>android 日记(1)</title>
		<link>http://www.tek-life.org/2010/05/07/android-%e6%97%a5%e8%ae%b01/</link>
		<comments>http://www.tek-life.org/2010/05/07/android-%e6%97%a5%e8%ae%b01/#comments</comments>
		<pubDate>Fri, 07 May 2010 08:35:15 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[android]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/07/android-%e6%97%a5%e8%ae%b01/</guid>
		<description><![CDATA[查看目标平台: omycle@omycle-desktop:~/ftk_emu/tools$ android list target Available Android targets: id: 1 or &#34;android-7&#34; &#160;&#160;&#160;&#160; Name: Android 2.1-update1 &#160;&#160;&#160;&#160; Type: Platform &#160;&#160;&#160;&#160; API level: 7 &#160;&#160;&#160;&#160; Revision: 1 &#160; &#160; Skins: WVGA800, WQVGA432, WVGA854, WQVGA400, HVGA (default), QVGA 查看avd: omycle@omycle-desktop:~/ftk_emu/tools$ android list &#8230; <a href="http://www.tek-life.org/2010/05/07/android-%e6%97%a5%e8%ae%b01/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>查看目标平台:   <br />omycle@omycle-desktop:~/ftk_emu/tools$ android list target    <br />Available Android targets:    <br />id: 1 or &quot;android-7&quot;    <br />&#160;&#160;&#160;&#160; Name: Android 2.1-update1    <br />&#160;&#160;&#160;&#160; Type: Platform    <br />&#160;&#160;&#160;&#160; API level: 7    <br />&#160;&#160;&#160;&#160; Revision: 1    <br />&#160; <strong>&#160; <font color="#ff0000"> Skins</font>: WVGA800, WQVGA432, WVGA854, WQVGA400, HVGA (default), QVGA</strong></p>
<p>查看avd:   <br />omycle@omycle-desktop:~/ftk_emu/tools$ android list avd    <br />Available Android Virtual Devices:    <br />&#160;&#160;&#160; Name: ftk    <br />&#160;&#160;&#160; Path: /home/omycle/.android/avd/ftk.avd    <br />&#160; Target: Android 2.1-update1 (API level 7)    <br />&#160;&#160;&#160; Skin: HVGA</p>
<p>查看现有的皮肤：   <br />omycle@omycle-desktop:~/ftk_emu/platforms/android-2.1-update1/skins$ ls    <br />HVGA&#160; NOTICE.txt&#160; QVGA&#160; WQVGA400&#160; WQVGA432&#160; WVGA800&#160; WVGA854</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/07/android-%e6%97%a5%e8%ae%b01/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>查看内核版本</title>
		<link>http://www.tek-life.org/2010/05/06/%e6%9f%a5%e7%9c%8b%e5%86%85%e6%a0%b8%e7%89%88%e6%9c%ac/</link>
		<comments>http://www.tek-life.org/2010/05/06/%e6%9f%a5%e7%9c%8b%e5%86%85%e6%a0%b8%e7%89%88%e6%9c%ac/#comments</comments>
		<pubDate>Thu, 06 May 2010 12:55:23 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[version]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/06/%e6%9f%a5%e7%9c%8b%e5%86%85%e6%a0%b8%e7%89%88%e6%9c%ac/</guid>
		<description><![CDATA[查看 内核的有 uname –a omycle@omycle-desktop:~$ uname -a Linux omycle-desktop 2.6.31-21-generic #59-Ubuntu SMP Wed Mar 24 07:28:56 UTC 2010 i686 GNU/Linux &#160; cat /etc/issue [root@cintel lhf]# cat /etc/issue Red Hat Enterprise Linux Server release 5 (Tikanga) Kernel \r on an \m &#8230; <a href="http://www.tek-life.org/2010/05/06/%e6%9f%a5%e7%9c%8b%e5%86%85%e6%a0%b8%e7%89%88%e6%9c%ac/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>查看 内核的有</p>
<p><strong>uname –a</strong></p>
<p>omycle@omycle-desktop:~$ uname -a   <br />Linux omycle-desktop 2.6.31-21-generic #59-Ubuntu SMP Wed Mar 24 07:28:56 UTC 2010 i686 GNU/Linux</p>
<p>&#160;</p>
<p><strong>cat /etc/issue</strong> </p>
<p>[root@cintel lhf]# cat /etc/issue   <br />Red Hat Enterprise Linux Server release 5 (Tikanga)    <br />Kernel \r on an \m</p>
<p>omycle@omycle-desktop:~$ cat /etc/issue   <br />Ubuntu 9.10 \n \l </p>
<p><strong>cat /etc/redhat_release</strong> </p>
<p>[root@cintel lhf]# cat /etc/redhat-release   <br />Red Hat Enterprise Linux Server release 5 (Tikanga)</p>
<p>&#160;</p>
<p>&#160;</p>
<p>查看发行版的有：</p>
<p>&#160;<strong>lsb_release –a</strong></p>
<p>omycle@omycle-desktop:~$ lsb_release -a   <br />No LSB modules are available.    <br />Distributor ID: Ubuntu    <br />Description:&#160;&#160;&#160; Ubuntu 9.10    <br />Release:&#160;&#160;&#160;&#160;&#160;&#160;&#160; 9.10    <br />Codename:&#160;&#160;&#160;&#160;&#160;&#160; karmic</p>
<p>[root@cintel lhf]# lsb_release -a   <br />LSB Version:&#160;&#160;&#160; :core-3.1-ia32:core-3.1-noarch:graphics-3.1-ia32:graphics-3.1-noarch    <br />Distributor ID: RedHatEnterpriseServer    <br />Description:&#160;&#160;&#160; Red Hat Enterprise Linux Server release 5 (Tikanga)    <br />Release:&#160;&#160;&#160;&#160;&#160;&#160;&#160; 5    <br />Codename:&#160;&#160;&#160;&#160;&#160;&#160; Tikanga</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/06/%e6%9f%a5%e7%9c%8b%e5%86%85%e6%a0%b8%e7%89%88%e6%9c%ac/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux 虚拟内存规划</title>
		<link>http://www.tek-life.org/2010/05/06/linux-%e8%99%9a%e6%8b%9f%e5%86%85%e5%ad%98%e8%a7%84%e5%88%92/</link>
		<comments>http://www.tek-life.org/2010/05/06/linux-%e8%99%9a%e6%8b%9f%e5%86%85%e5%ad%98%e8%a7%84%e5%88%92/#comments</comments>
		<pubDate>Thu, 06 May 2010 08:06:36 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[memory layout]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/06/linux-%e8%99%9a%e6%8b%9f%e5%86%85%e5%ad%98%e8%a7%84%e5%88%92/</guid>
		<description><![CDATA[从先静的csdn上看到的大内高手系列文章，其中有设计Linux虚拟内存模型的，这是他总结的要点，觉得挺好。 Linux的内存模型，一般为： 地址 作用 说明 &#62;=0xc000 0000 内核虚拟存储器 用户代码不可见区域 &#60;0xc000 0000 Stack（用户栈） ESP指向栈顶 &#160; ↓ ↑ 空闲内存 &#62;=0&#215;4000 0000 文件映射区 &#160; &#60;0&#215;4000 0000 ↑ 空闲内存 &#160; Heap(运行时堆) 通过brk/sbrk系统调用扩大堆，向上增长。 &#160; .data、.bss(读写段) 从可执行文件中加载 &#62;=0&#215;0804 8000 .init、.text、.rodata(只读段) 从可执行文件中加载 &#60;0&#215;0804 8000 保留区域 &#160; 用十进制的观点:（这样更便于接受） &#8230; <a href="http://www.tek-life.org/2010/05/06/linux-%e8%99%9a%e6%8b%9f%e5%86%85%e5%ad%98%e8%a7%84%e5%88%92/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>从先静的csdn上看到的大内高手系列文章，其中有设计Linux虚拟内存模型的，这是他总结的要点，觉得挺好。</p>
<p>Linux的内存模型，一般为：<br />
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="189">
<p>地址</p>
</td>
<td valign="top" width="189">
<p>作用</p>
</td>
<td valign="top" width="189">
<p>说明</p>
</td>
</tr>
<tr>
<td valign="top" width="189">
<p>&gt;=0xc000 0000</p>
</td>
<td valign="top" width="189">
<p>内核虚拟存储器</p>
</td>
<td valign="top" width="189">
<p>用户代码不可见区域</p>
</td>
</tr>
<tr>
<td valign="top" width="189">
<p>&lt;0xc000 0000</p>
</td>
<td valign="top" width="189">
<p>Stack（用户栈）</p>
</td>
<td valign="top" width="189">
<p>ESP指向栈顶</p>
</td>
</tr>
<tr>
<td valign="top" width="189">&#160;</td>
<td valign="top" width="189">
<p>↓</p>
<p>↑</p>
</td>
<td valign="top" width="189">
<p>空闲内存</p>
</td>
</tr>
<tr>
<td valign="top" width="189">
<p>&gt;=0&#215;4000 0000</p>
</td>
<td valign="top" width="189">
<p>文件映射区</p>
</td>
<td valign="top" width="189">&#160;</td>
</tr>
<tr>
<td valign="top" width="189">
<p>&lt;0&#215;4000 0000</p>
</td>
<td valign="top" width="189">
<p>↑</p>
</td>
<td valign="top" width="189">
<p>空闲内存</p>
</td>
</tr>
<tr>
<td valign="top" width="189">&#160;</td>
<td valign="top" width="189">
<p>Heap(运行时堆)</p>
</td>
<td valign="top" width="189">
<p>通过brk/sbrk系统调用扩大堆，向上增长。</p>
</td>
</tr>
<tr>
<td valign="top" width="189">&#160;</td>
<td valign="top" width="189">
<p>.data、.bss(读写段)</p>
</td>
<td valign="top" width="189">
<p>从可执行文件中加载</p>
</td>
</tr>
<tr>
<td valign="top" width="189">
<p>&gt;=0&#215;0804 8000</p>
</td>
<td valign="top" width="189">
<p>.init、.text、.rodata(只读段)</p>
</td>
<td valign="top" width="189">
<p>从可执行文件中加载</p>
</td>
</tr>
<tr>
<td valign="top" width="189">
<p>&lt;0&#215;0804 8000</p>
</td>
<td valign="top" width="189">
<p>保留区域</p>
</td>
<td valign="top" width="189">&#160;</td>
</tr>
</tbody>
</table>
<p>用十进制的观点:（这样更便于接受）</p>
<p>文件映射区是在1G以上的地方</p>
<p>栈 是在3G以下的地方</p>
<p>内核空间 是在3G以上的地方</p>
<p>在先静的文章中，还举出了单线程和多线程的例子。对于多线程，我还没有做过多的了解，对单线程，要区分如果一次从堆中划分较多的空间，那么会从文件映射去来划分，也就是从1G以上的地方去划分，而不是在1G一下(表格中brk/sbrk的地方)</p>
<p>先静文章地址：http://blog.csdn.net/absurd/archive/2006/06/19/814268.aspx</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/06/linux-%e8%99%9a%e6%8b%9f%e5%86%85%e5%ad%98%e8%a7%84%e5%88%92/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>RHEL /CentOS yum源的安装</title>
		<link>http://www.tek-life.org/2010/05/05/rhel-yum%e6%ba%90%e7%9a%84%e5%ae%89%e8%a3%85/</link>
		<comments>http://www.tek-life.org/2010/05/05/rhel-yum%e6%ba%90%e7%9a%84%e5%ae%89%e8%a3%85/#comments</comments>
		<pubDate>Wed, 05 May 2010 14:37:18 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>
		<category><![CDATA[rhel]]></category>
		<category><![CDATA[yum]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/05/rhel-yum%e6%ba%90%e7%9a%84%e5%ae%89%e8%a3%85/</guid>
		<description><![CDATA[方法一： 中科大的源： cd /etc/yum.repos.d mv CentOS-Base.repo CentOS-Base.repo.save wget http://centos.ustc.edu.cn/CentOS-Base.repo.5 mv CentOS-Base.repo.5 CentOS-Base.repo 参考网址：http://centos.ustc.edu.cn/ ———————————————————————————————————————————————————————————————— 方法二： 首先察看一下本RHEL的发行版本： cat /proc/redhat-release 1。确保RHEL5中已经安装了yum。 2。修改源配置文件 ＃gedit /etc/yum.repos.d/CentOS-Base.repo 在其中加入以下内容 [base] name=CentOS-5-Base #mirrorlist=http://mirrorlist.centos.org/?release=$releasever5&#38;arch=$basearch&#38;repo=os #baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/ baseurl=http://ftp.sjtu.edu.cn/centos/5/os/$basearch/ gpgcheck=1 gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5 #released updates [update] name=CentOS-5-Updates #mirrorlist=http://mirrorlist.centos.org/?release=4&#38;arch=$basearch&#38;repo=updates baseurl=http://ftp.sjtu.edu.cn/centos/5/updates/$basearch/ gpgcheck=1 gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5 #packages used/produced &#8230; <a href="http://www.tek-life.org/2010/05/05/rhel-yum%e6%ba%90%e7%9a%84%e5%ae%89%e8%a3%85/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>方法一：</p>
<p>中科大的源：</p>
<pre>cd /etc/yum.repos.d
mv CentOS-Base.repo  CentOS-Base.repo.save
wget http://centos.ustc.edu.cn/CentOS-Base.repo.5
mv CentOS-Base.repo.5 CentOS-Base.repo

参考网址：http://centos.ustc.edu.cn/
————————————————————————————————————————————————————————————————
</pre>
<p>方法二：</p>
<p>首先察看一下本RHEL的发行版本：</p>
<p>cat /proc/redhat-release</p>
<p>1。确保RHEL5中已经安装了yum。</p>
<p>2。修改源配置文件 ＃gedit /etc/yum.repos.d/CentOS-Base.repo</p>
<p>在其中加入以下内容</p>
<p>[base]<br />
name=CentOS-5-Base<br />
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever5&amp;arch=$basearch&amp;repo=os<br />
#baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/<br />
baseurl=http://ftp.sjtu.edu.cn/centos/5/os/$basearch/<br />
gpgcheck=1<br />
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5<br />
#released updates<br />
[update]<br />
name=CentOS-5-Updates<br />
#mirrorlist=http://mirrorlist.centos.org/?release=4&amp;arch=$basearch&amp;repo=updates<br />
baseurl=http://ftp.sjtu.edu.cn/centos/5/updates/$basearch/<br />
gpgcheck=1<br />
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5<br />
#packages used/produced in the build but not released<br />
[addons]<br />
name=CentOS-5-Addons<br />
#mirrorlist=http://mirrorlist.centos.org/?release=4&amp;arch=$basearch&amp;repo=addons<br />
baseurl=http://ftp.sjtu.edu.cn/centos/5/addons/$basearch/<br />
gpgcheck=1<br />
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5<br />
#additional packages that may be useful<br />
[extras]<br />
name=CentOS-5-Extras<br />
#mirrorlist=http://mirrorlist.centos.org/?release=4&amp;arch=$basearch&amp;repo=extras<br />
baseurl=http://ftp.sjtu.edu.cn/centos/5/extras/$basearch/<br />
gpgcheck=1<br />
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5<br />
#additional packages that extend functionality of existing packages<br />
[centosplus]<br />
name=CentOS-5-Plus<br />
#mirrorlist=http://mirrorlist.centos.org/?release=4&amp;arch=$basearch&amp;repo=centosplus<br />
baseurl=http://ftp.sjtu.edu.cn/centos/5/centosplus/$basearch/<br />
gpgcheck=1<br />
enabled=0<br />
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5<br />
#contrib &#8211; packages by Centos Users<br />
[contrib]<br />
name=CentOS-5-Contrib<br />
#mirrorlist=http://mirrorlist.centos.org/?release=4&amp;arch=$basearch&amp;repo=contrib<br />
baseurl=http://ftp.sjtu.edu.cn/centos/5/contrib/$basearch/<br />
gpgcheck=1<br />
enabled=0<br />
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5<br />
# vi dag.repo<br />
[dag]<br />
name=Dag RPM Repository for RHEL5<br />
baseurl=http://ftp.riken.jp/Linux/dag/redhat/el5/en/$basearch/dag/<br />
enabled=1<br />
gpgcheck=1<br />
gpgkey=http://ftp.riken.jp/Linux/dag/packages/RPM-GPG-KEY.dag.txt</p>
<p>3. 导入key<br />
＃rpm &#8211;import http://ftp.sjtu.edu.cn/centos/5/os/i386/RPM-GPG-KEY-CentOS-5</p>
<p>4. 运行，测试<br />
＃yum update</p>
<p>在安装软件的时候，会出现key失效，或者key太老的警告。</p>
<p>解决方法：</p>
<p>1、关闭验证，也就是把gpgcheck=0</p>
<p>2、将这两个导入，就可以了。<br />
rpm &#8211;import <a href="http://dag.wieers.com/rpm/packages/RPM-GPG-KEY.dag.txt">http://dag.wieers.com/rpm/packages/RPM-GPG-KEY.dag.txt</a><br />
rpm &#8211;import <a href="http://quattor.web.lal.in2p3.fr/packages/os/sl520-x86_64/base/RPM-GPG-KEYs/RPM-GPG-KEY-CentOS-5">http://quattor.web.lal.in2p3.fr/packages/os/sl520-x86_64/base/RPM-GPG-KEYs/RPM-GPG-KEY-CentOS-5</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/05/rhel-yum%e6%ba%90%e7%9a%84%e5%ae%89%e8%a3%85/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>android模拟器</title>
		<link>http://www.tek-life.org/2010/05/05/android%e6%a8%a1%e6%8b%9f%e5%99%a8/</link>
		<comments>http://www.tek-life.org/2010/05/05/android%e6%a8%a1%e6%8b%9f%e5%99%a8/#comments</comments>
		<pubDate>Wed, 05 May 2010 08:02:05 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[QEMU-VIRT]]></category>
		<category><![CDATA[android]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/05/android%e6%a8%a1%e6%8b%9f%e5%99%a8/</guid>
		<description><![CDATA[通过adb bugreport可以获取android的bug信息 adb bugreport &#62; bugreport.txt 然后通过winscp传到宿主机上： emulator所模拟的信息： &#60;6&#62;Initializing cgroup subsys cpu &#60;5&#62;Linux version 2.6.29-00261-g0097074 (digit@digit.mtv.corp.google.com) (gcc version 4.4.0 (GCC) ) #14 Tue Feb 2 15:49:02 PST 2010 &#60;4&#62;CPU: ARM926EJ-S [41069265] revision 5 (ARMv5TEJ), cr=00093177 &#60;4&#62;CPU: VIVT data cache, VIVT &#8230; <a href="http://www.tek-life.org/2010/05/05/android%e6%a8%a1%e6%8b%9f%e5%99%a8/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>通过adb bugreport可以获取android的bug信息</p>
<p>adb bugreport &gt; bugreport.txt</p>
<p>然后通过winscp传到宿主机上：</p>
<p>emulator所模拟的信息：</p>
<p>&lt;6&gt;Initializing cgroup subsys cpu   <br />&lt;5&gt;<strong>Linux version 2.6.29</strong>-00261-g0097074 (digit@digit.mtv.corp.google.com) (gcc version 4.4.0 (GCC) ) #14 Tue Feb 2 15:49:02 PST 2010    <br />&lt;4&gt;CPU: <strong>ARM926EJ-S</strong> [41069265] revision 5 (ARMv5TEJ), cr=00093177    <br />&lt;4&gt;CPU: VIVT data cache, VIVT instruction cache    <br />&lt;4&gt;Machine: <strong>Goldfish     <br /></strong>&lt;4&gt;Memory policy: ECC disabled, Data cache writeback</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/05/android%e6%a8%a1%e6%8b%9f%e5%99%a8/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ftk效果图</title>
		<link>http://www.tek-life.org/2010/05/04/ftk%e6%95%88%e6%9e%9c%e5%9b%be/</link>
		<comments>http://www.tek-life.org/2010/05/04/ftk%e6%95%88%e6%9e%9c%e5%9b%be/#comments</comments>
		<pubDate>Tue, 04 May 2010 14:13:31 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[QEMU-VIRT]]></category>
		<category><![CDATA[ftk]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10240</guid>
		<description><![CDATA[  ftk不错的小玩意儿 具体编译方法，参见： http://www.limodev.cn/blog/archives/1392 运行桌面：]]></description>
			<content:encoded><![CDATA[<p> </p>
<p>ftk不错的小玩意儿</p>
<p>具体编译方法，参见：</p>
<p><a href="http://www.limodev.cn/blog/archives/1392">http://www.limodev.cn/blog/archives/1392</a></p>
<p><a href="http://www.tek-life.org/wp-content/uploads/2010/05/lab1.jpg"><img title="lab" src="http://www.tek-life.org/wp-content/uploads/2010/05/lab1-193x300.jpg" alt="" width="193" height="300" /></a></p>
<p>运行桌面：</p>
<p><a href="http://www.tek-life.org/wp-content/uploads/2010/05/dk.png"><img class="alignnone size-medium wp-image-10242" title="dk" src="http://www.tek-life.org/wp-content/uploads/2010/05/dk-193x300.png" alt="" width="193" height="300" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/04/ftk%e6%95%88%e6%9e%9c%e5%9b%be/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>binutils的升级方式</title>
		<link>http://www.tek-life.org/2010/05/04/binutils%e7%9a%84%e5%8d%87%e7%ba%a7%e6%96%b9%e5%bc%8f/</link>
		<comments>http://www.tek-life.org/2010/05/04/binutils%e7%9a%84%e5%8d%87%e7%ba%a7%e6%96%b9%e5%bc%8f/#comments</comments>
		<pubDate>Tue, 04 May 2010 03:29:55 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/05/04/binutils%e7%9a%84%e5%8d%87%e7%ba%a7%e6%96%b9%e5%bc%8f/</guid>
		<description><![CDATA[binutils即为二进制工具，提供了as ld之类的工具。要查看本机的binutils版本，可以 as –vison 因为在安装交叉编译工具链的时候，我安装了2.19的低版本的binutils，因此，要想升级为2.20或者更高的版本，通过apt-get install 的方式是不行的。 在网上看到，有的童鞋说必须通过编译的方式才能升级。貌似有道理。]]></description>
			<content:encoded><![CDATA[<p>binutils即为二进制工具，提供了as ld之类的工具。要查看本机的binutils版本，可以</p>
<p>as –vison</p>
<p>因为在安装交叉编译工具链的时候，我安装了2.19的低版本的binutils，因此，要想升级为2.20或者更高的版本，通过apt-get install 的方式是不行的。</p>
<p>在网上看到，有的童鞋说必须通过编译的方式才能升级。貌似有道理。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/04/binutils%e7%9a%84%e5%8d%87%e7%ba%a7%e6%96%b9%e5%bc%8f/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>git获取android源码，稍微有点眉目</title>
		<link>http://www.tek-life.org/2010/05/01/git%e8%8e%b7%e5%8f%96android%e6%ba%90%e7%a0%81%ef%bc%8c%e7%a8%8d%e5%be%ae%e6%9c%89%e7%82%b9%e7%9c%89%e7%9b%ae/</link>
		<comments>http://www.tek-life.org/2010/05/01/git%e8%8e%b7%e5%8f%96android%e6%ba%90%e7%a0%81%ef%bc%8c%e7%a8%8d%e5%be%ae%e6%9c%89%e7%82%b9%e7%9c%89%e7%9b%ae/#comments</comments>
		<pubDate>Sat, 01 May 2010 01:08:19 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10235</guid>
		<description><![CDATA[tek-life@tek-life-desktop:~$ repo init -u git://android.git.kernel.org/platform/manifest.git Getting repo &#8230; from git://android.git.kernel.org/tools/repo.git fatal: cannot make /home/tek-life/.repo/repo directory: File exists tek-life@tek-life-desktop:~$ repo init -u git://android.git.kernel.org/platform/manifest.git Getting repo &#8230; from git://android.git.kernel.org/tools/repo.git remote: Counting objects: 1008, done. remote: Compressing objects: 100% (417/417), done. remote: Total &#8230; <a href="http://www.tek-life.org/2010/05/01/git%e8%8e%b7%e5%8f%96android%e6%ba%90%e7%a0%81%ef%bc%8c%e7%a8%8d%e5%be%ae%e6%9c%89%e7%82%b9%e7%9c%89%e7%9b%ae/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>tek-life@tek-life-desktop:~$ repo init -u git://android.git.kernel.org/platform/manifest.git<br />
Getting repo &#8230;<br />
   from git://android.git.kernel.org/tools/repo.git<br />
fatal: cannot make /home/tek-life/.repo/repo directory: File exists<br />
tek-life@tek-life-desktop:~$ repo init -u git://android.git.kernel.org/platform/manifest.git<br />
Getting repo &#8230;<br />
   from git://android.git.kernel.org/tools/repo.git<br />
remote: Counting objects: 1008, done.<br />
remote: Compressing objects: 100% (417/417), done.<br />
remote: Total 1008 (delta 633), reused 918 (delta 570)<br />
Receiving objects: 100% (1008/1008), 294.73 KiB | 54 KiB/s, done.<br />
Resolving deltas: 100% (633/633), done.<br />
From git://android.git.kernel.org/tools/repo<br />
 * [new branch]      maint      -> origin/maint<br />
 * [new branch]      master     -> origin/master<br />
 * [new branch]      stable     -> origin/stable<br />
 * [new tag]         v1.6.9.4   -> v1.6.9.4<br />
From git://android.git.kernel.org/tools/repo<br />
 * [new tag]         v1.0       -> v1.0<br />
 * [new tag]         v1.0.1     -> v1.0.1<br />
 * [new tag]         v1.0.2     -> v1.0.2<br />
 * [new tag]         v1.0.3     -> v1.0.3<br />
 * [new tag]         v1.0.4     -> v1.0.4<br />
 * [new tag]         v1.0.5     -> v1.0.5<br />
 * [new tag]         v1.0.6     -> v1.0.6<br />
 * [new tag]         v1.0.7     -> v1.0.7<br />
 * [new tag]         v1.0.8     -> v1.0.8<br />
 * [new tag]         v1.0.9     -> v1.0.9<br />
 * [new tag]         v1.1       -> v1.1<br />
 * [new tag]         v1.2       -> v1.2<br />
 * [new tag]         v1.3       -> v1.3<br />
 * [new tag]         v1.3.1     -> v1.3.1<br />
 * [new tag]         v1.3.2     -> v1.3.2<br />
 * [new tag]         v1.4       -> v1.4<br />
 * [new tag]         v1.4.1     -> v1.4.1<br />
 * [new tag]         v1.4.2     -> v1.4.2<br />
 * [new tag]         v1.4.3     -> v1.4.3<br />
 * [new tag]         v1.4.4     -> v1.4.4<br />
 * [new tag]         v1.5       -> v1.5<br />
 * [new tag]         v1.5.1     -> v1.5.1<br />
 * [new tag]         v1.6       -> v1.6<br />
 * [new tag]         v1.6.1     -> v1.6.1<br />
 * [new tag]         v1.6.2     -> v1.6.2<br />
 * [new tag]         v1.6.3     -> v1.6.3<br />
 * [new tag]         v1.6.4     -> v1.6.4<br />
 * [new tag]         v1.6.5     -> v1.6.5<br />
 * [new tag]         v1.6.6     -> v1.6.6<br />
 * [new tag]         v1.6.7     -> v1.6.7<br />
 * [new tag]         v1.6.7.1   -> v1.6.7.1<br />
 * [new tag]         v1.6.7.2   -> v1.6.7.2<br />
 * [new tag]         v1.6.7.3   -> v1.6.7.3<br />
 * [new tag]         v1.6.7.4   -> v1.6.7.4<br />
 * [new tag]         v1.6.7.5   -> v1.6.7.5<br />
 * [new tag]         v1.6.8     -> v1.6.8<br />
 * [new tag]         v1.6.8.1   -> v1.6.8.1<br />
 * [new tag]         v1.6.8.10  -> v1.6.8.10<br />
 * [new tag]         v1.6.8.11  -> v1.6.8.11<br />
 * [new tag]         v1.6.8.2   -> v1.6.8.2<br />
 * [new tag]         v1.6.8.3   -> v1.6.8.3<br />
 * [new tag]         v1.6.8.4   -> v1.6.8.4<br />
 * [new tag]         v1.6.8.5   -> v1.6.8.5<br />
 * [new tag]         v1.6.8.6   -> v1.6.8.6<br />
 * [new tag]         v1.6.8.7   -> v1.6.8.7<br />
 * [new tag]         v1.6.8.8   -> v1.6.8.8<br />
 * [new tag]         v1.6.8.9   -> v1.6.8.9<br />
 * [new tag]         v1.6.9     -> v1.6.9<br />
 * [new tag]         v1.6.9.1   -> v1.6.9.1<br />
 * [new tag]         v1.6.9.2   -> v1.6.9.2<br />
 * [new tag]         v1.6.9.3   -> v1.6.9.3<br />
Getting manifest &#8230;<br />
   from git://android.git.kernel.org/platform/manifest.git</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/05/01/git%e8%8e%b7%e5%8f%96android%e6%ba%90%e7%a0%81%ef%bc%8c%e7%a8%8d%e5%be%ae%e6%9c%89%e7%82%b9%e7%9c%89%e7%9b%ae/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ubuntu运行zhcon后死机</title>
		<link>http://www.tek-life.org/2010/04/30/ubuntu%e8%bf%90%e8%a1%8czhcon%e5%90%8e%e6%ad%bb%e6%9c%ba/</link>
		<comments>http://www.tek-life.org/2010/04/30/ubuntu%e8%bf%90%e8%a1%8czhcon%e5%90%8e%e6%ad%bb%e6%9c%ba/#comments</comments>
		<pubDate>Fri, 30 Apr 2010 03:23:20 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[zhcon]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10231</guid>
		<description><![CDATA[解决方法：zhcon &#8211;utf8 &#8211;drv=vga]]></description>
			<content:encoded><![CDATA[<p>解决方法：zhcon &#8211;utf8 &#8211;drv=vga</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/04/30/ubuntu%e8%bf%90%e8%a1%8czhcon%e5%90%8e%e6%ad%bb%e6%9c%ba/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>These critical programs are missing or too old: as ld</title>
		<link>http://www.tek-life.org/2010/04/29/these-critical-programs-are-missing-or-too-old-as-ld/</link>
		<comments>http://www.tek-life.org/2010/04/29/these-critical-programs-are-missing-or-too-old-as-ld/#comments</comments>
		<pubDate>Thu, 29 Apr 2010 12:57:04 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/04/29/these-critical-programs-are-missing-or-too-old-as-ld/</guid>
		<description><![CDATA[在安装交叉编译工具的时候，提示这个错误，因为binutils版本为2.20，因此会出现ld,as 版本不搭配的错误，其实这一点在glibc 的configure里面可以看到。 在google里面看到大多数人都是pathch的方法。我的办法是在我用的ubuntu 9.10系统里面重新安装binuntils 2.19版本，等安装完交叉编译链后，再重新将binuntils 升级为2.20即可。]]></description>
			<content:encoded><![CDATA[<p>在安装交叉编译工具的时候，提示这个错误，因为binutils版本为2.20，因此会出现ld,as 版本不搭配的错误，其实这一点在glibc 的configure里面可以看到。</p>
<p>在google里面看到大多数人都是pathch的方法。我的办法是在我用的ubuntu 9.10系统里面重新安装binuntils 2.19版本，等安装完交叉编译链后，再重新将binuntils 升级为2.20即可。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/04/29/these-critical-programs-are-missing-or-too-old-as-ld/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>制作initrd</title>
		<link>http://www.tek-life.org/2010/04/22/%e5%88%b6%e4%bd%9cinitrd/</link>
		<comments>http://www.tek-life.org/2010/04/22/%e5%88%b6%e4%bd%9cinitrd/#comments</comments>
		<pubDate>Thu, 22 Apr 2010 00:51:29 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[QEMU-VIRT]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10219</guid>
		<description><![CDATA[首先，制作Initrd，需要了解，inidrd是什么，它由什么构成的。 然后，我们会发现，在嵌入式的Initrd中 busybox是一个非常常用的工具，可是，到底busybox在制作initrd中扮演什么样的角色呢？ 其实，initrd只是引入了一些应用程序而已，只是，这些应用程序做的很好，体积很小。当然，因为它是缩水版的，于是，一些功能就没有啦，比如 ls -al ,就只相当于ls. 因此，不要指望busybox能够做所有的事情。 我们还需要做一些必要的目录，比如 etc lib 等。 下面是我做Initrd的一些步骤。其中，参考了cublog上的一篇日志。 http://blog.chinaunix.net/u2/70445/showart_1163143.html 转载，请注明。 首先下载busybox1.9.1版本，然后解压缩，相信大家都会。然后step by step 1、更改busybox1.9.1的Makefile ARCH ?=arm CROSS_COMPILE?= 自己的交叉编译环境的位置我的是：/opt/crosstool/gcc-4.1.0-glibc-2.3.2/arm-linux/bin/arm-linux- 2、make menuconfig Busybox Setting&#8212;&#8211;> build option&#8211;> [ ] Build BusyBox as a static binary (no shared libs) &#8230; <a href="http://www.tek-life.org/2010/04/22/%e5%88%b6%e4%bd%9cinitrd/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>首先，制作Initrd，需要了解，inidrd是什么，它由什么构成的。<br />
然后，我们会发现，在嵌入式的Initrd中 busybox是一个非常常用的工具，可是，到底busybox在制作initrd中扮演什么样的角色呢？<br />
其实，initrd只是引入了一些应用程序而已，只是，这些应用程序做的很好，体积很小。当然，因为它是缩水版的，于是，一些功能就没有啦，比如 ls -al ,就只相当于ls.<br />
因此，不要指望busybox能够做所有的事情。<br />
我们还需要做一些必要的目录，比如 etc lib 等。<br />
下面是我做Initrd的一些步骤。其中，参考了cublog上的一篇日志。</p>
<p>http://blog.chinaunix.net/u2/70445/showart_1163143.html</p>
<p>转载，请注明。<br />
首先下载busybox1.9.1版本，然后解压缩，相信大家都会。然后step by step<br />
1、更改busybox1.9.1的Makefile<br />
ARCH ?=arm<br />
CROSS_COMPILE?= 自己的交叉编译环境的位置我的是：/opt/crosstool/gcc-4.1.0-glibc-2.3.2/arm-linux/bin/arm-linux-<br />
2、make menuconfig<br />
Busybox Setting&#8212;&#8211;><br />
       build option&#8211;><br />
    [ ] Build BusyBox as a static binary (no shared libs)                     </p>
<p>Build shared libbusybox                                               </p>
<p>   Produce a binary for each applet, linked against libbusybox         </p>
<p>   Produce additional busybox binary linked against libbusybox<br />
    [ ] Build with Large File Support (for accessing files > 2 GB)  </p>
<p>installation option&#8211;></p>
<p>Don&#8217;t use /usr<br />
        Applets links (as soft-links) &#8212;><br />
     (./_install) BusyBox installation prefix </p>
<p>Busybox Library Tuning &#8212;> </p>
<p>MD5: Trade Bytes for Speed                                      </p>
<p>Faster /proc scanning code (+100 bytes)                            </p>
<p>Support for /etc/networks                                    </p>
<p>Support for /etc/networks</p>
<p>   Additional editing keys</p>
<p>   vi-style line editing commands</p>
<p>   History saving</p>
<p>   Tab completion</p>
<p>   Username completion</p>
<p>   Fancy shell prompts</p>
<p>Linux Module Utilities &#8212;></p>
<p>Support version 2.6.x Linux kernels</p>
<p>insmod  </p>
<p>   Enable load map (-m) option  </p>
<p>     Symbols in load map </p>
<p>rmmod  </p>
<p>lsmod </p>
<p>   lsmod pretty output for 2.6.x Linux kernels  </p>
<p>modprobe<br />
    [ ]   Multiple options parsing<br />
    [ ]   Fancy alias parsing<br />
    &#8212;   Options common to multiple modutils<br />
    [ ] Support tainted module checking with new kernels<br />
    [ ] Support version 2.2.x to 2.4.x Linux kernels</p>
<p>Support version 2.6.x Linux kernels</p>
<p>其他的用默认值</p>
<p>3、make install 开始安装<br />
在busybox的根目录下生成_install文件夹，里面的东西有3项：bin sbin linuxrc</p>
<p>4、修改安装Busybox后的busybox文件属性，具体原因，随后再续。<br />
chmod 4755 ./_install/bin/busybox<br />
5、在自己的home目录中，建立rootfs目录<br />
将_install的内容拷贝到rootfs目录中</p>
<p>6、在rootfs目录下，建立空目录：<br />
dev home proc tmp var<br />
boot etc lib   mnt    root sys   usr<br />
7、此时 rootfs目录下有如下内容：<br />
bin   dev home linuxrc proc sbin tmp var<br />
boot etc lib   mnt      root sys   usr<br />
8、<br />
以root身份建立节点文件/dev/console, /dev/null<br />
mknod -m 600 dev/console c 5 1<br />
mknod -m 666 dev/null c 1 3</p>
<p>9、进入etc目录，建立profile文件，内容如下：<br />
#!/bin/sh<br />
#/etc/profile:system-wide .profile file for the Bourne shells</p>
<p>echo<br />
echo -n &#8220;Processing /etc/profile&#8230;&#8230;&#8221;</p>
<p># Set search library path<br />
export LD_LIBRARY_PATH=/lib:/usr/lib</p>
<p># set user path<br />
export PATH=/bin:/sbin:/usr/bin:/usr/sbin</p>
<p>#Set PS1<br />
USER = &#8220;`id -un`&#8221;<br />
LOGNAME=$USER<br />
PS1=&#8217;[\u@\h\W]\$&#8217;<br />
PATH=$PATH</p>
<p>echo &#8220;Done!&#8221;<br />
10、<br />
在etc目录下建立init.d目录，然后在init.d目录下建立rcS文件<br />
init.d/rcS内容：<br />
#!/bin/sh</p>
<p># set hostname, needed host file in /etc directory<br />
#./etc/host<br />
hostname `cat /etc/host`</p>
<p># mount all filesystem defined in &#8220;fstab&#8221;<br />
echo &#8220;#mount all&#8230;&#8230;.&#8221;<br />
/bin/mount -a</p>
<p>#+/bin/chmod 0666 /dev/null</p>
<p>echo &#8220;# starting mdev&#8230;.&#8221;<br />
/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug<br />
mdev -s</p>
<p>/usr/etc/init</p>
<p>echo &#8220;******************************************&#8221;<br />
echo &#8221; &#8221;<br />
echo &#8221;   &#8221;<br />
echo &#8221;                         &#8221;<br />
echo &#8220;******************************************&#8221;</p>
<p>11、在etc目录下建立fstab文件<br />
proc    /proc   proc defaults 0 0<br />
none    /tmp    ramfs   defaults 0 0<br />
none    /var    ramfs   defaults 0 0<br />
mdev    /dev    ramfs   defaults 0 0<br />
sysfs   /sys    sysfs   defaults 0 0<br />
12、在etc目录下建立inittab文件<br />
::sysinit:/etc/init.d/rcS<br />
::respawn:-/bin/sh</p>
<p>tty2::askfirst:-/bin/sh</p>
<p>::ctrlaltdel:/bin/umount -a -r</p>
<p>::shutdown:/bin umount -a -r<br />
::shutdown:/sbin/swapoff -a<br />
13、在etc目录下建立空文件<br />
mdev.conf<br />
14、<br />
复制主机/etc/下面的文件passwd, group, shadow文件到/etc<br />
[root@centos etc]# cp /etc/group .<br />
[root@centos etc]# cp /etc/passwd .<br />
[root@centos etc]# cp /etc/shadow .<br />
15、<br />
因为是编译的时候使用的是动态链接。所以先看看~/busybox/_install/bin/busybox使用了哪些 lib，然后从glibc复制相应的lib到~/fsroot/lib中。<br />
[root@centos bin]# /usr/local/arm/3.4.1/arm-linux-gnu-readelf -d busybox<br />
Dynamic section at offset 0xb8014 contains 22 entries:<br />
Tag        Type                         Name/Value<br />
0&#215;00000001 (NEEDED)                     Shared library:[libcrypt.so.1]<br />
0&#215;00000001 (NEEDED)                     Shared library: [libm.so.6]<br />
0&#215;00000001 (NEEDED)                     Shared library: [libc.so.6]<br />
0x0000000c (INIT)                       0xc04c<br />
0x0000000d (FINI)                       0xa26f0<br />
0&#215;00000004 (HASH)                       0x80e8<br />
0&#215;00000005 (STRTAB)                     0xa384<br />
0&#215;00000006 (SYMTAB)                     0x8b24<br />
……<br />
……<br />
……<br />
复制lib 文件到lib目录下：<br />
[root@centos lib]cp /usr/local/arm/3.4.1/arm-linux/lib/ld* .<br />
[root@centos lib]cp /usr/local/arm/3.4.1/arm-linux/lib/libc-2.3.2.so .<br />
[root@centos lib]cp /usr/local/arm/3.4.1/arm-linux/lib/libc.so.6 .<br />
[root@centos lib]cp /usr/local/arm/3.4.1/arm-linux/lib/libm * .<br />
[root@centos lib]cp /usr/local/arm/3.4.1/arm-linux/lib/libcrypt* .<br />
16、进入busybox根目录下，进入usr目录下，建立etc目录<br />
在该etc目录下建立init文件<br />
#!/bin/sh<br />
ifconfig eth0 192.168.1.111 up<br />
ifconfig lo 127.0.0.1 </p>
<p>17、 mkcramfs fsroot fs_2.6.26_busybox.cramfs<br />
18、利用之前编译好的zImage引导操作系统<br />
<a href="http://www.tek-life.org/wp-content/uploads/2010/04/success.png"><img src="http://www.tek-life.org/wp-content/uploads/2010/04/success-300x236.png" alt="" title="success" width="300" height="236" class="alignnone size-medium wp-image-10223" /></a><br />
讨论贴：</p>
<p>http://linux.chinaunix.net/bbs/thread-1162326-1-1.html</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/04/22/%e5%88%b6%e4%bd%9cinitrd/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[转]研究生如何拥有并保持自信</title>
		<link>http://www.tek-life.org/2010/04/21/%e8%bd%ac%e7%a0%94%e7%a9%b6%e7%94%9f%e5%a6%82%e4%bd%95%e6%8b%a5%e6%9c%89%e5%b9%b6%e4%bf%9d%e6%8c%81%e8%87%aa%e4%bf%a1/</link>
		<comments>http://www.tek-life.org/2010/04/21/%e8%bd%ac%e7%a0%94%e7%a9%b6%e7%94%9f%e5%a6%82%e4%bd%95%e6%8b%a5%e6%9c%89%e5%b9%b6%e4%bf%9d%e6%8c%81%e8%87%aa%e4%bf%a1/#comments</comments>
		<pubDate>Wed, 21 Apr 2010 08:55:41 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/04/21/%e8%bd%ac%e7%a0%94%e7%a9%b6%e7%94%9f%e5%a6%82%e4%bd%95%e6%8b%a5%e6%9c%89%e5%b9%b6%e4%bf%9d%e6%8c%81%e8%87%aa%e4%bf%a1/</guid>
		<description><![CDATA[虽然年龄不小了，但刚进实验室的研究生，仍然是个“孩子”，刚离开按部就班的课堂，手上就捧一个啥都要探索的课题，不要说动手，即使思想转变也需要时日，因此， “不知道”、“不懂”、“不会”常挂嘴边、常上心头。 其实这“三不”倒没什么，什么都可以慢慢学、慢慢做嘛，导师再怎么催、甚至骂，也不至于要命吧？但是，对学生自己，倒有一点致命：自信心慢慢磨没了。没有自信心，轻者消沉、重者浮躁、再重就要出人命了。 网上的很多学生抱怨自己的导师打击自己的自信心，说连导师自己都不知道怎么做，还急于向学生要结果！这其实是个“借口”，而不是导致学生失去自信心的理由。 自信心是谁给的呢？有的人可能觉得是老板给的，老板一味迁就、手把手的教导、热心鼓励、不吝表扬等等，的确能让人感觉浑身来劲，精神备足。很可惜的是，这不是“自信”，而是“他信”，他人（老板）加在你身上的 “信”！拿他信当自信，这是很多研究生容易犯的一个想当然的美丽错误。 “他信”是别人给的，说没有就没有，一收走就立刻完蛋。“自信”是自己挣来的，谁也拿不走，自己想不要都不行。前者是施舍，施舍的东西最靠不住；后者是本领，有本领这个本钱才算是有“奔”的头。 因此，对研究生来说，最重要的就是要靠自己的本领“挣”得自信，而不是靠别人施舍，只有这样的自信才能长久保持。实验室是个“雷区”，无论导师还是学生，都要亲自去趟雷，老是踩到雷，炸的魂飞魄散，肯定会害怕，越害怕越不敢走，如此恶性循环，最后就会吓出神经病，毫无自信可言。那么如何才能避免踩雷而挣得属于自己的自信呢？很简单： 一不能“不懂装懂”：古人云：知之为知之，不知为不知；俗话又说：不知者不怪。你不知道的事情，不要装作精通；不懂的事情，不要装作内行。科学研究都是实打实的真刀实枪，不是水中月镜中花的文字游戏。不知道就是不知道，这不丢人；明明不知道，还硬着头皮说知道，点头如捣蒜，殊不知一句话就能漏马脚，那才丢人呢！ 与导师或同行讨论问题，千万不能“装”，不懂的一定要明说。多数导师都有个毛病：学生越是不知道，他教的就越来劲，呵呵！老师问“懂了吗？”学生要是随口回答“懂了”，老师会很失落的：）要是发现你不懂装懂，那就会失望的！因此，老师派你趟雷，不要轻易说“没问题”，其实问题一大堆，“懂”就是“咚”的一声谁头皮硬谁先玩完呀：） 二不能“一错再错”：说实话，做研究就是个“试错”的过程，错误无处不在、无时不在。如果说一个科学家的成就与他的犯错率成正比，恐怕还是很合适的。在一定程度上说，错误不可怕，没有错误那才可怕呢！因此，科学研究中出错是常态，不出错才是异常！这个“错”是好事，越错自信心会越强。 但是，话说回来，“一错再错”就成了打击自信心的狙击枪！同一个错误，今天犯了，明天还犯，后天还犯，这就是错上加错，不可原谅了。在趟雷的路上，哪儿跌倒了哪儿再爬起来继续前进；如果明明知道那里是个雷，还是一脚踏上去，这就是傻了。长此以往，自己就会怀疑自己是不是做科研的料？不要说“自信”，连“他信”估计都不会有了！ 三不能“得过且过”：人都说科学研究靠兴趣，但限于目前的体制，不是每个研究生都能对自己的课题具有浓厚的兴趣。调剂来调剂去的，够累不说，还要硬撑着过下去，就当下的行情，换导师也不是一幅良药。即使有兴趣，但苦于学业的压力、老板的催促，得过且过的事常有发生。明明没做的实验点，偏偏说做过了；明明没看的文献，偏偏说读过了，只求今天能躲过老板的检查就好，明天再说吧！这些都是科研之大忌，即使是急功近利的老板，他也不愿意看到学生如此！科学研究也要按部就班，不能超近道。做不出来就是做不出来，另谋出路就是了，没必要做假。明明没把雷挖出来就说没事，结果后来人一脚踩上，你说谁负这个责任呢？实在没有路可走了，那就趁早退一步，海阔天空，总比害人又害己好千万倍呀！ 如果说“不懂装懂”是摧毁自信心的定时炸弹，“一错再错”是瞄准自信心的狙击步枪，那么“得过且过”就是杀死自信心的慢性毒药。要想拥有长久的自信，请远离这三样东西吧！]]></description>
			<content:encoded><![CDATA[<p>虽然年龄不小了，但刚进实验室的研究生，仍然是个“孩子”，刚离开按部就班的课堂，手上就捧一个啥都要探索的课题，不要说动手，即使思想转变也需要时日，因此， “不知道”、“不懂”、“不会”常挂嘴边、常上心头。</p>
<p>其实这“三不”倒没什么，什么都可以慢慢学、慢慢做嘛，导师再怎么催、甚至骂，也不至于要命吧？但是，对学生自己，倒有一点致命：自信心慢慢磨没了。没有自信心，轻者消沉、重者浮躁、再重就要出人命了。</p>
<p>网上的很多学生抱怨自己的导师打击自己的自信心，说连导师自己都不知道怎么做，还急于向学生要结果！这其实是个“借口”，而不是导致学生失去自信心的理由。</p>
<p>自信心是谁给的呢？有的人可能觉得是老板给的，老板一味迁就、手把手的教导、热心鼓励、不吝表扬等等，的确能让人感觉浑身来劲，精神备足。<strong>很可惜的是，这不是“自信”，而是“他信”，他人（老板）加在你身上的 “信”！拿他信当自信，这是很多研究生容易犯的一个想当然的美丽错误。</strong></p>
<p>“他信”是别人给的，说没有就没有，一收走就立刻完蛋。“自信”是自己挣来的，谁也拿不走，自己想不要都不行。<strong>前者是施舍，施舍的东西最靠不住；后者是本领，有本领这个本钱才算是有“奔”的头。</strong></p>
<p>因此，对研究生来说，最重要的就是要靠自己的本领“挣”得自信，而不是靠别人施舍，只有这样的自信才能长久保持。实验室是个“雷区”，无论导师还是学生，都要亲自去趟雷，老是踩到雷，炸的魂飞魄散，肯定会害怕，越害怕越不敢走，如此恶性循环，最后就会吓出神经病，毫无自信可言。那么如何才能避免踩雷而挣得属于自己的自信呢？很简单：</p>
<p>一不能“不懂装懂”：古人云：知之为知之，不知为不知；俗话又说：不知者不怪。你不知道的事情，不要装作精通；不懂的事情，不要装作内行。科学研究都是实打实的真刀实枪，不是水中月镜中花的文字游戏。不知道就是不知道，这不丢人；<strong>明明不知道，还硬着头皮说知道，点头如捣蒜，殊不知一句话就能漏马脚，那才丢人呢！</strong></p>
<p>与导师或同行讨论问题，千万不能“装”，不懂的一定要明说。多数导师都有个毛病：学生越是不知道，他教的就越来劲，呵呵！老师问“懂了吗？”学生要是随口回答“懂了”，老师会很失落的：）要是发现你不懂装懂，那就会失望的！因此，老师派你趟雷，不要轻易说“没问题”，其实问题一大堆，“懂”就是“咚”的一声谁头皮硬谁先玩完呀：）</p>
<p>二不能“一错再错”：说实话，做研究就是个“试错”的过程，错误无处不在、无时不在。如果说一个科学家的成就与他的犯错率成正比，恐怕还是很合适的。在一定程度上说，错误不可怕，没有错误那才可怕呢！因此，科学研究中出错是常态，不出错才是异常！这个“错”是好事，越错自信心会越强。</p>
<p>但是，话说回来，“一错再错”就成了打击自信心的狙击枪！同一个错误，今天犯了，明天还犯，后天还犯，这就是错上加错，不可原谅了。在趟雷的路上，哪儿跌倒了哪儿再爬起来继续前进；如果明明知道那里是个雷，还是一脚踏上去，这就是傻了。长此以往，自己就会怀疑自己是不是做科研的料？不要说“自信”，连“他信”估计都不会有了！</p>
<p>三不能“得过且过”：人都说科学研究靠兴趣，但限于目前的体制，不是每个研究生都能对自己的课题具有浓厚的兴趣。调剂来调剂去的，够累不说，还要硬撑着过下去，就当下的行情，换导师也不是一幅良药。即使有兴趣，但苦于学业的压力、老板的催促，得过且过的事常有发生。明明没做的实验点，偏偏说做过了；明明没看的文献，偏偏说读过了，只求今天能躲过老板的检查就好，明天再说吧！这些都是科研之大忌，即使是急功近利的老板，他也不愿意看到学生如此！科学研究也要按部就班，不能超近道。做不出来就是做不出来，另谋出路就是了，没必要做假。明明没把雷挖出来就说没事，结果后来人一脚踩上，你说谁负这个责任呢？实在没有路可走了，那就趁早退一步，海阔天空，总比害人又害己好千万倍呀！</p>
<p><strong>如果说“不懂装懂”是摧毁自信心的定时炸弹，“一错再错”是瞄准自信心的狙击步枪，那么“得过且过”就是杀死自信心的慢性毒药。要想拥有长久的自信，请远离这三样东西吧！</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/04/21/%e8%bd%ac%e7%a0%94%e7%a9%b6%e7%94%9f%e5%a6%82%e4%bd%95%e6%8b%a5%e6%9c%89%e5%b9%b6%e4%bf%9d%e6%8c%81%e8%87%aa%e4%bf%a1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>交叉编译linux内核 for arm 出现错误</title>
		<link>http://www.tek-life.org/2010/04/20/%e4%ba%a4%e5%8f%89%e7%bc%96%e8%af%91linux%e5%86%85%e6%a0%b8-for-arm-%e5%87%ba%e7%8e%b0%e9%94%99%e8%af%af/</link>
		<comments>http://www.tek-life.org/2010/04/20/%e4%ba%a4%e5%8f%89%e7%bc%96%e8%af%91linux%e5%86%85%e6%a0%b8-for-arm-%e5%87%ba%e7%8e%b0%e9%94%99%e8%af%af/#comments</comments>
		<pubDate>Tue, 20 Apr 2010 07:44:26 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[QEMU-VIRT]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/04/20/%e4%ba%a4%e5%8f%89%e7%bc%96%e8%af%91linux%e5%86%85%e6%a0%b8-for-arm-%e5%87%ba%e7%8e%b0%e9%94%99%e8%af%af/</guid>
		<description><![CDATA[1、下载内核源码后，更改Makefile文件，更改 ARCH ?=arm CROSS_COMPILE?=交叉编译工具的目录 2、然后拷贝arch/arm/configs/versatile_defconfig到源文件根目录下面命名为.config cp arch/arm/configs/versatile_defconfig&#160; .config 3、然后make menuconfig ，这一步不用管，退出保存即可 4、然后make,这一步会产生vmlinux文件。 5、然后make zImage,会在arch/arm/boot/目录下产生 zImage，kernel就这样产生了。 6、编译出内核以后，开始编译initrd.img文件 7、在/lib/modules/目录下产生相应的模块文件 sudo modules_install 8、产生initrd.img-2.6.26的引导文件 mkinitramfs -o /boot/initrd.img-2.6.26 2.6.26 在/boot目录下，看了一下initrd.img-2.6.26，好大快5M了。 引导后，出现错误： request_module: runaway loop modprobe 循环4次，就开始打印错误信息。]]></description>
			<content:encoded><![CDATA[<p>1、下载内核源码后，更改Makefile文件，更改   <br />ARCH ?=arm    <br />CROSS_COMPILE?=交叉编译工具的目录    </p>
<p>2、然后拷贝arch/arm/configs/versatile_defconfig到源文件根目录下面命名为.config   <br />cp arch/arm/configs/versatile_defconfig&#160; .config</p>
<p>3、然后make menuconfig ，这一步不用管，退出保存即可</p>
<p>4、然后make,这一步会产生vmlinux文件。</p>
<p>5、然后make zImage,会在arch/arm/boot/目录下产生 zImage，kernel就这样产生了。   </p>
<p>6、编译出内核以后，开始编译initrd.img文件</p>
<p>7、在/lib/modules/目录下产生相应的模块文件   <br />sudo modules_install </p>
<p>8、产生initrd.img-2.6.26的引导文件   <br />mkinitramfs -o /boot/initrd.img-2.6.26 2.6.26</p>
<p>在/boot目录下，看了一下initrd.img-2.6.26，好大快5M了。</p>
<p>引导后，出现错误：</p>
<p>request_module: runaway loop modprobe</p>
<p>循环4次，就开始打印错误信息。</p>
<p><a href="http://www.tek-life.org/wp-content/uploads/2010/04/error.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="error" border="0" alt="error" src="http://www.tek-life.org/wp-content/uploads/2010/04/error_thumb.png" width="244" height="194" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/04/20/%e4%ba%a4%e5%8f%89%e7%bc%96%e8%af%91linux%e5%86%85%e6%a0%b8-for-arm-%e5%87%ba%e7%8e%b0%e9%94%99%e8%af%af/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>一张美图&#8211;vim快捷键</title>
		<link>http://www.tek-life.org/2010/04/18/%e4%b8%80%e5%bc%a0%e7%be%8e%e5%9b%be-vim%e5%bf%ab%e6%8d%b7%e9%94%ae/</link>
		<comments>http://www.tek-life.org/2010/04/18/%e4%b8%80%e5%bc%a0%e7%be%8e%e5%9b%be-vim%e5%bf%ab%e6%8d%b7%e9%94%ae/#comments</comments>
		<pubDate>Sun, 18 Apr 2010 05:43:08 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Reading notes]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/?p=10210</guid>
		<description><![CDATA[]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.tek-life.org/wp-content/uploads/2010/04/vi_tutorial.png"><img class="alignnone size-medium wp-image-10209" title="vi_tutorial" src="http://www.tek-life.org/wp-content/uploads/2010/04/vi_tutorial-300x211.png" alt="" width="300" height="211" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/04/18/%e4%b8%80%e5%bc%a0%e7%be%8e%e5%9b%be-vim%e5%bf%ab%e6%8d%b7%e9%94%ae/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>qemu 模拟Linux 2.6.30</title>
		<link>http://www.tek-life.org/2010/04/13/qemu-%e6%a8%a1%e6%8b%9flinux-2-6-30/</link>
		<comments>http://www.tek-life.org/2010/04/13/qemu-%e6%a8%a1%e6%8b%9flinux-2-6-30/#comments</comments>
		<pubDate>Tue, 13 Apr 2010 03:52:32 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[QEMU-VIRT]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/04/13/qemu-%e6%a8%a1%e6%8b%9flinux-2-6-30/</guid>
		<description><![CDATA[1、获取引导安装的initrd.gz 和 引导的内核 vmlinuz URL:http://ftp.nl.debian.org/debian/dists/testing/main/installer-armel/current/images/versatile/netboot/ 我把这两个文件上传到google code了.&#8211;因为,这个文件经常会更新,所以,为了保证童鞋们能够用下面的step by step ,so …. ,save it for myself and ourselves. http://code.google.com/p/emulate-linux-on-qemu-for-arm/downloads/list 2、制作模拟的磁盘 qemu-img create –f  hda.img 10G 3、开始安装啦，很振奋人心滴 qemu-system-arm -M versatilepb -kernel vmlinuz-2.6.30-2-versatile -initrd initrd.gz -hda hda.img -append &#8220;root=/dev/ram&#8221; 抓一个图，以示之。 4、当安装结束以后，我们会发现，没有引导系统用的initrd.怎么办？用2.6.26.1的initrd。 然后就启动起来啦。启动起来后，通过其他的方法，把vmlinuz和Initrd拷贝出来，启动用。]]></description>
			<content:encoded><![CDATA[<p>1、获取引导安装的initrd.gz 和 引导的内核 vmlinuz<br />
URL:<a title="http://ftp.nl.debian.org/debian/dists/testing/main/installer-armel/current/images/versatile/netboot/" href="http://ftp.nl.debian.org/debian/dists/testing/main/installer-armel/current/images/versatile/netboot/">http://ftp.nl.debian.org/debian/dists/testing/main/installer-armel/current/images/versatile/netboot/</a></p>
<p>我把这两个文件上传到google code了.&#8211;因为,这个文件经常会更新,所以,为了保证童鞋们能够用下面的step by step ,so …. ,save it for myself and ourselves.</p>
<p><a title="http://code.google.com/p/emulate-linux-on-qemu-for-arm/downloads/list" href="http://code.google.com/p/emulate-linux-on-qemu-for-arm/downloads/list">http://code.google.com/p/emulate-linux-on-qemu-for-arm/downloads/list</a></p>
<p>2、制作模拟的磁盘</p>
<p>qemu-img create –f  hda.img 10G</p>
<p>3、开始安装啦，很振奋人心滴</p>
<p>qemu-system-arm -M versatilepb -kernel vmlinuz-2.6.30-2-versatile -initrd initrd.gz -hda hda.img -append &#8220;root=/dev/ram&#8221;</p>
<p>抓一个图，以示之。</p>
<p><a href="http://www.tek-life.org/wp-content/uploads/2010/04/image1.png"><img style="display: inline; border: 0px;" title="image" src="http://www.tek-life.org/wp-content/uploads/2010/04/image_thumb1.png" border="0" alt="image" width="244" height="194" /></a></p>
<p><a href="http://www.tek-life.org/wp-content/uploads/2010/04/image2.png"><img style="display: inline; border: 0px;" title="image" src="http://www.tek-life.org/wp-content/uploads/2010/04/image_thumb2.png" border="0" alt="image" width="244" height="194" /></a></p>
<p>4、当安装结束以后，我们会发现，没有引导系统用的initrd.怎么办？用2.6.26.1的initrd。</p>
<p>然后就启动起来啦。启动起来后，通过其他的方法，把vmlinuz和Initrd拷贝出来，启动用。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/04/13/qemu-%e6%a8%a1%e6%8b%9flinux-2-6-30/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Grub对多硬盘的处理</title>
		<link>http://www.tek-life.org/2010/04/12/grub%e5%af%b9%e5%a4%9a%e7%a1%ac%e7%9b%98%e7%9a%84%e5%a4%84%e7%90%86/</link>
		<comments>http://www.tek-life.org/2010/04/12/grub%e5%af%b9%e5%a4%9a%e7%a1%ac%e7%9b%98%e7%9a%84%e5%a4%84%e7%90%86/#comments</comments>
		<pubDate>Mon, 12 Apr 2010 13:10:15 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux Skills]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/04/12/grub%e5%af%b9%e5%a4%9a%e7%a1%ac%e7%9b%98%e7%9a%84%e5%a4%84%e7%90%86/</guid>
		<description><![CDATA[1、若多个硬盘，那么从hd0,开始往后排，hd0,hd1,hd2,hd3… Grub不管是SCSI还是IDE，一律是hd打头。 2、Grub对硬盘的编号和Linux的不一样。MBR在那个磁盘，哪个磁盘就是hd0,而Linux是按照IDE或者SCSI顺序hda,hdb,hdc。较新版本的Linux把IDE硬盘和SCSI硬盘都当作sd来处理了。于是，便是sda,sdb,sdc…了。 技巧：在GRUB里面，不知道有几个磁盘，那么输入 boot (hd 后按TAB键，便会有提示啦～。]]></description>
			<content:encoded><![CDATA[<p>1、若多个硬盘，那么从hd0,开始往后排，hd0,hd1,hd2,hd3… Grub不管是SCSI还是IDE，一律是hd打头。</p>
<p>2、Grub对硬盘的编号和Linux的不一样。MBR在那个磁盘，哪个磁盘就是hd0,而Linux是按照IDE或者SCSI顺序hda,hdb,hdc。较新版本的Linux把IDE硬盘和SCSI硬盘都当作sd来处理了。于是，便是sda,sdb,sdc…了。</p>
<p>技巧：在GRUB里面，不知道有几个磁盘，那么输入 boot (hd 后按TAB键，便会有提示啦～。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/04/12/grub%e5%af%b9%e5%a4%9a%e7%a1%ac%e7%9b%98%e7%9a%84%e5%a4%84%e7%90%86/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>启动Qemu,VNC server running on `::1:5901&#8242;</title>
		<link>http://www.tek-life.org/2010/04/11/%e5%90%af%e5%8a%a8qemuvnc-server-running-on-15901/</link>
		<comments>http://www.tek-life.org/2010/04/11/%e5%90%af%e5%8a%a8qemuvnc-server-running-on-15901/#comments</comments>
		<pubDate>Sun, 11 Apr 2010 14:06:27 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[QEMU-VIRT]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/04/11/%e5%90%af%e5%8a%a8qemuvnc-server-running-on-15901/</guid>
		<description><![CDATA[&#160; The Qestion &#38; Answer: &#62;&#62;when I type the commandline&#160; "qemu -hda hda.img -boot d" &#62;&#62; Error happens. &#62;&#62; The scene is : &#62;&#62;&#160; &#62;&#62; omycle@ubuntu:~$ sudo qemu -hda hda.img&#160; -boot d &#62;&#62; VNC server running on `::1:5901'&#160;&#160;&#160; -------just show &#8230; <a href="http://www.tek-life.org/2010/04/11/%e5%90%af%e5%8a%a8qemuvnc-server-running-on-15901/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<pre>&nbsp;</pre>
<pre>The Qestion &amp; Answer:</pre>
<pre>&gt;&gt;when I type the commandline&nbsp; "qemu -hda hda.img -boot d"
&gt;&gt; Error happens.
&gt;&gt; The scene is :
&gt;&gt;&nbsp; &gt;&gt; omycle@ubuntu:~$ sudo qemu -hda hda.img&nbsp; -boot d
&gt;&gt; VNC server running on `::1:5901'&nbsp;&nbsp;&nbsp; -------just show this line.And
&gt;&gt; then nothing happens .
&gt;
&gt;You should use VNC to connect to localhost :1
&gt;
&gt;&gt; who can tell me why?
&gt;
&gt;<strong>You probably haven't compiled QEMU with libsdl, so it defaults to VNC
&gt;mode.</strong>
</pre>
<pre>安装 libsdl</pre>
<pre>sudo apt-get install libsdl1.2-dev</pre>
<pre>&nbsp;</pre>
<pre>重新编译qemu</pre>
<pre>即可</pre>
<pre>&nbsp;</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/04/11/%e5%90%af%e5%8a%a8qemuvnc-server-running-on-15901/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>远程查看ubuntu 9.10 桌面</title>
		<link>http://www.tek-life.org/2010/04/09/%e8%bf%9c%e7%a8%8b%e6%9f%a5%e7%9c%8bubuntu-9-10-%e6%a1%8c%e9%9d%a2/</link>
		<comments>http://www.tek-life.org/2010/04/09/%e8%bf%9c%e7%a8%8b%e6%9f%a5%e7%9c%8bubuntu-9-10-%e6%a1%8c%e9%9d%a2/#comments</comments>
		<pubDate>Fri, 09 Apr 2010 01:40:24 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[vnc;ubuntu]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/04/09/%e8%bf%9c%e7%a8%8b%e6%9f%a5%e7%9c%8bubuntu-9-10-%e6%a1%8c%e9%9d%a2/</guid>
		<description><![CDATA[通常情况下，我们用Putty查看的是字符界面。如何查看远程桌面呢？ 通过VNC 对于服务器，要安装VNCSERVER 对于客户端，要安装VNCVIEWER 具体步骤： 对于服务器： 1.安装VNC服务端组件： $ sudo apt-get install vnc4-common vnc4server 2、设置VNC登录密码 $ vncpasswd 3、修改vnc的默认设置，使启动时运行gnome作为X的桌面 $ vncserver :1 $ vncserver -kill :1 &#160; 注意：里面的&#34;:1&#34;代表display号，客户登录的时候得写相同的display号才能登录（见后面客户端部分）。 4、修改~/.vnc/xstartup文件，建议拷贝系统中Xsession的配置文件： $ cp /etc/X11/Xsession ~/.vnc/xstartup&#160; 5、再次启动VNC SERVER：//因为我们要测试一下哈 $ vncserver :1 对于客户端： 1、安装客户端组件 $ sudo &#8230; <a href="http://www.tek-life.org/2010/04/09/%e8%bf%9c%e7%a8%8b%e6%9f%a5%e7%9c%8bubuntu-9-10-%e6%a1%8c%e9%9d%a2/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>通常情况下，我们用Putty查看的是字符界面。如何查看远程桌面呢？   <br />通过VNC</p>
<p>对于服务器，要安装VNCSERVER</p>
<p>对于客户端，要安装VNCVIEWER</p>
<p>具体步骤：</p>
<p>对于服务器：</p>
<blockquote><p><font style="background-color: #ffffff">1.安装VNC服务端组件：</font></p>
<p>$ sudo apt-get install vnc4-common vnc4server</p>
<p><font style="background-color: #ffffff">2、设置VNC登录密码</font></p>
<p>$ vncpasswd</p>
<p><font style="background-color: #ffffff">3、修改vnc的默认设置，使启动时运行gnome作为X的桌面</font></p>
<li>$ vncserver :1</li>
<li>$ vncserver -kill :1</li>
<p>&#160;</p>
<p>注意：里面的&quot;:1&quot;代表display号，客户登录的时候得写相同的display号才能登录（见后面客户端部分）。      <br />4、修改~/.vnc/xstartup文件，建议拷贝系统中Xsession的配置文件： </p>
<p>$ cp /etc/X11/Xsession ~/.vnc/xstartup&#160; </p>
<p>5、再次启动VNC SERVER：//因为我们要测试一下哈</p>
<p>$ vncserver :1</p>
</blockquote>
<p>对于客户端：</p>
<blockquote><p><font style="background-color: #ffffff">1、安装客户端组件</font></p>
<p>$ sudo apt-get install vnc4-common xvnc4viewer&#160; </p>
<p><font style="background-color: #ffffff">2、登录吧</font></p>
<p>$ vncviewer 192.168.227.110:1</p>
<p>要输入密码</p>
<p>记得，如果你在windows桌面的话，要在windows环境下安装XMING，然后再putty.</p>
</blockquote>
<p><a href="http://www.tek-life.org/wp-content/uploads/2010/04/image.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.tek-life.org/wp-content/uploads/2010/04/image_thumb.png" width="429" height="341" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/04/09/%e8%bf%9c%e7%a8%8b%e6%9f%a5%e7%9c%8bubuntu-9-10-%e6%a1%8c%e9%9d%a2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Qemu支持的ARM系列</title>
		<link>http://www.tek-life.org/2010/04/06/qemu%e6%94%af%e6%8c%81%e7%9a%84arm%e7%b3%bb%e5%88%97/</link>
		<comments>http://www.tek-life.org/2010/04/06/qemu%e6%94%af%e6%8c%81%e7%9a%84arm%e7%b3%bb%e5%88%97/#comments</comments>
		<pubDate>Tue, 06 Apr 2010 09:37:23 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[QEMU-VIRT]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/2010/04/06/qemu%e6%94%af%e6%8c%81%e7%9a%84arm%e7%b3%bb%e5%88%97/</guid>
		<description><![CDATA[对于ARM来说，目前的SVN的版本中(2008-10-07)支持的SOC/machine包括: (1)&#160;&#160;&#160; integratorcp: ARM Integrator/CP (ARM926EJ-S) (2)&#160;&#160;&#160; versatilepb: ARM Versatile/PB (ARM926EJ-S) (3)&#160;&#160;&#160; versatileab: ARM Versatile/AB (ARM926EJ-S) (4)&#160;&#160;&#160; realview: ARM RealView Emulation Baseboard (ARM926EJ-S) (5)&#160;&#160;&#160; akita: Akita PDA (PXA270) (6)&#160;&#160;&#160; spitz: Spitz PDA (PXA270) (7)&#160;&#160;&#160; borzoi: Borzoi PDA (PXA270) (8)&#160;&#160;&#160; terrier: &#8230; <a href="http://www.tek-life.org/2010/04/06/qemu%e6%94%af%e6%8c%81%e7%9a%84arm%e7%b3%bb%e5%88%97/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>对于ARM来说，目前的SVN的版本中(2008-10-07)支持的SOC/machine包括:    <br />(1)&#160;&#160;&#160; integratorcp: ARM Integrator/CP (ARM926EJ-S)     <br />(2)&#160;&#160;&#160; versatilepb: ARM Versatile/PB (ARM926EJ-S)     <br />(3)&#160;&#160;&#160; versatileab: ARM Versatile/AB (ARM926EJ-S)     <br />(4)&#160;&#160;&#160; realview: ARM RealView Emulation Baseboard (ARM926EJ-S)     <br />(5)&#160;&#160;&#160; akita: Akita PDA (PXA270)     <br />(6)&#160;&#160;&#160; spitz: Spitz PDA (PXA270)     <br />(7)&#160;&#160;&#160; borzoi: Borzoi PDA (PXA270)     <br />(8)&#160;&#160;&#160; terrier: Terrier PDA (PXA270)     <br />(9)&#160;&#160;&#160; cheetah: Palm Tungsten|E aka. Cheetah PDA (OMAP310)     <br />(10)&#160;&#160;&#160; n800: Nokia N800 tablet aka. RX-34 (OMAP2420)     <br />(11)&#160;&#160;&#160; n810: Nokia N810 tablet aka. RX-44 (OMAP2420)     <br />(12)&#160;&#160;&#160; lm3s811evb: Stellaris LM3S811EVB     <br />(13)&#160;&#160;&#160; lm3s6965evb: Stellaris LM3S6965EVB     <br />(14)&#160;&#160;&#160; connex: Gumstix Connex (PXA255)     <br />(15)&#160;&#160;&#160; verdex: Gumstix Verdex (PXA270)     <br />(16)&#160;&#160;&#160; mainstone: Mainstone II (PXA27x)     <br />(17)&#160;&#160;&#160; musicpal: Marvell 88w8618 / MusicPal (ARM926EJ-S)     <br />(18)&#160;&#160;&#160; tosa: Tosa PDA (PXA255)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/04/06/qemu%e6%94%af%e6%8c%81%e7%9a%84arm%e7%b3%bb%e5%88%97/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Why cann’t we remove the ZONE_DMA from Linux</title>
		<link>http://www.tek-life.org/2010/04/05/why-cann%e2%80%99t-we-remove-the-zone_dma-from-linux/</link>
		<comments>http://www.tek-life.org/2010/04/05/why-cann%e2%80%99t-we-remove-the-zone_dma-from-linux/#comments</comments>
		<pubDate>Mon, 05 Apr 2010 09:47:34 +0000</pubDate>
		<dc:creator>tek-life</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.tek-life.org/blog/?p=10153</guid>
		<description><![CDATA[ZONE_DMA设置的原因是因为，当年主板上的ISA总线中的DMA控制器只能寻址24位物理地址，因此最多能够寻址到16MB的地方。现在，随着硬件的发展，ISA早已经成为历史。可是，为什么现在还要保留着ZONE_DMA区呢？ 这是在linux-kernel mail list上的答案： http://lkml.org/lkml/2010/4/4/2 While ISA is gone as a true peripheral interconnect for new systems it does, actually, still live on in a lot of systems that Linux still supports. While those systems, generally, are running the same &#8230; <a href="http://www.tek-life.org/2010/04/05/why-cann%e2%80%99t-we-remove-the-zone_dma-from-linux/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>ZONE_DMA设置的原因是因为，当年主板上的ISA总线中的DMA控制器只能寻址24位物理地址，因此最多能够寻址到16MB的地方。现在，随着硬件的发展，ISA早已经成为历史。可是，为什么现在还要保留着ZONE_DMA区呢？</p>
<p>这是在linux-kernel mail list上的答案：</p>
<p><a title="http://lkml.org/lkml/2010/4/4/2" href="http://lkml.org/lkml/2010/4/4/2">http://lkml.org/lkml/2010/4/4/2</a></p>
<pre>While ISA is gone as a true peripheral interconnect for new systems it does,
actually, still live on in a lot of systems that Linux still supports. While
those systems, generally, are running the same kernel and userspace they were
a decade ago I have no doubt that somebody might find an old machine and put
Linux on it - just because they could.

And that also discounts the non-IBM PC machines that are out there that Linux
also supports. While I don't know enough about them to say for sure, I am
quite certain that at least some of them are still using the ISA bus.

DRH</pre>
<pre></pre>
<pre>&gt; Thanks for your reply.And do you means that , If I use a modern PC,such as
&gt; my pc (CPU:Intel dual-core 2.6GHZ; Memory 2GB; And no pci ).I can remove
&gt; the ZONE_DMA .And make sure this system also run smoothly as before?

*MAYBE* - if you don't use parallel ports, floppy drives or similar. There
actually are still a lot of devices that use the ISA bus in a modern PC - even
the keyboard (well, not USB ones) is an ISA device.

Simple fact is that if it was possible to configure it out and not cause
massive problems somebody would have already spun out a patch to allow just
that. 

DRH</pre>
<pre></pre>
<p>Incorrect about that. The i810 chipset doesn&#8217;t support the ISA bus as  an<br />
*EXPANSION* *BUS* &#8211; it does, however, have an internal ISA bus that  happens to<br />
be hardwired for certain chips. Among those chips is the keyboard  controller<br />
that is a part of the PS/2 keyboard and mouse system. But for  those components<br />
that do DMA&#8230; Look at the parallel port and the floppy  drive &#8211; both<br />
(technically) live on the ISA bus.<br />
But I&#8217;m not a hardware  expert by any stretch &#8211; most of what I know is gleaned<br />
from reading code and  manual. However&#8230; the bits I&#8217;m talking about have<br />
interfaces that are, for  the most part, carved in stone. Changing them would<br />
require adding circuitry  to allow the existing interface continued function -<br />
so I doubt anyone has  changed them. (I mean&#8230; it&#8217;d be almost stupid &#8211; have<br />
you ever really looked  at the amount of real-estate on a modern MB devoted to<br />
nothing but making  sure traces are properly routed? There is a reason they<br />
talk about boards  being &#8220;4 layer&#8221; and such&#8230;)<br />
DRH</p>
<pre>---------------------</pre>
<pre>Last I heard this discussed it was mentioned that there were some
fairly common integrated sound devices using ISA DMA.

As for the i810 chipset not supporting ISA that isn't really true.  No
one implements ISA plug-in slots, but at least in the for of LPC
hardware that is effectively ISA continues to be supported for various
legacy devices.

Ultimately I don't see much point in disabling ZONE_DMA.  It is a lot of
work to track down if it is really needed, and it only costs 16MB.  So
unless someone shows me that ZONE_DMA triggers the OOM killer unnecessarily
or otherwise impacts the system what does legacy code hurt?

Eric</pre>
<pre>---------------------</pre>
<pre>Some onboard motherboard devices may also be ISA devices.</pre>
<pre>Thomas Fjellstrom</pre>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<pre>Floppy disks &amp; paralell ports still use DMA

	Regards
		Oliver</pre>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<pre>There are a couple of reasons to keep ZONE_DMA. It is not used by ISA
device drivers exclusivly. Old style PCI devices may have small DMA
masks (so they could address between 16 MB and 4GB depending on the
device) too. Drivers for these devices often use ZONE_DMA as a fallback
if they can't allocate memory addressable by the device using
ZONE_NORMAL. So there is a use for ZONE_DMA with 32bit PCI too.
(And I also remember I have seen an AM2 board with an ISA slot for
 embedded purposes too, so ISA is not completly dead)

	Joerg</pre>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>I think Joerg is right. In short wods, ZONE_DMA is not only an relative thing  to ISA devices. ISA devices need it in the ancient board because they could not  refer to all the memory addresses which are usually 32-bit, on the contrary, ISA  devices just use 24-bit address. ZONE_DMA serves those kmalloc call with GFP_DMA  and __GFP_DMA flags, and that is the true meaning of it.<br />
It is not reasonable  to truncate ZONE_DMA, because although it could be defined as a size of 16MB,  the actual size of DMA transferring region is decided by the watermark of   ZONE_NORMAL. It is saying that a part of ZONE_DMA could be used as ZONE_NORMAL  memory when lowmem_reserved_ratio is set to a certain value. Refer to  Documentation/sysctl/vm.txt for more information.<br />
Best regards,<br />
&#8211;<br />
Adam  Jiang</p>
<p>总结以上大牛的观点，我分析如下：</p>
<p>1、现在的一些机器必须使用ISA插槽的主板，比如税务部门，他们需要使用税控卡，税控卡有很多是ISA插槽的，另外，对于部分主板集成的声卡，内部也在使用ISA总线。我听说还有工程控制卡，用于工程监控，也是ISA接口的。</p>
<p>2、虽然在有些机器中，没有使用ISA扩展总线，但是ZONE_DMA仍然有它的作用：<br />
若ZONE_NORMAL区中的内存量不足的话，ZONE_DMA就会被当作ZONE_NORMAL区来使用。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tek-life.org/2010/04/05/why-cann%e2%80%99t-we-remove-the-zone_dma-from-linux/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

