linux系統調用的封裝格式,ARM Linux系統調用的原理

ARM Linux系統調用的原理ARM Linux系統調用的原理

操作系統為在用戶態運行的進程與硬件設備進行交互提供了一組接口。在應用程序和硬件之間設置一個額外層具有很多優點。首先,這使得編程更加容易,把用戶從學習硬件設備的低級編程特性中解放出來。其次,這極大地提高了系統的安全性,因為內核在試圖滿足某個請求之前在接口級就可以檢查這種請求的正確性。最后,更重要的是這些接口使得程序具有可移植性,因為只要內核所提供的一組接口相同,那么在任一內核之上就可以正確地編譯和執行程序。

Unix系統通過向內核發出系統調用(system call)實現了用戶態進程和硬件設備之間的大部分接口。系統調用是操作系統提供的服務,用戶程序通過各種系統調用,來引用內核提供的各種服務,系統調用的執行讓用戶程序陷入內核,該陷入動作由swi軟中斷完成。

應用編程接口(API)與系統調用的不同在于,前者只是一個函數定義,說明了如何獲得一個給定的服務,而后者是通過軟件中斷向內核發出的一個明確的請求。POSIX標準針對API,而不針對系統調用。Unix系統給程序員提供了很多API庫函數。libc的標準c庫所定義的一些API引用了封裝例程(wrapper routine)(其唯一目的就是發布系統調用)。通常情況下,每個系統調用對應一個封裝例程,而封裝例程定義了應用程序使用的API。反之則不然,一個API沒必要對應一個特定的系統調用。從編程者的觀點看,API和系統調用之間的差別是沒有關系的:唯一相關的事情就是函數名、參數類型及返回代碼的含義。然而,從內核設計者的觀點看,這種差別確實有關系,因為系統調用屬于內核,而用戶態的庫函數不屬于內核。

大部分封裝例程返回一個整數,其值的含義依賴于相應的系統調用。返回-1通常表示內核不能滿足進程的請求。系統調用處理程序的失敗可能是由無效參數引起的,也可能是因為缺乏可用資源,或硬件出了問題等等。在libc庫中定義的errno變量包含特定的出錯碼,每個出錯碼定義為一個常量宏。

當用戶態的進程調用一個系統調用時,CPU切換到內核態并開始執行一個內核函數。因為內核實現了很多不同的系統調用,因此進程必須傳遞一個名為系統調用號(system call number)的參數來識別所需的系統調用。所有的系統調用核都返回一個整數值。這些返回值與封裝例程返回值的約定是不同的。在內中,整數或0表示系統調用成功結束,而負數表示一個出錯條件。在后一種情況下,這個值就是存放在errno變量中必須返回給應用程序的負出錯碼。

ARM Linux系統利用SWI指令來從用戶空間進入內核空間,還是先讓我們了解下這個SWI指令吧。SWI指令用于產生軟件中斷,從而實現從用戶模式到管理模式的變換,CPSR保存到管理模式的SPSR,執行轉移到SWI向量。在其他模式下也可使用SWI指令,處理器同樣地切換到管理模式。指令格式如下:

SWI{cond} immed_24

其中:

immed_2424位立即數,值為從0——16777215之間的整數。

使用SWI指令時,通常使用以下兩種方法進行參數傳遞,SWI異常處理程序可以提供相關的服務,這兩種方法均是用戶軟件協定。

1)、指令中24位的立即數指定了用戶請求的服務類型,參數通過通用寄存器傳遞。SWI異常處理程序要通過讀取引起軟件中斷的SWI指令,以取得24為立即數。如:

MOV R0,#34

SWI 12

2)、指令中的24位立即數被忽略,用戶請求的服務類型由寄存器R0的值決定,參數通過其他的通用寄存器傳遞。如:

MOV R0, #12

MOV R1, #34

SWI 0

在SWI異常處理程序中,取出SWI立即數的步驟為:首先確定引起軟件中斷的SWI指令是ARM指令還是Thumb指令,這可通過對SPSR訪問得到;然后取得該SWI指令的地址,這可通過訪問LR寄存器得到;接著讀出指令,分解出立即數(低24位)。

由用戶空間進入系統調用

通常情況下,我們寫的用戶空間應用程序都是通過封裝的C lib來調用系統調用的。以0.9.30版uClibc中的open為例,來追蹤一下這個封裝的函數是如何一步一步的調用系統調用的。在include/fcntl.h中有定義:

#define open open64

open實際上只是open64的一個別名而已。

在libc/sysdeps/linux/common/open64.c中可以看到:

extern __typeof(open64) __libc_open64;

extern __typeof(open) __libc_open;

可見open64也只不過是__libc_open64的別名,而__libc_open64函數在同一個文件中定義:

libc_hidden_proto(__libc_open64)

int __libc_open64 (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 __libc_open(file, oflag | O_LARGEFILE, mode);

}

libc_hidden_def(__libc_open64)

最終__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的實現。

第六、結束。

參考文檔:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/372005.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/372005.shtml
英文地址,請注明出處:http://en.pswp.cn/news/372005.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

(延遲兩秒,跳轉相應頁面)(返回到上一個頁面并刷新)

1.setTimeout("window.location.href /moment/reason",2000);2.返回到上一個頁面并刷新 self.location document.referrer;2.1常見的幾種刷新方式 a.history.go(-1) 返回上一頁 b.location.reload() 刷新當前頁面 c.history.back() 返回上一頁2.2當…

檢索字符創 php

strstr()可以返回匹配的值 echo strstr("localhost", "os");返回ost echo substr_count("gggggs", "g"); 返回檢索匹配字符創次數 substr_replace 字串替換函數轉載于:https://www.cnblogs.com/lidepeng/p/6078064.html

android8強制將app移到sd卡,小內存手機?APP強制轉移至SD卡教程

雖然近兩年手機的機身內存越做越大&#xff0c;但是身邊總還是有些朋友在使用幾年前的手機。而面對如今海量的豐富應用&#xff0c;早年的手機中內置的存儲空間已經開始捉襟見肘。雖說對于這類機型系統通常都提供了將APP轉移至外置內存卡的功能&#xff0c;可是依然有一些頑固的…

在沒有XML的情況下測試Spring和Hibernate

我非常熱衷于Spring 3中的改進&#xff0c;這些改進最終使您能夠在IDE和編譯器的適當支持下從XML遷移到純Java配置。 它并沒有改變Spring是一個龐大的套件這一事實&#xff0c;并且有時發現您需要的東西可能需要一段時間。 圍繞Hibernate的無XML單元測試就是這樣一回事。 我知道…

Observer觀察者設計模式

Observer設計模式主要包括以下兩種對象: (1)被觀察對象:Subject,它往往包含其他對象感興趣的東西,上面例子中熱水器中就是Subject(被監視對象); (2)觀察對象:Observer,它觀察著Subject,當Subject中的某件事發生后,會告知Observer,Obersver會采取相應的行動。上面例子中顯示器和…

最小生成樹 prime zoj1586

題意&#xff1a;在n個星球&#xff0c;每2個星球之間的聯通需要依靠一個網絡適配器&#xff0c;每個星球喜歡的網絡適配器的價錢不同&#xff0c;先給你一個n&#xff0c;然后n個數&#xff0c;代表第i個星球喜愛的網絡適配器的價錢&#xff0c;然后給出一個矩陣M[i][j]代表第…

android 書架菜單,Android入門3--做一個書架

修改名稱創建項目的時候&#xff0c;APP的名字取為英文或者拼音&#xff0c;是為了簡便&#xff0c;但是顯示在界面上&#xff0c;我們當然希望它是中文的。taoguanstring>我們要做的很簡單&#xff0c;就是在string.xml中&#xff0c;將app_name的內容修改為我們希望的名字…

第一節:整體介紹

Python版本3.5.2&#xff0c;Django版本1.10 創建一個Django工程&#xff0c;并且生成一個名字為mainsite的app django-admin.py startproject myblog python3 manage.py startapp mainsite 文件結構如下&#xff1a; x-powerxpower-CW65S:~/chen/myblog$ tree ./ ./ ├── ma…

Spring @Configuration和FactoryBean

考慮使用FactoryBean通過Spring配置文件定義緩存&#xff1a; <cache:annotation-driven /><context:component-scan base-packageorg.bk.samples.cachexml></context:component-scan><bean idcacheManager classorg.springframework.cache.support.Simpl…

cookie解決 未登錄加入購物車 第一次訪問彈出新手引導頁面

瀏覽器攜帶cookie到服務器, 點擊加入購物車-->后臺檢查-->是否登錄(有沒有sessionid) 沒有登錄--->secookie()返回給瀏覽器,把傳遞過來的商品id, 屬性(多個屬性逗號拼接),數量存起來,(序列號成字符串_不同屬性用下劃線拼接) 轉載于:https://www.cnblogs.com/bj-tony/p…

REST + Spring Security會話問題

REST &#xff0c; 會話 ..等待。 REST應用程序中沒有會話&#xff0c;對嗎&#xff1f; 好吧&#xff0c;那是真的。 如果我們可以避免會議&#xff0c;我們應該這樣做。 REST是無狀態的 。 有關無狀態性的主要問題是身份驗證。 在通常的Web應用程序中&#xff0c;我們習慣于在…

程序猿果真有前端后端client嗎

前端 后端 client DBA OP 程序猿有分這么細的嗎?入行時候有區別. 殊途同歸 吾道一以貫之, 假設作為程序猿不能領悟一貫, 則永遠不清楚.轉載于:https://www.cnblogs.com/blfbuaa/p/6970139.html

HarmonyOS硬件創新合作伙伴,【HarmonyOS】HarmonyOS智能硬件開發學習指南 - HDC2020

2020年9月10日&#xff0c;華為HarmonyOS 2.0版本正式官宣&#xff01;這一次&#xff0c;借助 HarmonyOS 全場景分布式系統和設備生態&#xff0c;將定義全新的硬件、交互和服務體驗&#xff0c;打開煥然一新的全場景世界&#xff0c;不愧是HarmonyOS&#xff01; 那HarmonyOS…

處于RUNNABLE狀態的Java線程未真正運行

最近&#xff0c;我在Java應用程序服務器安裝上進行了分析/調整&#xff0c;以識別瓶頸并修復它們。 在此過程中&#xff08;調整&#xff09;&#xff0c;最常見的操作是在系統加載時檢索許多線程轉儲。 請記住&#xff0c;重載&#xff08;在某些情況下&#xff09;可能會產生…

2.3 關系完整性

關系模型的完整性規則是對關系的某種約束條件。 也就是說關系的值隨著時間變化時應該滿足一些約束條件。 &#xff08;這些約束條件實際上是現實世界的要求。任何關系在任何時刻都要滿足這些語義約束&#xff09; 關系模型中有三類完整性約束&#xff1a; 實體完整性 &#xff…

Android 顏色大全color.xml

使用方法&#xff1a; 將color.xml文件拷到res/values目錄下后我們只需要R.xml文件名稱.name名稱就可以調用了(例如:R.color.red) <?xml version"1.0" encoding"utf-8"?><resources> <color name"white">#FFFFFF</color&…

鴻蒙系統華為論壇,2020中國汽車論壇上 華為三大鴻蒙車載OS系統

在今天的2020中國汽車論壇上&#xff0c;華為公布了三大鴻蒙車載OS系統&#xff0c;同時還宣布已經有大量合作伙伴基于鴻蒙OS進行開發。根據智能汽車解決方案BU總裁王軍所說&#xff0c;這三大鴻蒙OS分別是——鴻蒙座艙操作系統HOS、智能駕駛操作系統AOS和智能車控操作系統VOS&…

只讀ViewObject和聲明性SQL模式

介紹 聲明式SQL模式被認為是基于實體的視圖對象的最有價值的優點之一。 在此模式下&#xff0c;根據UI中顯示的屬性在運行時生成VO的SQL。 例如&#xff0c;如果某個頁面包含一個只有兩列EmployeeId和FirstName的表&#xff0c;則查詢將生成為“從Employees中選擇Employee_ID&a…

android 360加固 例子,[原創]利用VirtualApp實現360加固的soHook簡單例子

簡單demo&#xff0c;為拋磚引玉吧&#xff01;360的加固我們先寫一個最簡單的so&#xff0c;帶導出函數。這已經足夠簡單了&#xff0c;界面上打印這個值。運行起來就這樣。簡單的簡直過分。好了&#xff0c;現在我們讓360加固一下。拿下來自己簽名下&#xff0c;安裝好&#…

scrapy架構解析

轉載于:https://www.cnblogs.com/tianboblog/p/6986695.html