现在的位置: 首页 > 综合 > 正文

Linux平台调用ACE_Task::putq分析(timeout=0,队列满)

2013年06月11日 ⁄ 综合 ⁄ 共 10677字 ⁄ 字号 评论关闭

本博客(http://blog.csdn.net/livelylittlefish )贴出作者(三二一@小鱼)相关研究、学习内容所做的笔记,欢迎广大朋友指正!

analysis of ACE_Task::putq with timeout=0 when queue is full on Linux platform

Linux平台调用ACE_Task::putq分析(timeout=0,队列满)

 

content

0. Introduction

1. ACE_Task::putq introduction

2. call stack

3. call stack analysis

3.1 block analysis

3.2 system call analysis

4. analysis of ACE_Task::putq

4.1 the call process of ACE_Task::putq

4.2 each function analysis on the call path

(1) ACE_Task<ACE_SYNCH_USE>::putq

(2) ACE_Message_Queue<ACE_SYNCH_USE>::enqueue_tail

(3) ACE_Message_Queue<ACE_MT_SYNCH>::wait_not_full_cond

(4) ACE_Condition_Thread_Mutex::wait(const ACE_Time_Value *abstime)

(5) ACE_Condition_Thread_Mutex::wait(ACE_Thread_Mutex &mutex, const ACE_Time_Value *abstime)

(6) ACE_OS::cond_timedwait

(7) __pthread_cond_wait

(8) lll_futex_wait, lll_futex_timed_wait

(9) sys_futex

(10) do_futex

(11) futex_wait

(12) schedule_timeout

(13) schedule

5. conclusion

 

 

0. Introduction

 

In this article, we will discuss most actions of calling ACE_Task::putq with timeout=0. It includes ACE, glibc and Linux kernel source analysis, such as ACE_Task::putq, ACE_Message_Queue::enqueue_tail, pthread_cond_wait, sys_futex and schedule.

 

1. ACE_Task::putq introduction

 

ACE_Task::putq will put a message into the message queue. It has two parameters, and the second is timeout with default value NULL, as follows. It is declared in file ace/Task_T.h as below.

 

  // For the following five method if @a timeout == 0, the caller will

  // block until action is possible, else will wait until the

  // <{absolute}> time specified in *@a timeout elapses).  These calls

  // will return, however, when queue is closed, deactivated, when a

  // signal occurs, or if the time specified in timeout elapses, (in

  // which case errno = EWOULDBLOCK).

 

  /// Insert message into the message queue.  Note that @a timeout uses

  /// <{absolute}> time rather than <{relative}> time.

  int putq (ACE_Message_Block *, ACE_Time_Value *timeout = 0);

 

from the comment, we can see that if timeout=0 and the queue is full, the caller will be blocked until action is possible, such as signal. Then it will NEVER return back until you kill the process.

 

2. call stack

 

When timeout=NULL, in the test, we will put many messages to fill the queue to the full, then it will be blocked and NEVER return. The call stack of it is as follows. We can see that it is blocked in __kernel_vsyscall at 0x00110402.

 

l   pstack result

 

#0  0x00110402 in __kernel_vsyscall ()

#1  0x007065d5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib/libpthread.so.0

#2  0x008aa7ac in ACE_Condition_Thread_Mutex::wait ()

#3  0x008aa7eb in ACE_Condition_Thread_Mutex::wait ()

#4  0x0804b132 in ACE_Message_Queue<ACE_MT_SYNCH>::wait_not_full_cond ()

#5  0x0804d132 in ACE_Message_Queue<ACE_MT_SYNCH>::enqueue_tail ()

#6  0x0804e15a in ACE_Task<ACE_MT_SYNCH>::putq ()

...

 

l   gdb bt result

 

(gdb) bt

#0  0x00110402 in __kernel_vsyscall ()

#1  0x007065d5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib/libpthread.so.0

#2  0x008aa7ac in ACE_Condition_Thread_Mutex::wait () from /usr/lib/libACE.so.5.6.2

#3  0x008aa7eb in ACE_Condition_Thread_Mutex::wait () from /usr/lib/libACE.so.5.6.2

#4  0x0804b132 in ACE_Message_Queue<ACE_MT_SYNCH>::wait_not_full_cond (this=0x9d425f8, timeout=0x0)

    at /usr/include/ace/Message_Queue_T.cpp:1588

#5  0x0804d132 in ACE_Message_Queue<ACE_MT_SYNCH>::enqueue_tail (this=0x9d425f8, new_item=0xb7404588, timeout=0x0)

    at /usr/include/ace/Message_Queue_T.cpp:1753

#6  0x0804e15a in ACE_Task<ACE_MT_SYNCH>::putq (this=0x9d42594, mb=0xb7404588, tv=0x0) at /usr/include/ace/Task_T.inl:36

...

 

3. call stack analysis

 

3.1 block analysis

 

Now, we disassemble the codes at 0x00110402 in gdb, like the following.

 

(gdb) disassemble 0x00110402

Dump of assembler code for function __kernel_vsyscall:

0x00110400 <__kernel_vsyscall+0>:       int    $0x80

0x00110402 <__kernel_vsyscall+2>:       ret    //it is blocked at ‘ret’, which is a return instruction

End of assembler dump.

 

or,

 

(gdb) x/i 0x00110402

0x110402 <__kernel_vsyscall+2>: ret            //it is blocked at ‘ret’, which is a return instruction

 

This return instruction is at <__kernel_vsyscall+2>, then, <__kernel_vsyscall> is at 0x00110400.

 

(gdb) p __kernel_vsyscall       //we can print this symbol

$1 = {<text variable, no debug info>} 0x110400 <__kernel_vsyscall>

 

(gdb) x/5i __kernel_vsyscall    //display 5 instructions from this symbol address

0x110400 <__kernel_vsyscall>:   int    $0x80

0x110402 <__kernel_vsyscall+2>: ret   

0x110403:       nop   

0x110404:       nop   

0x110405:       nop   

 

(gdb) x/5i 0x00110400           // display 5 instructions from this symbol address

0x110400 <__kernel_vsyscall>:   int    $0x80

0x110402 <__kernel_vsyscall+2>: ret   

0x110403:       nop   

0x110404:       nop   

0x110405:       nop

 

(gdb) disassemble 0x110400      //disassemble codes from this address

Dump of assembler code for function __kernel_vsyscall:

0x00110400 <__kernel_vsyscall+0>:       int    $0x80

0x00110402 <__kernel_vsyscall+2>:       ret   

End of assembler dump.

 

Besides this way, we can use ‘disassemble’ directly by default, then, it disassemble the codes surrounding the pc of the selected frame, as below. And this is the most direct way.

 

(gdb) help disassemble

Disassemble a specified section of memory.

Default is the function surrounding the pc of the selected frame.

With a single argument, the function surrounding that address is dumped.

Two arguments are taken as a range of memory to dump.

(gdb) disassemble

Dump of assembler code for function __kernel_vsyscall:

0x00110400 <__kernel_vsyscall+0>:       int    $0x80   //system call by ‘operating system trap’

0x00110402 <__kernel_vsyscall+2>:       ret   

End of assembler dump.

 

At last, from the disassemble code, we can see that this system call is done by interrupt with ‘operating system trap’ ‘int $0x80’.(通过操作系统陷入进行系统调用,对本例来讲,通过0x80软中断实现系统调用). The system call service handler sys_futex is declared as follows.

 

asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val, struct timespec __user *utime, u32 __user *uaddr2, u32 val3)

 

then, from registers of frame 0, we can determine some parameters of this system call like the following.

 

(gdb) frame 0  //enter frame 0

#0  0x00110402 in __kernel_vsyscall ()

(gdb) info registers

eax            0xfffffe00       -512

ecx            0x80     128                             //the second parameter op=128

edx            0x3      3

ebx            0x9d42678        164898424

esp            0xbfca1b1c       0xbfca1b1c

ebp            0xbfca1b68       0xbfca1b68

esi            0x0      0                               //the third parameter utime=NULL

edi            0x3      3

eip            0x110402 0x110402 <__kernel_vsyscall+2>  //this instruction of the address will be executed

eflags         0x200206 [ PF IF ID ]

cs             0x73     115

ss             0x7b     123

ds             0x7b     123

es             0x7b     123

fs             0x0      0

gs             0x33     51

 

How to know these parameters will be discussed below.

Note, each time you run this program, only %ebx, %esp, %ebp are different, other registers’ value are all the same.

 

3.2 system call analysis

 

Now, we disassemble code at 0x007065d5, that is, pthread_cond_wait@@GLIBC_2.3.2, as follows.

 

(gdb) disassemble 0x007065d5

Dump of assembler code for function pthread_cond_wait@@GLIBC_2.3.2:

...

0x007065ab <pthread_cond_wait@@GLIBC_2.3.2+107>:        call   0x709060 <__pthread_enable_asynccancel>

0x007065b0 <pthread_cond_wait@@GLIBC_2.3.2+112>:        mov    %eax,(%esp)      //%eax=0, %ebx=0x862c63c, %ecx=0

0x007065b3 <pthread_cond_wait@@GLIBC_2.3.2+115>:        cmpl   $0xffffffff,0x20(%ebx)

0x007065b7 <pthread_cond_wait@@GLIBC_2.3.2+119>:        sete   %cl

0x007065ba <pthread_cond_wait@@GLIBC_2.3.2+122>:        sub    $0x1,%ecx        //%ecx=-1, that is, 0xffffffff

0x007065bd <pthread_cond_wait@@GLIBC_2.3.2+125>:        and    %gs:0x20,%ecx    //%ecx=0x80, the soft interrupt

0x007065c4 <pthread_cond_wait@@GLIBC_2.3.2+132>:        mov    %edi,%edx        //%edi=1, then %edx=1

0x007065c6 <pthread_cond_wait@@GLIBC_2.3.2+134>:        add    $0x4,%ebx        //%ebx=0x862c640

0x007065c9 <pthread_cond_wait@@GLIBC_2.3.2+137>:        mov    $0xf0,%eax     //%eax=0xf0=240=SYS_futex, the system call number

0x007065ce <pthread_cond_wait@@GLIBC_2.3.2+142>:        call   *%gs:0x10      //jump to 0x00110400 ‘int $0x80’, use stepi to see it

0x007065d5 <pthread_cond_wait@@GLIBC_2.3.2+149>:        sub    $0x4,%ebx        //the return address of call the system-call-handler

0x007065d8 <pthread_cond_wait@@GLIBC_2.3.2+152>:        mov    (%esp),%eax

0x007065db <pthread_cond_wait@@GLIBC_2.3.2+155>:        call   0x709020 <__pthread_disable_asynccancel>

...

 

Contrast with the source code of glibc, we can find the system call is done at 0x007065ce. Wehre, %eax=0sf0=240 is the system call number.

 

      /* Enable asynchronous cancellation.  Required by the standard.  */

      cbuffer.oldtype = __pthread_enable_asynccancel ();

 

      /* Wait until woken by signal or broadcast.  */

      lll_futex_wait (&cond->__data.__futex, futex_val, pshared);

 

      /* Disable asynchronous cancellation.  */

      __pthread_disable_asynccancel (cbuffer.oldtype);

 

(gdb) frame 1

#1  0x007065d5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib/libpthread.so.0

(gdb) info registers

eax            0xfffffe00       -512

ecx            0x80     128

edx            0x1      1

ebx            0x93b7678        154891896

esp            0xbfd55c90       0xbfd55c90

ebp            0xbfd55cd8       0xbfd55cd8

esi            0x0      0

edi            0x1      1

eip            0x7065d5 0x7065d5 <pthread_cond_wait@@GLIBC_2.3.2+149>   //this instruction will be executed

eflags         0x200206 [ PF IF ID ]

cs             0x73     115

ss             0x7b     123

ds             0x7b     123

es             0x7b     123

fs             0x0      0

gs             0x33     51

 

if want to get all parameters passed to system call service handler (系统调用服务程序), it is necessary to arrive at the codes before call ‘int $0x80’. Then, set a breakpoint at 0x007065ce, and run to this breadpoint to watch all registers.

 

(gdb) b *0x007065ce

Breakpoint r at 0x7065ce

(gdb) r

...

(gdb) info registers

eax            0xf0     240                    //the interrupt number SYS_futex=240, is passed by %eax

ecx            0x80     128                    //the second parameter op=128

edx            0x1      1                      //_val=1

ebx            0x83c9640        138188352      //the first parameter uaddr=0x83c9640

esp            0xb7f74200       0xb7f74200

ebp            0xb7f74248       0xb7f74248

esi            0x0      0                      //the third parameter utime=NULL

edi            0x1      1

eip            0x7065ce 0x7065ce <pthread_cond_wait@@GLIBC_2.3.2+142>

eflags         0x200212 [ AF IF ID ]

cs             0x73     115

ss             0x7b     123

ds             0x7b     123

es             0x7b     123

fs             0x0      0

gs             0x33     51

(gdb) stepi

0x00110400 in __kernel_vsyscall ()             //int $0x80

(gdb) info registers

eax            0xf0     240                    //the interrupt number SYS_futex=240, is passed by %eax

ecx            0x80     128                    //the second parameter op=128

edx            0x1      1                      //_val=1

ebx            0x83c9640        138188352      //the first parameter uaddr=0x83c9640

esp            0xb7f741fc       0xb7f741fc

ebp            0xb7f74248       0xb7f74248

esi            0x0      0                      //the third parameter utime=NULL

edi            0x1      1

eip            0x110400 0x110400 <__kernel_vsyscall>  //will execute ‘int $0x80’

eflags         0x200212 [ AF IF ID ]

cs             0x73     115

ss             0x7b     123

ds             0x7b     123

es             0x7b     123

fs             0x0      0

gs             0x33     51

(gdb)

 

About this system call, it will be detailed discussed later again.

 

抱歉!评论已关闭.