Usleep-System Call process-and inaccurate problems-android4.0.1

Source: Internet
Author: User
Tags usleep

1. usleep is inaccurate on different hardware platforms, for example, usleep (2*1000). The result is sleep for 10 ms. Is it too much? The test code is as follows:

#include <stdio.h>#include <stdlib.h>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++;    }}

Of course, to prevent exceptions, you are not allowed to set the system time during the test.

 

2. Based on previous experience, this usleep is not accurate mainly because the Rating Value of the system timer in the kernel is too high.

 

3. The following analyzes the Implementation Details of usleep from the source code perspective and further analyzes the causes. The following uses android4.0.1 as an example for analysis. Note that this issue is mainly related to kernel and has nothing to do with glibc or bionic. Because younger brother has recently developed Android, he will take Android as an example to study.

 

4. First find the source code of usleep:

// At/bionic/libc/unistd/usleep. C # include <time. h> # include <errno. h> int usleep (unsigned long USEC) {struct timespec ts; Ts. TV _sec = USEC/000000ul; # ifdef _ arm _/* avoid divisions and modulos on the arm */ts. TV _nsec = (USEC-Ts. TV _sec * 000000ul) * 1000; # else ts. TV _nsec = (USEC % 000000ul) * 1000ul; # endif for (;) {If (nanosleep (& TS, & TS) = 0) return 0; // We try again if the nanosleep failur E is eintr. // The other possible failures are Val (which we shoshould pass through), // and enosys, which doesn't happen. If (errno! = Eintr) Return-1 ;}}

It is also very lazy, it calls nanosleep, where to look at the source code of nanasleep! Unfortunately, only one extern int nanosleep (const struct timespec *, struct timespec *) is found; it is located in/bionic/libc/include/sys/linux-unistd.h and does not find its implementation. In fact, when we look at the Linux system call, we have long known that it is a system call. Where can we analyze how to call the system? Previously we only talked about the principle and didn't have an instance. We have completed it here.

 

5. Find the system call Function

If this function is not implemented, it cannot be called. As Professor MIT said in the open class, it is impossible to create a computer. No matter how you design it, it makes sense to look at your implementation results. It also proves that people who engage in if else cannot do harm. Let's start with its Android. mk and see what else link. Open Android. mk of libc and find one line.

Include $ (local_path)/arch-$ (target_arch)/syscils. mk

This is the key. Isn't it exactly what we are looking for the syscils system call? Enter arch-arm/syscils. MK saw one of them. s, search to see if there is nanosleep. s, there is such a line, it is really amazing: syscall_src + = arch-arm/syscils/nanosleep. s

Hurry up. The Arm Assembly level is not high. Can you understand it? Paste the code first and then ask Google if you don't understand it.

/* autogenerated by gensyscalls.py */#include <sys/linux-syscalls.h>    .text    .type nanosleep, #function    .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

What is _ nr_nanosleep, intuition, certainly defined in sys/linux-syscalls.h. Open/libc/include/sys/linux-syscalls.h and search _ nr_nanosleep, understand, it defines the value of _ nr_nanosleep (_ nr_syscall_base + 162 ), actually, the system call number is defined. This is connected to the previous article SWI. The above code passes the system call number to R7, and then triggers a soft interrupt to enter the kernel state for execution.

 

6. Soft interrupt handling process

According to common sense, since it is soft interrupt, there must be a corresponding ISR, open/kernel/ARCH/ARM/kernel/entry-common.S, and find one of them entry (vector_swi ), this is the ISR we are looking for. The detailed code is as follows:

.align5ENTRY(vector_swi)subsp, sp, #S_FRAME_SIZEstmiasp, {r0 - r12}@ Calling r0 - r12 ARM(addr8, sp, #S_PC) ARM(stmdbr8, {sp, lr}^)@ Calling sp, lr THUMB(movr8, sp) THUMB(store_user_sp_lr r8, r10, S_SP)@ calling sp, lrmrsr8, spsr@ called from non-FIQ mode, so ok.strlr, [sp, #S_PC]@ Save calling PCstrr8, [sp, #S_PSR]@ Save CPSRstrr0, [sp, #S_OLD_R0]@ Save OLD_R0zero_fp/* * Get the system call number. */#if defined(CONFIG_OABI_COMPAT)/* * 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. */#ifdef CONFIG_ARM_THUMBtstr8, #PSR_T_BITmovner10, #0@ no thumb OABI emulationldreqr10, [lr, #-4]@ get SWI instruction#elseldrr10, [lr, #-4]@ get SWI instruction  A710(andip, r10, #0x0f000000@ check for SWI)  A710(teqip, #0x0f000000)  A710(bne.Larm710bug)#endif#ifdef CONFIG_CPU_ENDIAN_BE8revr10, r10@ little endian instruction#endif#elif defined(CONFIG_AEABI)/* * Pure EABI user space always put syscall number into scno (r7). */  A710(ldrip, [lr, #-4]@ get SWI instruction)  A710(andip, ip, #0x0f000000@ check for SWI)  A710(teqip, #0x0f000000)  A710(bne.Larm710bug)#elif defined(CONFIG_ARM_THUMB)/* Legacy ABI only, possibly thumb mode. */tstr8, #PSR_T_BIT@ this is SPSR from save_user_regsaddnescno, r7, #__NR_SYSCALL_BASE@ put OS number inldreqscno, [lr, #-4]#else/* Legacy ABI only. */ldrscno, [lr, #-4]@ get SWI instruction  A710(andip, scno, #0x0f000000@ check for SWI)  A710(teqip, #0x0f000000)  A710(bne.Larm710bug)#endif#ifdef CONFIG_ALIGNMENT_TRAPldrip, __cr_alignmentldrip, [ip]mcrp15, 0, ip, c1, c0@ update control register#endifenable_irqget_thread_info tskadrtbl, sys_call_table@ load syscall table pointerldrip, [tsk, #TI_FLAGS]@ check for syscall tracing#if defined(CONFIG_OABI_COMPAT)/* * 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. */bicsr10, r10, #0xff000000eornescno, r10, #__NR_OABI_SYSCALL_BASEldrnetbl, =sys_oabi_call_table#elif !defined(CONFIG_AEABI)bicscno, scno, #0xff000000@ mask off SWI op-codeeorscno, scno, #__NR_SYSCALL_BASE@ check OS number#endifstmdbsp!, {r4, r5}@ push fifth and sixth argststip, #_TIF_SYSCALL_TRACE@ are we tracing syscalls?bne__sys_tracecmpscno, #NR_syscalls@ check upper syscall limitadrlr, BSYM(ret_fast_syscall)@ return addressldrccpc, [tbl, scno, lsl #2]@ call sys_* routineaddr1, sp, #S_OFF2:movwhy, #0@ no longer a real syscallcmpscno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE)eorr0, scno, #__NR_SYSCALL_BASE@ put OS number backbcsarm_syscallbsys_ni_syscall@ not private funcENDPROC(vector_swi)

7. Find the processing function corresponding to nanosleep

The code above shows that it will call a function in sys_call_table. Search for sys_call_table in the same file. The Code is as follows:

.typesys_call_table, #objectENTRY(sys_call_table)#include "calls.S"

Check the content in Linux/ARCH/ARM/kernel/CILS. S:

/* 0 */CALL(sys_restart_syscall)CALL(sys_exit)CALL(sys_fork_wrapper)CALL(sys_read)CALL(sys_write)                .../* 160 */CALL(sys_sched_get_priority_min)CALL(sys_sched_rr_get_interval)CALL(sys_nanosleep)CALL(sys_mremap)CALL(sys_setresuid16)

The original function called by the nanosleep System in the kernel is sys_nanosleep. Now let's analyze how to implement high-precision sleep, busy wait (execute NOP command), idle wait (give out CPU usage right) what about it? There will be an answer immediately. The younger brother has limited knowledge, so it's easy. I haven't found the answer for two hours. Sorry!

 

8. first look at the familiar system call open!

We are not lucky to have a function like sys_open. The basic principles of this stuff in FS/open. C should be the same. The following function is found in this file:

Syscall_define3 (open, const char _ User *, filename, Int, flags, Int, Mode)

 

Linux/syscils. H is defined as follows:

Asmlinkage long sys_open (const char _ User * filename, int flags, int mode); (asmlinkage is an extern "C ")

 

The brothers look very similar. Let's look at the definition of syscall_define3 to see if we can find the relationship between them.

Haha, haha..., finally found the answer in Linux/syscall. h. The definition of syscall_define3 is as follows:

#define __SYSCALL_DEFINEx(x, name, ...)asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))#define SYSCALL_DEFINEx(x, sname, ...)__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) 

Restore syscall_define3 (open, const char _ User *, filename, Int, flags, Int, mode):

Asmlinkage long sys_open (const char _ User * filename, int flags, int mode); is it exactly the same as the function to be searched? Finally, I found a way to read this code!

 

9. Continue to find the implementation code of sys_nanosleep.

Let's take a look at commnets in Linux/kernel/hrtimer. C:

* 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:
*-Itimers
*-POSIX timers
*-Nanosleep
*-Precise in-kernel timing

Have you seen the nanosleep above? It indicates that you have the opportunity to find it.

Syscall_define2 (nanosleep, struct timespec _ User *, rqtp, struct timespec _ User *, rmtp) Isn't that what I'm looking? Because this is a macro, The nanosleep function in sourceinsight cannot be found, and the search string nanosleep is feasible. The Code is as follows:

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 is implemented as follows:

long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp,       const enum hrtimer_mode mode, const clockid_t clockid){struct restart_block *restart;struct hrtimer_sleeper t;int ret = 0;unsigned long slack;slack = current->timer_slack_ns;if (rt_task(current))slack = 0;hrtimer_init_on_stack(&t.timer, clockid, mode);hrtimer_set_expires_range_ns(&t.timer, timespec_to_ktime(*rqtp), slack);if (do_nanosleep(&t, mode))goto out;/* Absolute timers do not update the rmtp value and restart: */if (mode == HRTIMER_MODE_ABS) {ret = -ERESTARTNOHAND;goto out;}if (rmtp) {ret = update_rmtp(&t.timer, rmtp);if (ret <= 0)goto out;}restart = ¤t_thread_info()->restart_block;restart->fn = hrtimer_nanosleep_restart;restart->nanosleep.index = t.timer.base->index;restart->nanosleep.rmtp = rmtp;restart->nanosleep.expires = hrtimer_get_expires_tv64(&t.timer);ret = -ERESTART_RESTARTBLOCK;out:destroy_hrtimer_on_stack(&t.timer);return ret;}
static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode){hrtimer_init_sleeper(t, current);do {set_current_state(TASK_INTERRUPTIBLE);hrtimer_start_expires(&t->timer, mode);if (!hrtimer_active(&t->timer))t->task = NULL;if (likely(t->task))schedule();hrtimer_cancel(&t->timer);mode = HRTIMER_MODE_ABS;} while (t->task && !signal_pending(current));__set_current_state(TASK_RUNNING);return t->task == NULL;}

The calling process is as follows:

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; get 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) <this function is provided in the registered clock_event_device. Its main function is to set related registers to set this timeout event>

 

 

 

 

 

 

 

 

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.