usleep的--系统调用流程--及不准确的问题 - Android4 0 1
首先給大家分享一個巨牛巨牛的人工智能教程,是我無意中發現的。教程不僅零基礎,通俗易懂,而且非常風趣幽默,還時不時有內涵段子,像看小說一樣,哈哈~我正在學習中,覺得太牛了,所以分享給大家!點這里可以跳轉到教程
1.由于在不同的硬件平臺上經常遇到usleep不準確的問題,比如usleep(2*1000),結果sleep了10ms,是不是有點過分,測試代碼如下:
int main(int argc,char **argv){??? struct timeval oldTime, newTime;??? int iStime,i,j;??? iStime=5;??? for(i=0;i<60;i++)??? {??????? for(j=0;j<10;j++)??????? {??????????? gettimeofday( &oldTime, NULL );??????????? usleep( iStime * 1000 );??????????? gettimeofday( &newTime, NULL );??????????? printf("iStime:%d,actual time:%lld\n",iStime,((long long)(newTime.tv_sec*1000 + newTime.tv_usec/1000)-(long long)(oldTime.tv_sec*1000 + oldTime.tv_usec/1000)));??????? }??????? iStime++;??? }}當然為防止出現意外,禁止測試期間設置系統時間。
?
2. 根據以前的經驗,此usleep不準主要是由于Kernel中系統timer的rating值過高引起的。
?
3. 下面從源碼的角度分析一下usleep的實現細節,并進一步分析其原因。以下以Android4.0.1為例進行分析。注此問題主要與Kernel有關,與glibc或bionic無關,因為小弟最近搞Android,所以就以Android為例進行研究。
?
4. 首先找到usleep的源碼:
//位于/bionic/libc/unistd/usleep.cint usleep(unsigned long usec){? struct timespec ts;? ts.tv_sec? = usec/1000000UL;??? /* avoid divisions and modulos on the ARM */? ts.tv_nsec = (usec - ts.tv_sec*1000000UL)*1000;? ts.tv_nsec = (usec % 1000000UL) * 1000UL;? for (;;)? {??? if ( nanosleep( &ts, &ts ) == 0 )??????? return 0;??? // We try again if the nanosleep failure is EINTR.??? // The other possible failures are EINVAL (which we should pass through),??? // and ENOSYS, which doesn't happen.??? if ( errno != EINTR )??????? return -1;? }}
它也很懶的,就調用了nanosleep,哪就看看nanasleep的源碼吧! 不幸是只找到一個extern int? nanosleep(const struct timespec *, struct timespec *); 它位于/bionic/libc/include/sys/linux-unistd.h,并沒有找到它的實現。其實看看Linux系統調用,早就知道它是一個系統調用,哪就分析一下是如何進行系統調用的,以前只是講過原理,并沒有實例,在此把它完成了。
?
5. 尋找系統調用函數
如果這個函數沒有實現,哪肯定是不能調用的,就像MIT教授在公開課上所講的,搞計算機的不像搞別的,做不了假,別人不管你怎么設計的,只看你實現的結果,很有道理。也證明了搞if else的人不能做弊。哪就從它的Android.mk入手吧,看看還Link了什么東東。打開libc的Android.mk發現,其中有一行
include $(LOCAL_PATH)/arch-$(TARGET_ARCH)/syscalls.mk
這就是關鍵所在,syscalls系統調用,不正是我們要找的嗎?進入arch-arm/syscalls.mk一看,其中一大片.s,Search一下,看有沒有nanosleep.s,還真有這么一行,真是大快人心:syscall_src += arch-arm/syscalls/nanosleep.S
趕緊去瞧瞧,ARM匯編水平不高,能看懂嗎?先把代碼貼上再說,不懂就問google.
/* autogenerated by gensyscalls.py */??? .text??? .type nanosleep, ??? .globl nanosleep??? .align 4??? .fnstartnanosleep:??? .save?? {r4, r7}??? stmfd?? sp!, {r4, r7}??? ldr???? r7, =__NR_nanosleep??? swi???? #0??? ldmfd?? sp!, {r4, r7}??? movs??? r0, r0??? bxpl??? lr??? b?????? __set_syscall_errno??? .fnend__NR_nanosleep是個什么東東,憑直覺,肯定在sys/linux-syscalls.h中有定義。打開/libc/include/sys/linux-syscalls.h并search __NR_nanosleep, 明白了,它定義了__NR_nanosleep的值為(__NR_SYSCALL_BASE + 162),其實就是定義了其系統調用號。這就與前一文swi連接起來了。上面的代碼把系統調用號傳遞給r7,然后觸發了一個軟中斷,從而進入內核態執行。
?
6. 軟中斷處理流程
根據常識,既然是軟中斷,就一定有一個對應的ISR,打開/kernel/arch/arm/kernel/entry-common.S,發現其中有一個ENTRY(vector_swi),這就是我們要找的ISR,其詳細代碼如下:
.align 5ENTRY(vector_swi) sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12}?? @ Calling r0 - r12 ARM( add r8, sp, #S_PC? ) ARM( stmdb r8, {sp, lr}^? ) @ Calling sp, lr THUMB( mov r8, sp?? ) THUMB( store_user_sp_lr r8, r10, S_SP ) @ calling sp, lr mrs r8, spsr?? @ called from non-FIQ mode, so ok. str lr, [sp, #S_PC]?? @ Save calling PC str r8, [sp, #S_PSR]? @ Save CPSR str r0, [sp, #S_OLD_R0]? @ Save OLD_R0 zero_fp /*? * Get the system call number.? */ /*? * If we have CONFIG_OABI_COMPAT then we need to look at the swi? * value to determine if it is an EABI or an old ABI call.? */ tst r8, #PSR_T_BIT movne r10, #0??? @ no thumb OABI emulation ldreq r10, [lr, #-4]?? @ get SWI instruction ldr r10, [lr, #-4]?? @ get SWI instruction? A710( and ip, r10, #0x0f000000? @ check for SWI? )? A710( teq ip, #0x0f000000????? )? A710( bne .Larm710bug????? ) rev r10, r10?? @ little endian instruction /*? * Pure EABI user space always put syscall number into scno (r7).? */? A710( ldr ip, [lr, #-4]?? @ get SWI instruction )? A710( and ip, ip, #0x0f000000? @ check for SWI? )? A710( teq ip, #0x0f000000????? )? A710( bne .Larm710bug????? ) /* Legacy ABI only, possibly thumb mode. */ tst r8, #PSR_T_BIT?? @ this is SPSR from save_user_regs addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in ldreq scno, [lr, #-4] /* Legacy ABI only. */ ldr scno, [lr, #-4]?? @ get SWI instruction? A710( and ip, scno, #0x0f000000? @ check for SWI? )? A710( teq ip, #0x0f000000????? )? A710( bne .Larm710bug????? ) ldr ip, __cr_alignment ldr ip, [ip] mcr p15, 0, ip, c1, c0? @ update control register enable_irq get_thread_info tsk adr tbl, sys_call_table? @ load syscall table pointer ldr ip, [tsk, #TI_FLAGS]? @ check for syscall tracing /*? * If the swi argument is zero, this is an EABI call and we do nothing.? *? * If this is an old ABI call, get the syscall number into scno and? * get the old ABI syscall table address.? */ bics r10, r10, #0xff000000 eorne scno, r10, #__NR_OABI_SYSCALL_BASE ldrne tbl, =sys_oabi_call_table bic scno, scno, #0xff000000? @ mask off SWI op-code eor scno, scno, #__NR_SYSCALL_BASE @ check OS number stmdb sp!, {r4, r5}?? @ push fifth and sixth args tst ip, #_TIF_SYSCALL_TRACE? @ are we tracing syscalls? bne __sys_trace cmp scno, #NR_syscalls? @ check upper syscall limit adr lr, BSYM(ret_fast_syscall) @ return address ldrcc pc, [tbl, scno, lsl #2]? @ call sys_* routine add r1, sp, #S_OFF2: mov why, #0??? @ no longer a real syscall cmp scno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE) eor r0, scno, #__NR_SYSCALL_BASE @ put OS number back bcs arm_syscall? b sys_ni_syscall?? @ not private funcENDPROC(vector_swi)
7. 找與nanosleep對應的處理函數
從上面的代碼中可以看出,它將調用sys_call_table中的某個函數。在同一個文件中尋找sys_call_table,其代碼如下:
.type sys_call_table, ENTRY(sys_call_table)
看看linux/arch/arm/kernel/calls.S中的內容:
原來nanosleep系統調用在Kernel中的函數為sys_nanosleep,現在去分析一下是如何實現高精度的sleep的,是忙等(執行nop指令),還是閑等(讓出CPU使用權)呢? 馬上就會有答案了。由于小弟知識有限,沒哪么簡單,我找了2個小時也沒有找到答案,慚愧啊!
?
8. 先看看熟悉的系統調用open吧!
也不幸運,沒有sys_open這樣的函數。反正知道這個東東在fs/open.c中,基本原理應該是一樣的。在此文件中找到了下面這個函數:
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
?
linux/syscalls.h定義如下:
asmlinkage long sys_open(const char __user *filename,int flags, int mode); (asmlinkage就是一個extern "C")
?
這兄弟倆長得太像了,再看看SYSCALL_DEFINE3的定義,看看能不能找到二者的關系。
哈哈哈哈哈哈.....,終于在linux/syscalls.h中找到答案了,SYSCALL_DEFINE3的定義如下:
把SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)還原就變成了:
asmlinkage long sys_open(const char __user *filename,int flags, int mode);是不是與要找的函數一模一樣呢?終于找到如何看這個代碼的方法了!
?
9. 繼續找sys_nanosleep的實現代碼
先看看linux/kernel/hrtimer.c中的commnets:
?*? High-resolution kernel timers
?*
?*? In contrast to the low-resolution timeout API implemented in
?*? kernel/timer.c, hrtimers provide finer resolution and accuracy
?*? depending on system configuration and capabilities.
?*
?*? These timers are currently used for:
?*?? - itimers
?*?? - POSIX timers
?*?? - nanosleep
?*?? - precise in-kernel timing
看到上面的nanosleep了嗎?說明有機會找到了。
SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,??struct timespec __user *, rmtp)這不就是我要找的嗎? 由于這是一個宏,在SourceInsight中查找函數nanosleep是找不到的,search字符串nanosleep是可行的。其代碼如下:
SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,? struct timespec __user *, rmtp){ struct timespec tu; if (copy_from_user(&tu, rqtp, sizeof(tu)))? return -EFAULT; if (!timespec_valid(&tu))? return -EINVAL; return hrtimer_nanosleep(&tu, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC);}
hrtimer_nanosleep實現如下:
調用流程如下:
nanosleep()--> sys_nanosleep()--> hrtimer_nanosleep()--> do_nanosleep()--> hrtimer_start()--> enqueue_hrtimer() -->hrtimer_enqueue_reprogram()--> hrtimer_reprogram()-->int tick_program_event(ktime_t expires, int force)->
? (struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev; 獲得clock_event_device)
int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires, int force)->
int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,ktime_t now) ->
dev->set_next_event((unsigned long) clc, dev)<在注冊的clock_event_device中提供此函數,其主要功能是設置相關寄存器,以設置此超時事件>
?
?
?
?
?
?
?
?
?
???????????瀏覽人工智能教程
總結
以上是生活随笔為你收集整理的usleep的--系统调用流程--及不准确的问题 - Android4 0 1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020自动化控制科学与工程保研面试经历
- 下一篇: 3.9