⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 4.html

📁 网上一个牛人整理的关于linux内核编译
💻 HTML
📖 第 1 页 / 共 5 页
字号:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312">
   <META NAME="GENERATOR" CONTENT="《良友》v2.1, 作者:安富国,http://winking.126.com">
   <TITLE>进程</TITLE>
</HEAD>
<BODY style="font-family: 宋体; font-size: 9pt">
&nbsp;
<CENTER><TABLE CELLSPACING=10 CELLPADDING=10 WIDTH="60%" BGCOLOR="#FFB693" >
<TR>
<TD ALIGN=CENTER><FONT SIZE=+2><!--标题由此开始-->进程</TD>
</TR>
</TABLE></CENTER>

<p><h3>目       录</h3>
<!--目录由此开始--><A NAME="Content" ID="Content"></A>
<OL><LI><A HREF="#I407">进程</A></LI>
<OL><LI><A HREF="#I408">信号</A></LI>
<LI><A HREF="#I409">sched.c</A></LI>
<LI><A HREF="#I410">进程信号队列</A></LI>
<LI><A HREF="#I411">SMP</A></LI>
<LI><A HREF="#I412">内核线程页目录的借用</A></LI>
<LI><A HREF="#I413">代码分析</A></LI>
<LI><A HREF="#I414">线程</A></LI>
<LI><A HREF="#I415">进程描述符</A></LI>
<LI><A HREF="#I416">init进程从内核态切换到用户态</A></LI>
<LI><A HREF="#I417">SET_LINKS</A></LI>
<LI><A HREF="#I418">REMOVE_LINKS</A></LI>
<LI><A HREF="#I419">get_wchan()</A></LI>
<LI><A HREF="#I420">sigframe的结构</A></LI>
<LI><A HREF="#I421">rt_sigframe结构</A></LI>
<LI><A HREF="#I422">信号队列的结构</A></LI>
<LI><A HREF="#I423">内核线程简介</A></LI>
<LI><A HREF="#I424">进程切换简介</A></LI>
<LI><A HREF="#I425">同步机制</A></LI></OL></OL>
<hr><br><A NAME="I407" ID="I407"></A><center><b><font size=+2>进程</font></b></center><br>
一&nbsp; 进程调度<p>
&nbsp;&nbsp;&nbsp; 进程的状态([include/linux.h]):<p>
TASK_RUNNING, it means that it is in the &quot;Ready List&quot;<br>
TASK_INTERRUPTIBLE, task waiting for a signal or a resource (sleeping)<br>
TASK_UNINTERRUPTIBLE, task waiting for a resource (sleeping), it is in same &quot;Wait Queue&quot;<br>
TASK_ZOMBIE, task child without father<br>
TASK_STOPPED, task being debugged<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ______________&nbsp;&nbsp;&nbsp;&nbsp; CPU Available&nbsp;&nbsp;&nbsp;&nbsp; ______________<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; ----------------&gt;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | TASK_RUNNING |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Real Running |<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |______________|&nbsp; &lt;----------------&nbsp; |______________|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CPU Busy<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp; /|\<br>
Waiting for |&nbsp;&nbsp;&nbsp; | Resource<br>
 Resource&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp; | Available<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \|/&nbsp;&nbsp; |<br>
&nbsp;&nbsp;&nbsp; ______________________<br>
&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br>
&nbsp;&nbsp; | TASK_INTERRUPTIBLE / |<br>
&nbsp;&nbsp; | TASK-UNINTERRUPTIBLE |<br>
&nbsp;&nbsp; |______________________|<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Main Multitasking Flow<p>
&nbsp;&nbsp;&nbsp; 从系统内核的角度看来,一个进程仅仅是进程控制表(process table)中的一项。进程控制表中的每一项都是一个task_struct 结构,而task_struct 结构本身是在include/linux/sched.h中定义的。在task_struct结构中存储各种低级和高级的信息,包括从一些硬件设备的寄存器拷贝到进程的工作目录的链接点。<p>
&nbsp;&nbsp;&nbsp; 进程控制表既是一个数组,又是一个双向链表,同时又是一个树。其物理实现是一个包括多个指针的静态数组。此数组的长度保存在include/linux/tasks.h 定义的常量NR_TASKS中,其缺省值为128,数组中的结构则保存在系统预留的内存页中。链表是由next_task 和prev_task两个指针实现的,而树的实现则比较复杂。<p>
&nbsp;&nbsp;&nbsp; 系统启动后,内核通常作为某一个进程的代表。一个指向task_struct的全局指针变量current用来记录正在运行的进程。变量current只能由kernel/sched.c中的进程调度改变。当系统需要查看所有的进程时,则调用for_each_task,这将比系统搜索数组的速度要快得多。<p>
二、用户进程和内核线程<p>
&nbsp;&nbsp;&nbsp; 某一个进程只能运行在用户方式(user mode)或内核方式(kernel mode)下。用户程序运行在用户方式下,而系统调用运行在内核方式下。在这两种方式下所用的堆栈不一样:用户方式下用的是一般的堆栈,而内核方式下用的是固定大小的堆栈(一般为一个内存页的大小)<p>
&nbsp;&nbsp;&nbsp; 尽管linux是一个宏内核系统,内核线程依然存在,以便并行地处理一些内核的“家务室”。这些任务不占用USER memory(用户空间),而仅仅使用KERNEL memory。和其他内核模块一样,它们也在高级权限(i386系统中的RING 0)下工作作。内核线程是被kernel_thread [arch/i386/kernel/process]创建的,它又通过调用著名的clone系统调用[arch/i386/kernel/process.c] (类似fork系统调用的所有功能都是由它最终实现):<p>
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long retval, d0;<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __asm__ __volatile__(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;movl %%esp,%%esi\n\t&quot;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;int $0x80\n\t&quot;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Linux/i386 system call */<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;cmpl %%esp,%%esi\n\t&quot;&nbsp; /* child or parent? */<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;je 1f\n\t&quot;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* parent - jump */<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Load the argument into eax, and push it.&nbsp; That way, it does<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * not matter whether the called function is compiled with<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * -mregparm or not.&nbsp; */<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;movl %4,%%eax\n\t&quot;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;pushl %%eax\n\t&quot;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;call *%5\n\t&quot;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* call fn */<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;movl %3,%0\n\t&quot;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* exit */<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;int $0x80\n&quot;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;1:\t&quot;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :&quot;=&amp;a&quot; (retval), &quot;=&amp;S&quot; (d0)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :&quot;0&quot; (__NR_clone), &quot;i&quot; (__NR_exit),<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;r&quot; (arg), &quot;r&quot; (fn),<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;b&quot; (flags | CLONE_VM)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : &quot;memory&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return retval;<br>
}<p>
&nbsp;&nbsp;&nbsp; 一旦调用,我们就有了一个新的任务(Task) (一般PID都很小, 例如2,3,等) 等待一个响应很慢的资源,例如swap或者usb事件,以便同步。下面是一些最常用的内核线程(你可以用ps x命令):<p>
PID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; COMMAND<br>
 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; init<br>
 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; keventd<br>
 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kswapd<br>
 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kreclaimd<br>
 5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bdflush<br>
 6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kupdated<br>
 7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kacpid<br>
67&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; khubd<p>
&nbsp;&nbsp;&nbsp;&nbsp; init内核线程也是启动以后最初的进程。 它会调用其它用户模式的任务,(/etc/inittab)例如控制台守护进程(daemons), tty守护进程以及网络守护进程(rc脚本)。<p>
下面是一个典型的内核线程kswapd [mm/vmscan.c].<br>
kswapd是被clone()建立的 [arch/i386/kernel/process.c]''<p>
|do_initcalls<br>
&nbsp;&nbsp; |kswapd_init<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |kernel_thread<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |syscall fork (in assembler)<p>
&middot;do_initcalls [init/main.c]<br>
&middot;kswapd_init [mm/vmscan.c]<br>
&middot;kernel_thread [arch/i386/kernel/process.c]<p>
三 进程创建,运行和消失<p>
&nbsp;&nbsp;&nbsp; Linux系统使用系统调用fork( )来创建一个进程,使用exit( )来结束进程。fork( )和exit( )的源程序保存在kernel/fork.c and kernel/exit.c中。fork( )的主要任务是初始化要创建进程的数据结构,其主要的步骤有:<p>
1)申请一个空闲的页面来保存task_struct。<br>
2)查找一个空的进程槽(find_empty_process( ))。<br>
3)为kernel_stack_page申请另一个空闲的内存页作为堆栈。<br>
4)将父进程的LDT表拷贝给子进程。<br>
5)复制父进程的内存映射信息。<br>
6)管理文件描述符和链接点。<p>
|sys_fork<br>
&nbsp;&nbsp; |do_fork<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |alloc_task_struct<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |__get_free_pages<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |p-&gt;state = TASK_UNINTERRUPTIBLE<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |copy_flags<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |p-&gt;pid = get_pid<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |copy_files<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |copy_fs<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |copy_sighand<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |copy_mm // should manage CopyOnWrite (I part)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |allocate_mm<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |mm_init<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |pgd_alloc -&gt; get_pgd_fast<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |get_pgd_slow<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |dup_mmap<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |copy_page_range<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |ptep_set_wrprotect<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |clear_bit // set page to read-only<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |copy_segments // For LDT<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |copy_thread<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |childregs-&gt;eax = 0<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |p-&gt;thread.esp = childregs // child fork returns 0<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |p-&gt;thread.eip = ret_from_fork // child starts from fork exit<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |retval = p-&gt;pid // parent fork returns child pid<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |SET_LINKS // insertion of task into the list pointers<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |nr_threads++ // Global variable<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |wake_up_process(p) // Now we can wake up just created child<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |return retval<p>
&middot;sys_fork [arch/i386/kernel/process.c]<br>
&middot;do_fork [kernel/fork.c]<br>
&middot;alloc_task_struct [include/asm/processor.c]<br>
&middot;__get_free_pages [mm/page_alloc.c]<br>
&middot;get_pid [kernel/fork.c]<br>
&middot;copy_files<br>
&middot;copy_fs<br>
&middot;copy_sighand<br>
&middot;copy_mm<br>
&middot;allocate_mm<br>
&middot;mm_init<br>
&middot;pgd_alloc -&gt; get_pgd_fast [include/asm/pgalloc.h]<br>
&middot;get_pgd_slow<br>
&middot;dup_mmap [kernel/fork.c]<br>
&middot;copy_page_range [mm/memory.c]<br>
&middot;ptep_set_wrprotect [include/asm/pgtable.h]<br>
&middot;clear_bit [include/asm/bitops.h]<br>
&middot;copy_segments [arch/i386/kernel/process.c]<br>
&middot;copy_thread<br>
&middot;SET_LINKS [include/linux/sched.h]<br>
&middot;wake_up_process [kernel/sched.c]<p>
&nbsp;&nbsp;&nbsp; 撤消一个进程可能稍微复杂些,因为撤消子进程必须通知父进程。另外,使用kill( )也可以结束一个进程。sys_kill( )、sys_wait( )和sys_exit( )都保存在文件exit.c中。<p>
&nbsp;&nbsp;&nbsp; 使用fork ( )创建一个进程后,程序的两个拷贝都在运行。通常一个拷贝使用exec ( )调用另一个拷贝。系统调用exec ( )负责定位可执行文件的二进制代码,并负责装入和运行。Linux系统中的exec ( )通过使用linux_binfmt结构支持多种二进制格式。每种二进制格式都代表可执行代码和链接库。linux _binfmt结构种包含两个指针,一个指向装入可执行代码的函数,另一个指向装入链接库的函数。<p>
&nbsp;&nbsp;&nbsp; Unix系统提供给程序员6种调用exec( ) 的方法。其中的5种是作为库函数实现,而sys_execve( )是由系统内核实现的。它执行一个十分简单的任务:装入可执行文件的文件头,并试图执行它。如果文件的头两个字节是#! ,那么它就调用在文件第一行中所指定的解释器,否则,它将逐个尝试注册的二进制格式。


<center><A HREF="#Content">[目录]</A></center>
<hr><br><A NAME="I408" ID="I408"></A><center><b><font size=+2>信号</font></b></center><br>

struct semaphore {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; atomic_t count; 进程抓取semaphore时减1<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int sleepers; 抓取semaphore失败时增1<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait_queue_head_t wait; semaphore的等待队列<br>
};<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; down(&amp;sem) 编绎成:<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; movl $sem,% ecx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通过寄存器ecx向__down函数传递sem指针<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; decl sem<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; js 2f 如果为负值,表示semaphore已被占用,执行__down_failed过程<br>
1:<br>
由于出现semaphore竞争的可能性比较小,将分支代码转移到.text.lock段,以缩短正常的指令路径.<br>
.section .text.lock,&quot;ax&quot;<br>
2:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; call __down_failed<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jmp 1b<br>
.previous<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; up(&amp;sem) 编绎成:<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; movl $sem,% ecx<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; incl sem<br>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -