1 First of all, let's talk about how the application program can realize the system call ( User mode -> Kernel mode )?

We use the app's write() Function as an example :

1) First of all, user mode write() The function will enter glibc library , It will write() Convert to swi(Software Interrupt) Instructions , This creates software interrupts ,swi The instructions are as follows :

swi   #val   //val: bit[23:0] Count now , The val It is used to determine which kernel function the user function needs to call 

2) then CPU Will jump to the entry of the exception vector vector_swi It's about , according to swi After the order val value , Find the corresponding in an array table sys_write() function

The code is as follows ( be located arch\arm\kernel\entry-common.S):

ENTRY(vector_swi)              
           /* Protect the user state of the scene */            sub  sp, sp, #S_FRAME_SIZE
       stmia      sp, {r0 - r12}                 @ Calling r0 - r12
       add r8, sp, #S_PC
       stmdb     r8, {sp, lr}^                   @ 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  ... ...   ldr   scno, [lr, #-4]                 @ get SWI instruction  // obtain SWI value A710(       and  ip, scno, #0x0f000000 @ check for SWI)A710(       teq  ip, #0x0f000000)                               // check SWI Of bit[27:24] Is it 0xfA710(       bne  .Larm710bug) ... ...   enable_irq                           // call enable_irq() function    get_thread_info tsk
       adr  tbl, sys_call_table            @ load syscall table pointer  // tbl Equal to the base address of the array table    ldr   ip, [tsk, #TI_FLAGS]          @ check for syscall tracing  
     ... ...bic  scno, scno, #0xff000000              @ mask off SWI op-code // Only keep SWI Of bit[23:0], That is to say val value eor  scno, scno, #__NR_SYSCALL_BASE @ check OS number    
// about 2440 But speaking ,__NR_SYSCALL_BASE The base address equals 0x900000, in other words val The value is 0x900000 when , After exclusive or ,scno It is equal to 0, Represents the base address of an array table ( The first function position )... ...ldrcc pc, [tbl, scno, lsl #2]             @ call sys_* routine          //pc=(tbl+scno)<<2, Implementation calls sys_write()   //tbl: Array table base address ,  scno: To call sys_write() The index of the value      lsl #2: Move left 2 position , A function pointer occupies 4 Bytes 

As you can see from the code above ,2440 Of val The base value is 0x900000, That is to say, to call the first function of the array table , Then use :

swi  #0x900000

2 Next , Let's make a system call

1) In kernel , Copy one sys_hello function , And then put it in the array table , for swi call
2) Write applications , Directly through swi Instructions , To call sys_hello function

3 Modelled on the sys_hello()

3.1 First, look up the array table , With sys_write For example , Search for the location of arch/arm/kernel/calls.S, As shown in the figure below :

 Insert picture description here
among CALL The definitions are as follows :

.equ NR_syscalls,0     // take NR_syscalls=0#define CALL(x) .equ NR_syscalls,NR_syscalls+1   // take CALL(x)  Defined as :NR_syscalls=NR_syscalls+1 , That is, every one of them CALL(), Then CALL It's worth it +1#include "calls.S"              // take calls.S The contents of the package come in ,CALL(x) It's already defined , Will be calls.S Everything in it CALL(sys_xx) line up #undef CALL                    // revoke CALL Definition #define CALL(x) .long x        // And then put them all together sys_xx With long(4 byte ) alignment , A function pointer occupies 4 byte 

3.2 So we are call.S Of documents CALL() Add a paragraph at the end of the list , As shown in the figure below , sys_hello() Of val The value is 352:

 Insert picture description here

3.3 fs\read_write.c Write one in the file sys_hello() function

asmlinkage void sys_hello(const char __user * buf, size_t count)     // Print count Long data {char ker_buf[100];if(buf){ copy_from_user(ker_buf, buf, (count<100)? count : 100);  ker_buf[99]='\0';  printk("sys_hello:%s\n",ker_buf);}}

3.4 include\linux\syscalls.h The document states sys_hello()

asmlinkage void sys_hello(const char __user * buf, size_t count);

4. Write applications

#include <errno.h>#include <unistd.h>#define __NR_SYSCALL_BASE       0x900000void hello(char *buf, int count){/* swi */asm ("mov r0, %0\n"   /* save the argment in r0 */  //%0 be equal to buf  "mov r1, %1\n"   /* save the argment in r0 */   //%1 be equal to count "swi %2\n"   /* do the system call */        //%2 be equal to 0x900352 :                                                       // Output section  : "r"(buf), "r"(count), "i" (__NR_SYSCALL_BASE + 352)  // Input unit  : "r0", "r1");                         // Damage department , The original data will be destroyed }int main(int argc, char **argv){printf("in app, call hello\n");hello("www.100ask.net", 15);// This function calls the kernel sys_hello()return 0;}

4.1 among asm () It's an embedded assembly ( Reference resources linux Scenario analysis of kernel source code 1.5.2 section )

The format is as follows :
asm( The command department : Output section : Input unit : Damage department );
The command department
In the command department , If it appears %0、%1、%2 etc. , Represents the variable after the instruction unit .
For example, the code above "mov r0, %0\n".
among %0 It will correspond to buf value , and "r" It's a constraint letter ,r Represents any register , In pretreatment , A register is automatically allocated , take buf The value is put in this register , And then run mov r0 (buf The corresponding register )

Output section
The constraint letter of each output unit should be added with "=", such as :

int num=5,val;asm("mov %0,%1\n":"=r"(val)                // Appoint val It's an output unit , perform mov after ,val Is equal to 5:"i"(num)                // "i" Constraint letter , Express num It's an immediate number :      );

Input unit
The only difference from the output is , You can't precede the constraint letter with "="
Common constraint letters , As shown in the figure below :
 Insert picture description here

Damage department
Similar to the input and output , It is generally used to deal with the intermediate process of operation , Because the original content will be damaged , Like the one above hello() Inside "r0", “r1”, It's just used as a parameter , Passed to the kernel sys_hello()

5. Reburning the kernel , Test application

 Insert picture description here
As shown in the figure above , A simple system call will OK 了

After the call is successful , You can modify it sys_hello(), To print the register values of the application , Breaking point , To debug the application , Need to use :

task_pt_regs(current);          // Get the contents of each register of the current application , Will return a pt_regs Structure