??xml version="1.0" encoding="utf-8" standalone="yes"?> 表示方式 section:disp(base,index,scale) 变址d AT&T: _variable(%eax) Intel: [eax + _variable]
AT&T: _array(%ebx,%eax,8) Intel: [ebx + eax*8 + _array]
]]>
关键字:旉中断、Q务切换、堆栈、LINUX0.01
引言Q?
d切换与堆栈的关系怎样Q很多朋友可能不知道她们之间有什么关p,q有一些朋友可能认Z们之间不会有太大的关p(文献4Q。而我认ؓQQ务切换跟堆栈有着密切的关p!下面是我对它们之间关p进行的探讨Q这里的d切换我指的是发生旉中断时进行强制调度发生的d切换Q所以下面考虑堆栈时我是从中断开始探讨的。当Ӟ我在q行q方面分析的时候,也愈感它们的复杂性,错误之处在所隑օQ望各位朋友多多指正。徏议读者水qI* * *
一、时间中断?
假设一个进E在用户I间执行Ӟq时CPL=3Q,发生了时间中断。这时的中断处理q程为(文献1QP438Q:
1、根据中断向量号扑ֈ中断门描q符Q?
2、从描述W中分解出选择子、偏U量、属性字Dƈq行相应的特权检查;
3、根据描q符cd转入相应中断处理E序中去执行?
好象太肤了一些?再看看(文献1QP439?0.20Q:
1、选择子ؓI?nol箋Q?
2、取得对应描q符Q(描述W中DPL属性应该ؓ0Q文?中断向量初始化部分)
3、存储段描述W?yesl箋Q?
4、非一致代码段且DPL<CPL且段存在Qyesl箋Q根据假设CPL=3QDPL=0Q所以到5Q?
5、切换成内层堆栈Q?
如何切换Q?因ؓ一个进E有用户I间堆栈和系l空_也叫内核I间Q堆栈,用户I间堆栈在哪儿我不管Q它应该是由该进E的d状态段TSS中SS2指定QSS0指定pȝI间堆栈Q它和该q程dl构task_struct共占一늩_见文?:sched.cQ。所以这里的切换成内层堆栈应该是该q程的TSS中SS0的DlSS寄存器?
6、RPL=0Q?
7、把描述W装入CSQ?
8、入口偏U越界?nol箋Q?
9、EFLAG、CS、EIP入栈Q呵Q开始栈的改变了哟!
10、TF=0、NT=0、IF=0Q这里考虑的是中断门?
11、{入处理程序?
别急,先看看现在的堆栈情况Q?
| 外层EIP |
| 外层CS |
| EFLAG |
| 外层ESP |
| 外层SS |
-----------
q个栈在什么地方呢Q这相当重要Q这是在当初切换臛_层堆栈时q行的,卛_l到了当前进E的pȝI间堆栈Q也是跟task_struct共占一늚那个堆栈。而这里保存的是该进E在用户I间的堆栈和代码信息Q以便中断完成后恢复q程执行?
二、中断处理程序?
q里指的是时间中断。(文献3Qsystem_call.c: timer_interrupt:Q?
timer_interrupt:
1. push %ds
2. push %es
3. push %fs
4. pushl %edx
5. pushl %ecx
6. pushl %ebx
7. pushl %eax
8. movl $0x10,%eax
9. mov %ax,%ds
10. mov %ax,%es
11. movl $0x17,%eax
12. mov %ax,%fs
13. incl jiffies
14. movb $0x20,%al
15. outb %al,$0x20
16. movl CS(%esp),%eax
17. andl $3,%eax
18. pushl %eax
19. call do_timer
20. andl $4,%esp
21. jmp ret_from_sys_call
1-7行ؓ压栈操作Q这是我们所兛_的!16-18xCPLQCPL=CS&3Q压栈,目的是用于do_tiemr(long cpl)函数。那么在执行到do_timer里面时的堆栈怎么样呢Q看看:
|q回地址 |
-----------
| CPL |
| eax |
| ebx |
| ecx |
| edx |
| fs |
| es |
| ds |
-----------
| 外层EIP |
| 外层CS |
| EFLAG |
| 外层ESP |
| 外层SS |
-----------
上面的返回地址当然是调用do_timer后的那条语句Q即20行的andl $4,%esp语句。那么是不是do_timer函数执行完就q回到这儿呢Q也是,当然要复杂得多,因ؓ在do_timer()函数中调用了schedule()q且发生了Q务切换!哎,好麻烦,也不知道什么时候才能返回到q儿来呢Q还是一步一步来看吧?
三、do_timer()Q文?Qsched.c: do_timer()Q?
void do_timer(long cpl)
{
...
if ((--current->counter)>0) return;
current->counter=0;
if(!cpl)return;
schedule();
}
省略号ؓ无关紧要的两条语句,q行q程的计时。如果时间片没有用完(counter>0)或CPL?Q不发生调度直接q回Q当然这里也不是q接返回到以前执行的进E空_而是q回到do_timer()中,注意开始的q回地址Q然后再通过iret指o从中断处理返回到q程中去。当ӞҎ我们的假设,q儿CPL应该?Q因为是在用L间发生中断的。我们要从最复杂的情冉|讨论q个问题。好了,p我们q入C心点吧,误schedule()?
四、schedule()?Q文?Qsched.c: schedule()Q?
void schedule(void )
{
int next;
...
switch_to(next);
}
呵,q里我又省略了几句代码,它执行的是调度算法,即从所有状态ؓ‘q行’的进E中扑և下一个要执行的进E,然后编可lnext。进行切换!
switch_to()是一个宏Q它在(文献3Q?sched.hQ中定义Q?
#define switch_to(n) { \
struct (long a,b;} __tmp; \
__asm__("cmpl %%ecx,current \n\t" \
"je 1f\n\t" \
"xchgl %%ecx, current\n\t" \
"movw %%dx, %1\n\t" \
"ljmp *%0\n\t" \
"cmpl %%ecx, %2\n\t" \
"jne 1f\n\t" \
"clts\n" \
"1:" \
::"m" (*&__tmp.a), "m" (*&__tmp.b), \
"m" (last_task_used_math),"d" _TSS(n), "c" ((long) task[n])); \
}
q是d切换的关键代码,原理是直接通过TSS来进行Q务的切换Q文?QP420Q。那我就这D关键代码逐行解说一下吧。cmpl %%ecx, currentQ比较Q务n是不是当前进E,如果是当然就不用切换了,直接l束schedule()。xchgl %%ecx,currentQcurrent指针指向dn的Q务结构,ecx寄存器保存当前进E的dl构指针。movw %%dx, %1Q?使__tmp.b=‘GDT中第n个Q务的TSS选择?#8217;Q注意_TSS(n)是求选择子的宏!ljmp *%0Q这句代码就是真正的d切换|, AT&T语法的ljmp相当于INTEL的jmp far SECTION:OFFSET指o格式Q它的绝对地址前加*受这里引用(文献1QP420Q一D话Q当D间转移指oJMP所含指针的选择子指CZ个可用Q务状态段TSS描述W时Q正常情况下发生从当前d到由该可用Q务的切换。目标Q务的入口点由目标dTSS内的CS和EIP字段所规定的指针确定,q样的JMP指o内的偏移被丢弃。再具体的Q务切换你也许得翻(文献1QP421Q,q里我只讲有兛_栈的处理Q那是把寄存器现场保存到当前Q务的TSS。把通用寄存器、段寄存器、EIP及EFLAGS的当前g存到当前的TSS中。保存的EIP的值是q回地址Q指向引起Q务切换指令的下一条指令;恢复目标d的寄存器现场Q根据保存在TSS中的内容恢复各通用寄存器、段寄存器、EFLAG、EIP。好了,基本概念引用这么多Q那么,刚才提到的进E马上要被切换出MQ它保存TSS中EIP是什么呢Q显ӞҎ刚才的分析应该是cmpl %%ecx, %2q条指o。这意味着什么呢Q这是_如果下次q个d要被切换成运行状态时Q它从cmpl %%ecx, %2q条指o开始执行!那么Q由gQ务推到此dQ也是说我们切换至dnextӞ它也是从q条指o开始执行的Q于是我们进入到dnext的堆栈空_q开始执行,但由于Q务next和当前的d有着相同的堆栈\径(q和LINUX中的内核控制路径是不是一回事呢?Q,所以我们还是引用当前的堆栈来l分析?
哦,有点p涂了,好象是。休息一下,再参考一下(文献2Q上册P373Q。专家也是这栯?)
要不Q我们这么理解,刚才被中断的q程发生了强制调度,且也发生了Q务切换,只不q是切换到它自己Q实际上不是哟。好吧,JMP成功Q开始执行?
五、{折点Q从schedule()q回?
cmpl %%ecx, %2Qjne 1f; clts;1: q几句是与协处理器有养Iq有TS标志Q我们就直接?Q吧Q开始从schedule()q回Q注意switch_to()是宏Q它在schedule()末端。返回到哪儿M呢?跟踪一下,看看上面的堆栈示意图Q返回地址是调用do_timer后的那条语句Q?
addl $4, %esp
jmp ret_from_sys_call
q儿esp?是把堆栈中的CPLLQ因为我们不用了Q蟩转到ret_from_sys_call。哦Q剩下的处理与系l调用返回共用代码?
六、ret_from_sys_call Q文?Qkernel/system_call.sQ?
先看看我们的焦点Q堆栈怎么样了呢?
| eax |
| ebx |
| ecx |
| edx |
| fs |
| es |
| ds |
-----------
| 外层EIP |
| 外层CS |
| EFLAG |
| 外层ESP |
| 外层SS |
-----------
ret_from_sys_call:
movel current, %eax
cmpl task, %eax
je 3f
movl CS(%esp), %ebx
testl $3, %ebx
je 3f
cmpw $0x17, OLDSS(%esp)
jne 3f
2:
....
3:
popl %eax
popl %ebx
popl %ecx
popl %edx
pop %fs
pop %es
pop %ds
iret
2标号处我省略了一些有关信号及其它一些处理。让我们分析一下,如果当前d?可E,或是d先前的CPL?Q即用户态)Q或是Q务先前的堆栈DؓLDT中指定的堆栈QJMP?标号处。由先前的假讑֏知,此Q务的CPL?Q那p吧。把eax, ebc, ecx, edx, fs, es, ds寄存器从堆栈中恢复出来?
现在堆栈如下Q?
| 外层EIP |
| 外层CS |
| EFLAG |
| 外层ESP |
| 外层SS |
-----------
记得我们q有最后一条语句哟Qiret。这条指令大家想必已l很熟悉了,它恢复EIP、CS、EFLAG、ESP、SS。记得不Q这是不是已l恢复到了最初的旉中断时进E被中断的那一刻?恭喜Q你l于可以l箋做你需要做的事情了Q小心,q有下一个时间中断,哦,你不怕?因ؓ它不会媄响你的连贯性?
l束语:
我们走过了一D艰辛的历程Q但我们走的是一D近乎直U的路径Qƈ没有分析到其它各个方面的情况。不q我怿Q通过q段路程Q会让我们对于Q务切换机制有一个更深入的认识。希望对大家有所帮助Q这也是我写出来与大家分享的~故?
参考文献:
1?0X86汇编语言E序设计教程 杨季文编?清华大学出版C?
2、LINUX内核源代码情景分?毛d?胡希明著 江大学出版C?
3、LINUX0.01源代?
4、http://www.linuxforum.net/ LINUX内核分析-->_֍?->q程调度
]]>
]]>
]]>
Here are the different values that the s, stat and state output
specifiers (header "STAT" or "S") will display to describe the state of
a process.
D Uninterruptible sleep (usually IO)
R Running or runnable (on run queue)
S Interruptible sleep (waiting for an event to complete)
T Stopped, either by a job control signal or because it is being
traced.
W paging (not valid since the 2.6.xx kernel)
X dead (should never be seen)
Z Defunct ("zombie") process, terminated but not reaped by its
parent.
For BSD formats and when the stat keyword is used, additional
characters may be displayed:
< high-priority (not nice to other users)
N low-priority (nice to other users)
L has pages locked into memory (for real-time and custom IO)
s is a session leader
l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
+ is in the foreground process group
对于提供了MMUQ存储管理器Q辅助操作系l进行内存管理,提供虚实地址转换{硬件支持)的处理器而言QLinux提供了复杂的存储理pȝQ得进E所能访问的内存辑ֈ4GB?/p>
q程?GB内存I间被h为的分ؓ两个部分--用户I间与内核空间。用L间地址分布??GB(PAGE_OFFSETQ在0x86中它{于0xC0000000)Q?GB?GB为内核空间?/p>
内核I间中,?G到vmalloc_startq段地址是物理内存映区域(该区域中包含了内栔R像、物理页框表mem_map{等Q,比如我们使用?VMware虚拟pȝ内存?60MQ那?G?G+160Mq片内存应该映物理内存。在物理内存映射Z后,是vmalloc区域。对?160M的系l而言Qvmalloc_start位置应在3G+160M附近Q在物理内存映射Zvmalloc_start期间q存在一?M的gap 来防止跃界)Qvmalloc_end的位|接q?G(最后位|系l会保留一?28k大小的区域用于专用页面映?
kmalloc和get_free_page甌的内存位于物理内存映区域,而且在物理上也是q箋的,它们与真实的物理地址只有一个固定的偏移Q因此存在较单的转换关系Qvirt_to_phys()可以实现内核虚拟地址转化为物理地址Q?br> #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
extern inline unsigned long virt_to_phys(volatile void * address)
{
return __pa(address);
}
上面转换q程是将虚拟地址减去3GQPAGE_OFFSET=0XC000000Q?/p>
与之对应的函Cؓphys_to_virt()Q将内核物理地址转化拟地址Q?br> #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
extern inline void * phys_to_virt(unsigned long address)
{
return __va(address);
}
virt_to_phys()和phys_to_virt()都定义在include\asm-i386\io.h中?/p>
-------------------------------------------------------------------------------------
1、kmalloc() 分配q箋的物理地址Q用于小内存分配?
2、__get_free_page() 分配q箋的物理地址Q用于整分配?
至于Z么说以上函数分配的是q箋的物理地址和返回的到底是物理地址q是虚拟地址Q下面的记录会做释?
kmalloc() 函数本n是基?slab 实现的。slab 是ؓ分配内存提供的一U高效机制。但 slab q种分配机制又不是独立的Q它本n也是在页分配器的基础上来划分更细_度的内存供调用者用。也是说系l先用页分配器分配以ؓ最单位的q箋物理地址Q然?kmalloc() 再在q上面根据调用者的需要进行切分?
关于以上Q我们可以查?kmalloc() 的实玎ͼkmalloc()函数的实现是?__do_kmalloc() 中,可以看到?__do_kmalloc()代码里最l调用了 __cache_alloc() 来分配一?slabQ其?
kmem_cache_alloc() {函数的实现也是调用了这个函数来分配新的 slab。我们按?__cache_alloc()函数的调用\径一直跟t下M发现?cache_grow() 函数中用了 kmem_getpages()函数来分配一个物理页面,kmem_getpages() 函数中调用的alloc_pages_node() 最l是使用 __alloc_pages() 来返回一个struct page l构Q而这个结构正是系l用来描q物理页面的。这样也p实了上面所说的Qslab 是在物理面基础上实现的。kmalloc() 分配的是物理地址?
__get_free_page() 是页面分配器提供l调用者的最底层的内存分配函数。它分配q箋的物理内存。__get_free_page() 函数本n是基?buddy 实现的。在使用 buddy 实现的物理内存管理中最分配粒度是以页为单位的。关于以上论qͼ我们可以查看__get_free_page()的实玎ͼ可以看到__get_free_page()函数只是一个非常简单的状Q它的整个函数实现就是无条g的调?__alloc_pages() 函数来分配物理内存,上面记录 kmalloc()实现时也提到q是在调?__alloc_pages() 函数来分配物理页面的前提下进行的 slab 理。那么这个函数是如何分配到物理页面又是在什么区域中q行分配的?回答q个问题只能看下相关的实现。可以看到在 __alloc_pages() 函数中,多次试调用get_page_from_freelist() 函数?zonelist 中取得相?zoneQƈ从其中返回一个可用的 struct page 面Q这里的有些调用分支是因为标志不同)。至此,可以知道一个物理页面的分配是从 zonelistQ一?zone 的结构数l)中的 zone q回的。那?zonelist/zone 是如何与物理面兌Q又是如何初始化的呢Ql来?free_area_init_nodes() 函数Q此函数在系l初始化时由 zone_sizes_init() 函数间接调用的,zone_sizes_init()函数填充了三个区域:ZONE_DMAQZONE_NORMALQZONE_HIGHMEM。ƈ把他们作为参数调?free_area_init_nodes()Q在q个函数中会分配一?pglist_data l构Q此l构中包含了 zonelist/zonel构和一?struct page 的物理页l构Q在函数最后用此结构作为参数调用了 free_area_init_node() 函数Q在q个函数中首先?calculate_node_totalpages() 函数标记 pglist_data 相关区域Q然后调?alloc_node_mem_map() 函数初始?pglist_datal构中的 struct page 物理c最后?free_area_init_core()函数兌 pglist_data ?zonelist。现在通以上分析已l明了__get_free_page() 函数分配物理内存的流E。但q里又引Z几个新问题,那就是此函数分配的物理页面是如何映射的?映射C什么位|?到这里不得不ȝ下与 VMM 相关的引g码?
在看 VMM 相关的引g码前Q先来看一?virt_to_phys() 与phys_to_virt q两个函数。顾名思义Q即是虚拟地址到物理地址和物理地址到虚拟地址的{换。函数实现十分简单,前者调用了__pa( address ) 转换虚拟地址到物理地址Q后者调?__va(addrress ) 物理地址转换拟地址。再看下 __pa __va q两个宏到底做了什么?
#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
通过上面可以看到仅仅是把地址加上或减?PAGE_OFFSETQ而PAGE_OFFSET ?x86 下定义ؓ 0xC0000000。这里又引出了疑问,?linux 下写q?driver 的h都知道,在?kmalloc() ?
__get_free_page() 分配完物理地址后,如果惛_到正的物理地址需要?virt_to_phys() q行转换。那么ؓ什么要有这一步呢Q我们不分配的不是物理地址么?怎么分配完成q需要{换?如果q回的是虚拟地址Q那么根据如上对 virt_to_phys() 的分析,Z么仅仅对 PAGE_OFFSET 操作p实现地址转换呢?虚拟地址与物理地址之间的{换不需要查表么?代着以上诸多疑问来看 VMM 相关的引g码?
直接?start_kernel() 内核引导部分来查?VMM 相关内容。可以看到第一个应该关注的函数?setup_arch()Q在q个函数当中使用paging_init() 函数来初始化和映硬仉表(在初始化前已?8M内存被映,在这里不做记录)Q?paging_init() 则是调用的pagetable_init() 来完成内核物理地址的映以及相兛_存的初始化。在 pagetable_init() 函数中,首先是一?PAE/PSE/PGE 相关判断与设|,然后使用 kernel_physical_mapping_init() 函数来实现内核物理内存的映射。在q个函数中可以很清楚的看刎ͼpgd_idx 是以PAGE_OFFSET 为启始地址q行映射的,也就是说循环初始化所有物理地址是以 PAGE_OFFSET v点的。l观察我们可以看到在 PMD 被初始化后,所有地址计算均是?PAGE_OFFSET 作ؓ标记来递增的。分析到q里已经很明昄可以看出Q物理地址被映到?PAGE_OFFSET 开始的虚拟地址I间。这样以上所有疑问就都有了答案。kmalloc() 与__get_free_page() 所分配的物理页面被映射C PAGE_OFFSET 开始的虚拟地址Q也是说实际物理地址与虚拟地址有一l一一对应的关p,
正是因ؓ有了q种映射关系Q对内核?PAGE_OFFSET 启始的虚拟地址的分配也是对物理地址的分配(当然q有一定的范围Q应该在 PAGE_OFFSET?VMALLOC_START 之间Q后者ؓ vmalloc() 函数分配内存的启始地址Q。这也就解释了ؓ什?virt_to_phys() ?phys_to_virt() 函数的实C仅是??PAGE_OFFSET 卛_在虚拟地址与物理地址之间转换Q正是因Z有了q种映射Q且固定不变Q所以才不用L表q行转换。这也同样回{了开始的问题Q即 kmalloc() / __get_free_page() 分配的是物理地址Q而返回的则是虚拟地址Q虽然这听上L些别扭)。正是因为有了这U映关p,所以需要将它们的返回地址减去 PAGE_OFFSET 才可以得到真正的物理地址?/p>
另一更Ҏ理解的:
kmalloc, vmalloc分配的内存结?zz
2008-01-20 16:05
q程I间Q| <-用户I间-> | <-内核I间-> |
内核I间Q| <-物理内存映射?> | <-vmalloc区域-> |
==============原文================================
对于提供了MMUQ存储管理器Q辅助操作系l进行内存管理,提供虚实地址转换{硬件支持)的处理器而言QLinux提供了复杂的存储理pȝQ得进E所能访问的内存辑ֈ4GB?/p>
q程?GB内存I间被h为的分ؓ两个部分--用户I间与内核空间。用L间地址分布??GB(PAGE_OFFSETQ在0x86中它{于0xC0000000)Q?GB?GB为内核空间?/p>
内核I间中,?G到vmalloc_startq段地址是物理内存映区域(该区域中包含了内栔R像、物理页框表mem_map{等Q,比如我们使用 ?VMware虚拟pȝ内存?60MQ那?G?G+160Mq片内存应该映物理内存。在物理内存映射Z后,是vmalloc区域。对?160M的系l而言Qvmalloc_start位置应在3G+160M附近Q在物理内存映射Zvmalloc_start期间q存在一?M的gap 来防止跃界)Qvmalloc_end的位|接q?G(最后位|系l会保留一?28k大小的区域用于专用页面映?
kmalloc和get_free_page甌的内存位于物理内存映区域,而且在物理上也是q箋的,它们与真实的物理地址只有一个固定的偏移Q因此存在较单的转换关系Qvirt_to_phys()可以实现内核虚拟地址转化为物理地址Q?br> #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
extern inline unsigned long virt_to_phys(volatile void * address)
{
return __pa(address);
}
上面转换q程是将虚拟地址减去3GQPAGE_OFFSET=0XC000000Q?/p>
与之对应的函Cؓphys_to_virt()Q将内核物理地址转化拟地址Q?br> #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
extern inline void * phys_to_virt(unsigned long address)
{
return __va(address);
}
virt_to_phys()和phys_to_virt()都定义在include\asm-i386\io.h中?/p>
而vmalloc甌的内存则位于vmalloc_start~vmalloc_end之间Q与物理地址没有单的转换关系Q虽然在逻辑上它们也是连l的Q但是在物理上它们不要求q箋?/p>
我们用下面的E序来演Ckmalloc、get_free_page和vmalloc的区别:
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
MODULE_LICENSE("GPL");
unsigned char *pagemem;
unsigned char *kmallocmem;
unsigned char *vmallocmem;
int __init mem_module_init(void)
{
//最好每ơ内存申请都查申h否成?br>//下面q段仅仅作ؓ演示的代码没有检?br>pagemem = (unsigned char*)get_free_page(0);
printk("<1>pagemem addr=%x", pagemem);
kmallocmem = (unsigned char*)kmalloc(100, 0);
printk("<1>kmallocmem addr=%x", kmallocmem);
vmallocmem = (unsigned char*)vmalloc(1000000);
printk("<1>vmallocmem addr=%x", vmallocmem);
return 0;
}
void __exit mem_module_exit(void)
{
free_page(pagemem);
kfree(kmallocmem);
vfree(vmallocmem);
}
module_init(mem_module_init);
module_exit(mem_module_exit);
我们的系l上?60MB的内存空_q行一ơ上q程序,发现pagemem的地址?xc7997000Q约3G+121MQ、kmallocmem 地址?xc9bc1380Q约3G+155MQ、vmallocmem的地址?xcabeb000Q约3G+171MQ处Q符合前文所q的内存布局?
本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/sandflee/archive/2009/07/28/4388322.aspx
POSIX是Portable Operating System Interface of Unix的羃写。由IEEEQInstitute of Electrical and Electronic EngineeringQ开发,由ANSI和ISO标准化?br> POSIX的诞生和Unix的发展是密不可分的,Unix?0q代诞生于Bell labQƈ?0q代向美各大高校分发V7版的源码以做研究。UC Berkeley在V7的基上开发了BSD Unix。后来很多商业厂家意识到Unix的hgUL以Bell Lab的System V或BSD为基来开发自qUnixQ较著名的有Sun OSQAIXQVMS。由于各厂家对Unix的开发各自ؓ政,造成了Unix的版本相当乱,lY件的可移植性带来很大困难,对Unix的发展极Z利。ؓl束q种局面,IEEE开发了POSIXQPOSIX在源代码U别上定义了一l最的Unix(cUnix)操作pȝ接口?br> POSIX 表示可移植操作系l接口(Portable Operating System Interface Q羃写ؓ POSIX 是ؓ了读x?UNIXQ。电气和电子工程师协会(Institute of Electrical and Electronics EngineersQIEEEQ最初开?POSIX 标准Q是Z提高 UNIX 环境下应用程序的可移植性。然而,POSIX q不局限于 UNIX。许多其它的操作pȝQ例?DEC OpenVMS ?Microsoft Windows NTQ都支持 POSIX 标准Q尤其是 IEEE Std. 1003.1-1990Q?995 q修订)?POSIX.1QPOSIX.1 提供了源代码U别?C 语言应用~程接口QAPIQ给操作pȝ的服务程序,例如d文g。POSIX.1 已经被国际标准化l织QInternational Standards OrganizationQISOQ所接受Q被命名?ISO/IEC 9945-1:1990 标准?
POSIX 现在已经发展成ؓ一个非常庞大的标准族,某些部分正处在开发过E中。表 1-1 l出?POSIX 标准的几个重要组成部分。POSIX ?IEEE 1003 ?2003 家族的标准是可互换的。除 1003.1 之外Q?003 ?2003 家族也包括在表中?
?Q标准的重要l成部分
1003.0
理 POSIX 开攑ּpȝ环境QOSEQ。IEEE ?1995 q通过了这Ҏ准?ISO 的版本是 ISO/IEC 14252:1996?
1003.1
被广泛接受、用于源代码U别的可UL性标准?003.1 提供一个操作系l的 C 语言应用~程接口QAPIQ。IEEE ?ISO 已经?1990 q通过了这个标准,IEEE ?1995 q重C订了该标准?
1003.1b
一个用于实时编E的标准Q以前的 P1003.4 ?POSIX.4Q。这个标准在 1993 q被 IEEE 通过Q被合ƈq?ISO/IEC 9945-1?
1003.1c
一个用于线E(在一个程序中当前被执行的代码D)的标准。以前是 P1993.4 ?POSIX.4 的一部分Q这个标准已l在 1995 q被 IEEE 通过Q归?ISO/IEC 9945-1:1996?
1003.1g
一个关于协议独立接口的标准Q该接口可以使一个应用程序通过|络与另一个应用程序通讯?1996 q_IEEE 通过了这个标准?
1003.2
一个应用于 shell ?工具软g的标准,它们分别是操作系l所必须提供的命令处理器和工L序?1992 q?IEEE 通过了这个标准。ISO 也已l通过了这个标准(ISO/IEC 9945-2:1993Q?
1003.2d
改进?1003.2 标准?
1003.5
一个相当于 1003.1 ?Ada 语言?API。在 1992 q_IEEE 通过了这个标准。ƈ?1997 q对其进行了修订。ISO 也通过了该标准?
1003.5b
一个相当于 1003.1bQ实时扩展)?Ada 语言?API。IEEE ?ISO 都已l通过了这个标准。ISO 的标准是 ISO/IEC 14519:1999?
1003.5c
一个相当于 1003.1qQ协议独立接口)?Ada 语言?API。在 1998 q_ IEEE 通过了这个标准。ISO 也通过了这个标准?
1003.9
一个相当于 1003.1 ?FORTRAN 语言?API。在 1992 q_IEEE 通过了这个标准,q于 1997 q对其再ơ确认。ISO 也已l通过了这个标准?
1003.10
一个应用于计算应用环境框架QApplication Environment ProfileQAEPQ的标准。在 1995 q_IEEE 通过了这个标准?
1003.13
一个关于应用环境框架的标准Q主要针对?POSIX 接口的实时应用程序。在 1998 q_IEEE 通过了这个标准?
1003.22
一个针?POSIX 的关于安全性框架的指南?
1003.23
一个针对用Ll的指南Q主要是Z指导用户开发和使用支持操作需求的开攑ּpȝ环境QOSEQ框?
2003
针对指定和用是否符?POSIX 标准的测试方法,有关其定义、一般需求和指导斚w的一个标准。在 1997 q_IEEE 通过了这个标准?
2003.1
q个标准规定了针?1003.1 ?POSIX 试Ҏ的提供商要提供的一些条件。在 1992 q_IEEE 通过了这个标准?
2003.2
一个定义了被用来检查与 IEEE 1003.2Qshell ?工具 APIQ是否符合的试Ҏ的标准。在 1996 q_IEEE 通过了这个标准?
除了 1003 ?2003 家族以外Q还有几个其它的 IEEE 标准Q例?1224 ?1228Q它们也提供开发可UL应用E序?API。要惛_到关?IEEE 标准的最C息,可以讉K IEEE 标准的主,|址?http://standards.ieee.org/。有?POSIX 标准的概qC息,误?Web 站点 http://standards.ieee.org/reading/ieee/stad_public/description/posix/?/p>
Linuxpȝ下的多线E遵循POSIXU程接口Q称为pthread。从上面的描qC隄道,POSIXU程接口是POSIX众多标准中的一个(POSIX 1003.1-2001Q?/strong>?/p>
~写Linux下的多线E程序,需要用头文gpthread.hQ连接时需要用库libpthread.a。顺便说一下,Linux下pthread的实现是通过pȝ调用 clone() 来实现的。clone() 是Linux所Ҏ的系l调用,它的使用方式cMforkQ关?clone() 的详l情况,有兴的读者可以去查看有关文档说明?/p>
下面是一?POSIX U程的简单示例程?thread1.c)Q? 要编译这个程序,只需先将E序存ؓ thread1.cQ然后输入: q行则输入: Windows本n没有提供对POSIX的支持。但有一个叫 POSIX Threads for Win32 的开源项目给Z一个功能比较完善的Windows下pthreads API的实现。目前的最新版本是Pthreads-w32 release 2.8.0 (2006-12-22)?/p>
我没有测试过q个最新版本,q里只给?.7.0版的链接Q?a title=ftp://sources.redhat.com/pub/pthreads-win32/pthreads-w32-2-7-0-release.exe href="ftp://sources.redhat.com/pub/pthreads-win32/pthreads-w32-2-7-0-release.exe">ftp://sources.redhat.com/pub/pthreads-win32/pthreads-w32-2-7-0-release.exe?/p>
关于该开源项目的详细介绍见:http://sources.redhat.com/pthreads-win32/?/p>
上面的exe文g是一个自解压文gQ解压后QPre-built.2目录中有~译所需要的头文Ӟinclude子目录)和库文gQlib子目录)?/p>
一个简单的试E序(main.cpp)Q?/p>
使用 cl.exe ~译Q不熟悉 cl.exe 的请参考:http://blog.csdn.net/liuyongjin1984/archive/2008/01/07/2029405.aspx 或者参见下?.2部分Q: 3.2 使用VC++ 6.0或Visual Studio 2005来运行上面的E序 关键有两点: 1. 是将头文Ӟinclude子目录)和库文gQlib子目录)中的内容d到VC++ 6.0或Visual Studio 2005开发环境对应的include和lib目录下?/p>
具体来说Q?strong>以添加include目录ZQ添加lib目录cM#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
void *thread_function(void *arg) ...{
int i;
for ( i=0; i<20; i++) ...{
printf("Thread says hi! ");
sleep(1);
}
return NULL;
}
int main(void) ...{
pthread_t mythread;
if ( pthread_create( &mythread, NULL, thread_function, NULL) ) ...{
printf("error creating thread.");
abort();
}
if ( pthread_join ( mythread, NULL ) ) ...{
printf("error joining thread.");
abort();
}
exit(0);
}
3. Windows下POSIXU程~程
3.1 单?/strong>
#include <stdio.h>
#include <pthread.h>
#include <assert.h>
void* Function_t(void* Param)
...{
printf("I am a thread! ");
pthread_t myid = pthread_self();
printf("thread ID=%d ", myid);
return NULL;
}
int main()
...{
pthread_t pid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_PROCESS);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&pid, &attr, Function_t, NULL);
printf("======================================== ");
getchar();
pthread_attr_destroy(&attr);
return 1;
}
?span style="COLOR: #000000">rem cl.bat
》cl.exe main.cpp /c /I"c:pthreads-w32-2-7-0-releasePre-built.2include"
》link.exe /out:main_cl.exe main.obj /LIBPATH:"c:pthreads-w32-2-7-0-releasePre-built.2lib" pthreadVC2.lib
?QVC++ 6.0Q添加include目录Q工?-》选项--》目录)
?QVisual Studio 2005(dinclude目录Qtools--》options)
2. 指定link时要q接的库的名UͼpthreadVC2.libQ?/strong>
?QVC++ 6.0Q工E?-》设|?-》连接)
?QVisual Studio 2005(project-->* property pages)
2. 接下来点?#8220;下一?#8221;Q然后从一pd服务器列表中选择一个你认ؓ|速最快的一个作Z载服务器。接着点击“下一?#8221;׃出现如下图所C的界面Q用来选择你想安装的程序?/p>
如果你不做Q何修改,默认安装Cygwinq行所需的最的E序和组件。不q,gcc~译器不在默认安装程序之列,所以你必须选择安装gcc。具体是Q?在上q窗口中的列表中展开Devel目录Q找到gcc-g++一,点击“Default”Q它变成了“Install”Q同时由于程序之间的依赖?gcc-core条目?#8220;Default”Q也变成?#8220;Install”?/p>
此外Qؓ了gcc能正编译源文gQ还必须安装win32api库,否则会编译出错。因此,你需要在上述H口中展开Libs目录Q找到win32api一,它Ҏ“Install”?/p> 3. 接下来就是耐心{待E序M载所需的安装文件了。一旦下载完毕,你再ơ运行setup.exeQ先选择你将要把Cygwin安装在什么地方,然后再选择?刚才所下蝲的那些安装文件所在的盘位置。接着你还需要再ơ选择安装gcc和win32api。这P你就可以开始安装了?
4. 安装完成后,q行Cygwin。在H口中敲入gcc可以直接用gcc来编译了。编译链接生成的可执行文Ӟpȝ会自动加?exe后缀Q在Cygwin 环境中可以直接运行。但如果qCygwin环境Q而在MS DOS下运行,则会出错。解军_法很单,那就是将cygwin1.dll文g拯到C:\WINDOWS目录下即可?br>
在这里我惛_调,如果囄事,完全可以直接all双的default设ؓinstall。但是这这样你4个G的空_所以如果你不是拥有特别大的I间的话Q还是徏议选择性的安装Q需要什么可以随时点d前的setup.exe加装新的lg除去以上所说的lg外,在devel下的GDB和make怿也是大多数开发者所必须的?br>x已将解决的了文章开头提及的前三要Ӟ而第四项man则是cygwin默认安装的,不过不是很全面,所以可以通过The Linux Documentation Project Q很好很强大的文档网站)来解冻I不过貌似q个|站大陆打不开Q反正我是没打开Q还好有香港同胞建立的mirrorQ比较好用的?a href="ftp://ftp.hkmirror.org/pub/ldp/manpages/">ftp://ftp.hkmirror.org/pub/ldp/manpages/Q在其中扑ֈ适合你的man版本Q下载,解压?(cygwin)目录下的/usr/share/man/下面Q编?usr/share/misc/man.confQ文件末֊上MANPATH /usr/share/man/man-pages-*.**卛_?br>
好啦Q如下配|后Q启动cygwinQ来~一个小E序helloworld热热w?br>随便选一个目录徏立helloworld.cc文gQ记事本打开Q写入以下代码:
#include "stdio.h"
int
main(int args,char* argv[])
{
printf("helloworld");
return 0;
}
保存Q启动cygwin cd到helloworld.cc文g目录Q执?br>
1 用ubuntu自带的新立得软g包安装ark软gQ搜索arkQ然后选中p了,会自动安装附带的10多个库文件?/p>
2 安装完arkQ会在菜单的附g中找刎ͼ你会发现ark其实已经设计成ؓ支持rarQ因为在新徏打开{操作中都可以选择rar格式Q不q只是写了出来,不是真正的支持。接下来主角要登Z?/p>
3 到这?a >RARLABQ你可以下蝲rar for linux的文Ӟ当然不是用rar格式压羃的,用默认打开的归档管理器解压~,其中的文g找一个\径存放?/p>
4 刚才解压羃的rar for linux的文件中的四个可执行文gQdefault.sfxQrarQrar_staticQ?unrarQ复制到 /usr/local/bin 目录下。你可能需要管理员帐号。如果在l端下操作,可以单的sudo mv一下?/p>
.tar
解包Qtar xvf FileName.tar
打包Qtar cvf FileName.tar DirName
Q注Qtar是打包,不是压羃Q)
---------------------------------------------
.gz
解压1Qgunzip FileName.gz
解压2Qgzip -d FileName.gz
压羃Qgzip FileName
.tar.gz ?.tgz
解压Qtar zxvf FileName.tar.gz
压羃Qtar zcvf FileName.tar.gz DirName
---------------------------------------------
.bz2
解压1Qbzip2 -d FileName.bz2
解压2Qbunzip2 FileName.bz2
压羃Q?bzip2 -z FileName
.tar.bz2
解压Qtar jxvf FileName.tar.bz2
压羃Qtar jcvf FileName.tar.bz2 DirName
---------------------------------------------
.bz
解压1Qbzip2 -d FileName.bz
解压2Qbunzip2 FileName.bz
压羃Q?font color="#ffa500">未知
.tar.bz
解压Qtar jxvf FileName.tar.bz
压羃Q?font color="#ffa500">未知
---------------------------------------------
.Z
解压Quncompress FileName.Z
压羃Qcompress FileName
.tar.Z
解压Qtar Zxvf FileName.tar.Z
压羃Qtar Zcvf FileName.tar.Z DirName
---------------------------------------------
.zip
解压Qunzip FileName.zip
压羃Qzip FileName.zip DirName
---------------------------------------------
.rar
解压Qrar x FileName.rar
压羃Qrar a FileName.rar DirName
rar请到Q?a style="color: #003793;" target="_blank">http://www.rarsoft.com/download.htm 下蝲Q?br>
解压后请rar_static拯?usr/bin目录Q其他由$PATH环境变量指定的目录也可以Q:
[root@www2 tmp]# cp rar_static /usr/bin/rar
---------------------------------------------
.lha
解压Qlha -e FileName.lha
压羃Qlha -a FileName.lha FileName
lha请到Q?a style="color: #003793;" target="_blank">http://www.infor.kanazawa-it.ac.jp/~ishii/lhaunix/下蝲Q?br>
>解压后请lha拯?usr/bin目录Q其他由$PATH环境变量指定的目录也可以Q:
[root@www2 tmp]# cp lha /usr/bin/
---------------------------------------------
.rpm
解包Qrpm2cpio FileName.rpm | cpio -div
---------------------------------------------
.deb
解包Qar p FileName.deb data.tar.gz | tar zxf -
---------------------------------------------
.tar .tgz .tar.gz .tar.Z
.tar.bz .tar.bz2 .zip .cpio .rpm .deb .slp .arj .rar .ace .lha .lzh
.lzx .lzs .arc .sda .sfx .lnx .zoo .cab .kar .cpt .pit .sit .sea
解压QsEx x FileName.*
压羃QsEx a FileName.* FileName
sEx只是调用相关E序Q本wƈ无压~、解压功能,h意!
sEx请到Q?http://sourceforge.net/projects/sex下蝲Q?br>
解压后请sEx拯?usr/bin目录Q其他由$PATH环境变量指定的目录也可以Q:
[root@www2 tmp]# cp sEx /usr/bin/