免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 9183 | 回复: 1
打印 上一主题 下一主题

[FreeBSD] freebsd9.2-系统调用机制-实现 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2014-07-09 01:26 |只看该作者 |倒序浏览
假设:
1:当前正在运行的进程为p1,其对应的struct proc对象简记为p1_proc,相关联的线程为th1,其对应的struct thread对象简记为th1_thread。

2:cpu已经完成了调用系统调用异常处理程序前的硬件处理,此时将开始执行系统调用异常处理程序。

3:执行的是rfork系统调用,系统调用号为251,十六进制为0xfb;该系统调用只有一个参数flags,这样可以看一下参数是如何传递的。

4:p1_proc的p_sysent成员指向elf32_freebsd_sysvec对象。

先来一张线程th1_thread内核栈和用户栈的结构图,下面的分析结合图会更清楚:



[函数IDTVEC(int0x80_syscall)]-系统调用异常处理程序:
  1. /***********************************************************************************************
  2. * Trap gate entry for syscalls (int 0x80).
  3. * This is used by FreeBSD ELF executables, "new" NetBSD executables, and all
  4. * Linux executables.
  5. *
  6. * Even though the name says 'int0x80', this is actually a trap gate, not an
  7. * interrupt gate.  Thus interrupts are enabled on entry just as they are for
  8. * a normal syscall.

  9.    这里的宏IDTVEC展开后为Xint0x80_syscall

  10.    260:
  11.    当执行指令"int 0x80"时,返回地址指向紧接该指令后面那条指令,返回地址由保存在线程th1_thread
  12.    内核栈上的CS和EIP寄存器来描述。
  13.    这里将数值常量2(指令"int 0x80"的字节数)压入线程th1_thread的内核栈,就可以通过struct trapframe
  14.    对象的tf_err成员访问这个数值常量,在需要重新执行系统调用时,只要将保存在线程th1_thread内核栈上
  15.    的EIP寄存器的值减去2,系统调用返回后就会重新执行指令"int 0x80",这样就能重新执行系统调用。
  16.    见后面cpu_set_syscall_retval函数中的描述。

  17.    pushal指令:
  18.    Pushes the contents of the general-purpose registers onto the stack. The registers
  19.    are stored on the stack in the following order: EAX, ECX, EDX, EBX, ESP (original
  20.    value), EBP, ESI, and EDI (if the current operand-size attribute is 32)。

  21.    266:SET_KERNEL_SREGS宏见下面的描述。

  22.    267:
  23.    清DF标志,string operations increment the index registers (ESI and/or EDI).

  24.    270:调用函数syscall。

  25. ***************************************/
  26.    258                SUPERALIGN_TEXT
  27.    259        IDTVEC(int0x80_syscall)
  28.    260                pushl        $2                        /* sizeof "int 0x80" */
  29.    261                subl        $4,%esp                        /* skip over tf_trapno */
  30.    262                pushal
  31.    263                pushl        %ds
  32.    264                pushl        %es
  33.    265                pushl        %fs
  34.    266                SET_KERNEL_SREGS
  35.    267                cld
  36.    268                FAKE_MCOUNT(TF_EIP(%esp))
  37.    269                pushl        %esp
  38.    270                call        syscall
  39.    271                add        $4, %esp
  40.    272                MEXITCOUNT
  41.    273                jmp        doreti
复制代码
[宏SET_KERNEL_SREGS]:
  1. /**********************************************************************************
  2. * Setup the kernel segment registers.
  3.    此时处理器x运行在内核态,段寄存器也要更改。
  4.    
  5.    173-175:
  6.    用内核数据段的segment selector,即KDSEL加载DS,ES两个寄存器,访问内核数据。

  7.    176-177:
  8.    用KPSEL加载FS寄存器,这样就能访问数据对象__pcpu[x],也是宏PCPU_*的基础。   
  9. ******************************************/
  10.    172        #define        SET_KERNEL_SREGS                                                \
  11.    173                movl        $KDSEL, %eax ;        /* reload with kernel's data segment */        \
  12.    174                movl        %eax, %ds ;                                                \
  13.    175                movl        %eax, %es ;                                                \
  14.    176                movl        $KPSEL, %eax ;        /* reload with per-CPU data segment */        \
  15.    177                movl        %eax, %fs
复制代码
[函数syscall]:
  1. /******************************************************************************
  2. * syscall - system call request C handler.  A system call is
  3. * essentially treated as a trap by reusing the frame layout.

  4.    参数描述:

  5.    frame:
  6.    从上面线程th1_thread的内核栈结构可以明显看出,调用syscall函数时,传递
  7.    的是一个指向struct trapframe对象的指针,而不是整个struct trapframe结构体。
  8. ********************************************/
  9.   1140        void
  10.   1141        syscall(struct trapframe *frame)
  11.   1142        {
  12. /***************************************************************************
  13. * 局部变量描述:
  14.    td:指向描述当前运行线程的struct thread对象。

  15.    sa:一个类型struct syscall_args的对象,用来保存系统调用的参数等信息。

  16.    orig_tf_eflags:用来临时保存线程th1_thread内核栈上的EFLAGS寄存器的值。

  17.    error:保存系统调用的返回值。

  18.    ksi:信号相关,暂时忽略。
  19. *******************************************/
  20.   1143                struct thread *td;
  21.   1144                struct syscall_args sa;
  22.   1145                register_t orig_tf_eflags;
  23.   1146                int error;
  24.   1147                ksiginfo_t ksi;
  25.   1148       
  26. /********************************************************************************
  27. * 1149-1154:
  28.    编译内核时选择了DIAGNOSTIC这个选项时,才执行额外的检查。
  29.    
  30.    #define      SEL_UPL         3
  31.    #define        ISPL(s)        ((s)&3)        what is the priority level of a selector

  32.    这里将检查执行"int 0x80"指令陷入内核时cpu是否运行在用户态。
  33.    这是通过检查保存在线程th1_thread内核栈上的CS寄存器的值来实现的,根据之前的
  34.    描述,CS寄存器保存的是segment selector,而segment selector中RPL字段的值
  35.    标识了cpu运行在用户态还是内核态,如果RPL字段的值为3,就表示cpu运行在
  36.    用户态。

  37.    从这里可以看出,只有在cpu运行在用户态时才能触发系统调用异常。  
  38. **************************************/
  39.   1149        #ifdef DIAGNOSTIC
  40.   1150                if (ISPL(frame->tf_cs) != SEL_UPL) {
  41.   1151                        panic("syscall");
  42.   1152                        /* NOT REACHED */
  43.   1153                }
  44.   1154        #endif
  45.   1155                orig_tf_eflags = frame->tf_eflags;
  46.   1156       
  47. /*******************************************************************************
  48. * 1157:
  49.    这里td的值为&th1_thread。
  50.    
  51.    1158:
  52.    th1_thread的td_frame用来访问保存在线程th1_thread内核栈上的用户态硬件上下文,
  53.    即通过类型为struct trapframe的对象来描述。
  54.    和前面描述"处理器J间中断-接收处理"中对比一下,是通过td_intr_frame这个成员访问
  55.    struct trapframe对象的。
  56.    从这里可以看出,貌似这两个成员在不同的场合中使用。

  57.    1160:
  58.    调用syscallenter函数,这个函数任务有点杂,见下面的描述。

  59.    1162-1172:
  60.    single-step mode for debugging, 暂时将其忽略。
  61. *****************************************************/
  62.   1157                td = curthread;
  63.   1158                td->td_frame = frame;
  64.   1159       
  65.   1160                error = syscallenter(td, &sa);
  66.   1161       
  67.   1162                /*
  68.   1163                 * Traced syscall.
  69.   1164                 */
  70.   1165                if ((orig_tf_eflags & PSL_T) && !(orig_tf_eflags & PSL_VM)) {
  71.   1166                        frame->tf_eflags &= ~PSL_T;
  72.   1167                        ksiginfo_init_trap(&ksi);
  73.   1168                        ksi.ksi_signo = SIGTRAP;
  74.   1169                        ksi.ksi_code = TRAP_TRACE;
  75.   1170                        ksi.ksi_addr = (void *)frame->tf_eip;
  76.   1171                        trapsignal(td, &ksi);
  77.   1172                }
  78.   1173       
  79.   1174                KASSERT(PCB_USER_FPU(td->td_pcb),
  80.   1175                    ("System call %s returning with kernel FPU ctx leaked",
  81.   1176                     syscallname(td->td_proc, sa.code)));
  82.   1177                KASSERT(td->td_pcb->pcb_save == &td->td_pcb->pcb_user_save,
  83.   1178                    ("System call %s returning with mangled pcb_save",
  84.   1179                     syscallname(td->td_proc, sa.code)));
  85.   1180       
  86. /*********************************************************************************
  87. * 1181:
  88.    函数syscallret完成的工作的还是挺多的,比如重新设置线程th1_thread的优先级,
  89.    当线程th1_thread是通过vfork创建的,就要进行相应的处理,进程跟踪相关处理等等。
  90.    这里暂时将其忽略。
  91.    现在系统调用已经执行完,主要的工作也就完成,这个函数留作以后再分析。
  92. ******************************/
  93.   1181                syscallret(td, error, &sa);
  94.   1182        }
复制代码
[函数syscallenter]:
  1. /******************************************************************************
  2. * 参数描述:
  3.    td:这里为&th1_thread。
  4.    sa:指向一个类型为struct syscall_args的对象,保存系统调用的参数。

  5.    这里略去了进程跟踪等相关的代码,重点看一下系统调用处理相关的代码。
  6.    完整的函数可以在"$FreeBSD: release/9.2.0/sys/kern/subr_syscall.c"中找到。
  7. **************************************/
  8.     55        static inline int
  9.     56        syscallenter(struct thread *td, struct syscall_args *sa)
  10.     57        {
  11.     58                struct proc *p;
  12.     59                int error, traced;
  13.     60                
  14. /*********************************************************************************
  15. * 61:递增相关的计数器。
  16.    cnt是内核定义的一个类型为struct vmmeter的对象,该对象的成员相当于一个计数器。
  17.    u_int v_syscall; calls to syscall() 即调用函数syscall的次数。

  18.    62:p为&p1_proc
  19. *****************************************/
  20.     61                PCPU_INC(cnt.v_syscall);
  21.     62                p = td->td_proc;
  22. .........................................................................................
  23. .........................................................................................
  24. /*********************************************************************************************************
  25. * 75:
  26.    这里将调用函数cpu_fetch_syscall_args获取系统调用参数,系统调用号等,并以系统调用号为索引,在数组sysent
  27.    中找到其对应的数组元素,见下面的简单分析。
  28.    从函数cpu_fetch_syscall_args的实现来看,在freebsd9.2中,系统调用号是通过
  29.    EAX寄存器传递的,而系统调用的参数是通过用户栈来传递的,这点和linux有点区别,
  30.    linux中系统调用的参数是通过寄存器来传递。

  31.    当cpu_fetch_syscall_args函数执行完后:
  32.    1->系统调用号超过最大限制时,忽略传递进来的系统调用号,此时sa指向的对象的成员被设置为:
  33.       sa->callp被设置为&sysent[0].
  34.       sa->narg被设置为sysent[0].sy_narg,这里的值为0。
  35.       sa->code被设置为系统调用号,此时该成员的值被忽略。
  36.       sa->args中保存了系统调用的参数。
  37.       
  38.       数组元素sysent[0]的各成员取值如下:
  39.       sysent[0].sy_narg = 0;       
  40.       sysent[0].sy_call= nosys;       
  41.       sysent[0].sy_auevent = AUE_NULL;       
  42.       sysent[0].sy_systrace_args_func = NULL;
  43.       sysent[0].sy_entry = 0;       
  44.       sysent[0].sy_return = 0;
  45.       sysent[0].sy_flags = 0;
  46.       sysent[0].sy_thrcnt = SY_THR_STATIC;


  47.    2->系统调用号在正常的大小范围内,这里执行的是系统调用rfork,其系统调用号是251,此时sa指向的对象的
  48.       成员被设置为:
  49.       sa->callp被设置为&sysent[251].
  50.       sa->narg被设置为sysent[251].sy_narg,这里的为AS(rfork_args),值为1,即1个4字节。
  51.       sa->code被设置为系统调用号251。
  52.       sa->args中保存了系统调用的参数,系统调用rfork只有一个参数flags。

  53.       数组元素sysent[251]的各成员取值如下:
  54.       sysent[251].sy_narg = AS(rfork_args);       
  55.       sysent[251].sy_call= sys_rfork;       
  56.       sysent[251].sy_auevent = AUE_RFORK;       
  57.       sysent[251].sy_systrace_args_func = NULL;
  58.       sysent[251].sy_entry = 0;       
  59.       sysent[251].sy_return = 0;
  60.       sysent[251].sy_flags = 0;
  61.       sysent[251].sy_thrcnt = SY_THR_STATIC;
  62.       
  63. ****************************************/
  64.     75                error = (p->p_sysent->sv_fetch_syscall_args)(td, sa);
  65. .........................................................................................
  66. .........................................................................................
  67.     86                if (error == 0) {
  68. .........................................................................................
  69. .........................................................................................
  70. /******************************************************************************************************
  71. * 119:
  72.    如果通过模块向内核注册了一个系统调用,那么描述该系统调用的struct sysent对象的sy_thrcnt
  73.    成员将被设置为0,从函数syscall_thread_enter的实现来看,该函数只对这种系统调用感兴趣,此时
  74.    sy_thrcnt成员相当于一个计数器,每次执行相应的系统调用时,函数syscall_thread_enter都给其递增
  75.    一个常量SY_THR_INCR。

  76.    135:
  77.    这里将调用内核中实现相应系统调用的内核函数。

  78.    情况1->系统调用号超过最大限制时,从上面可以看出,此时将调用函数nosys,该函数将向线程th1_thread
  79.           发送一个信号SIGSYS,该信号的默认操作是杀死线程th1_thread。
  80.           #define SIGSYS          12      non-existent system call invoked

  81.    情况2->系统调用号在正常的大小范围内,这里将调用sys_rfork函数,此时控制将传递给内核中实现
  82.           rfork系统调用的内核函数。

  83.    从这里还以看出,不管实现系统调用的内核函数是否需要参数,都将sa->args作为参数传递给相应的内核函数,
  84.    因为相应的内核函数很清楚自己是否需要参数,如果需要,此时sa->args中已经包含了所需要的参数;
  85.    如果不需要,sa->args中也没有包含参数,相应的内核函数也不会使用sa->args这个参数。

  86.    152:
  87.    和syscall_thread_enter函数的操作相反,这里将执行递减操作。
  88. ************************************************************/
  89.    119                        error = syscall_thread_enter(td, sa->callp);
  90.    120                        if (error != 0)
  91.    121                                goto retval;
  92. ........................................................................................
  93. ........................................................................................
  94.    135                        error = (sa->callp->sy_call)(td, sa->args);
  95. ........................................................................................
  96. ........................................................................................
  97.    152                        syscall_thread_exit(td, sa->callp);
  98.    153                        CTR4(KTR_SYSC, "syscall: p=%p error=%d return %#lx %#lx",
  99.    154                            p, error, td->td_retval[0], td->td_retval[1]);
  100.    155                }
  101.    156         retval:
  102. ........................................................................................
  103. ........................................................................................
  104. /**************************************************************************
  105. * 162: 对返回值进行处理。
  106.    这里将调用cpu_set_syscall_retval函数。
  107. ****************************/
  108.    162                (p->p_sysent->sv_set_syscall_retval)(td, error);
  109.    163                return (error);
  110.    164        }
复制代码
[函数cpu_fetch_syscall_args]-获取系统调用的参数等,可以结合上面的图一起看:
  1. /***************************************************************
  2. * 参数描述:
  3.    td:&th1_thread。
  4.    sa:指向类型为struct syscall_args的数据对象。  
  5. ***************************************************/
  6.   1081        cpu_fetch_syscall_args(struct thread *td, struct syscall_args *sa)
  7.   1082        {
  8.   1083                struct proc *p;
  9.   1084                struct trapframe *frame;
  10.   1085                caddr_t params;
  11.   1086                int error;
  12.   1087                
  13. /**********************************************************************
  14. * 1088:这里p为&p1_proc。

  15.    1089:通过变量frame访问保存在线程th1_thread内核栈上的用户态
  16.          硬件上下文。
  17. ***********************************/
  18.   1088                p = td->td_proc;
  19.   1089                frame = td->td_frame;
  20.   1090                
  21. /**********************************************************************
  22. * 1091:tf_esp指向用户栈的栈顶,这里加上sizeof(int)是要跳过
  23.          保存在用户栈上的返回地址,所以此时params指向的位置保存
  24.          了传递给系统调用的参数。

  25.    1092:系统调用号一般通过EAX寄存器来传递,所以这里的sa->code
  26.          保存的就是系统调用号。
  27. *******************************************/
  28.   1091                params = (caddr_t)frame->tf_esp + sizeof(int);
  29.   1092                sa->code = frame->tf_eax;
  30.   1093       
  31. /******************************************************************************
  32. * 1097-1110:对系统调用号进行检查。
  33. * Need to check if this is a 32 bit or 64 bit syscall.
  34.    #define        SYS_syscall        0
  35.    #define        SYS___syscall        198

  36.    1097-1103:
  37.    如果通过eax寄存器传递进来的系统调用号为SYS_syscall,那么保存在用户
  38.    栈上的第一个参数为系统调用号,此时重新设置sa->code,并更新变量
  39.    params,使其指向正确的位置
  40.                   
  41.    1103-1110:同上,貌似这里的系统调用号大小为64bit。

  42.    至于为什么要进行这个检查,估计和函数库中的封装有关系,这块大家
  43.    了解的话,麻烦及时联系一下。不过一般情况下,通过EAX寄存器传递的
  44.    是标准的系统调用号,不会执行1097-1110之间的代码。

  45.    这里看一下freebsd函数库中是怎么调用rfork系统调用的:
  46.    00057764 <__sys_rfork>:
  47.       57764:        b8 fb 00 00 00               mov    $0xfb,%eax
  48.       57769:        cd 80                        int    $0x80
  49.       5776b:        72 e3                        jb     57750 <__sys_lchown+0xc>
  50.    上面的mov指令,0xfb就是系统调用rfork的系统调用号,值为251,可以看出EAX
  51.    传递的是标准的系统调用号。
  52. ***********************************************/
  53.   1096               
  54.   1097                if (sa->code == SYS_syscall) {
  55.   1098                        /*
  56.   1099                         * Code is first argument, followed by actual args.
  57.   1100                         */
  58.   1101                        sa->code = fuword(params);
  59.   1102                        params += sizeof(int);
  60.   1103                } else if (sa->code == SYS___syscall) {
  61.   1104                        /*
  62.   1105                         * Like syscall, but code is a quad, so as to maintain
  63.   1106                         * quad alignment for the rest of the arguments.
  64.   1107                         */
  65.   1108                        sa->code = fuword(params);
  66.   1109                        params += sizeof(quad_t);
  67.   1110                }
  68.   1111                
  69. /**********************************************************************************
  70. * 执行到这里的话,sa->code保存的都是有意义的系统调用号。
  71. * 1112-1113:
  72.    elf32_freebsd_sysvec对象的sv_mask成员为0,这里不会进行处理。

  73.    1114-1117:检查系统调用号是否在指定的范围内。
  74.    #define        SYS_MAXSYSCALL        533
  75.    elf32_freebsd_sysvec对象的sv_size成员为SYS_MAXSYSCALL

  76.    1114-1116:系统调用号超过最大限制,这时将忽略传递进来的系统调用号。
  77.    sa->callp将被设置为&sysent[0]。
  78.    
  79.    1116-1117:系统调用号没有超过最大限制。
  80.    sa->callp将被设置为&sysent[sa->code]。

  81.    1118:sa->narg被设置为系统调用实际需要的参数的数目。

  82.    1120-1124:
  83.    struct sysent对象的sy_narg成员明确指定了相应的系统调用需要的参数的数目,
  84.    所以这里根据需要将传递进来的系统调用参数copy到sa->args中。
  85. ***************************************************/
  86.   1112                 if (p->p_sysent->sv_mask)
  87.   1113                         sa->code &= p->p_sysent->sv_mask;
  88.   1114                 if (sa->code >= p->p_sysent->sv_size)
  89.   1115                         sa->callp = &p->p_sysent->sv_table[0];
  90.   1116                  else
  91.   1117                         sa->callp = &p->p_sysent->sv_table[sa->code];
  92.   1118                sa->narg = sa->callp->sy_narg;
  93.   1119                
  94.   1120                if (params != NULL && sa->narg != 0)
  95.   1121                        error = copyin(params, (caddr_t)sa->args,
  96.   1122                            (u_int)(sa->narg * sizeof(int)));
  97.   1123                else
  98.   1124                        error = 0;
  99.   1125                
  100. /***************************************************
  101. * register_t        td_retval[2]; Syscall aux returns.
  102.                
  103.    这里设置的返回值暂时可以忽略。
  104. ******************************/
  105.   1126                if (error == 0) {
  106.   1127                        td->td_retval[0] = 0;
  107.   1128                        td->td_retval[1] = frame->tf_edx;
  108.   1129                }
  109.   1130                       
  110.   1131                return (error);
  111.   1132        }
复制代码
[函数cpu_set_syscall_retval]- 处理返回值,一般情况下,EAX寄存器包含的是返回值:
  1.    391        void
  2.    392        cpu_set_syscall_retval(struct thread *td, int error)
  3.    393        {
  4.    394       
  5.    395                switch (error) {
  6. /**********************************************************************
  7. * 396-400:
  8.    系统调用成功执行,error为0。
  9.    此时td_retval[0],td_retval[1]已经被相应的内核函数设置为有意义的值。
  10.    清除EFLAGS寄存器中的进位标志,表示没有错误发生。
  11. ***************/
  12.    396                case 0:
  13.    397                        td->td_frame->tf_eax = td->td_retval[0];
  14.    398                        td->td_frame->tf_edx = td->td_retval[1];
  15.    399                        td->td_frame->tf_eflags &= ~PSL_C;
  16.    400                        break;
  17.    401                
  18. /**********************************************************************************
  19. * 402-408:
  20.    系统调用被中断,但是内核返回错误码ERESTART:
  21.    #define        ERESTART  (-1)         restart syscall

  22.    将保存在线程th1_thread内核栈上的EIP寄存器的值减去2,在系统调用异常返回后,
  23.    EIP寄存器的值为指令"int 0x80"的地址,此时将重新执行系统调用。  
  24. **************************************/
  25.    402                case ERESTART:
  26.    403                        /*
  27.    404                         * Reconstruct pc, assuming lcall $X,y is 7 bytes, int
  28.    405                         * 0x80 is 2 bytes. We saved this in tf_err.
  29.    406                         */
  30.    407                        td->td_frame->tf_eip -= td->td_frame->tf_err;
  31.    408                        break;
  32.    409                
  33. /**************************************************************
  34. * 410-411:
  35. * #define  EJUSTRETURN (-2) don't modify regs, just return
  36.    不做任何处理。            
  37. ************************/
  38.    410                case EJUSTRETURN:
  39.    411                        break;
  40.    412                
  41. /****************************************************************
  42. * 413-422:
  43.    由于某种原因,系统调用执行失败,error包含了相应的错误码。

  44.    414-419:不做处理
  45.    elf32_freebsd_sysvec对象的sv_errsize成员为0。
  46.    
  47.    420:
  48.    将系统调用执行失败的的错误码通过eax寄存器返回给应用程序。
  49.    注意,如果没有函数库或者封装例程的介入,此时系统调用的返回值
  50.    就是执行失败的错误码,而不是-1;返回-1是因为函数库或者封装例程
  51.    做了额外的处理,这个额外的处理就是errno变量的引入。
  52.    
  53.    421:
  54.    设置EFLAGS寄存器的进位标志,表示系统调用执行失败。
  55. *******************************/                       
  56.    413                default:
  57.    414                        if (td->td_proc->p_sysent->sv_errsize) {
  58.    415                                if (error >= td->td_proc->p_sysent->sv_errsize)
  59.    416                                        error = -1;        /* XXX */
  60.    417                                else
  61.    418                                        error = td->td_proc->p_sysent->sv_errtbl[error];
  62.    419                        }
  63.    420                        td->td_frame->tf_eax = error;
  64.    421                        td->td_frame->tf_eflags |= PSL_C;
  65.    422                        break;
  66.    423                }
  67.    424        }
复制代码

论坛徽章:
0
2 [报告]
发表于 2014-08-19 20:42 |只看该作者
**************************************/
  1149        #ifdef DIAGNOSTIC
  1150                if (ISPL(frame->tf_cs) != SEL_UPL) {
  1151                        panic("syscall");
  1152                        /* NOT REACHED */
  1153                }
  1154        #endif
  1155                orig_tf_eflags = frame->tf_eflags;
  1156        
/*******************************************************************************
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP