`
byebyegov5qq
  • 浏览: 1207811 次
文章分类
社区版块
存档分类
最新评论

ARM Linux系统调用的原理

 
阅读更多

http://blog.chinaunix.net/space.php?uid=12567959&do=blog&id=160981

最终__libc_open64又调用了__libc_open函数,这个函数在文件libc/sysdeps/linux/common/open.c中定义:

libc_hidden_proto(__libc_open)

int __libc_open(const char *file, int oflag, ...)

{

mode_t mode = 0;

if (oflag & O_CREAT) {

va_list arg;

va_start (arg, oflag);

mode = va_arg (arg, mode_t);

va_end (arg);

}

return __syscall_open(file, oflag, mode);

}

libc_hidden_def(__libc_open)

这个函数,也是仅仅根据打开标志oflag的值,来判断是否有第三个参数,若由,则获得其值。之后,便用获得的参数来调用__syscall_open(file, oflag, mode)

__syscall_open在同一个文件中定义:

static __inline__ _syscall3(int, __syscall_open, const char *, file,

int, flags, __kernel_mode_t, mode)

在文件libc/sysdeps/linux/arm/bits/syscalls.h文件中可以看到:

#undef _syscall3

#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \

type name(type1 arg1,type2 arg2,type3 arg3) \

{ \

return (type) (INLINE_SYSCALL(name, 3, arg1, arg2, arg3)); \

}

这个宏实际上完成定义一个函数的工作,宏的第一个参数是函数的返回值类型,第二个参数是函数名,之后的参数就如同它们的参数名所表明的那样,分别是函数的参数类型及参数名。__syscall_open实际上为:

int __syscall_open (const char * file,int flags, __kernel_mode_t mode)

{

return (int) (INLINE_SYSCALL(__syscall_open, 3, file, flags, mode));

}

INLINE_SYSCALL为同一个文件中定义的宏:

#undef INLINE_SYSCALL

#define INLINE_SYSCALL(name, nr, args...)\

({ unsigned int _inline_sys_result = INTERNAL_SYSCALL (name, , nr, args);\

if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (_inline_sys_result, ), 0))\

{\

__set_errno (INTERNAL_SYSCALL_ERRNO (_inline_sys_result, ));\

_inline_sys_result = (unsigned int) -1;\

}\

(int) _inline_sys_result; })

INLINE_SYSCALL宏中最值得注意的是INTERNAL_SYSCALL,其定义如下:

#undef INTERNAL_SYSCALL

#if !defined(__thumb__)

#if defined(__ARM_EABI__)

#define INTERNAL_SYSCALL(name, err, nr, args...)\

({unsigned int __sys_result;\

{\

register int _a1 __asm__ ("r0"), _nr __asm__ ("r7");\

LOAD_ARGS_##nr (args)\

_nr = SYS_ify(name);\

__asm__ __volatile__ ("swi0x0@ syscall " #name\

: "=r" (_a1)\

: "r" (_nr) ASM_ARGS_##nr\

: "memory");\

__sys_result = _a1;\

}\

(int) __sys_result; })

#else /* defined(__ARM_EABI__) */

#define INTERNAL_SYSCALL(name, err, nr, args...)\

({ unsigned int __sys_result;\

{\

register int _a1 __asm__ ("a1");\

LOAD_ARGS_##nr (args)\

__asm__ __volatile__ ("swi%1@ syscall " #name\

: "=r" (_a1)\

: "i" (SYS_ify(name)) ASM_ARGS_##nr\

: "memory");\

__sys_result = _a1;\

}\

(int) __sys_result; })

#endif

这里也将同文件中的LOAD_ARGS宏的定义贴出来:

#define LOAD_ARGS_0()

#define ASM_ARGS_0

#define LOAD_ARGS_1(a1)\

_a1 = (int) (a1);\

LOAD_ARGS_0 ()

#define ASM_ARGS_1ASM_ARGS_0, "r" (_a1)

#define LOAD_ARGS_2(a1, a2)\

register int _a2 __asm__ ("a2") = (int) (a2);\

LOAD_ARGS_1 (a1)

#define ASM_ARGS_2ASM_ARGS_1, "r" (_a2)

#define LOAD_ARGS_3(a1, a2, a3)\

register int _a3 __asm__ ("a3") = (int) (a3);\

LOAD_ARGS_2 (a1, a2)

这几个宏用来在寄存器中加载相应的参数,参数传递的方式和普通的C函数也没有什么太大的区别,同样都是将参数列表中的参数依次放入寄存器r0、r1、r2、r3…中。

上面的SYS_ify(name)宏,是用来获得系统调用号的。

#define SYS_ify(syscall_name)(__NR_##syscall_name)

也就是__NR___syscall_open,在libc/sysdeps/linux/common/open.c中可以看到这个宏的定义:

#define __NR___syscall_open __NR_open

__NR_open在内核代码的头文件中有定义。

在这里我们忽略定义__thumb__的情况,而假设我们编译出来的库函数使用的都是ARM指令集。在上面的代码中,我们看到,根据是否定义宏__ARM_EABI__INTERNAL_SYSCALL会被展开为两种不同的版本。关于这一点,与应用二进制接口ABI有关,不同的ABI,则会有不同的传递系统调用号的方法。对于比较新的EABI,则在r7寄存器保存系统调用号,通过swi0x0来陷入内核。否则,通过swi指令的24位立即数参数来传递系统调用号。后面还会有内核中关于这个问题的更详细的说明。

同时这两种调用方式的系统调用号也是存在这区别的,在内核的文件arch/arm/inclue/asm/unistd.h中可以看到:

#define __NR_OABI_SYSCALL_BASE0x900000

#if defined(__thumb__) || defined(__ARM_EABI__)

#define __NR_SYSCALL_BASE0

#else

#define __NR_SYSCALL_BASE__NR_OABI_SYSCALL_BASE

#endif

/*

* This file contains the system call numbers.

*/

#define __NR_restart_syscall(__NR_SYSCALL_BASE+0)

#define __NR_exit(__NR_SYSCALL_BASE+1)

#define __NR_fork(__NR_SYSCALL_BASE+2)

#define __NR_read(__NR_SYSCALL_BASE+3)

#define __NR_write(__NR_SYSCALL_BASE+4)

#define __NR_open(__NR_SYSCALL_BASE+5)

……

接下来来看操作系统对系统调用的处理。我们回到ARM Linux的异常向量表,因为当执行swi时,会从异常向量表中取例程的地址从而跳转到相应的处理程序中。在文件arch/arm/kernel/entry-armv.S中我们看到SWI异常向量:

W(ldr)pc, .LCvswi + stubs_offset

而.LCvswi在同一个文件中定义为:

.LCvswi:

.wordvector_swi

也就是最终会执行例程vector_swi来完成对系统调用的处理,接下来我们来看下在arch/arm/kernel/entry-common.S中定义的vector_swi例程(删去一些和我们的示例平台无关的代码):

.align5

ENTRY(vector_swi)

subsp, sp, #S_FRAME_SIZE

stmiasp, {r0 - r12}@ Calling r0 - r12

ARM(addr8, sp, #S_PC)

ARM(stmdbr8, {sp, lr}^)@ Calling sp, lr

mrsr8, spsr@ called from non-FIQ mode, so ok.

strlr, [sp, #S_PC]@ Save calling PC

strr8, [sp, #S_PSR]@ Save CPSR

strr0, [sp, #S_OLD_R0]@ Save OLD_R0

zero_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.

*/

ldrr10, [lr, #-4]@ get SWI instruction

#ifdef CONFIG_CPU_ENDIAN_BE8

//rev指令的功能是反转字中的字节序

revr10, r10@ little endian instruction

#endif

#elif defined(CONFIG_AEABI)

#else

/* Legacy ABI only. */

ldrscno, [lr, #-4]@ get SWI instruction

#endif

#ifdef CONFIG_ALIGNMENT_TRAP

ldrip, __cr_alignment

ldrip, [ip]

mcrp15, 0, ip, c1, c0@ update control register

#endif

enable_irq

// tsk是寄存器r9的别名,在arch/arm/kernel/entry-header.S中定义:// tsk.reqr9@ current thread_info

//获得线程对象的基地址。

get_thread_info tsk

// tbl是r8寄存器的别名,在arch/arm/kernel/entry-header.S中定义:

// tbl.reqr8@ syscall table pointer,

//用来存放系统调用表的指针,系统调用表在后面调用

adrtbl, sys_call_table@ load syscall table pointer

ldrip, [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, #0xff000000

eornescno, r10, #__NR_OABI_SYSCALL_BASE

ldrnetbl, =sys_oabi_call_table

#elif !defined(CONFIG_AEABI)

// scno是寄存器r7的别名

bicscno, scno, #0xff000000@ mask off SWI op-code

eorscno, scno, #__NR_SYSCALL_BASE@ check OS number

#endif

stmdbsp!, {r4, r5}@ push fifth and sixth args

tstip, #_TIF_SYSCALL_TRACE@ are we tracing syscalls?

bne__sys_trace

cmpscno, #NR_syscalls@ check upper syscall limit

adrlr, BSYM(ret_fast_syscall)@ return address

ldrccpc, [tbl, scno, lsl #2]@ call sys_* routine

addr1, sp, #S_OFF

// why也是r8寄存器的别名

2:movwhy, #0@ no longer a real syscall

cmpscno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE)

eorr0, scno, #__NR_SYSCALL_BASE@ put OS number back

bcsarm_syscall

bsys_ni_syscall@ not private func

ENDPROC(vector_swi)

上面的zero_fp是一个宏,在arch/arm/kernel/entry-header.S中定义:

.macrozero_fp

#ifdef CONFIG_FRAME_POINTER

movfp, #0

#endif

.endm

而fp位寄存器r11。

像每一个异常处理程序一样,要做的第一件事当然就是保护现场了。紧接着是获得系统调用的系统调用号。然后以系统调用号作为索引来查找系统调用表,如果系统调用号正常的话,就会调用相应的处理例程来处理,就是上面的那个ldrccpc, [tbl, scno, lsl #2]语句,然后通过例程ret_fast_syscall来返回。

在这个地方我们接着来讨论ABI的问题。现在,我们首先来看两个宏,一个是CONFIG_OABI_COMPAT意思是说与old ABI兼容,另一个是CONFIG_AEABI意思是说指定现在的方式为EABI。这两个宏可以同时配置,也可以都不配,也可以配置任何一种。我们来看一下内核是怎么处理这一问题的。我们知道,sys_call_table在内核中是个跳转表,这个表中存储的是一系列的函数指针,这些指针就是系统调用函数的指针,如(sys_open)。内核是根据一个系统调用号(对于EABI来说为系统调用表的索引)找到实际该调用内核哪个函数,然后通过运行该函数完成系统调用的。

首先,对于old ABI,内核给出的处理是为它建立一个单独的system call table,叫sys_oabi_call_table。这样,兼容方式下就会有两个system call table,以old ABI方式的系统调用会执行old_syscall_table表中的系统调用函数,EABI方式的系统调用会用sys_call_table中的函数指针。
配置无外乎以下4中:
第一、两个宏都配置行为就是上面说的那样。
第二、只配置CONFIG_OABI_COMPAT,那么以old ABI方式调用的会用sys_oabi_call_table,以EABI方式调用的用sys_call_table,和1实质上是相同的。只是情况1更加明确。
第三、只配置CONFIG_AEABI系统中不存在sys_oabi_call_table,对old ABI方式调用不兼容。只能 以EABI方式调用,用sys_call_table。

第四、两个都没有配置,系统默认会只允许old ABI方式,但是不存在old_syscall_table,最终会通过sys_call_table完成函数调用

系统会根据ABI的不同而将相应的系统调用表的基地址加载进tbl寄存器,也就是r8寄存器。接下来来看系统调用表,如前面所说的那样,有两个,同样都在文件arch/arm/kernel/entry-armv.S中:

#define ABI(native, compat) native

#ifdef CONFIG_AEABI

#define OBSOLETE(syscall) sys_ni_syscall

#else

#define OBSOLETE(syscall) syscall

#endif

.typesys_call_table, #object

ENTRY(sys_call_table)

#include "calls.S"

#undef ABI

#undef OBSOLETE

另外一个为:

#define ABI(native, compat) compat

#define OBSOLETE(syscall) syscall

.typesys_oabi_call_table, #object

ENTRY(sys_oabi_call_table)

#include "calls.S"

#undef ABI

#undef OBSOLETE

这样看来貌似两个系统调用表是完全一样的。这里预处理指令include的独特用法也挺有意思,系统调用表的内容就是整个arch/arm/kernel/calls.S文件的内容(由于太长,这里就不全部列出了):

/* 0 */CALL(sys_restart_syscall)

CALL(sys_exit)

CALL(sys_fork_wrapper)

CALL(sys_read)

CALL(sys_write)

/* 5 */CALL(sys_open)

CALL(sys_close)

……

上面的CALL()是个宏,它同样在文件arch/arm/kernel/entry-armv.S中定义:

#define CALL(x) .equ NR_syscalls,NR_syscalls+1

#include "calls.S"

#undef CALL

#define CALL(x) .long x

在定义宏CALL()的地方,我们看到calls.S已经被包含了一次,只不过在这里,不是为了建立系统调用表,而仅仅是为了获得系统的系统调用的数量,并保存在宏NR_syscalls中。在SWI向量中,我们也看到,是使用了这个宏的。

最后再罗嗦一点,如果用sys_open来搜的话,是搜不到系统调用open的定义的,系统调用函数都是用宏来定义的,比如对于open,有这样的定义:

---------------------------------------------------------------------

fs/open.c

1066 SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)

1067 {

1068long ret;

1069

1070if (force_o_largefile())

1071flags |= O_LARGEFILE;

1072

1073ret = do_sys_open(AT_FDCWD, filename, flags, mode);

1074/* avoid REGPARM breakage on x86: */

1075asmlinkage_protect(3, ret, filename, flags, mode);

1076return ret;

1077 }

---------------------------------------------------------------------

继续回到vector_swi,如果系统调用号不正确,则会调用arm_syscall函数来进行处理,这个函数定义如下:

---------------------------------------------------------------------

arch/arm/kernel/traps.c

465 #define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE)

466 asmlinkage int arm_syscall(int no, struct pt_regs *regs)

467 {

468struct thread_info *thread = current_thread_info();

469siginfo_t info;

470

471if ((no >> 16) != (__ARM_NR_BASE>> 16))

472return bad_syscall(no, regs);

473

474switch (no & 0xffff) {

475case 0: /* branch through 0 */

476info.si_signo = SIGSEGV;

477info.si_errno = 0;

478info.si_code= SEGV_MAPERR;

479info.si_addr= NULL;

480

481arm_notify_die("branch through zero", regs, &info, 0, 0);

482return 0;

483

484case NR(breakpoint): /* SWI BREAK_POINT */

485regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;

486ptrace_break(current, regs);

487return regs->ARM_r0;

488

489/*

490* Flush a region from virtual address 'r0' to virtual address 'r1'

491 * _exclusive_.There is no alignment requirement on either address;

492* user space does not need to know the hardware cache layout.

493*

494* r2 contains flags.It should ALWAYS be passed as ZERO until it

495* is defined to be something else.For now we ignore it, but may

496* the fires of hell burn in your belly if you break this rule. ;)

497*

498* (at a later date, we may want to allow this call to not flush

499* various aspects of the cache.Passing '' will guarantee that

500* everything necessary gets flushed to maintain consistency in

501* the specified region).

502*/

503case NR(cacheflush):

504do_cache_op(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2);

505return 0;

506

507case NR(usr26):

508if (!(elf_hwcap & HWCAP_26BIT))

509break;

510regs->ARM_cpsr &= ~MODE32_BIT;

511return regs->ARM_r0;

512

513case NR(usr32):

514if (!(elf_hwcap & HWCAP_26BIT))

515break;

516regs->ARM_cpsr |= MODE32_BIT;

517return regs->ARM_r0;

518

519case NR(set_tls):

520thread->tp_value = regs->ARM_r0;

521 #if defined(CONFIG_HAS_TLS_REG)

522asm ("mcr p15, 0, %0, c13, c0, 3" : : "r" (regs->ARM_r0) );

523 #elif !defined(CONFIG_TLS_REG_EMUL)

524/*

525* User space must never try to access this directly.

526* Expect your app to break eventually if you do so.

527* The user helper at 0xffff0fe0 must be used instead.

528* (see entry-armv.S for details)

529*/

530*((unsigned int *)0xffff0ff0) = regs->ARM_r0;

531 #endif

532return 0;

533

534 #ifdef CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG

535/*

536* Atomically store r1 in *r2 if *r2 is equal to r0 for user space.

537* Return zero in r0 if *MEM was changed or non-zero if no exchange

538* happened.Also set the user C flag accordingly.

539* If access permissions have to be fixed up then non-zero is

540* returned and the operation has to be re-attempted.

541*

542* *NOTE*: This is a ghost syscall private to the kernel.Only the

543* __kuser_cmpxchg code in entry-armv.S should be aware of its

544* existence.Don't ever use this from user code.

545*/

546case NR(cmpxchg):

547for (;;) {

548extern void do_DataAbort(unsigned long addr, unsigned int fsr,

549struct pt_regs *regs);

550unsigned long val;

551unsigned long addr = regs->ARM_r2;

552struct mm_struct *mm = current->mm;

553pgd_t *pgd; pmd_t *pmd; pte_t *pte;

554spinlock_t *ptl;

555

556regs->ARM_cpsr &= ~PSR_C_BIT;

557down_read(&mm->mmap_sem);

558pgd = pgd_offset(mm, addr);

559if (!pgd_present(*pgd))

560goto bad_access;

561pmd = pmd_offset(pgd, addr);

562if (!pmd_present(*pmd))

563goto bad_access;

564pte = pte_offset_map_lock(mm, pmd, addr, &ptl);

565if (!pte_present(*pte) || !pte_dirty(*pte)) {

566pte_unmap_unlock(pte, ptl);

567goto bad_access;

568}

569val = *(unsigned long *)addr;

570val -= regs->ARM_r0;

571if (val == 0) {

572*(unsigned long *)addr = regs->ARM_r1;

573regs->ARM_cpsr |= PSR_C_BIT;

574}

575pte_unmap_unlock(pte, ptl);

576up_read(&mm->mmap_sem);

577return val;

578

579bad_access:

580up_read(&mm->mmap_sem);

581/* simulate a write access fault */

582do_DataAbort(addr, 15 + (1 << 11), regs);

583}

584 #endif

585

586default:

587/* Calls 9f00xx..9f07ff are defined to return -ENOSYS

588if not implemented, rather than raising SIGILL.This

589way the calling program can gracefully determine whether

590afeature is supported.*/

591if ((no & 0xffff) <= 0x7ff)

592return -ENOSYS;

593break;

594}

595 #ifdef CONFIG_DEBUG_USER

596/*

597* experience shows that these seem to indicate that

598* something catastrophic has happened

599*/

600if (user_debug & UDBG_SYSCALL) {

601printk("[%d] %s: arm syscall %d\n",

602task_pid_nr(current), current->comm, no);

603dump_instr("", regs);

604if (user_mode(regs)) {

605__show_regs(regs);

606c_backtrace(regs->ARM_fp, processor_mode(regs));

607}

608}

609 #endif

610info.si_signo = SIGILL;

611info.si_errno = 0;

612info.si_code= ILL_ILLTRP;

613info.si_addr= (void __user *)instruction_pointer(regs) -

614(thumb_mode(regs) ? 2 : 4);

615

616arm_notify_die("Oops - bad syscall(2)", regs, &info, no, 0);

617return 0;

618 }

---------------------------------------------------------------------

这个函数处理所有的辨别不出来的系统调用。系统调用号正确也好不正确也好,最终都是通过ret_fast_syscall例程来返回,因为我们看到,在进入系统调用处理函数之前,先加载了符号ret_fast_syscall进lr寄存器。ret_fast_syscall定义如下:

---------------------------------------------------------------------

arch/arm/kernel/entry-common.S

ret_fast_syscall:

UNWIND(.fnstart)

UNWIND(.cantunwind)

disable_irq@ disable interrupts

ldrr1, [tsk, #TI_FLAGS]

tstr1, #_TIF_WORK_MASK

bnefast_work_pending

/* perform architecture specific actions before user return */

arch_ret_to_user r1, lr

restore_user_regs fast = 1, offset = S_OFF

UNWIND(.fnend)

fast_work_pending:

strr0, [sp, #S_R0+S_OFF]!@ returned r0

work_pending:

tstr1, #_TIF_NEED_RESCHED

bnework_resched

tstr1, #_TIF_SIGPENDING|_TIF_NOTIFY_RESUME

beqno_work_pending

movr0, sp@ 'regs'

movr2, why@ 'syscall'

bldo_notify_resume

bret_slow_syscall@ Check work again

work_resched:

blschedule

/*

* "slow" syscall return path."why" tells us if this was a real syscall.

*/

ENTRY(ret_to_user)

ret_slow_syscall:

disable_irq@ disable interrupts

ldrr1, [tsk, #TI_FLAGS]

tstr1, #_TIF_WORK_MASK

bnework_pending

no_work_pending:

/* perform architecture specific actions before user return */

arch_ret_to_user r1, lr

restore_user_regs fast = 0, offset = 0

ENDPROC(ret_to_user)

---------------------------------------------------------------------

对于我们的平台来说,上面的arch_ret_to_user为空。restore_user_regs宏用于恢复现场并返回,restore_user_regs宏定义如下:

---------------------------------------------------------------------

arch/arm/kernel/entry-header.S

.macrorestore_user_regs, fast = 0, offset = 0

ldrr1, [sp, #\offset + S_PSR]@ get calling cpsr

ldrlr, [sp, #\offset + S_PC]!@ get pc

msrspsr_cxsf, r1@ save in spsr_svc

#if defined(CONFIG_CPU_32v6K)

clrex@ clear the exclusive monitor

#elif defined (CONFIG_CPU_V6)

strexr1, r2, [sp]@ clear the exclusive monitor

#endif

.if\fast

ldmdbsp, {r1 - lr}^@ get calling r1 - lr

.else

ldmdbsp, {r0 - lr}^@ get calling r0 - lr

.endif

movr0, r0@ ARMv5T and earlier require a nop

@ after ldm {}^

addsp, sp, #S_FRAME_SIZE - S_PC

movspc, lr@ return & move spsr_svc into cpsr

.endm

---------------------------------------------------------------------

添加新的系统调用

第一、打开arch/arm/kernel/calls.S,在最后添加系统调用的函数原型的指针,例如:

CALL(sys_set_senda)

补充说明一点关于NR_syscalls的东西,这个常量表示系统调用的总的个数,在较新版本的内核中,文件arch/arm/kernel/entry-common.S中可以找到:

.equ NR_syscalls,0

#define CALL(x) .equ NR_syscalls,NR_syscalls+1

#include "calls.S"

#undef CALL

#define CALL(x) .long x

相当的巧妙,不是吗?在系统调用表中每添加一个系统调用,NR_syscalls就自动增加一。在这个地方先求出NR_syscalls,然后重新定义CALL(x)宏,这样也可以不影响文件后面系统调用表的建立。

第二、打开include/asm-arm/unistd.h,添加系统调用号的宏,感觉这步可以省略,因为这个地方定义的系统调用号主要是个C库,比如uClibc、Glibc用的。例如:

#define __NR_plan_set_senda (__NR_SYSCALL_BASE+365)

为了向后兼容,系统调用只能增加而不能减少,这里的编号添加时,也必须按顺序来。否则会导致核心运行错误。

第三,实例化该系统调用,即编写新添加系统调用的实现例如:

SYSCALL_DEFINE1(set_senda, int,iset)

{

if(iset)

UART_PUT_CR(&at91_port[2],AT91C_US_SENDA);

else

UART_PUT_CR(&at91_port[2],AT91C_US_RSTSTA);

return 0;

}

第四、打开include/linux/syscalls.h添加函数声明

asmlinkage long sys_set_senda(int iset);

第五、在应用程序中调用该系统调用,可以参考uClibc的实现。

第六、结束。

参考文档:

[精华] arm Linux 2.6高版本中的系统调用方式

http://www.unixresources.net/linux/clf/linuxK/archive/00/00/67/92/679297.html

ARMLinux下添加新的系统调用

http://blog.sina.com.cn/s/blog_3e681643010009h9.html

分享到:
评论

相关推荐

    ARM linux系统调用的实现原理

    ARMlinux系统调用的实现原理,大家都知道linux的应用程序要想访问内核必须使用系统调用从而实现从usr模式转到svc模式。下面咱们看看它的实现过程。系统调用是os操作系统提供的服务,用户程序通过各种系统调用,来引用...

    嵌入式系统/ARM技术中的ARM linux系统调用的实现原理

     大家都知道linux的应用程序要想访问内核必须使用系统调用从而实现从usr模式转到svc模式。下面咱们看看它的实现过程。  系统调用是os操作系统提供的服务,用户程序通过各种系统调用,来引用内核提供的各种服务,...

    ARM-Linux系统移植

    2.1.2 Linux操作系统 .......................................................................................................8 2.1.3 目标板最后运行的环境..................................................

    嵌入式Linux 开发ARM课件第一讲

    万龙44b0 开发板是专门为arm 开发的实验板,这了有十一节基于此的课堂PPT其中有: ...ARMlinux移植过程。 引导程序Uboot移植。 最小系统启动开发过程。 嵌入式文件系统移植:ramdisk、JFFS2、yaff 等等内容相当丰富哦!

    嵌入式Linux ARM开发课件第六讲

    linux操作系统原理及组成 介绍linux系统目录组织结构 linux 运行基理,系统运行过程 配置裁减编译linux内核 进程属性和状态转换;进程控制与调度;进程相关系统调用,构建守护进程 进程间通信: 进程通信的基本概念...

    嵌入式Linux ARM开发课件第九讲

    linux操作系统原理及组成 介绍linux系统目录组织结构 linux 运行基理,系统运行过程 配置裁减编译linux内核 进程属性和状态转换;进程控制与调度;进程相关系统调用,构建守护进程 进程间通信: 进程通信的基本概念...

    嵌入式Linux ARM开发课件第十一讲

    linux操作系统原理及组成 介绍linux系统目录组织结构 linux 运行基理,系统运行过程 配置裁减编译linux内核 进程属性和状态转换;进程控制与调度;进程相关系统调用,构建守护进程 进程间通信: 进程通信的基本概念...

    嵌入式Linux ARM开发课件第三讲

    linux操作系统原理及组成 介绍linux系统目录组织结构 linux 运行基理,系统运行过程 配置裁减编译linux内核 进程属性和状态转换;进程控制与调度;进程相关系统调用,构建守护进程 进程间通信: 进程通信的基本概念...

    嵌入式Linux ARM开发课件第十讲

    linux操作系统原理及组成 介绍linux系统目录组织结构 linux 运行基理,系统运行过程 配置裁减编译linux内核 进程属性和状态转换;进程控制与调度;进程相关系统调用,构建守护进程 进程间通信: 进程通信的基本概念...

    嵌入式Linux ARM开发课件第五讲

    linux操作系统原理及组成 介绍linux系统目录组织结构 linux 运行基理,系统运行过程 配置裁减编译linux内核 进程属性和状态转换;进程控制与调度;进程相关系统调用,构建守护进程 进程间通信: 进程通信的基本概念...

    嵌入式Linux ARM开发课件第七讲

    linux操作系统原理及组成 介绍linux系统目录组织结构 linux 运行基理,系统运行过程 配置裁减编译linux内核 进程属性和状态转换;进程控制与调度;进程相关系统调用,构建守护进程 进程间通信: 进程通信的基本概念...

    嵌入式Linux ARM开发课件第八讲

    linux操作系统原理及组成 介绍linux系统目录组织结构 linux 运行基理,系统运行过程 配置裁减编译linux内核 进程属性和状态转换;进程控制与调度;进程相关系统调用,构建守护进程 进程间通信: 进程通信的基本概念...

    嵌入式Linux ARM开发课件第二讲

    linux操作系统原理及组成 介绍linux系统目录组织结构 linux 运行基理,系统运行过程 配置裁减编译linux内核 进程属性和状态转换;进程控制与调度;进程相关系统调用,构建守护进程 进程间通信: 进程通信的基本概念...

    嵌入式Linux ARM开发课件第四讲

    linux操作系统原理及组成 介绍linux系统目录组织结构 linux 运行基理,系统运行过程 配置裁减编译linux内核 进程属性和状态转换;进程控制与调度;进程相关系统调用,构建守护进程 进程间通信: 进程通信的基本概念...

    ARM应用系统开发详解

    第3章 ARM微处理器的指令系统 3.1 ARM微处理器的指令集概述 3.1.1 ARM微处理器的指令的分类与格式 3.1.2 指令的条件域 3.2 ARM指令的寻址方式 3.2.1 立即寻址 3.2.2 寄存器寻址 3.2.2 寄存器间接寻址 3.2.3 基址变址...

    Linux内核工作原理 word版本 强烈推荐

    大多数Linux核心工作在基于Intel处理器的系统上,但非Intel系统的Linux用户也越来越多。它们是Alpha AXP, ARM, MIPS, Sparc与Power PC。 虽然我可以根据上叙任何一种平台来编写本书的内容,但是我的技术知识与背景...

    基于ARM-linux的简易电子相册c源码+项目说明(课程设计).zip

    基于ARM-linux的简易电子相册c源码+项目说明(课程设计).zip 基于ARM-linux的简易电子相册 开发平台: VScode Ubuntu16.04 S5P6818开发板 技术栈: C语言,文件IO,多线程,滑屏算法 实现原理: 将要播放的图片存储在二...

    嵌入式Linux C编程入门(第2版) PPT

    9.1 linux系统调用及用户编程接口(api) 257 9.1.1 系统调用 257 9.1.2 用户编程接口(api) 257 9.1.3 系统命令 258 9.2 arm linux文件i/o系统概述 258 9.2.1 虚拟文件系统(vfs) 258 9.2.2 通用...

    基于ARM Cortex-A8和Android 4.x的联动报警系统 (Android 、A8、Linux、驱动、NDK)

    第二部分内容,先简单讲解Android系统移植相关原理,然后一步步手把手教大家如何进行Linux内核移植、Android源码编译、以及Android到Cortex A8开发板的移植;第三部分内容,先教大家如何搭建裸机开发环境,然后带领...

    嵌入式Linux视频教程全套2011新版-国嵌嵌入式培训下载地址

    -国嵌内核驱动进阶班-5-3(Linux系统调用).avi -国嵌内核驱动进阶班-5-4(Proc文件系统).avi -国嵌内核驱动进阶班-5-5(内核异常分析).avi -国嵌内核驱动进阶班-5-6(必修实验).avi -第6天(简单字符设备驱动...

Global site tag (gtag.js) - Google Analytics