momo zone

调核人的blog

Monthly Archives: 三月 2011

strtol()详解

今天读代码时看到了这个函数,感觉非常有用,试了几下把解释记录下来:

string to long

long int strtol(const char *nptr, char **endptr, int base)


strtol()会将nptr指向的字符串,根据参数base,按权转化为long int, 然后返回这个值。
参数base的范围为2~36,和0;它决定了字符串以被转换为整数的权值。


可以被转换的合法字符依据base而定,举例来说,当base为2时,合法字符为‘0’,‘1’;base为8时,合法字符为‘0’,‘1’,……‘7’;base为10时,合
法字符为‘0’,‘1’,……‘9’;base 为16时,合法字符为‘0’,‘1’,……‘9’,‘a’,……‘f’;base为24时,合法字符为‘0’,……‘9’,‘a’,……‘n’,base为36时,合法字符为‘0’,……‘9’,‘a’,……‘z’;等等。其中,不区分大小写,比如,‘A’和‘a’会都会被转化为10。
当字符合法时,‘0’,……‘9’依次被转换为十进制的0~9,‘a’,……‘z’一次北转换为十进制的10~35。


strtol()函数检测到第一个非法字符时,立即停止检测,其后的所有字符都会被当作非法字符处理。合法字符串会被转换为long int, 作为函数的返
回值。非法字符串,即从第一个非法字符的地址,被赋给*endptr。**endptr是个双重指针,即指针的指针。strtol()函数就是通过它改变*endptr的值,即把第一个非法字符的地址传给endptr。

多数情况下,endptr设置为NULL, 即不返回非法字符串。
下面看几个例子:
——————————————————
char buffer[20]=”10379cend$3″;
char *stop;
printf(“%d\n”,strtol(buffer, &stop, 2));
printf(“%s\n”, stop);
输出结果:
2
379cend$3
——————————————————-
char buffer[20]=”10379cend$3″;
char *stop;
printf(“%d\n”,strtol(buffer, &stop, 8));
printf(“%s\n”, stop);
输出结果:
543
9cend$3
——————————————————–
char buffer[20]=”10379cend$3″;
char *stop;
printf(“%d\n”,strtol(buffer, &stop, 10));
printf(“%s\n”, stop);
输出结果:
10379
cend$3
——————————————————-
char buffer[20]=”10379cend$3″;
char *stop;
printf(“%d\n”,strtol(buffer, &stop, 16));
printf(“%s\n”, stop);
输出结果:
17005006
nd$3
另外,如果base为0,且字符串不是以0x(或者0X)开头,则按十进制进行转化。如果base为0或者16,并且字符串以0x(或者0X)开头,那么,x(
或者X)被忽略,字符串按16进制转化。如果base不等于0和16,并且字符串以0x(或者0X)开头,那么x被视为非法字符。
例如:
——————————————————-
char buffer[20]=”0x31da6c”;
char *stop;
printf(“%d\n”,strtol(buffer, &stop, 0));
printf(“%s\n”, stop);
输出结果(stop为空):
3267180

——————————————————-
char buffer[20]=”0x31da6c”;
char *stop;
printf(“%d\n”,strtol(buffer, &stop, 13));
printf(“%s\n”, stop);
输出结果:
0
0x31da6c
——————————————————-

最后,需要说明的是,对于nptr指向的字符串,其开头和结尾处的空格被忽视,字符串中间的空格被视为非法字符。
例如:
——————————————————-
char buffer_1[20]=”10379c”;
char buffer_2[20]=”      10379c        “;
char buffer_3[20]=”      10      379c        “;
printf(“%d\n”,strtol(buffer_1,NULL,0));
printf(“%d\n”,strtol(buffer_2,NULL,0));
printf(“%d\n”,strtol(buffer_3,NULL,0));
输出结果为:
10379
10379
10

Advertisements

关于IRQ和睡眠

今天看了cu上的讨论,关于IRQ为什么不能睡眠/进程切换,感觉获益匪浅,因为这个问题是有一定深度的 ,所以说出来的理由也是五花八门。

但有一个理由很多人认同: 因为中断没有堆栈(上下文),所以一旦睡眠/进程切换后很难被唤醒/调度。

“中断没有堆栈”在2.4 时代中断没有自己的堆栈,发生中断时,因为要保存现场需要把数据压入内核栈,中断必须“ 借用” 当前进程的堆栈,所以中断不是没有堆栈而是堆栈不确定,谁叫中断是异步呢(同步的叫异常 ,必须马上处理的) 。

“一旦睡眠/进程切换后很难被唤醒/调度” :正如上面讲的,因为借用的是当前进程的堆栈,中断虽然没有上下文,但他实际所在的是当前进程上下文 ,所以一旦中断的被睡眠,那么现场还是能够保护的:内核将中断的CS:eip和SS:esp保存在被抢占任务A的thread_info中,当任务A被重新唤醒的时候, 任务A从中断的CS:eip开始执行, 这也能正常执行下去, 中断执行完后, 从ret_from_intr中返回. 可以恢复任务A的抢占前的场景。

然后事情没有那么简单,上述任务A因为一个中断被强占了,不能继续执行,然后中断又睡眠了,那么无辜的任务A也只能等再次被唤醒/调度才有可能执行,这不符合内核实时性的要求。如果真要让中断可睡眠/调度还有做大量的小概率事件的考虑,比如睡眠之后又出现了同一IRQ号的中断,中断比较频繁堆栈溢出怎么办。另外还要从中断的设计初衷思考: 中断本来就是比较紧急的任务,如果被休眠那么延迟很久后中断程序的执行还有意义吗? 不过也不能一概而论。也许中断不能休眠/调度真的是一个设计问题,而不是技术上不能实现

下面再说一下 kernel 2.6中关于中断方面的演化:

首先中断处理例程分为上半部/下半部,上半部主要是响应中断,下半部是处理中断handler 。中断handler被放置在一个tasklet或工作队列中。因为工作队列以内核线程去执行,在进程上下文 ,所以可以安全地休眠,而tasklet工作在软中断上下文(和上半部一样?),不能休眠。

总之把中断handler交给工作队列去处理可以实现一定程度的中断休眠,尽管2.6改进的是中断延后处理方面。

kernel 2.6的另一个重大改进是设置的中断堆栈:

内核在编译的时候设置了THREAD_SIZE的值为8K的话, 那么每个进程的内核栈的大小就为8K, 此时如果发生中断时, 那么进程的寄存器等值就会保存到它的8K的内核栈中. 但是如果设置了THREAD_SIZE的大小为4K的话, 内核就会使用3种类型的内核栈, 异常栈, 硬件中断请求栈以及软中断请求栈( When using 4K stacks, interrupts get their own stack instead of using the currently active kernel stack.)

* 异常栈:每个进程一个。

* 硬中断请求栈:每个CPU一个,每个占用一个单独的页框。do_IRQ()函数内部通过调用execute_on_irq_stack负责切换到该栈中来。

* 软中断请求栈:每个CPU一个,每个占用一个单独的页框。

当使用了中断栈以后,如果一旦中断睡眠/调度 ,那么中断将永远不会被唤醒了,因为调度器根本就看不到中断栈,也无法从进程上下文看到中断。

FC,GB(C&A),PC-E,SFC,MD 怀旧大行动

搞了一个32GB 的记忆棒,就开始突然怀旧起来,妄想把84-99年全FC,GB(C&A),PC-E,SFC,MD ROM纳入囊中,目前进度FC,SFC,MD已完成。头痛的是MEGA_CD的光碟游戏都很大。意外发现日版和美欧版的SONIC CD 音轨完全不一样,我先在也不知道之前玩的是日版还是美版了,这个正在考据中。

FC,MD,SFC,GBA 全rom分别都是2400多 ,看着众多rom 及一幅幅截图,感觉岁月如梭,自己真的老了,80后: 游戏,你还玩得动吗?

附:多玩的模拟首页 http://psp.duowan.com/emu/

怀旧论坛:http://bbs.duowan.com/forumdisplay.php?fid=1119&page=1

支持mega cd的模拟器 picodrive首页 http://notaz.gp2x.de/pico.php

rom集合站点:

http://www.emuparadise.org/roms-isos-games.php

http://edgeemu.net/search.php

http://romhustler.net/

http://www.doperoms.com/roms/

http://www.romnation.net/srv/roms.html

http://www.romfind.com/ (不错的rom搜索引擎)

http://www.snesorama.us/board/showthread.php?t=62045 ( megaupload )

http://sonar.heranbago.com/top.html (全部都和sonic 相关)

 

pspmod 站点:

http://www.pspmod.com/forums/

kernel 2.6.38 特性–Transparent huge pages

翻译过来应该是透明大内存页。所谓的大内存页就是大于4KB的页。什么? 教课书上不是说页都是4KB吗?  哦,有此疑问的需要更新一下新知识了,以免落伍。大内存页的好处显而易见,免得频繁调度内存页,减少缺页异常,提高应用程序效率。 缺点嘛 就是写这类程序时要考虑一下内存分配以及回收策略(应用程序的话应该有库摆平了)。

另外大内存页还有一个隐含的好处:可完全避免虚拟地址到物理地址的转换。为什么?  因为cpu里面有个TLB缓存(恨死了,当初oracle面试我这个是啥东西我竟不知道 ,很丢人)里面放的就是虚拟地址对应的物理地址,当程序需要寻址/载入某虚拟地址时,CPU会首先读/写TLB缓存,如果有对应虚拟地址的项就直接发出/更新TLB中的物理地址,而不用CPU按照页表去计算物理地址。然而TLB是一个稀缺资源,用cpuid可以查看TLB的缓存相数:

   cache and TLB information (2):
      0xb1: instruction TLB: 2M/4M, 4-way, 4/8 entries
      0xb0: instruction TLB: 4K, 4-way, 128 entries
      0x05: data TLB: 4M pages, 4-way, 32 entries

指令4K页的有128项缓冲,数据4M页有32项缓冲。指令2M/4M页有4/8项

看起来相当稀缺啊(增加这个会提高cpu设计/制造成本吗?),所以当使用4K页的时候这128项地址映射缓冲将会频繁更新,导致性能下降。那么还有2M/4M页缓冲呢,能用吗?

答案是能用,但问题之前仅是内核代码和数据段在使用。一般的应用程序如果想用,必须程序员和系统管理员都忙活一下,借助libhugetlbfs这个库来启用(可以参考 http://www.ibm.com/developerworks/cn/linux/es-lop-leveragepages/index.html)。

这次内核的透明大内存页特性就是使内核发现适合启用大内存页的情景,并自动启用。实现方法是在内核中启动khugepaged 这个内核线程来收集4KB页表,一旦发现有连续分配的情况就把页表项合并。目前它的开发进度是仅能够应付2MB的大内存页。

kernel 2.6.38 特性–Automatic grouping scheduling

2.6.38 例行更新带来了众盼已久的鸡血特性(Automatic grouping scheduling)

为了理解这个特性的作用要从内核CFS调度器说起:CFS算法的最大特点就是它没有进程队列,而是构建了一个红黑树。任何进程都会在有限的时间被调度。优先级在CFS调度算法中失去了本来的意义,nice值改称为权值,顾名思义它将参与一系列数学运算,然后参与调度器的进程挑选工作:

虚拟时钟的步伐(vruntime的增量)=(实际时间)*(nice为0的进程的权值/进程权值) [其中,nice为0的进程的权值为1024]

优先选择vruntime最小的进程来执行。

以及计算进程时间片的运算:

进程运行时间=(系统调度周期)*(本进程的权值/本运行队列的总权值)

……上述扯远了(了解CFS可以看看这个http://www.ibm.com/developerworks/cn/linux/l-completely-fair-scheduler/index.html?ca=drs-cn-0125

重要的是与CFS一块到来的还有组调度概念及实现。这样就可以实现用户或组的公平性,而不是任务公平性。可以将任务进行分组,形成多个实体,调度程序将平等对待这些实体,继而公平对待实体中的任务。自此内核调度可以分为两层:组与组的竞争,以及组内进程的竞争。

2.6.24中调度组可以按照以下划分:

  • 用户 ID。
  • cgroup pseudo 文件系统:这个选项使管理员可以根据需要创建组。有关更多细节,阅读内核源文档目录中的 cgroups.txt 文件。

值得注意的是上述分组都是在用户层进行的,所以需要用户进行手工设置分组

Without automatic process grouping:

[proc. 1 | proc. 2 | proc. 3 | proc. 4 | proc. 5 | proc. 6]

With automatic process grouping:

[proc. 1, 2, 3, 4  |     proc. 5       |     proc. 6      ]

后来有人提出自动设置分组的想法,但由于是自动设置分组,所以分组的依据成为了关键。Galbraith 提出了按照TTY 自动分组的算法,并提交了内核补丁,linus也非常赞同这种做法,原因是这种分组能够大幅提高桌面程序的交互响应。

那么在这个补丁正式进入主线后,在2.6.38中又把自动分组依据变更为session ID : 内核会自动按照sid分组,这会使CFS的第一层调度单位改为session id ,因为每开一个shell(TTY) 肯定会建立一个新的session(用ps -eo pid ,session,cmd查看)。因此可以把这种分组看作是TTY based的一种扩展。当然,用setsid系统调用创建的新session 也算一个分组。

###################################################################################

我之前测试了一下TTY-based-grouping schedule 补丁,发现在以下情况效果比较明显: 后台(终端)执行make -j64 编译一个大程序,前台(KDE等桌面环境)执行一个chrome 或mplayer 看h264。这样原本不开启自动分组的情况下64个编译进程以及chrome,mplayer 将会相互争夺资源,依据CFS的脾气,chrome或mplayer 肯定会cpu 饥饿,因为在至少66个进程中,轮到他们执行的机会还是比较小的。 那么在开启分组后CFS调度实体变为一个make 进程组和mplayer及其桌面环境进程。mplayer的调度机会大大增加,同时make 进程组中的进程相互争夺被mplayer占用后剩余的cpu 时间。本质上是牺牲了某些TTY进程组的时间换取了另外进程组程序的实时性。

那么这次主线内核改为session ID分组实际性能改善及适用场合还不确定,相信会比TTY based更公正一些。使得桌面实时性与后台程序的吞吐更平衡一些。

现在回顾一下,好像这样linux kernel 就实现了真正的“线程”调度了,第一层组调度相当于“进程”,第二层进程调度相当于“线程” ,而且应用程序开发人员不用手动创建“线程”。

 

附一些有用的链接:

http://blog.csdn.net/peimichael/archive/2010/01/20/5218335.aspx

http://www.ibm.com/developerworks/cn/linux/l-completely-fair-scheduler/index.html?ca=drs-cn-0125

http://www.ibm.com/developerworks/cn/linux/l-cfs/index.html

http://zh.wikipedia.org/zh/%E7%BA%A2%E9%BB%91%E6%A0%91

对于抢盐的那些人我要说

上联—日本是大核民族, 下联—中国为盐荒子孙, 横批—有碘意思。

上联—日本人在核辐射中等待碘盐,下联—中国人抢碘盐以等待核辐射,横批—无盐以对。

内核代码奇技淫巧–do{}while() 宏

其实不仅内核代码里面能看到这类宏,其他的应用或中间代码也能看到其身影,虽然是奇技淫巧,但确实非常有用。

该宏有两个不同的用法:

1. 精简判断分支(不使用goto语句)。 通常,如果在一个函数中开始要分配一些资源,然后在中途执行过程中如果遇到错误则退出函数,当然,退出前先释放资源,我们的代码可能是这样:
version 1

bool Execute()
{
// 分配资源
int *p = new int;
bool bOk(true);// 执行并进行错误处理
bOk = func1();
if(!bOk)
{
delete p;
p = NULL;
return false;
}
bOk = func2();
if(!bOk)
{
delete p;
p = NULL;
return false;
}
bOk = func3();
if(!bOk)
{
delete p;
p = NULL;
return false;
}
// ..........
// 执行成功,释放资源并返回
delete p;
p = NULL;
return true;
}

这里一个最大的问题就是代码的冗余,而且我每增加一个操作,就需要做相应的错误处理,非常不灵活。于是我们想到了goto:
version 2

bool Execute()
{
// 分配资源
int *p = new int;
bool bOk(true);// 执行并进行错误处理
bOk = func1();
if(!bOk) goto errorhandle;
bOk = func2();
if(!bOk) goto errorhandle;
bOk = func3();
if(!bOk) goto errorhandle;
// ..........
// 执行成功,释放资源并返回
delete p;
p = NULL;
return true;
errorhandle:
delete p;
p = NULL;
return false;
}

代码冗余是消除了,但是我们引入了C++中身份比较微妙的goto语句,虽然正确的使用goto可以大大提高程序的灵活性与简洁性,但太灵活的东西往往是很危险的,它会让我们的程序捉摸不定,那么怎么才能避免使用goto语句,又能消除代码冗余呢,请看do…while(0)循环:
version3

bool Execute()
{
// 分配资源
int *p = new int;bool bOk(true);
do
{
// 执行并进行错误处理
bOk = func1();
if(!bOk) break;
bOk = func2();
if(!bOk) break;
bOk = func3();
if(!bOk) break;
// ..........
}while(0);
// 释放资源
delete p;
p = NULL;
return bOk;
}

2.形成单独逻辑块(避免宏替换以后产生错误)
举个例子看内核代码中udp部分:/usr/src/linux-2.6.37/include/net/udp.h
其实不仅内核代码里面能看到这类宏,其他的应用或中间代码也能看到其身影,虽然是奇技淫巧,但确实非常有用。

……

#define UDP_INC_STATS_BH(net, field, is_udplite) 
do { \
 if (is_udplite) SNMP_INC_STATS_BH((net)->mib.udplite_statistics, field);         \
 else SNMP_INC_STATS_BH((net)->mib.udp_statistics, field);   \
}  while(0)
#define UDP6_INC_STATS_BH(net, field, is_udplite) \
do { \
 if (is_udplite) SNMP_INC_STATS_BH((net)->mib.udplite_stats_in6, field);\
 else  SNMP_INC_STATS_BH((net)->mib.udp_stats_in6, field);  \
} while(0)
......
#define UDPX_INC_STATS_BH(sk, field) \
do { \
 if ((sk)->sk_family == AF_INET) \
UDP_INC_STATS_BH(sock_net(sk), field, 0); \
 else \
UDP6_INC_STATS_BH(sock_net(sk), field, 0); \
} while (0);
......
看起来这个几个宏是维护SNMP中upd的相关属性的,具体目的先不去了解了。先看前两个宏用了do {…} while(0) ,如果展开到第三个宏就是:
#define UDPX_INC_STATS_BH(sk, field) \ do { \ if ((sk)->sk_family == AF_INET) \ 
do { \
if (is_udplite) SNMP_INC_STATS_BH((net)->mib.udplite_statistics, field);         \
else SNMP_INC_STATS_BH((net)->mib.udp_statistics, field);   \
}  while(0); \
else \
do { \
if (is_udplite) SNMP_INC_STATS_BH((net)->mib.udplite_stats_in6, field);\
else SNMP_INC_STATS_BH((net)->mib.udp_stats_in6, field);  \
} while(0); \
} while (0);

如果去掉前两个宏的do while(上述红色部分)去掉, 就变成:

#define UDPX_INC_STATS_BH(sk, field) \
do { \
 if ((sk)->sk_family == AF_INET) \
if (is_udplite) SNMP_INC_STATS_BH((net)->mib.udplite_statistics, field);         \
else SNMP_INC_STATS_BH((net)->mib.udp_statistics, field); \
else \
if (is_udplite) SNMP_INC_STATS_BH((net)->mib.udplite_stats_in6, field);\
else SNMP_INC_STATS_BH((net)->mib.udp_stats_in6, field);  \
} while (0);
显而易见,else无法匹配,代码完全错乱,无法编译通过。
所以一般只要在宏定义中含有逻辑判断的情况,都要用do while去分割,这样比较安全,再修改代码也不需要会过头去重新匹配if else 等。

关于日本地震

转载而已,不代表个人观点…

 

日本地震了,里氏8.8级,伤亡人数还在继续扩大。为他们祈福吧。希望一切安好。

日本地震了。

日本的NHK不停播报,余震预报,海啸预警,伤亡人数。在持续余震剧烈摇晃的大楼中直播,所有东西都在往下砸。

某国地震了。

某国的XXX不停的播报,领导非常重,视亲临现场,此次地震真的真的是百年一遇,大爱无疆,众志成城,重建家园。多难兴邦。

日本地震了。

日本三得利株式会宣布,三得利旗下的所有自动售货机只要按键无需投币,马上可以免费取得你需要的水或者食物。马上得到了其他厂商的响应。

某国地震了。

某国的XXX公司向灾区捐了1亿人民币,XXX集团向灾区捐了2亿人民币,XXX有限公司向灾区捐了3亿人民币。最后那笔钱去哪里了?

日本地震了。

菅直人宣布所有超市,大型卖场成为临时避难所。超市里的所有水,食品,日常用品可以免费拿取,一切花费由日本政府承担。

某国地震了。

XXX来到了灾区,对着大家说,我来X了。

日本地震了。

原本震级是里氏8.9级。之后经过几个小时的纠正改成8.8级。

某国地震了。

原本震级是里氏7.8级。之后经过数周的纠正,改成了8级。

日本地震了。

8.8级地震,死了300人。

某国地震了。

8.0级地震,官方统计死了10万人。

日本地震了。

在超市,举行卖场长达10几个小时的临时避难,吃,喝,睡过后。大家开始离开超市,没有一点垃圾,跟没有人待过一样。日本人真变态!

某国没地震。

在大型的活动之后,跟地震了一样。

日本地震了。

执政党公开自我批评,没有做好预报工作,没有做好第一反应,援救工作实施不够快。

某国地震了。

我们众志成城,重建家园。“因为地形险要,实施救助相当困难。”每天放10遍。“XX军全部努力投入援救工作,援救工作实施的紧张有序。”每天放100遍。

日本地震了。

HNK独家直播所有灾情和新闻以及预报,其他电视台依旧。但是大家依旧24小时关心灾情。

某国地震了。

某家电视机消失功能N个小时。全有电视出现一个频道。收视率100%。

日本地震了。

某国的最大电视台还在大肆播放他们的大型政治会议。其中插播日本的地震。只有一个叫东方卫视的电视台通过自己的电台关系,借助国外直播画面和连线在日本的驻地记者直播日本灾情(PS:现在的东方卫视,真他妈让我刮目相看,这就叫海派,这就叫国际化)(PS2:我还特地关注了下某国当年地震所在地点省份的直属卫视,SC卫视,竟然在放连续剧!还没有任何字幕播报,看来是忘了当时人家是怎么对我们的了。)

某国地震了。

日本各大电视台24小时字幕滚动播放灾情,当下援助情况。NHK,24小时直播灾情。

某国地震了。

日本所有街头,24小时便利超市,公共厕所,自动售货机胖都有募捐箱。我的朋友当年在罗森买东西亲眼看见每一个日本人都把钱往里扔。日本政府要求救援队尽快出发,只等某国说YES!于是所有救援队一整晚在成田机场原地待命。日本自民党出钱包机,向成都运送救灾物资。日本救援队在北川挖出已经断气的遗体时,所有人在遗体前跪着痛哭自己的失职,最后所有队员对着遗体脱帽默哀3分钟。

日本地震了。

某国某论坛为“热烈庆祝日本地震”盖楼,竟盖了8480000楼。真是一个强大团结的民族!

 

ps3 DS3 手柄驱动

也许是当年ps3 可以安装yellow dog 的原因,现在kernel包直接包含了ds3(sixaxis)的驱动。

如果要让ds3 在linux 下工作需要两个部分:

1. HID    –人体工程学接口设备, 鼠标,键盘及其他控制器都需要它。它遵循HID驱动构架。仅处理模块初始化和设备的探测(probe)

具体位置在: drivers/hid/hid-sony.c / *  HID driver for some sony “special” devices

*
*  Copyright (c) 1999 Andreas Gal
*  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
*  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
*  Copyright (c) 2007 Paul Walmsley
*  Copyright (c) 2008 Jiri Slaby
*  Copyright (c) 2006-2008 Jiri Kosina
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include “hid-ids.h”
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
unsigned long quirks = id->driver_data;
struct sony_sc *sc;
switch (hdev->bus) {
case BUS_USB:
ret = sony_set_operational_usb(hdev);
break;
case BUS_BLUETOOTH:
ret = sony_set_operational_bt(hdev);
break;
default:
ret = 0;

hid-ids.h 包含ds3 的vid ,pid

#define USB_VENDOR_ID_SONY                      0x054c
#define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE       0x024b
#define USB_DEVICE_ID_SONY_PS3_CONTROLLER       0x0268

kernel 默认把这个hid-sony 包含进内核,而不是模块形式。 CONFIG_HID_SONY=y

2. input –这个是kernel 2.6以后抽象出来的,专门处理具体输入输出外设事件。input作为设备作业流的最后一个环节他将I/O外设分为这几类:

obj-$(CONFIG_INPUT_KEYBOARD)    += keyboard/
obj-$(CONFIG_INPUT_MOUSE)       += mouse/
obj-$(CONFIG_INPUT_JOYSTICK)    += joystick/
obj-$(CONFIG_INPUT_TABLET)      += tablet/
obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
obj-$(CONFIG_INPUT_MISC)        += misc/

手柄控制器就属于 joystick 。然后在joystick 有专门处理一种手柄/摇杆的驱动。对应DS3的就是gamecon.c

/*
* NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux
*
*  Copyright (c) 1999-2004     Vojtech Pavlik <vojtech@suse.cz>
*  Copyright (c) 2004          Peter Nelson <rufus-kernel@hackish.org>
*
*  Based on the work of:
*      Andree Borrmann         John Dahlstrom
*      David Kuder             Nathan Hand
*      Raphael Assenat
*/

看注释不知道像NES这样的古老非usb手柄怎么在linux上面用~ 作者还有文档呢:

/*
* PSX support
*
* See documentation at:
*      http://www.geocities.co.jp/Playtown/2004/psx/ps_eng.txt
*      http://www.gamesx.com/controldata/psxcont/psxcont.htm
*
*/
关于如何让DS3/sixaxsi 的BT工作可以参照这个:http://www.pabr.org/sixlinux/sixlinux.en.html
另外还有一个关于ps系主机的资料站点:http://wiki.ps2dev.org/

内核代码中关于线程的一些简单认识

这里所谓的线程是指的内核线程,并不是用户线程。后者由上层的线程库调用系统调用clone创建的。

内核线程一般并不被用户程序直接使用,而是在内核空间中并发执行某些代码而由内核自己设置的一个“进程”。(比如驱动程序中同步设备与缓存) 在linux中线程和进程都用thread_info数据结构(具体和体系结构相关)进行描述,都参与调度,其实对内核而言根本就不区分所谓进程和线程,它们都是可调度的执行流,比如在设备驱动中可以通过与nice相关的函数设置新创建的内核线程的优先级。 在用户态执行fork时如果指定了CLONE_VM标志,那么父子进程共享父进程的上下文,即mm_struct,此时就把它们都称作线程。

内核提供一个方便创建内核线程的函数叫kernel_thread ,原型: int kernel_thread(int (*fn)(void *),void *arg ,unsigned long flag) 他会先进行一些其他数据或结构的维护,然后最终还要调用do_fork:

int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
regs.si = (unsigned long) fn;
regs.di = (unsigned long) arg;
#ifdef CONFIG_X86_32
regs.ds = __USER_DS;
regs.es = __USER_DS;
regs.fs = __KERNEL_PERCPU;
regs.gs = __KERNEL_STACK_CANARY;
#else
regs.ss = __KERNEL_DS;
#endif
regs.orig_ax = -1;
regs.ip = (unsigned long) kernel_thread_helper;
regs.cs = __KERNEL_CS | get_kernel_rpl();
regs.flags = X86_EFLAGS_IF | 0x2;
/* Ok, create the new process.. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}
struct pt_regs {
long ebx;
long ecx;
long edx;
long esi;
long edi;
long ebp;
long eax;
int  xds;
int  xes;
int  xfs;
int  xgs;
long orig_eax;
long eip;
int  xcs;
long eflags;
long esp;
int  xss;
};

如果没有这个标志而只有CLONE_FS或者CLONE_FILES标志,那么创建的子进程只共享父进程的fs_struct和files_struct结构,此时还把它们叫做进程。所以在linux上线程本质是一个共享资源的手段,对内核而言根本不区分它们。 mm_struct用于描述用户态进程上下文,所以内核线程都没有这个数据结构(其实并不是没有,而是将它指向另一个用户进程的mm_struct)。当调度内核线程时,它将借用上一个用户进程的mm_struct(内核线程的active_mm指向上一个用户进程的mm_struct),而所有用户进程页表的高端都是内核页表,描述内核空间,由此内核线程就可以在内核空间内运行了。 总结一下 :

内核线程

内核线程只运行在内核态,不受用户态上下文的拖累。

Ø         处理器竞争:可以在全系统范围内竞争处理器资源;

Ø         使用资源:唯一使用的资源是内核栈和上下文切换时保持寄存器的空间(看一下struct pt_regs的设置)

Ø         调度:调度的开销可能和进程自身差不多昂贵

Ø         同步效率:资源的同步和数据共享比整个进程的数据同步和共享要低一些。

—————————————————————————————————-

顺便再说一下用户线程,这类线程首先由应用程序调用线程库为出发点。线程库调用系统调用clone。也就是说一个用户线程会对应内核的一个进程(当然资源是共享的),创建流程和内核进程类似。 LinuxThreads是用户空间的线程库,所采用的是线程-进程1对1模型(即一个用户线程(注意这里的用户线程也不是教科书上的用户线程)对应一个轻量级进程,而一个轻量级进程对应一个特定的内核线程),将线程的调度等同于进程的调度,调度交由内核完成,而线程的创建、同步、销毁由核外线程库完成(LinuxThreads已绑定到GLIBC中发行)。在LinuxThreads中,由专门的一个管理线程处理所有的线程管理工作。 最后,不要将教科书上的线程模型和linux中线程的实现搞混,这两者名字差不多,但概念相差较远(原因是理论的那套太复杂,linux没有实现)。貌似windows实现的进程比较接近教科书… 没研究了

再总结一下:

轻量级进程

轻量级进程(LWP)是建立在内核之上并由内核支持的用户线程,它是内核线程的高度抽象,每一个轻量级进程都与一个特定的内核线程关联。内核线程只能由内核管理并像普通进程一样被调度。

轻量级进程由clone()系统调用创建,参数是CLONE_VM,即与父进程是共享进程地址空间和系统资源。

与普通进程区别:LWP只有一个最小的执行上下文和调度程序所需的统计信息。

Ø         处理器竞争:因与特定内核线程关联,因此可以在全系统范围内竞争处理器资源

Ø         使用资源:与父进程共享进程地址空间

Ø         调度:像普通进程一样调度

关于linux线程实现一个更具体的介绍可以看看这个 http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/

如果搞不清哪些所谓的XX线程就看看这个吧,应该是权威的说明:http://linas.org/linux/threads-faq.html