usleep的--系统调用流程--及不准确的问题 - Android4 0 1
首先給大家分享一個(gè)巨牛巨牛的人工智能教程,是我無(wú)意中發(fā)現(xiàn)的。教程不僅零基礎(chǔ),通俗易懂,而且非常風(fēng)趣幽默,還時(shí)不時(shí)有內(nèi)涵段子,像看小說(shuō)一樣,哈哈~我正在學(xué)習(xí)中,覺(jué)得太牛了,所以分享給大家!點(diǎn)這里可以跳轉(zhuǎn)到教程
1.由于在不同的硬件平臺(tái)上經(jīng)常遇到usleep不準(zhǔn)確的問(wèn)題,比如usleep(2*1000),結(jié)果sleep了10ms,是不是有點(diǎn)過(guò)分,測(cè)試代碼如下:
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++;??? }}當(dāng)然為防止出現(xiàn)意外,禁止測(cè)試期間設(shè)置系統(tǒng)時(shí)間。
?
2. 根據(jù)以前的經(jīng)驗(yàn),此usleep不準(zhǔn)主要是由于Kernel中系統(tǒng)timer的rating值過(guò)高引起的。
?
3. 下面從源碼的角度分析一下usleep的實(shí)現(xiàn)細(xì)節(jié),并進(jìn)一步分析其原因。以下以Android4.0.1為例進(jìn)行分析。注此問(wèn)題主要與Kernel有關(guān),與glibc或bionic無(wú)關(guān),因?yàn)樾〉茏罱鉇ndroid,所以就以Android為例進(jìn)行研究。
?
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;? }}
它也很懶的,就調(diào)用了nanosleep,哪就看看nanasleep的源碼吧! 不幸是只找到一個(gè)extern int? nanosleep(const struct timespec *, struct timespec *); 它位于/bionic/libc/include/sys/linux-unistd.h,并沒(méi)有找到它的實(shí)現(xiàn)。其實(shí)看看Linux系統(tǒng)調(diào)用,早就知道它是一個(gè)系統(tǒng)調(diào)用,哪就分析一下是如何進(jìn)行系統(tǒng)調(diào)用的,以前只是講過(guò)原理,并沒(méi)有實(shí)例,在此把它完成了。
?
5. 尋找系統(tǒng)調(diào)用函數(shù)
如果這個(gè)函數(shù)沒(méi)有實(shí)現(xiàn),哪肯定是不能調(diào)用的,就像MIT教授在公開(kāi)課上所講的,搞計(jì)算機(jī)的不像搞別的,做不了假,別人不管你怎么設(shè)計(jì)的,只看你實(shí)現(xiàn)的結(jié)果,很有道理。也證明了搞if else的人不能做弊。哪就從它的Android.mk入手吧,看看還Link了什么東東。打開(kāi)libc的Android.mk發(fā)現(xiàn),其中有一行
include $(LOCAL_PATH)/arch-$(TARGET_ARCH)/syscalls.mk
這就是關(guān)鍵所在,syscalls系統(tǒng)調(diào)用,不正是我們要找的嗎?進(jìn)入arch-arm/syscalls.mk一看,其中一大片.s,Search一下,看有沒(méi)有nanosleep.s,還真有這么一行,真是大快人心:syscall_src += arch-arm/syscalls/nanosleep.S
趕緊去瞧瞧,ARM匯編水平不高,能看懂嗎?先把代碼貼上再說(shuō),不懂就問(wèn)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是個(gè)什么東東,憑直覺(jué),肯定在sys/linux-syscalls.h中有定義。打開(kāi)/libc/include/sys/linux-syscalls.h并search __NR_nanosleep, 明白了,它定義了__NR_nanosleep的值為(__NR_SYSCALL_BASE + 162),其實(shí)就是定義了其系統(tǒng)調(diào)用號(hào)。這就與前一文swi連接起來(lái)了。上面的代碼把系統(tǒng)調(diào)用號(hào)傳遞給r7,然后觸發(fā)了一個(gè)軟中斷,從而進(jìn)入內(nèi)核態(tài)執(zhí)行。
?
6. 軟中斷處理流程
根據(jù)常識(shí),既然是軟中斷,就一定有一個(gè)對(duì)應(yīng)的ISR,打開(kāi)/kernel/arch/arm/kernel/entry-common.S,發(fā)現(xiàn)其中有一個(gè)ENTRY(vector_swi),這就是我們要找的ISR,其詳細(xì)代碼如下:
.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對(duì)應(yīng)的處理函數(shù)
從上面的代碼中可以看出,它將調(diào)用sys_call_table中的某個(gè)函數(shù)。在同一個(gè)文件中尋找sys_call_table,其代碼如下:
.type sys_call_table, ENTRY(sys_call_table)
看看linux/arch/arm/kernel/calls.S中的內(nèi)容:
原來(lái)nanosleep系統(tǒng)調(diào)用在Kernel中的函數(shù)為sys_nanosleep,現(xiàn)在去分析一下是如何實(shí)現(xiàn)高精度的sleep的,是忙等(執(zhí)行nop指令),還是閑等(讓出CPU使用權(quán))呢? 馬上就會(huì)有答案了。由于小弟知識(shí)有限,沒(méi)哪么簡(jiǎn)單,我找了2個(gè)小時(shí)也沒(méi)有找到答案,慚愧啊!
?
8. 先看看熟悉的系統(tǒng)調(diào)用open吧!
也不幸運(yùn),沒(méi)有sys_open這樣的函數(shù)。反正知道這個(gè)東東在fs/open.c中,基本原理應(yīng)該是一樣的。在此文件中找到了下面這個(gè)函數(shù):
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就是一個(gè)extern "C")
?
這兄弟倆長(zhǎng)得太像了,再看看SYSCALL_DEFINE3的定義,看看能不能找到二者的關(guān)系。
哈哈哈哈哈哈.....,終于在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);是不是與要找的函數(shù)一模一樣呢?終于找到如何看這個(gè)代碼的方法了!
?
9. 繼續(xù)找sys_nanosleep的實(shí)現(xiàn)代碼
先看看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了嗎?說(shuō)明有機(jī)會(huì)找到了。
SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,??struct timespec __user *, rmtp)這不就是我要找的嗎? 由于這是一個(gè)宏,在SourceInsight中查找函數(shù)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實(shí)現(xiàn)如下:
調(diào)用流程如下:
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)<在注冊(cè)的clock_event_device中提供此函數(shù),其主要功能是設(shè)置相關(guān)寄存器,以設(shè)置此超時(shí)事件>
?
?
?
?
?
?
?
?
?
???????????瀏覽人工智能教程
總結(jié)
以上是生活随笔為你收集整理的usleep的--系统调用流程--及不准确的问题 - Android4 0 1的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2020自动化控制科学与工程保研面试经历
- 下一篇: 3.9