溃败
为了支持自己心爱的球队,冒着挨揍的危险毅然决然的买了张主席台附近的票.
周日很早就来到了工体,在周围徘徊了几圈后居然没有发现一个客队球迷,稍有些失望.但是当我进入体育场的时候发现他们早已经入场了,整齐划一静静的等待着比赛的开始.他们都是不远千里赶过来加油的(终于发现比我还”傻”的人了),可是在我眼里他们真的很可爱!
北京的球市真的很好,上座率很高.他们衣装整齐口号整齐且响亮,无论男女老少都可以参与进来,把整个工体汇成了一片绿色的海洋,而我却显得那么的多余和渺小.国安有这么一群痴心的球迷,你们真的很幸福啊!
比赛很快就失去了悬念,两队的真实差距绝对不是比分体现的那样.关于比赛的公平性以及那两个点球的判罚我不想说,因为首先你们没有踢出自己的东西,毫无斗志,在球场上唯一能让人信服的就是进球.
落寞的我和周围得意并且语气中夹杂着侮辱的北京球迷形成了鲜明的对比.望着远处的阿尔滨球迷和记分牌我的眼神中充满着孤独,迷茫与失望,我们明明近在咫尺却不能在一起,此情此景让我联想到了现实中的我 ……
输并不可怕,可怕的是明明知道自己的不足却不去努力改正.阿尔滨让我们一起努力,国安明年金州见!
Welcome to Houston Rockets, Dwight Howard !
从姚明刚进入NBA的那时候起我就开始关注了,被那种激烈的身体对抗,扣篮,盖帽,以及绝杀深深的吸引了,渐渐的喜欢
上休斯顿火箭队并且成为一名忠实的火箭粉丝.从弗老大老猫到麦迪,Yao 身边换了无数个队友,可是却始终没有换来一个总冠军,最好的成绩才是突破了
季后赛首轮.看了火箭这么些年球,快乐过,痛苦过,甚至绝望过!快乐是火箭赢球并打出精彩的比赛,痛苦是一到关键时候就手软被绝杀,见过了太多
被绝杀的比赛_(Roy 0.8s 的三分是绝杀中的经典)_总是感慨为什么火箭总被绝杀,难道是麦迪那次35秒13分用光了火箭这几年所有的运气?绝望的
是经理的碌碌无为,每年的休赛期莫雷那个胖子总是吵吵着要签这个交易那个球星,可是每年都是竹篮打水一场空,这对于球迷来说是难以接受的,对这只
球队感到绝望因为看不到任何希望!后来 Yao 退役了,伤心,因为他很努力却没有赢得总冠军,正如他说的_“努力不一定成功,但放弃一定失败”_,但是我觉得他仍然很成功,因为他影响了像我一样的一批人,这一点谁也做不到!在他退役后我决定不再关注火箭队,但是我发现我做不到,难道这就是爱?
总之就是从看NBA比赛中寻找激情,快乐,回忆,以及人生的感悟.其实人生就像一场四节比赛,我的第一节已经结束仿佛已经进入了垃圾时间,不舍求翻盘
只盼少输一点!忘不了高中的时候下课玩命的往篮球场跑的情景,也忘不了与同学们畅聊 NBA 的情景,更忘不了为了看场火箭比赛而逃课被班主任臭骂的情景 … 那个时候的生活是痛并快乐着,很是怀念!可是岁月却是残酷的,后来终于在眼泪中明白,有些东西或人一旦错过永远都不会再回来 ……
Damn it,我这是说哪去了?
言归正传,Dwight Howard 欢迎加入休斯顿火箭 ! 莫雷干的漂亮,终于没有让火箭迷们再次失望,同时特别感谢一下科比 :D.
知道这两天魔兽会作出最终决定,早上醒来看了新闻又看了twitter, 貌似搞定,下午终于尘埃落定了,作为火箭迷真的很高兴,很高兴!可是恕我直言
即使弄来魔兽这阵容也不足以拿总冠军,季后赛想从西部的乱军中杀出重围仍然很难,雷霆,马刺,快船都是不好弄的主,东部还有卫冕冠军热火_(篮网的阵容太不要脸了).火箭4号位上仍然是软肋,所以还需一个人,如果能搞定约什史密斯的话,在外线再配置几个火炮,最后签约个老将(加西
亚之类的),我觉得这就差不多了,就这内线的实力,谁不害怕!我始终认为得内线者得天下(热火除外,因为他们有詹姆斯,这就是我不喜欢的>热火的原因,毁了我的三观)_ !
作为火箭球迷今天是值得高兴的一天,期待莫雷给我们更多的惊喜,祝愿哈登,魔兽_(霍哈哈组合)_早日率领火箭问鼎总冠军!!
Kernel Thread 的创建
在 Linux 中有很多的内核线程,可以通过 ps
command 查看到,比如: kthreadd ksoftirqd watchdog 等等等 … 它们都是由内核从无到有创建的,通过它们的 pid 以及 ppid 可以得出以下几点:
- 在内核初始化
rest_init
函数中,由进程 0 (swapper 进程)创建了两个 process <ol style="list-style-type: decimal;"> - init 进程 (pid = 1, ppid = 0)
- kthreadd (pid = 2, ppid = 0)
- 所有其它的内核线程的 ppid 都是 2,也就是说它们都是由 kthreadd thread 创建的
- 所有的内核线程在大部分时间里都处于阻塞状态(TASK_INTERRUPTIBLE)只有在系统满足进程需要的某种资源的情况下才会运行
</ol>
创建一个内核 thread 的接口函数是:
kthread_create()
kthread_run()
这两个函数的区别就是 kthread_run()
函数中封装了前者,由 kthread_create()
创建的 thread 不会立即运行,而后者创建的 thread 会立刻运行,原因是在 kthread_run()
中调用了 wake_up_process()
.用什么函数看你自己的需求,如果你要让你创建的 thread 运行在指定的 cpu 上那必须用前者(因为它创建的 thread 不会运行),然后再用 kthread_bind()
完成绑定,最后 wake up.
下面就大概说一下内核 thread 创建的过程.
kthreadd
rest_init()
{
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
kthreadd_task = find_task_by_pid(pid);
}
kernel thread
kthread_create(usb_stor_control_thread, us, "usb-storage")
struct task_struct *kthread_create(int (*threadfn)(void *data),
void *data,
const char namefmt[],
...)
{
struct kthread_create_info create;
create.threadfn = threadfn;
新创建的线程运行的函数
create.data = data;
init_completion(&create.done);
初始化一个 completion 用于一个线程告诉其它线程某个工作已经完成
spin_lock(&kthread_create_lock);
list_add_tail(&create.list, &kthread_create_list);
加到待创建 thread list 中去
spin_unlock(&kthread_create_lock);
wake_up_process(kthreadd_task);
唤醒 kthreadd 线程来创建新的内核线程
wait_for_completion(&create.done);
等待新的线程创建完毕(睡眠在等待队列中)
......
}
kthreadd 是一个死循环,大部分时间在睡眠,只有创建内核线程时才被唤醒.
int kthreadd(void *unused)
{
关注循环体
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (list_empty(&kthread_create_list))
schedule();
首先将线程状态设置为 TASK_INTERRUPTIBLE, 如果当前
没有要创建的线程则主动放弃 CPU 完成调度.此进程变为阻塞态
__set_current_state(TASK_RUNNING);
运行到此表示 kthreadd 线程被唤醒(就是我们当前)
设置进程运行状态为 TASK_RUNNING
spin_lock(&kthread_create_lock);
while (!list_empty(&kthread_create_list)) {
struct kthread_create_info *create;
create = list_entry(kthread_create_list.next,
struct kthread_create_info, list);
从链表中取得 kthread_create_info 结构的地址,在上文中已经完成插入操作(将
kthread_create_info 结构中的 list 成员加到链表中,此时根据成员 list 的偏移
获得 create)
list_del_init(&create->list);
取出后从列表删除
spin_unlock(&kthread_create_lock);
create_kthread(create);
完成真正线程的创建
spin_lock(&kthread_create_lock);
}
spin_unlock(&kthread_create_lock);
}
}
create_kthread 函数完成真正线程的创建
static void create_kthread(struct kthread_create_info *create)
{
int pid;
pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
其实就是调用首先构造一个假的上下文执行环境,最后调用 do_fork()
返回进程 id, 创建后的线程执行 kthread 函数
if (pid < 0) {
create->result = ERR_PTR(pid);
complete(&create->done);
}
}
此时回到 kthreadd thread,它在完成了进程的创建后继续循环,检查 kthread_create_list 链表,如果为空,则 kthreadd 内核线程昏睡过去 ……
当前有三个线程:
- kthreadd thread 已经光荣完成使命,睡眠
- 唤醒 kthreadd 的线程由于新创建的线程还没有创建完毕而继续睡眠 (在 kthread_create 函数中)
-
新创建的线程已经正在运行
kthread
,但是由于还有其它工作没有做所以还没有最终创建完成.
新创建的线程 kthread 函数
static int kthread(void *_create) { struct kthread_create_info *create = _create; // create 指向 kthread_create_info 中的 kthread_create_info int (*threadfn)(void *data) = create->threadfn; // 新的线程创建完毕后执行的函数 void *data = create->data; struct kthread self; int ret; self.should_stop = 0; // 在 kthread_should_stop() 函数中会检查这个值,表示当前线程是否 // 运行结束. kthread_should_stop() 常被用于线程函数中. init_completion(&self.exited); current->vfork_done = &self.exited; /* OK, tell user we're spawned, wait for stop or wakeup */ __set_current_state(TASK_UNINTERRUPTIBLE); // 设置运行状态为 TASK_UNINTERRUPTIBLE create->result = current; // current 表示当前新创建的 thread 的 task_struct 结构 complete(&create->done); // new thread 创建完毕 schedule(); // 执行任务切换,让出 CPU ...... }
线程创建完毕:
- 创建新 thread 的进程恢复运行
kthread_create()
并且返回新创建线程的任务描述符 - 新创建的线程由于执行了
schedule()
调度,此时并没有执行.
最后唤醒新创建的线程 :
wake_up_process(p);
当线程被唤醒后,继续 kthread()
ret = -EINTR;
if (!self.should_stop)
ret = <span style="color: #ff0000;">threadfn</span>(data);
执行相应的线程函数
do_exit(ret);
最后退出
总结 {#总结}
- 任何一个内核线程入口都是
kthread()
- 通过
kthread_create()
创建的内核线程不会立刻运行.需要手工 wake up. - 通过
kthread_create()
创建的内核线程有可能不会执行相应线程函数threadfn
而直接退出
最后写了一个创建内核线程的模块仅供参考:
#include <linux/init.h> #include <linux/time.h> #include <linux/errno.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/kthread.h> #include <linux/cpumask.h> #define sleep_millisecs 1000*60 static int thread(void *arg) { long ns1, ns2, delta; unsigned int cpu; struct timespec ts1, ts2; cpu = *((unsigned int *)arg); printk(KERN_INFO "### [thread/%d] test start \n", cpu); while (!kthread_should_stop()) { /* * Do What you want */ schedule_timeout_interruptible( msecs_to_jiffies(1)); } printk(KERN_INFO "### [thread/%d] test end \n", cpu); return 0; } static int __init XXXX(void) { int cpu; unsigned int cpu_count = num_online_cpus(); unsigned int parameter[cpu_count]; struct task_struct *t_thread[cpu_count]; for_each_present_cpu(cpu){ parameter[cpu] = cpu; t_thread[cpu] = kthread_create(thread, (void *) (parameter+cpu), "thread/%d", cpu); if (IS_ERR(t_thread[cpu])) { printk(KERN_ERR "[thread/%d]: creating kthread failed\n", cpu); goto out; } kthread_bind(t_thread[cpu], cpu); wake_up_process(t_thread[cpu]); } schedule_timeout_interruptible( msecs_to_jiffies(sleep_millisecs)); for (cpu = 0; cpu < cpu_count; cpu++) { kthread_stop(t_thread[cpu]); } out: return 0; } static void __exit XXXX_exit(void) { } module_init(XXXX); module_exit(XXXX_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("bluezd"); MODULE_DESCRIPTION("Kernel study");
Raspberry Pi 折腾笔记
在去年4月份就入手了一个 Raspberry Pi, 拿到后首先买了个 sd card, USB ttl 串口线 和 hdmi 转 DVI 的线,dd 装完 debian 后,玩了两天就把他装到了原先的盒子里再也没折腾。
这几个月突然心血来潮决定折腾一下,特别是看了 34 个使用 Raspberry Pi 的酷创意之后,决定先弄个 NAS 玩玩,实现白天远程连接下载,这样就不会浪费了我的 10 M 带宽的网络~
系统有了,可是怎么连上去呢? 我住这条件简陋,无显示器,只能通过 USB ttl 串口连接了,成功连上后简单配置了一下拨号连接,此时它可以连接到 internet 了,但是问题来了,我的其他设备无法上网了,于是乎把我的那个只有一个 Wan 口功能弱到爆的 TP-LINK 无线路由器拿了出来(怎么突然想起年会大典了 ?),我的笔记本连 wifi, raspberry pi 连到笔记本的网口上,配置了一下 SNAT 后,raspberry pi 成功联网,这样就更方便我配置 pi 了。
但是这样用起来太不方便,于是思前想后决定买个BUFFALO的路由器,花了我 120 大洋,路由器到手后第一件事就是刷 dd-wrt, firmware , 以前在家的时候刷过,家里用的是 link sys 的路由器,很方便,可以远程 WOL。由于我得到的是公网 ip ,所以我可以远程连到路由器中,在路由器中配置好 ddns 和 DMZ or 端口转发后,我就可以远程连接到我的 Pi 中了。
哈哈,终于搞定了,可以远程下那些剧情场景简单,女主角漂亮的节目了!(新闻联播之类的,不要想歪哦 ~~) 哎,当初买 Raspberry Pi 的目的是研究内核的,结果却 …… 罪过啊
BTW Concert YY 终于下完了,倾城 真好听!!!
问题又来了,搞这种下载一定要有大容量快速存储设备的支持,本来想买个西数 1T 的硬盘,或者直接干脆就买个 NAS,但是一向勤俭节约的我怎可能买这么贵的东西呢,最终决定过年回家把以前买的那个移动硬盘拿来。哎,做人男,做男人更难啊,愁苦 ……
接下来再配置个 Samba NAS 也就简单的实现了,但是一向追求完美的我怎么可能满足呢? 接下来准备再折腾下 xbmc.
Kernel Debug Tips
printk 是 kernel debug 中经常用到的方法,可以在某个代码片段中加入 printk 来 view 某些关键的变量,但是如果要调试的代码是中断处理程序,比如 timer_interrupt, smp_apic_timer_interrupt 那用 printk 的话结果就悲剧了,但是我们可以通过某种方法来设置何时 printk .此时就用到了这个 panic_on_oops 变量
- 使用方法很简单,不过要重新 build 个 kernel <ol style="list-style-type: decimal;">
- 修改 /kernel/panic.c 中 panic_on_oops 变量为 0
- 在需要调试的代码段中加入 if (panic_on_oops) { instructions }
</ol>
Example :
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index 187e252..ac816b7 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c @@ -624,6 +624,11 @@ static inline void set_cyc2ns_scale(unsigned long cpu_khz) static inline unsigned long long cycles_2_ns(unsigned long long cyc) { + if (panic_on_oops){ + printk(KERN_INFO "=> XXXXXX"); + ...... + } + return (cyc * cyc2ns_scale) >> NS_SCALE; } diff --git a/kernel/panic.c b/kernel/panic.c index edb779e..37cb952 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -20,7 +20,7 @@ #include <linux/kexec.h> #include <linux/debug_locks.h> -int panic_on_oops = 1; +int panic_on_oops = 0; int tainted; static int pause_on_oops; static int pause_on_oops_flag;
Checking and Setting
待新 kernel build 好之后,查看以及设置当前 kernel panic_on_oops vaule
-
Checking
cat /proc/sys/kernel/panic_on_oops
-
Setting
-
enable
echo “1” > /proc/sys/kernel/panic_on_oops
-
disable
echo “0” > /proc/sys/kernel/panic_on_oops
-