-
Notifications
You must be signed in to change notification settings - Fork 2
/
log200811.txt
1780 lines (1572 loc) · 81.5 KB
/
log200811.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1:22 2008-11-2
(2:06 2008-11-13)
课余学习,arm开发板购买
1, 北京大道纵横科技有限公司
http://www.study-kit.com/
北京,arm板,问有没有底板核心板的2440板子;
销售电话:010-82623343 传真:010-82533223
地址: 北京市海淀区中关村大街32号中发新市场地下一层精品间B1029号(中发南面) 邮编:100086
QQ:18525046,187221877,308304450 MSN: studykit@hotmail.com
Email:studykit@vip.sina.com studykit@vip.163.com
联系人:霍志强
2, 优龙:
http://www.ucdragon.com
3, 北京蓝海微芯科技发展有限公司
http://www.bluemcu.com/ProductView.aspx?bid=2&sid=20&id=88
是nor flash的方案,这点不理想。
地址: 北京市海淀区中关村大街32号和盛大厦 0918-0919室
电话: 010-52926768
Email: bomc@vip.163.com
(和盛大厦6-21层为5A级写字楼 B1- 5层为新中发电子市场)
门市1: 北京精品展示厅
地址: 北京市海淀区中关村大街32号新中发电子市场2楼2102室
门市2: 中发电子市场
地址: 北京市海淀区知春路28号中发电子市场2楼2087柜台
4, http://www.yctek.com/shop/view_62.html
北京杨创科技,看介绍东西还不错,但网上有人说不稳定,光盘内容不对应。
2440板子980。实际看了,感觉做的一般。
5, http://auction1.taobao.com/auction/item_detail-0db1-f000f2ed14ebd3e34fe68a98f61bfacd.jhtml#
飞凌北京
6,
知春大厦A座606
7,
http://auction1.taobao.com/auction/item_detail-0db2-ce69e6af8e857b38605fa8ef0c85e1dc.jhtml
LCD 转 VGA 视频加速卡 -- 无TV输出版,260元。
8,北京革新科技
北京中关村苏州街 18 号长远天地大厦 A2 座 609
82608898, 82608676, 82608677。
10:46 2008-11-3
思想,最近需要关注的时事
1, 陈云林访台;
2, 世界金融危机。
2008-11-3的参考消息文章“中东欧经济开始‘失速’ 欧洲可能出现真正混乱”,原题“依赖外资的中东欧经济开始失速”。
文中提到,
1, 中东欧经济以民营化和引进外资为特点。金融危机开始后,外资撤离造成本国汇率急剧下跌,“这种情况已经开始影响居民生活”。
2, 由于这些国家市场开放,
1), 有用外币结算的贷款
2), 很多当地重要银行是欧洲大型银行的子行。
bamvor: 这里指的中东欧应该就是原来东欧社会主义阵营国家。这些国家在苏东剧变后完全转向资本主义,按照西方建议进行改革。可以说是“最资本主义”的地区。现在的金融危机对这些国家影响很大,这说明什么呢?
能否说明这些国家这些“彻底”的资本主义改革是不正确的?为时尚早。
但至少能说明,过于依赖外资是不行的,就像97年亚洲金融危机中的东南亚国家一样。
关注进一步的消息。
11:43 2008-11-3
(13:39 2008-11-20)
嵌入式Linux,ARM学习相关图书,推荐书
注:排名部分先后;有精力情况下多看英文版的书。
1, 嵌入式Linux入门类:
1), 嵌入式Linux应用开发完全手册,这本书我没看过。
我看的网上评论:http://www.china-pub.com/member/bookpinglun/viewpinglun.asp?bookname=&reader=0&gid=0&id=41512&Curpage=3。和百问网:http://www.100ask.net/showforum-2.aspx的勘误和问题讨论。感觉作者还比较负责。
2), 实验室已有的书,skyeye等等
注:其实嵌入式Linux包含的内容很广,一本书写不完。所以名为嵌入式Linux的书一般都是入门类的书,感觉自己对嵌入式Linux有些了解后,建议深入看各类的书籍。例如Linux开发,arm体系结构等等。另外,Linux内核代码和文档是很经典的资料,可以结合看书和做项目多看。
2, Linux学习类
1), 驱动
Linux设备驱动,第三版,即LDD3;
bamvor: 其实讲驱动的书是围绕驱动讲内核。
2), 内核
Linux内核设计与实现,第二版;
深入理解Linux内核,第三版,即ULK3;
前一本书比较基础,后一本比较深入。
3), 应用
UNIX/LINUX编程实践教程
实战Linux编程精髓
前一本书比较基础,后一本比较深入。
3, ARM学习类
1), ARM SOC体系结构
2), ARM嵌入式系统开发:软件设计与优化
前一本书比较基础,后一本比较深入。
相关链接,ARM学习相关图书
1, ARM Architecture Reference Manual (2nd Edition),Known as the "ARM ARM。
http://www.manley.com.cn/bbs/dispbbs.asp?boardID=15&ID=3174&page=1
http://www.altera.com/literature/third-party/ddi0100e_arm_arm.pdf
http://www.arm.com/community/university/eulaarmarm.html
2, Arm system developer's giude
http://forum.eviloctal.com/read-htm-tid-23998.html
[转载]《ARM嵌入式系统开发:软件设计与优化》中文版PDF格式
13:39 2008-11-3
Linux内核,调试,oops,风河&群硕面试总结
1, 文档:oops-tracing.txt
(3) Use Kdump (see Documentation/kdump/kdump.txt), extract the kernel ring buffer from old memory with using dmesg
gdbmacro in Documentation/kdump/gdbmacros.txt.
13:49 2008-11-3
Linux,导出符号表,风河&群硕面试总结
1, 阅读内核"scripts/mksysmap",学习如何输出符号表:
nm -n是导出符号表,待做:看nm所有命令。看skyeye书的相关部分。
# The second row specify the type of the symbol:
# A = Absolute
# B = Uninitialised data (.bss)
# C = Comon symbol
# D = Initialised data
# G = Initialised data for small objects
# I = Indirect reference to another symbol
# N = Debugging symbol
# R = Read only
# S = Uninitialised data for small objects
# T = Text code symbol
# U = Undefined symbol
# V = Weak symbol
# W = Weak symbol
# Corresponding small letters are local symbols
# For System.map filter away:
# a - local absolute symbols
# U - undefined global symbols
# w - local weak symbols
# readprofile starts reading symbols when _stext is found, and
# continue until it finds a symbol which is not either of 'T', 't',
# 'W' or 'w'. __crc_ are 'A' and placed in the middle
# so we just ignore them to let readprofile continue to work.
# (At least sparc64 has __crc_ in the middle).
$NM -n $1 | grep -v '\( [aUw] \)\|\(__crc_\)\|\( \$[adt]\)' > $2
13:59 2008-11-3
编程,各种符号位置,风河&群硕面试总结
待做:编程实验,全局,局部auto和static等各种类型变量,是否在elf文件中,在哪个段?
17:09 2008-11-3
(11:32 2008-11-6)
求职进展,续
1, 汉王笔试
http://jobs.chinahr.com/html/2008-10/07/22200200370115000732.htm?prj=cvapplied
1), 岗位描述:
1、负责嵌入式应用软件的开发;
2、进行软件设计、编码以及测试工作;
3、负责研发文档编写。
应聘人员素质要求:
1、计算机及相关专业,本科以上学历;
2、熟练Wince/Linux操作系统,熟悉嵌入式Wince/Linux开发调试方法及工具;
3、熟练掌握C/C++及VC++;
4、工作踏实、认真,有良好的团队合作精神,虚心好学;
5、有一定的项目经验。
2), 笔试题目(2008年11月4日)
=============================
位置:bamvor张健的文档\micro微电子与计算机\career个人发展 厂商资料 求职招聘\厂商资料_求职招聘\公司资料\嵌入式系统及其相关公司\汉王\20081104\笔试题目.txt
3), 面试(2008年11月5日)
=========================
bamvor: 这个项目是做电纸书,目前是linux方案,过段时间会做wince方案。
两轮面试:
第一轮是部门经理和两个leader。
----------------------------
shiyu@hanwang.com,应该是做驱动的负责人,目前有三个人做驱动,再招一个人。
面试时又偷懒了,没有完整介绍自己项目经验,以后一定注意。
昨天面试完又修改了简历,加入了了解工作队列机制的描述。今天要完成这个,这样有利于引导面试官发问。
平台:目前是pxa270(blob+linux2.6),要转到君正的平台(u-boot+linux2.6.20)。查君正平台近况。
提问:
a, Linux内核中使用的"do () while(0)"起什么作用。
bamvor: 呵呵,这个真是不知道,当时说感觉是可能会改为while(1),进行调试。回来一查发现完全不对,简单说就是保证代码的独立性,详见"23:17 2008-11-5"6。
b, 在不同的SOC间移植驱动需要注意什么。
bamvor: 感觉没什么需要注意的。
c, fb TFT驱动中与硬件有关的结构体。
这部分看s3c2410fb.c。
第二轮是王邦江副总裁,他主管包括电纸书在内的多个项目。
-----------------------------------------------------
问题:
a, cpu读写寄存器时序;待做:看datasheet。
b, 扑克牌。54张牌4个人分剩几张,83个人分138付牌剩几张。
聊了聊待遇:和我现在差不多,加上项目绩效和年底绩效可能能到达年薪140k。试用期80%工资,可以提前转正。
补助:餐补200多,100元电话费,40元交通费。
电纸书:会加上汉王的电磁板技术。bamvor: 虽然东西不错,而且加上电磁板后还可以加汉王的手写识别技术,但是这和我没关系啊,呵呵。
最后等董事长面试,他比较忙,没安排出时间。易先生说董事长过了后HR会和我谈待遇。
最新进展:等待董事长面试中,最可能是下周。
感想:对我来说,今后5年内,可能都是风河这种深入研究Linux内核的公司适合我,这几天面试闪联和汉王感觉他们只是用现成的技术,现成的东西,改改出产品就行。相比之下,风河,群硕这些公司面试问题比较深入,要求了解内核机制实现。
2, 面试准备
待做:打印:pxafb代码。
先看完调度算法和相关代码,然后学习oops调试,解决xia问题;gdb调试;pxafb代码。
3, 待做:加入纸上的今日面经和pxafb基本介绍;汉王笔试题编程练习。
17:58 2008-11-3
Linux,GNU工具学习,ojbdump
ojbdump -S,如果加入了-g,可以显示出c源代码和反汇编的对于关系。
17:11 2008-11-4
各种计算器,包括贷款计算器,提前还款计算器,个人所得税计算器等等。
http://www.boc.cn/cn/common/fourth.jsp?category=1142474796100
11:36 2008-11-5
网址,电子墨水,电子书
1, 电纸书厂商
http://www.irexreader.cn/,飞利浦的子公司;
http://www.jinke.com.cn
2, 电子书论坛
http://bbs.mobread.com/index.php
3, e-link,电子墨水提供商
http://www.eink.com
23:17 2008-11-5
求职进展,续,待做
1, 芸芸帮我建行和招行还款。
2, 联系朋友:新洲,豆豆,敏悦等;
4, 总结工作队列机制;
5, 学习do{}while(0)作用,Memory.txt是内存映射,理解后记忆。
下载工作队列相关网页
http://misuland.com/HTML/1180403166496.html
http://www.lupaworld.com/26540/viewspace-85167.html
http://blog.chinaunix.net/u2/69889/showart_971040.html
http://tech.ddvip.com/2007-05/117946628824884.html
http://www.bitscn.com/linux/kernel/200604/7556.html
http://blog.ednchina.com/tiloog/133368/message.aspx
6, do{} while(0)的作用
http://blog.ednchina.com/fpga2006/34658/message.aspx
http://www.cnblogs.com/flying_bat/archive/2008/01/18/1044693.html
简单说有两方面作用:
a,宏定义中:
a), 空的宏定义避免warning:
#define foo() do{}while(0)
b),存在一个独立的block,可以用来进行变量定义,进行比较复杂的实现。
c),如果出现在判断语句过后的宏,这样可以保证作为一个整体来是实现:
b, 函数中:用do...while(0)中break语句,消除goto语句。
详见:bamvor张健的文档\micro微电子与计算机\Embeded嵌入式系统\软件设计\语言\c语言\do{}while(0)宏定义的好处=++.txt
6, 三个网站和水木论坛投简历。
7, 总结风河和群硕面试。
14:24 2008-11-6
汽车卡,比较
http://www.cardcmb.com/carcard/fun.html
招行主要是加油1%优惠。
http://creditcard.cmbchina.com/products/intro/car/1236.htm
需要看会员章程。
http://www.ccb.com/portal/cn/mini_set/second.jsp?column=ROOT%3E%B9%FA%C4%DA%D7%DC%D0%D0%3E%B8%F6%C8%CB%B7%FE%CE%F1%3E%B2%FA%C6%B7%3E%C1%FA%BF%A8%D0%C5%D3%C3%BF%A8%3E%B2%FA%C6%B7%BD%E9%C9%DC%3E%C1%FA%BF%A8%C6%FB%B3%B5%BF%A8&miniset_column=ROOT%3E%B9%FA%C4%DA%D7%DC%D0%D0%3E%B8%F6%C8%CB%B7%FE%CE%F1%3E%B2%FA%C6%B7%3E%C1%FA%BF%A8%D0%C5%D3%C3%BF%A8#
建行是一年6次免费洗车。
14:36 2008-11-6
求职进展,续,面试,风河
kmalloc返回的是什么地址?逻辑地址。
15:24 2008-11-6
Linux学习,移植,arm,存储器(memory)空间, memory.txt分析, 文档
分析arm存储空间分配。这里分析的都是有MMU的arm芯片,对于没有MMU的arm芯片差异很大。例如:
PAGE_OFFSET在无MMU的ARM上是PHYS_OFFSET,无MMU的PHYS_OFFSET是CONFIG_DRAM_BASE。
PAGE_OFFSET在有MMU的arm上是0xc0000000.
Linux内核中文档memory.txt:
Kernel Memory Layout on ARM Linux
Russell King <rmk@arm.linux.org.uk> November 17, 2005 (2.6.15)
This document describes the virtual memory layout which the Linux kernel uses for ARM processors. It indicates which regions are free for platforms to use, and which are used by generic code.
The ARM CPU is capable of addressing a maximum of 4GB virtual memory space, and this must be shared between user space processes, the kernel, and hardware devices.
As the ARM architecture matures, it becomes necessary to reserve certain regions of VM space for use for new facilities; therefore this document may reserve more VM space over time.
Start End Use
--------------------------------------------------------------------------
ffff8000 ffffffff copy_user_page / clear_user_page use. For SA11xx
and Xscale, this is used to setup a minicache
mapping.
ffff1000 ffff7fff Reserved. Platforms must not use this address
range.
ffff0000 ffff0fff CPU vector page. The CPU vectors are mapped here
if the CPU supports vector relocation (control
register V bit.)
ffc00000 fffeffff DMA memory mapping region. Memory returned by the dma_alloc_xxx functions will be dynamically
mapped here.
ff000000 ffbfffff Reserved for future expansion of DMA mapping region.
VMALLOC_END feffffff Free for platform use, recommended. VMALLOC_END
must be aligned to a 2MB boundary. 暂时不知道有
何用处.待做: 查.
VMALLOC_START VMALLOC_END-1 vmalloc() / ioremap() space. Memory returned by
vmalloc/ioremap will be dynamically placed in
this region. VMALLOC_START may be based upon the
value of the high_memory variable.
PAGE_OFFSET high_memory-1 Kernel direct-mapped RAM region. This maps the
platforms RAM, and typically
maps all platform RAM in a 1:1 relationship.
TASK_SIZE PAGE_OFFSET-1 Kernel module space Kernel modules inserted via
insmod are placed here using dynamic mappings.
00001000 TASK_SIZE-1 User space mappings Per-thread mappings are
placed here via the mmap() system call.
00000000 00000fff CPU vector page / null pointer trap CPUs which
do not support vector remapping place their
vector page here. NULL pointer dereferences by
both the kernel and user space are also caught
via this mapping.
Please note that mappings which collide with the above areas may result in a non-bootable kernel, or may cause the kernel to (eventually) panic at run time.
Since future CPUs may impact the kernel mapping layout, user programs must not access any memory which is not mapped inside their 0x0001000 to TASK_SIZE address range. If they wish to access these areas, they must set up their own mappings using open() and mmap().
1, VMALLOC_START:
================
(17:16 2009-1-16)修改:
1), porting(documentation\arm):
VMALLOC_START
VMALLOC_END
Virtual addresses bounding the vmalloc() area. There must not be
any static mappings in this area; vmalloc will overwrite them.
The addresses must also be in the kernel segment (see above).
Normally, the vmalloc() area starts VMALLOC_OFFSET bytes above the
last virtual RAM address (found using variable high_memory
2), memory.txt(documentation\arm):
VMALLOC_START VMALLOC_END-1 vmalloc() / ioremap() space.
3), 定义
pgtable.h(include\asm-arm)
/*
* Just any arbitrary offset to the start of the vmalloc VM area: the
* current 8MB value just means that there will be a 8MB "hole" after the
* physical memory until the kernel virtual memory starts. That means that
* any out-of-bounds memory accesses will hopefully be caught.
* The vmalloc() routines leaves a hole of 4kB between each vmalloced
* area for the same reason. ;)
*
* Note that platforms may override VMALLOC_START, but they must provide
* VMALLOC_END. VMALLOC_END defines the (exclusive) limit of this space,
* which may not overlap IO space.
*/
#ifndef VMALLOC_START
#define VMALLOC_OFFSET (8*1024*1024)
#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
#endif
VMALLOC_OFFSET是8M, 目的是为物理地址空间和kernel 虚拟地址空间留个空洞, 这样即使访问内核直接映射的物理sdram越界, 也不会影响到vmalloc/ioremap区域的数据. vmalloc函数为每个区域留4k也是这个目的.
vmalloc.h(include\asm-arm\arch-s3c2410)
#define VMALLOC_END (0xE0000000)
在arm其它arch中也有用"PAGE_OFFSET+偏移"表示VMALLOC_END的。待做:移植machine相关.
"17:16 2009-1-16"end
2, high_memory
===============
1), 定义
--------
Memory.c (mm):
void * high_memory;
EXPORT_SYMBOL(high_memory);
struct meminfo定义在include\asm-arm\setup.h,描述所有的物理内存。
struct meminfo {
int nr_banks;
struct membank bank[NR_BANKS];
};
2), 结合代码分析:
----------------
"high_memory"在"bootmem_init()"函数中根据"struct meminfo"相加得到.
start_kernel()
->setup_arch()(arch\arm\kernel\setup.c)
->paging_init()
->bootmem_init()
bootmem_init()分析:
void __init bootmem_init(struct meminfo *mi)
{
unsigned long memend_pfn = 0;
int node, initrd_node, i;
/*
* Invalidate the node number for empty or invalid memory banks
*/
for (i = 0; i < mi->nr_banks; i++)
if (mi->bank[i].size == 0 || mi->bank[i].node >= MAX_NUMNODES)
mi->bank[i].node = -1;
memcpy(&meminfo, mi, sizeof(meminfo));
/*
* Locate which node contains the ramdisk image, if any.
*/
/*
通过查找meminfo中所有bank,确定initrd在哪个bank中,返回该bank的起始页号。若initrd指定的地址不在所有的meminfo bank中,说明initrd不在实际的物理内存中,Linux会关闭initrd(phys_initrd_start = phys_initrd_size = 0;)
*/
initrd_node = check_initrd(mi);
/*
* Run through each node initialising the bootmem allocator.
*/
for_each_node(node) {
unsigned long end_pfn;
end_pfn = bootmem_init_node(node, initrd_node, mi);
/*
* Remember the highest memory PFN.
*/
if (end_pfn > memend_pfn)
memend_pfn = end_pfn;
}
high_memory = __va(memend_pfn << PAGE_SHIFT);
/*
* This doesn't seem to be used by the Linux memory manager any
* more, but is used by ll_rw_block. If we can get rid of it, we
* also get rid of some of the stuff above as well.
*
* Note: max_low_pfn and max_pfn reflect the number of _pages_ in
* the system, not the maximum PFN.
*/
max_pfn = max_low_pfn = memend_pfn - PHYS_PFN_OFFSET;
}
for_each_node (include\linux\nodemask.h):
#define for_each_node(node) for_each_node_mask((node), node_possible_map)
for_each_node宏的作用就是循环访问每个node,例如littleton中node_possible_map是0x1111 1111。
#if MAX_NUMNODES > 1
#define for_each_node_mask(node, mask) \
for ((node) = first_node(mask); \
(node) < MAX_NUMNODES; \
(node) = next_node((node), (mask)))
#else /* MAX_NUMNODES == 1 */
#define for_each_node_mask(node, mask) \
if (!nodes_empty(mask)) \
for ((node) = 0; (node) < 1; (node)++)
#endif /* MAX_NUMNODES */
first_node函数最终使用在findbit.S (arch\arm\lib)的_find_first_bit_le汇编代码查找第一个置一的位。
next_node则是寻找下一个置一的位。
PAGE_SHIFT:
-----------
#ifndef PAGE_SHIFT
#define PAGE_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))
#endif
MAX_NUMNODES是(1 << NODES_SHIFT),若未定义NODES_SHIFT,则NODES_SHIFT是0。
NUMA(Documentation\vm):
NUMA是一个体系结构,这种体系结构中cpu访问不同内存区域的“距离”(bamvor: 我理解就是速度)不同。
cpu访问时间相等的一个内存区域成为一个节点(node)。
3), 结论
--------
发现arm中就是把所有memory size加在一起得到的memory结束位置就是high_memory的位置。ULK3中提到的如果内存大于896M不能完全进行1:1映射的情况是针对x86而言的, 对于arm平台一般不会有如此大的ram, 所以暂时没有考虑.
这时与jack一起看i386的代码,发现里面mem_init是起类似bootmem_init作用。但"mem_init"会限制high_memory的大小。
3, PAGE_OFFSET
==============
memory.h (include\asm-arm)
#ifndef PAGE_OFFSET
#define PAGE_OFFSET UL(0xc0000000)
#endif
4, TASK_SIZE PAGE_OFFSET-1 Kernel module space Kernel modules inserted via insmod are placed here using dynamic mappings.
这个区域保存insmod插入模块后kernel module使用的空间, 是动态映射的. Linux内核模块是位于下面这个地址空间的,不在原来以为的0xc0000000上面。
5, 异常向量表(CPU vector page)
==============================
即ffff0000-ffff0fff, 见"17:06 2009-1-12"2
11:25 2008-11-7
c语言,编程练习
代码和测试代码:reverse_linklist.c,bamvor张健的文档\micro微电子与计算机\Embeded嵌入式系统\软件设计\语言\c语言\c_study.
将单向链表reverse,如ABCD变成DCBA,只能搜索链表一次
typedef struct __linkList
{
int data;
struct __linkList * next;
} linkList;
void reverse(linkList *list)
{
if list or list->next is NULL, return
prev = list;
while( list->next is not NULL ) {
next = list->next;
if ( prev == list )
list->next=NULL;
else
list->next = prev;
prev=list;
list=next;
}
prev=list;
list=list->next;
list->next=prev;
}
11:25 2008-11-7
待做:51job简历,水木简历。
17:40 2008-11-7
求职进展,中星微
周二(11月11日)下午2点。北航东门世宁大厦15层,技术面试。
电话:8610-68948888-8480王
北京市海淀区学院路35号
世宁大厦15层,100083
18:11 2008-11-7
14家银行信用卡任意分期成本评比
http://www.bankrate.com.cn/cms/2008/11/14_6.php
18:11 2008-11-7
中星微:
1, oops;
2, 工作队列;
3, gdb调试共享库。
10:25 2008-11-8
(16:47 2008-11-10)
(10:26 2008-11-11)
Linux内核学习,工作队列,使用
1, 建立工作队列。create_singlethread_workqueue
##############################################
FM驱动中使用的是create_singlethread_workqueue,其余函数类似。
FM_wq = create_singlethread_workqueue("FM_Work");
-> workqueue.h (include\linux):
#define create_singlethread_workqueue(name) __create_workqueue((name), 1, 0)
-> workqueue.c (kernel):
struct workqueue_struct *__create_workqueue(const char *name,
int singlethread, int freezeable)
分析:__create_workqueue函数:
1), 用kzalloc分配workqueue_struct *wq;
kzalloc根据使用使用slab(CONFIG_SLAB),有不同的定义。呵呵,一看内核的代码,东西联系都很紧密,等后面分析slab机制时再看kzalloc代码。待做。
可以看到在littleton和s3c2410的配置文件都开启了slab功能,slab机制是一定要分析的。
2), 用alloc_percpu分配cpu_workqueue_struct结构体: wq->cpu_wq。
alloc_percpu
-> percpu.h(include\linux):
#define alloc_percpu(type) (type *)__alloc_percpu(sizeof(type))
-> #define __alloc_percpu(size) percpu_alloc_mask((size), GFP_KERNEL, \
cpu_possible_map)
-> #define percpu_alloc_mask(size, gfp, mask) \
__percpu_alloc_mask((size), (gfp), &(mask))
-> __percpu_alloc_mask根据是否SMP有不同定义,由于littleton和s3c2410都不是SMP,所以这里只分析单CPU情况:
static __always_inline void *__percpu_alloc_mask(size_t size, gfp_t gfp, cpumask_t *mask)
{
return kzalloc(size, gfp);
}
3), wq->name = name;
4), 互斥信号量:mutex_lock(&workqueue_mutex);和mutex_unlock(&workqueue_mutex);。待做:学习与down_xxx, up的差异。
(1), 单线程情况:
a, 初始化wq->list,单CPU情况这个list是空,SMP中则是把workqueues加入到wq->list中。
===============================================================================
list.h(include\linux):
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
b, p = create_workqueue_thread(wq, singlethread_cpu, freezeable);
=================================================================
其中p是task_struct。singlethread_cpu是在init_workqueues中通过first_cpu宏得到的。对于单CPU,first_cpu(src)是0。
create_workqueue_thread首先得到针对本CPU的cpu_workqueue_struct *cwq,然后对cwq初始化,并利用kthread_create建立内核线程p。cwq->thread中保存p的地址。函数create_workqueue_thread返回p。
对于单CPU情况(根据wq->list是否为空判断是否是单CPU):
p = kthread_create(worker_thread, cwq, "%s", wq->name);
建立一个名字是'"%s", wq->name',回调函数是worker_thread,回调函数的数据是cwq。
待做:分析内核线程相关函数。
create_workqueue_thread得到cpu所属cpu_workqueue_struct的方法:
------------------------------------------------------------
struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu);
percpu.h: #define per_cpu_ptr(ptr, cpu) percpu_ptr((ptr), (cpu))
对于单CPU(未定义SMP):#define percpu_ptr(ptr, cpu) ({ (void)(cpu); (ptr); })
从这个宏看,应该就是返回的ptr这个指针,不过不明白(void)(cpu)起什么作用。
所以per_cpu_ptr(wq->cpu_wq, cpu);应该返回wq->cpu_wq。
-------------------------------------------------------------
c, 如果建立内核线程成功,wake_up_process(p);否则设置destroy = 1,在mutex_unlock后destroy_workqueue(wq);
(a), wake_up_process(p)
(b), destroy_workqueue()
destroy_workqueue函数首先调用flush_workqueue运行全部未运行的工作队列,而后调用cleanup_workqueue_thread停止cwq->thread线程。
d, 分析前面提到的worker_thread:
工作者线程是实际执行工作队列的线程,主要工作是检查cwq->worklist是否为空,若非空则执行全部(全部是哪些?!)。否则进行调度。
static int worker_thread(void *__cwq)
{
struct cpu_workqueue_struct *cwq = __cwq;
//建立等待队列
DECLARE_WAITQUEUE(wait, current);
struct k_sigaction sa;
sigset_t blocked;
//a),
if (!cwq->freezeable)
current->flags |= PF_NOFREEZE;
//b),设置当前线程优先级为-5。set_user_nice是改变静态优先级。
set_user_nice(current, -5);
//c),
/* Block and flush all signals */
sigfillset(&blocked);
sigprocmask(SIG_BLOCK, &blocked, NULL);
flush_signals(current);
/*
* We inherited MPOL_INTERLEAVE from the booting kernel.
* Set MPOL_DEFAULT to insure node local allocations.
*/
numa_default_policy();
/* SIG_IGN makes children autoreap: see do_notify_parent(). */
sa.sa.sa_handler = SIG_IGN;
sa.sa.sa_flags = 0;
siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD));
do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0);
//d), 设置当前内核线程为可中断线程
set_current_state(TASK_INTERRUPTIBLE);
/**
e), 判断当前工作者线程是否需要停止,若需要停止直接返回。
参考:"16:56 2008-11-10"分析。
*/
while (!kthread_should_stop()) {
if (cwq->freezeable)
try_to_freeze();
//把新建立的等待队列wait加入到cwq->more_work等待队列中。
//add_wait_queue的主要功能是在自旋锁的保护下通过__add_wait_queue()调用list_add函数把等待队列项wait_queue_t *wait加入到某个等待队列中wait_queue_head_t *q。
add_wait_queue(&cwq->more_work, &wait);
//若cwq->worklist空,则执行调度,工作者线程会睡眠。queue_work函数会把需要推迟的工作加入到cwq->worklist中。
// 所以,当queue_work加入需要推迟工作前,工作者线程处于睡眠状态。
//若cwq->worklist非空,设置当前工作者线程为运行状态。为后面执行需要推迟的工作做准备。
if (list_empty(&cwq->worklist))
schedule();
else
__set_current_state(TASK_RUNNING);
//把wait从cwq->more_work移除。
remove_wait_queue(&cwq->more_work, &wait);
//如果有需要执行的工作,则调用run_workqueue函数执行这些工作,
//run_workqueue会执行cwq->worklist中所有工作(work_struct)。
//如果run_workqueue调用深度大于3,内核打出提示信息和dump_stack。
//wake_up(&cwq->work_done);把cwq->work_done中每个等待任务都加入运行队列。
if (!list_empty(&cwq->worklist))
run_workqueue(cwq);
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return 0;
}
注:相关结构体分析
1), cpu_workqueue_struct分析,未完,待做:
struct cpu_workqueue_struct {
spinlock_t lock;
//每运行一个工作,remove_sequence增一。run_workqueue和__run_work。
//queue_work每加入一个工作,就会把insert_sequence增一。
//remove_sequence和insert_sequence都在flush_cpu_workqueue函数中使用,判断是否处理了所有工作。
//详见flush_cpu_workqueue函数。
long remove_sequence; /* Least-recently added (next to run) */
long insert_sequence; /* Next to add */
struct list_head worklist; //参见见workstruct.entry
//工作者线程把自己加入more_work等待queue_work把自己唤醒,
//通过run_workqueue执行queue_work加入到cwq->worklist中的工作。
wait_queue_head_t more_work;
//用于flush_cpu_workqueue()等待__run_work或run_workqueue完成工作。
wait_queue_head_t work_done;
//cwq所属的workqueue_struct。
struct workqueue_struct *wq;
struct task_struct *thread; //工作者线程
int run_depth; /* Detect run_workqueue() recursion depth */
//与电源管理有关,似乎是可以freeze,降低功耗。待做:补充,查如何唤醒freeze的进程。
int freezeable; /* Freeze the thread during suspend */
} ____cacheline_aligned;
2), work_struct分析,未完,待做:
struct work_struct {
//data包括flag和cpu_workqueue_struct两部分。使用set_wq_data和get_wq_data访问cwq,
//使用Linux位操作函数访问flag。
atomic_long_t data;
#define WORK_STRUCT_PENDING 0 /* T if work item pending execution */
#define WORK_STRUCT_NOAUTOREL 1 /* F if work item automatically released on exec */
#define WORK_STRUCT_FLAG_MASK (3UL)
#define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
//加入到cwq->worklist中,两个作用:
//1, 用list_empty判断entry是否为空(也就是是否加入了cwq->worklist中)确定是否已经加入了工作队列
//2, run_workqueue中,完成cwq->worlist中所有工作。
struct list_head entry;
//工作队列执行函数。2.6.20开始(具体版本可能不对),去掉了工作队列中func所操作的数据。
//改由包含work_struct的用户结构体提供,这样结构更清楚。另外这个版本的工作队列中定时器也移到了
//work_struct结构体外:
//struct delayed_work {
// struct work_struct work;
// struct timer_list timer;
//};
work_func_t func;
};
2, 推迟所需的工作,queue_work.
###############################
queue_work主要工作由__queue_work完成。
调用__queue_work之前主要是检查是否确实由工作需要执行。
__queue_work是在自旋锁保护下:
1), set_wq_data(work, cwq);
2), 把工作加入到cwq->worklist队列
list_add_tail(&work->entry, &cwq->worklist);
3), insert_sequence++,
cwq->insert_sequence++;
4), 唤醒cwq->more_work等待队列,先前睡眠都工作者线程会继续执行。
wake_up(&cwq->more_work);
3, 工作队列机制总结:
######################################
create_singlethread_workqueue,create_workqueue,create_freezeable_workqueue等函数建立workqueue_struct结构体并初始化,建立工作者线程,工作者线程在无工作情况下睡眠。
queue_work加入需要处理的工作后唤醒工作者线程执行所需工作。
这样看来__queue_work中的wake_up会导致调用wake_up的线程睡眠。
workqueue_struct是工作队列,每个cpu都会分配一个cpu_workqueue_struct,用于管理该cpu上的workqueue_struct,每个cpu_workqueue_work都有个thread,指向这个cpu上的工作者线程。这个工作队列上面可以处理多个需要延时的工作,没有工作都是由queue_work加入的。
工作队列机制: worker_thread把自己的wait等待队列加入到cwq->more_work中, 若cwq->worklist空则睡眠, 否则运行工作队列.
queue_work把工作加入cwq->worklist链表, 唤醒cwq->more_work.
待做:
1), 目前分析的都是单cpu的情况,所以workqueue_struct,cpu_workqueue_struct,工作者线程数量相等。有时间分析不等的情况。
1), 查create_freezeable_workqueue的作用。现在对于工作队列中所有和freezeable相关都没有分析。
2), 查wake_up到底会不会导致睡眠。
10:25 2008-11-8
Linux内核学习,工作队列,机制,待做。
void init_workqueues(void)
{
singlethread_cpu = first_cpu(cpu_possible_map);
hotcpu_notifier(workqueue_cpu_callback, 0);
keventd_wq = create_workqueue("events");
BUG_ON(!keventd_wq);
}
singlethread_cpu是在init_workqueues中通过first_cpu宏得到的。对于单CPU,first_cpu(src)是0。
init_workqueues的运行:
init\main.c: init()->do_basic_setup()->init_workqueues()。
其中do_basic_setup后,主要就是执行用户指定或默认的init程序/脚本了。
待做:完成。
22:53 2008-11-9
Linux,内核调试函数
1, BUG_ON
如果出现bug,而且影响内核运行,需要打出back trace,然后内核停止运行。
#ifndef HAVE_ARCH_BUG_ON
#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)
#endif
注:待做:查HAVE_ARCH_BUG_ON是谁定义的。
bug.h(include\asm-arm)
#define BUG() __bug(__FILE__, __LINE__)
traps.c(arch\arm\kernel)
void __attribute__((noreturn)) __bug(const char *file, int line)
{
printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line);
*(int *)0 = 0;
/* Avoid "noreturn function does return" */
for (;;);
}
EXPORT_SYMBOL(__bug);
2, 如果出现bug,不影响内核运行。
则使用dump_stack,打出back strace。不过,目前没有找到里面实际代码,可能是汇编语言,文件没加全,所以找不到。
待做,补充完整。
16:45 2008-11-10
Linux内核学习,工作队列,使用,待做。
1, 分析函数的代码加入日志.
分析create_workqueue中SMP情况:
查wq->list起什么作用. kthread_bind作用.
5, queue_delay_work 也是工作队列通用函数, 待做:分析.
6, 分析内核链表用法. 到了该学的时候.
7, wake_up应该不能进入睡眠, 因为queue_work中wake_up在自悬锁中.
8, 也可以结合工作队列学习定时器的使用
16:45 2008-11-10
Linux,存储器管理,待做
1,把ULK3中x86存储器管理和arm memory.txt内容对比.
16:56 2008-11-10
Linux代码分析,kernel\kthread.c
关键字:内核线程,完成变量
1, kthread_stop函数:
使用kthread_should_stop判断是否需要退出的threadfn(),不应该调用do_exit。
待做:看do_exit()函数。似乎是会杀死自己这个kthread?
/**
* kthread_stop - stop a thread created by kthread_create().
* @k: thread created by kthread_create().
*
* Sets kthread_should_stop() for @k to return true, wakes it, and
* waits for it to exit. Your threadfn() must not call do_exit()
* itself if you use this function! This can also be called after
* kthread_create() instead of calling wake_up_process(): the thread
* will exit without calling threadfn().
*
* Returns the result of threadfn(), or %-EINTR if wake_up_process()
* was never called.
*/
/**
kthread_stop函数分析:
在mutex保护下:
1), 设置task_struct任务k是需要停止的线程。
kthread_stop_info.k = k
2), 通过wake_up_process把任务k加入到运行队列中。
3), 等待任务k停止:
wait_for_completion(&kthread_stop_info.done);
wait_for_completion函数会把自己所在的当前任务加入等待队列,设置为不可中断模式,然后schedule(进入睡眠)。完成变量x->done设置后退出。
注:由于wait_for_completion会睡眠,所以kthread_stop()使用是mutex,不能用自旋锁。
同时,wait_for_completion()中使用了自旋锁,所以在睡眠前释放自旋锁,睡眠后再得到锁。
可以做为需要内核同步的参考代码:spin_lock,mutex。
*/
int kthread_stop(struct task_struct *k)
{
int ret;
mutex_lock(&kthread_stop_lock);
/* It could exit after stop_info.k set, but before wake_up_process. */
get_task_struct(k);
/* Must init completion *before* thread sees kthread_stop_info.k */
init_completion(&kthread_stop_info.done);
smp_wmb();
/* Now set kthread_should_stop() to true, and wake it up. */
kthread_stop_info.k = k;
wake_up_process(k);
put_task_struct(k);
/* Once it dies, reset stop ptr, gather result and we're done. */
wait_for_completion(&kthread_stop_info.done);
kthread_stop_info.k = NULL;
ret = kthread_stop_info.err;
mutex_unlock(&kthread_stop_lock);
return ret;
}
2, kthread_should_stop()
/**
* kthread_should_stop - should this kthread return now?
*
* When someone calls kthread_stop() on your kthread, it will be woken
* and this will return true. You should then return, and your return
* value will be passed through to kthread_stop().
*/
int kthread_should_stop(void)
{
return (kthread_stop_info.k == current);
}
判断当前任务是否是需要停止的任务,如果是返回1。
kthread_should_stop一般是内核线程的threadfn()使用。这种线程一般都有很多工作需要处理(可能是无限个任务),每次处理工作前都查询kthread_should_stop是否为真,如果为真则直接返回,也就是kthread()继续执行。如果不为真,则处理这项工作,处理后调用scheduel(),让出控制权,自己睡眠。睡眠时如果希望可以由信号中断则设置为TASK_INTERRUPTIBLE。从睡眠中被唤醒时要修改状态为TASK_RUNNING。
例如:
workqueue.c(kernel)的worker_thread()。
fmr_pool.c (drivers\infiniband\core)的ib_fmr_cleanup_thread()。
3, 建立内核线程:
kthread_create()根据helper_wq()是否存在采用不同的方式。若helper_wq()存在,则可以由工作队列负责建立内核线程,其回调函数是keventd_create_kthread。否则直接执行工作函数"create.work.func(&create.work);",就是keventd_create_kthread。
bamvor: 感觉二者的区别是使用helper_wq()可以减轻系统负载,等系统“有空”时再建立内核线程。
kthread_create自己则用完成变量(create->done)等待keventd_create_kthread完成。
keventd_create_kthread()调用kernel_thread()建立线程,等待完成变量(create->started)完成后返回。由于kernel_thread()建立线程后会唤醒新建的线程,也就唤醒了回调函数。这里的回调函数是kthread()。kthread()会执行"complete(&create->started);"。这样内核线程建立过程结束。
内核线程建立完成后,kthread首先调用schedule()然后控制权,当内核再次调度kthread运行时,kthread()检测该内核线程是否需要停止(kthread_should_stop),如果不需要停止则执行回调函数。执行完成后再次判断是否需要停止(kthread_should_stop),如果是设置内核线程回调函数threadfn的返回值ret给kthread_stop_info.err,并通过完成变量kthread_stop_info.done唤醒发送kthread_stop的任务(task_struct):
kthread_stop_info.err = ret;
complete(&kthread_stop_info.done);
如果kthread_should_stop不为真,kthread是直接返回。感觉设置kthread_should_stop的作用是为了同步:让其它等待这个内核线程完成的任务(task_struct,可能也是内核线程)知道已经完成了,等待中的进程可以进行后续操作。
0), helper_wq的建立:
static __init int helper_init(void)
{
helper_wq = create_singlethread_workqueue("kthread");
BUG_ON(!helper_wq);
return 0;
}
core_initcall(helper_init);
1), kthread()
static int kthread(void *_create)
{
struct kthread_create_info *create = _create;
int (*threadfn)(void *data);
void *data;
sigset_t blocked;
int ret = -EINTR;
kthread_exit_files();
/* Copy data: it's on keventd's stack */
threadfn = create->threadfn;
data = create->data;
/* Block and flush all signals (in case we're not from keventd). */
sigfillset(&blocked);
sigprocmask(SIG_BLOCK, &blocked, NULL);
flush_signals(current);
/* By default we can run anywhere, unlike keventd. */
set_cpus_allowed(current, CPU_MASK_ALL);
/* OK, tell user we're spawned, wait for stop or wakeup */
__set_current_state(TASK_INTERRUPTIBLE);
complete(&create->started);
schedule();
if (!kthread_should_stop())
ret = threadfn(data);
/* It might have exited on its own, w/o kthread_stop. Check. */
if (kthread_should_stop()) {
kthread_stop_info.err = ret;
complete(&kthread_stop_info.done);
}
return 0;
}
2), kthread_create()
/**
* kthread_create - create a kthread.
* @threadfn: the function to run until signal_pending(current).
* @data: data ptr for @threadfn.
* @namefmt: printf-style name for the thread.
*
* Description: This helper function creates and names a kernel
* thread. The thread will be stopped: use wake_up_process() to start
* it. See also kthread_run(), kthread_create_on_cpu().
*
* When woken, the thread will run @threadfn() with @data as its
* argument. @threadfn() can either call do_exit() directly if it is a
* standalone thread for which noone will call kthread_stop(), or
* return when 'kthread_should_stop()' is true (which means
* kthread_stop() has been called). The return value should be zero
* or a negative error number; it will be passed to kthread_stop().
*
* Returns a task_struct or ERR_PTR(-ENOMEM).
*/
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.started);
init_completion(&create.done);
INIT_WORK(&create.work, keventd_create_kthread);
/*
* The workqueue needs to start up first:
*/
if (!helper_wq)
create.work.func(&create.work);
else {
queue_work(helper_wq, &create.work);
wait_for_completion(&create.done);
}
if (!IS_ERR(create.result)) {
va_list args;
va_start(args, namefmt);
vsnprintf(create.result->comm, sizeof(create.result->comm),
namefmt, args);
va_end(args);
}
return create.result;
}
EXPORT_SYMBOL(kthread_create);
3),
/* We are keventd: create a thread. */
static void keventd_create_kthread(struct work_struct *work)
{
struct kthread_create_info *create =
container_of(work, struct kthread_create_info, work);
int pid;
/* We want our own signal handler (we take no signals by default). */
pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
if (pid < 0) {
create->result = ERR_PTR(pid);
} else {
wait_for_completion(&create->started);
read_lock(&tasklist_lock);
create->result = find_task_by_pid(pid);
read_unlock(&tasklist_lock);
}
complete(&create->done);
}
4), kernel_thread()。待做:补充。
kernel_thread()(arch\arm\kerenl\process.c)与体系结构相关,似乎主要是kernel_thread_helper不同,待做,看。
主体是调用do_fork()(kernel\fock.c)建立新进程:
do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
copy_process()复制和父进程一样的子进程,然后用wake_up_new_task唤醒该进程。wake_up_new_task()中会调用__activate_task()。
注:do_fock()中只要没有设置CLONE_STOPPED都会调用wake_up_new_task()。
23:31 2008-11-10