level-triggered interrupt & edge-triggered interrupt (电平触发中断 与 边沿触发中断)

(谢谢 wanpin Li对这篇博客的反馈,让我重新发现了一些问题)

两种触发方式,是为了在多个设备共享中断线时,实现流量控制。

  • 所谓边沿触发,就是电平发生变化(通常是从高到低)时触发的中断 (CPU忙时中断可能丢失)
  • 所谓电平触发,就是设备保持中断请求引脚(中断线)处于预设的有效触发电频(通常为高),中断会一直被请求(CPU忙时中断也不会丢失)。为了避免中断请求被重复处理,需要在处理前 标记 irq,然后 ACK irq(复位中断请求引脚为无效电频,以便能够接受其他中断请求),处理中断,然后 取消 irq 标记。

KVM 最先只支持 edge-triggered 中断,使用一个irqfd通知guest,电频下降边沿可在注入模拟中一次完成。看下面代码连续调用两个kvm_set_irq(),倒数第二个参数为电平。

虚拟机 level-triggered 的注入仍然依赖的是userspace,在QEMU里模拟了 PCI / IOapic /LAPIC(8259)。KVM_IRQFD ioctl 是中断注入的接口,唯一参数 kvm_irqfd 结构体包含了一个irqfd、gsi(全局系统中断),flags。

KVM 在 2012 年也添加了 level-triggered 中断模拟 [1],添加了一个 resamplefd (为eventfd) 到 kvm_irqfd 结构体,用它来通知guest取消 irq 标记。来自所有guests、设备的请求被放在链表中管理,所有的 resample irqfds使用同一个 IRQ源ID,所以 de-assert(复位中断请求引脚为无效电平,也就是清除中断状态寄存器–ISR)只需要一次,而每个irqfd都需要单独通知 guest 一次(用于通知新的resampler使用这个gsi)。

static void
irqfd_inject(struct work_struct *work)
{
	struct _irqfd *irqfd = container_of(work, struct _irqfd, inject);
	struct kvm *kvm = irqfd->kvm;

	if (!irqfd->resampler) {  //edge-triggered
		kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, irqfd->gsi, 1, false);
		kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, irqfd->gsi, 0, false);
	} else //level-triggered
		kvm_set_irq(kvm, KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID,
			    irqfd->gsi, 1, false); //assert irq
}
/*
 * Since resampler irqfds share an IRQ source ID, we de-assert once
 * then notify all of the resampler irqfds using this GSI.  We can't
 * do multiple de-asserts or we risk racing with incoming re-asserts.
 */
static void
irqfd_resampler_ack(struct kvm_irq_ack_notifier *kian)
{
	...
	kvm_set_irq(resampler->kvm, KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID,
		    resampler->notifier.gsi, 0, false); //de-assert irq for level-triggered interrupt

	list_for_each_entry_rcu(irqfd, &resampler->list, resampler_link)
		eventfd_signal(irqfd->resamplefd, 1); //notify resimplers
	...
}

[1] commit 7a84428af  [PATCH] KVM: Add resampling irqfds for level triggered interrupts

Reference:
linux/virt/kvm/eventfd.c:
  irqfd_inject(struct work_struct *work)
  irqfd_shutdown(struct _irqfd *irqfd)
  irqfd_resampler_ack(struct kvm_irq_ack_notifier *kian)
  struct kvm_irqfd
  struct _irqfd
  struct _irqfd_resampler
linux/virt/kvm/irqchip.c:
  kvm_set_irq(kvm, source_id, level, line_status)

linux/virt/kvm/irq_comm.c:
  kvm_reqeust_irq_source_id(struct kvm *kvm)

通过 http://kej.tw/flvretriever 在线下载YouTube视频

为了强奸自己的耳朵,我想从下载 colleague 的讲座视频,这样就可以随时随地,反复加强对这种口音的适应。 豆瓣上(http://www.douban.com/group/topic/24048538/)推荐了几个,多数都需要安装EXE的客户端 😦

Screenshot from 2014-03-24 10:17:46

好玩的 JavaScript 小游戏 :2048

Screenshot from 2014-03-24 23:56:21Screenshot from 2014-03-25 01:08:16

 

 

用Django创建一个简单的动态请求站点

目标

在数梅派上启动一个HTTP服务,通过 GPIO 接口控制继电器 (可以参考 使用RaspberryPi 的GPIO接口控制LED灯闪烁 ),从而控制电器的开关。这样可以随时随地控制电源开关。

如果想在任何地方都能控制数梅派GPIO这里有很多解决方案:

  • 使用花生壳这样在任何地方都可以访问一个私网里的IP了
  • 自己假设一个web服务器,数梅派轮循(pool)从服务器URL检查状态值,并通过GPIO同步设置到硬件状态
  • 使用共有的服务器,如 http://yeelink.net,我们可以通过yelink客户端,设置服务器上的状态值,数梅派可以通过yelink分配的唯一URL,查询这个状态值,并同步设置硬件状态。可参考:http://www.kankanews.com/ICkengine/archives/97226.shtml

下面的web服务可以直接放在数梅派上,也可以放在公共服务器上,它的作用只是提供HTTP控制接口。

下载演示代码

首先使用django-admin创建一个project

# django-admin.py startproject PowerManagement
# cd PowerManagement/
# ls
  manage.py  PowerManagement

启动服务

# python manage.py runserver example.com:80
# w3m http://example.com/

这时候访问网站展示的是一个默认的欢迎页面

下面我们来添加一个自己的首页

创建templates目录,并添加到settings.py
+++ b/PowerManagement/settings.py
@@ -26,6 +26,10 @@ TEMPLATE_DEBUG = True

 ALLOWED_HOSTS = []

+TEMPLATE_DIRS = (
+    './templates',
+)
+
更新URL响应方法
+++ b/PowerManagement/urls.py
@@ -9,4 +9,5 @@ urlpatterns = patterns('',
     # url(r'^blog/', include('blog.urls')),

     url(r'^admin/', include(admin.site.urls)),
+    url(r'^$', 'PowerManagement.views.home', name='home'),
 )
在views.py中实现响应方法,home.html需要在./templates目录添加
+++ b/PowerManagement/views.py
@@ -0,0 +1,4 @@
+from django.shortcuts import render_to_response
+
+def home(request):
+    return render_to_response('home.html')

添加新的model (Switch)

# python manage.py startapp Switch
# ls Switch/
  admin.py  __init__.py  models.py  tests.py  views.py
添加 子url解析文件
# vim ./Switch/urls.py
from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    url(r'^getstate$', 'Switch.views.getstate', name='getstate'),
    url(r'^setstate$', 'Switch.views.setstate', name='setstate'),
)
把子url解析文件include到主urls.py
+++ b/PowerManagement/urls.py
@@ -10,4 +10,5 @@ urlpatterns = patterns('',

     url(r'^admin/', include(admin.site.urls)),
     url(r'^$', 'PowerManagement.views.home', name='home'),
+    url(r'^switch/', include('Switch.urls')),
 )
创建 Switch 类,其包含一个state 的属性
+++ b/Switch/models.py
@@ -1,3 +1,6 @@
 from django.db import models

+
+class Switch(models.Model):
+    state = models.IntegerField(default=0)
实现getstate/setstate方法
+++ b/Switch/views.py
@@ -1,3 +1,25 @@
 from django.shortcuts import render
+from django.shortcuts import render_to_response
+from django.http import HttpResponse
+from Switch.models import Switch

 # Create your views here.
+
+def getobj():
+    try:
+        s = Switch.objects.get()
+    except Exception:
+        s = Switch()
+        s.save()
+    return s
+
+def getstate(request):
+    s = getobj()
+    return HttpResponse('Switch state is (%d)' % s.state)
+
+def setstate(request):
+    s = getobj()
+    if request.GET.get('state', -1) > 0:
+        s.state = int(request.GET.get('state'))
+        s.save()
+    return HttpResponse('Set switch state to (%d)' % s.state)

然后把新的 model 更新到 INSTALLED_APPS,并同步数据库

+++ b/PowerManagement/settings.py
@@ -40,6 +40,7 @@ INSTALLED_APPS = (
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
+    'Switch',
 )

# python manage.py syncdb

启动服务

# python manage.py runserver example.com:8000

访问

# w3m http://example.com/switch/getstate

Switch state is (4)

# w3m http://example.com/switch/setstate?state=2

Set switch state to (2)

reboot Or shutdown ? (一)

09年3月临近毕业的我,还在苦苦等待腾讯的offer,由于错过了校园招聘流程,迟迟没有结果。我心里知道对于我来说时间和机会都不太多了,似乎已经把这个offer当成唯一的希望了。

日子一天天的过,磊子看出了我的忧虑,建议我多试几家,北京上海都可以看看。我给自己设置的思维限制似乎被打开了,一下子就没有那么多没必要的顾虑了,不管是什么结果,自己都有勇气去承受了。

就这样,我把简历投到 Red Hat,开始自己准备英语,本来提到的开发职位,考虑我的经验压根没有给我机会,不过面试中有自动化测试框架的Python开发,这个也合我胃口,能加入当时心目中最最优秀的Linux企业发型商,在加上步入社会前对于未来的美好憧憬,心中的喜悦无法用语言表达。心里想着:北京我来了! I will hack my life by Free Software!

大学里几乎把所有课余时间都花在Linux兴趣小组的实验室了,每天晚上10点多被保安赶走,偶尔也在小黑屋里通宵。不管是开始和小康学习黑别人机器,郭老师给我们讲Linux基础,贾孟树教我编译mplayer,老大帮我解决GCC编译,和郭拓装红帽5,王聪讲GCC+makefile,还是后来一起开发XylFTP,举办各种活动、讲座。讲不玩的人,说不完的事。是这个团队让我有了归属感,给了我更多的自新,让我们一起成长,收获了知识和深厚的友谊!

就这样,我来到了北京。最初我似乎没有给自己限定要去哪里,似乎哪里都行,只要能做自己喜欢的技术。

刚来开始工作的日子,周围只有认识王聪,平时找他也不是很多,所以很多时候我都待在公司,待在这个有很多伙伴一起玩,一起讨论技术的天堂。台球、三国杀、魔方等等,最晚一次三国杀打到半夜三点半,几乎每天晚上都是翻墙进小区,回去早就自己听听广播,写写日记。我感觉这个陌生的世界真美好,虽然刚开始很多东西都才起步开始学,但自己的内心和脑海,早已被无限的遐想充斥的丰富多彩。没有想过自己要去改变世界,但我觉得我至少可以改变自己。

[amos@amosk ~]$ date -d "Thu Mar 13 18:00:00 CST 2014" +%s
1394704800
[amos@amosk ~]$ date -d "Thu Jun 24 18:00:00 CST 2009" +%s
1245837600
[amos@amosk ~]$ echo "(1394704800-1245837600)/3600/24" | bc
1723

1723天后,我把北京房子退了,回家一段时间,此刻我的内心是复杂和矛盾的。脑子里已经屋里去回想这4年多都发生了什么,我只知道我不能离开身边的这帮兄弟。

从开始考虑在西安买房那一刻起,我北漂的决心一定不那么坚决了。不管这是一个智慧的追求生活的选择,还是一种软弱的面对困难的退缩,事实是我走到了今天这一步,不是一个好的表率,我甚至内心觉得懊悔。自己嘴里喊着让其他人在北京好好奋斗,兄弟们难得聚到一起,自己却默默的走了。

在今天清理东西时候,我很享受把东西扔进垃圾桶的感觉,因为我又少了一个包袱。令我惊讶的事,整理完有用的东西只有一点点。我希望这次回去也是卸下自己的包袱,来一次 Reboot,把无用的内存释放掉,换回一个全新的生活状态。

2014.3.13  18点

封装一个sendkey函数向虚拟机一次性发送字符串

QEMU 提供一个 sendkey的 Monitor 命令,用来向虚拟机发送单个字符,或者组合键。之所以只支持单个字符,是因为这里需要对空格、ctrl,回车等进行转换,也需要支持keycode的输入。
libvir的virsh调用QEMU的sendkey命令为上层提供类似的功能。但有一个新的需求就是一次输入连续的字符串。我们可以从三个不同层面来实现这个需求,分别是QEMU、libvirt,还有用户自己的脚本、程序里。

这里我跟倾向从用户程序封装出一个转换函数,实现一次连续输入,这样也更加灵活一些,方便扩展。因为QEMU、libvirt接受的多种类型的KEY,如果再把字符转换加进去,会导致接口复杂,语义表达带来冲突。

[amos@amosk qemu]$ cat sendkey.sh 
DOM=rhel6u5_x64

# 封装一下sendkey()函数,调用virsh send-key 命令想虚拟机发送字符串
function sendkey() {
    str=$@
    length=`expr length "$str"`
    for ((i=1; i<=$length; i++)); do
        char=`expr substr "$str" $i 1`
        if [ "$char" = " " ];then
            char="spc"
        fi
        echo virsh send-key $DOM "$char"
    done
}

sendkey "root"
echo virsh send-key $DOM kp_enter
sendkey "shutdown -h now"
echo virsh send-key $DOM kp_enter
[amos@amosk qemu]$ bash sendkey.sh 
virsh send-key rhel6u5_x64 r
virsh send-key rhel6u5_x64 o
virsh send-key rhel6u5_x64 o
virsh send-key rhel6u5_x64 t
virsh send-key rhel6u5_x64 kp_enter
virsh send-key rhel6u5_x64 s
virsh send-key rhel6u5_x64 h
virsh send-key rhel6u5_x64 u
virsh send-key rhel6u5_x64 t
virsh send-key rhel6u5_x64 d
virsh send-key rhel6u5_x64 o
virsh send-key rhel6u5_x64 w
virsh send-key rhel6u5_x64 n
virsh send-key rhel6u5_x64 spc
virsh send-key rhel6u5_x64 -
virsh send-key rhel6u5_x64 h
virsh send-key rhel6u5_x64 spc
virsh send-key rhel6u5_x64 n
virsh send-key rhel6u5_x64 o
virsh send-key rhel6u5_x64 w
virsh send-key rhel6u5_x64 kp_enter