内核与应用的分离(上)

问题

当前的设计中,内核与应用之间的界限是否清楚?

D.T.OS 当前架构图

重新架构

系统启动流程回顾

重构方案

将应用代码和内核代码分离 (app & kernel)

在实模式下分别加载应用和内核到不同内存区域

进入保护模式并跳转到内核代码执行

D.T.OS 内存布局

关键问题

如何分离应用和内核?他们之间又如何交互?

如何加载应用到指定的内存区域(0xF000)?

具体实现

1. 修改 LoadTarget 函数,用参数替代全局变量

2. 创建 app 入口文件 aentry.asm (定义入口函数)

3. 修改 makefile 分开编译 kernel 和 app

4. 修改 task.c 通过共享内存区获取 app 接口

内核与应用的物理分离

blfunc.asm


jmp short _start
nop

header:
    BS_OEMName     db "D.T.Soft"
    BPB_BytsPerSec dw 512
    BPB_SecPerClus db 1
    BPB_RsvdSecCnt dw 1
    BPB_NumFATs    db 2
    BPB_RootEntCnt dw 224
    BPB_TotSec16   dw 2880
    BPB_Media      db 0xF0
    BPB_FATSz16    dw 9
    BPB_SecPerTrk  dw 18
    BPB_NumHeads   dw 2
    BPB_HiddSec    dd 0
    BPB_TotSec32   dd 0
    BS_DrvNum      db 0
    BS_Reserved1   db 0
    BS_BootSig     db 0x29
    BS_VolID       dd 0
    BS_VolLab      db "D.T.OS-0.01"
    BS_FileSysType db "FAT12   "
    
const:
    RootEntryOffset  equ 19
    RootEntryLength  equ 14
    SPInitValue      equ BaseOfStack - EntryItemLength
    EntryItem        equ SPInitValue
    EntryItemLength  equ 32
    FatEntryOffset   equ 1
    FatEntryLength   equ 9
    
_start:
    jmp BLMain
    
; ushort LoadTarget(char* Target,      notice ==> sizeof(char*) == 2
;                   ushort TarLen,
;                   ushort BaseOfTarget,
;                   ushort BOT_Div_0x10
;                   char* Buffer);                                    
; return:
;     dx --> (dx != 0) ? success : failure
LoadTarget:
	mov bp, sp

	mov ax, RootEntryOffset
	mov cx, RootEntryLength
	mov bx, [bp + 10]        ; mov bx, Buffer
	
	call ReadSector
	
	mov si, [bp + 2]         ; mov si, Target
	mov cx, [bp + 4]         ; mov cx, TarLen
	mov dx, 0
	
	call FindEntry
	
	mov bp, sp
	
	cmp dx, 0
	jz finish
	
	mov si, bx
	mov di, EntryItem
	mov cx, EntryItemLength
	
	call MemCpy
	
	mov ax, FatEntryLength
	mov cx, [BPB_BytsPerSec]
	mul cx
	mov bx, [bp + 6]        ; mov bx, BaseOfTarget
	sub bx, ax
	
	mov ax, FatEntryOffset
	mov cx, FatEntryLength
	
	call ReadSector
	
	mov dx, [EntryItem + 0x1A]
	mov es, [bp + 8]       ; mov si, BaseOfTarget / 0x10   mov es, si
	xor si, si
	
loading:
    mov ax, dx
    add ax, 31
    mov cx, 1
    push dx
    push bx
    mov bx, si
    call ReadSector
    pop bx
    pop cx
    call FatVec
    cmp dx, 0xFF7
    jnb finish
    add si, 512
    cmp si, 0
    jnz continue
    mov si, es
    add si, 0x1000
    mov es, si
    mov si, 0
continue:
    jmp loading
 
finish:   
    ret

; cx --> index
; bx --> fat table address
;
; return:
;     dx --> fat[index]
FatVec:
    push cx
    
    mov ax, cx
    shr ax, 1
    
    mov cx, 3
    mul cx
    mov cx, ax
    
    pop ax
    
    and ax, 1
    jz even
    jmp odd

even:    ; FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i];
    mov dx, cx
    add dx, 1
    add dx, bx
    mov bp, dx
    mov dl, byte [bp]
    and dl, 0x0F
    shl dx, 8
    add cx, bx
    mov bp, cx
    or  dl, byte [bp]
    jmp return
    
odd:     ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F );
    mov dx, cx
    add dx, 2
    add dx, bx
    mov bp, dx
    mov dl, byte [bp]
    mov dh, 0
    shl dx, 4
    add cx, 1
    add cx, bx
    mov bp, cx
    mov cl, byte [bp]
    shr cl, 4
    and cl, 0x0F
    mov ch, 0
    or  dx, cx

return: 
    ret

; ds:si --> source
; es:di --> destination
; cx    --> length
MemCpy:
    
    cmp si, di
    
    ja btoe
    
    add si, cx
    add di, cx
    dec si
    dec di
    
    jmp etob
    
btoe:
    cmp cx, 0
    jz done
    mov al, [si]
    mov byte [di], al
    inc si
    inc di
    dec cx
    jmp btoe
    
etob: 
    cmp cx, 0
    jz done
    mov al, [si]
    mov byte [di], al
    dec si
    dec di
    dec cx
    jmp etob

done:   
    ret

; es:bx --> root entry offset address
; ds:si --> target string
; cx    --> target length
;
; return:
;     (dx !=0 ) ? exist : noexist
;        exist --> bx is the target entry
FindEntry:
    push cx
    
    mov dx, [BPB_RootEntCnt]
    mov bp, sp
    
find:
    cmp dx, 0
    jz noexist
    mov di, bx
    mov cx, [bp]
    push si
    call MemCmp
    pop si
    cmp cx, 0
    jz exist
    add bx, 32
    dec dx
    jmp find

exist:
noexist: 
    pop cx
       
    ret

; ds:si --> source
; es:di --> destination
; cx    --> length
;
; return:
;        (cx == 0) ? equal : noequal
MemCmp:

compare:
    cmp cx, 0
    jz equal
    mov al, [si]
    cmp al, byte [di]
    jz goon
    jmp noequal
goon:
    inc si
    inc di
    dec cx
    jmp compare
    
equal: 
noequal:   

    ret


; ax    --> logic sector number
; cx    --> number of sector
; es:bx --> target address
ReadSector:
    mov ah, 0x00
    mov dl, [BS_DrvNum]
    int 0x13
    
    push bx
    push cx
    
    mov bl, [BPB_SecPerTrk]
    div bl
    mov cl, ah
    add cl, 1
    mov ch, al
    shr ch, 1
    mov dh, al
    and dh, 1
    mov dl, [BS_DrvNum]
    
    pop ax
    pop bx
    
    mov ah, 0x02

read:    
    int 0x13
    jc read
    
    ret

我们修改了 LoadTarget 函数,需要通过调用者将参数入栈的方式来获得参数,而不是通过全局变量来获得参数。

boot.asm


%include "blfunc.asm"
%include "common.asm"

org BaseOfBoot

BaseOfStack    equ    BaseOfBoot

Loader db  "LOADER     "
LdLen equ ($-Loader)

BLMain:
    mov ax, cs
	mov ss, ax
	mov ds, ax
	mov es, ax
	mov sp, SPInitValue
	
	push Buffer
	push BaseOfLoader / 0x10
	push BaseOfLoader
	push LdLen
	push Loader
	
	call LoadTarget
	
	cmp dx, 0
	jz output
	jmp BaseOfLoader
	
output:	
	mov ax, cs
	mov es, ax
	
    mov bp, ErrStr
    mov cx, ErrLen
	
	xor dx, dx
    mov ax, 0x1301
	mov bx, 0x0007
	int 0x10
	
	jmp $	

ErrStr db  "NOLD"	
ErrLen equ ($-ErrStr)

Buffer:
	times 510-($-$$) db 0x00
	db 0x55, 0xaa

加载 loader 到 0x9000 地址处,并跳转到 loader 处执行。

loader.asm


%include "blfunc.asm"
%include "common.asm"

org BaseOfLoader

BaseOfStack    equ    BaseOfLoader

Kernel db  "KERNEL     "
KnlLen equ ($-Kernel)

App    db  "APP        "
AppLen equ ($-App)



[section .gdt]
; GDT definition
;                                       Base,         Limit,                Attribute
GDT_ENTRY            :     Descriptor    0,            0,                   0
CODE32_DESC          :     Descriptor    0,            Code32SegLen - 1,    DA_C + DA_32 + DA_DPL0
VIDEO_DESC           :     Descriptor    0xB8000,      0x07FFF,             DA_DRWA + DA_32 + DA_DPL0
CODE32_FLAT_DESC     :     Descriptor    0,            0xFFFFF,             DA_C + DA_32 + DA_DPL0
DATA32_FLAT_DESC     :     Descriptor    0,            0xFFFFF,             DA_DRW + DA_32 + DA_DPL0
TASK_LDT_DESC        :     Descriptor    0,            0,                   0
TASK_TSS_DESC        :     Descriptor    0,            0,                   0
; GDT end

GdtLen    equ   $ - GDT_ENTRY

GdtPtr:

          dw   GdtLen - 1

          dd   0
                   
; GDT Selector
Code32Selector        equ (0x0001 << 3) + SA_TIG + SA_RPL0
VideoSelector         equ (0x0002 << 3) + SA_TIG + SA_RPL0
Code32FlatSelector    equ (0x0003 << 3) + SA_TIG + SA_RPL0
Data32FlatSelector    equ (0x0004 << 3) + SA_TIG + SA_RPL0

; end of [section .gdt]

[section .idt]
align 32
[bits 32]
IDT_ENTRY:
; IDT definition
;                        Selector,             Offset,       DCount,    Attribute
%rep 256
              Gate      Code32Selector,    DefaultHandler,   0,         DA_386IGate + DA_DPL0
%endrep

IdtLen    equ    $ - IDT_ENTRY

IdtPtr:

          dw    IdtLen - 1
          dd    0

; end of [section .idt]



[section .s16]
[bits 16]
BLMain:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, SPInitValue

    ; initialize GDT for 32 bits code segment
    mov esi, CODE32_SEGMENT
    mov edi, CODE32_DESC 

    call InitDescItem 

    ; initialize GDT pointer struct
    mov eax, 0
    mov ax, ds
    shl eax, 4
    add eax, GDT_ENTRY
    mov dword [GdtPtr + 2], eax    

    ; initialize IDT pointer struct
    mov eax, 0
    mov ax, ds
    shl eax, 4
    add eax, IDT_ENTRY
    mov dword [IdtPtr + 2], eax   

    ; load app
    push Buffer
	push BaseOfApp / 0x10
	push BaseOfApp
	push AppLen
	push App	

    call LoadTarget

	add sp, 10	
	cmp dx, 0
	jz AppErr
   
    ; reset es register
    mov ax, cs
    mov es, ax
    
    ; load kernel
    push Buffer
	push BaseOfKernel / 0x10
	push BaseOfKernel
	push KnlLen
	push Kernel
	
    call LoadTarget

	add sp, 10
	cmp dx, 0
	jz KernelErr
	
	call StoreGlobal

    ; 1. load GDT
    lgdt [GdtPtr] 

    ; 2. close interrupt
    ;    load IDT
    ;    set IOPL to 3
    cli 
    
    lidt [IdtPtr]  

    pushf
    pop eax    
    or eax, 0x3000   
    push eax
    popf   

    ; 3. open A20
    in al, 0x92
    or al, 00000010b
    out 0x92, al  

    ; 4. enter protect mode
    mov eax, cr0
    or eax, 0x01
    mov cr0, eax

    ; 5. jump to 32 bits code
    jmp dword Code32Selector : 0

KernelErr:
	mov bp, NoKernel
    mov cx, NKLen
    jmp output  

AppErr:
	mov bp, NoApp
    mov cx, NALen
    jmp output

output:	
	mov ax, cs
	mov es, ax	

	mov dx, 0
    mov ax, 0x1301
	mov bx, 0x0007

	int 0x10

	jmp $

; esi    --> code segment label
; edi    --> descriptor label
InitDescItem:
    push eax

    mov eax, 0
    mov ax, cs
    shl eax, 4
    add eax, esi
    mov word [edi + 2], ax
    shr eax, 16
    mov byte [edi + 4], al
    mov byte [edi + 7], ah

    pop eax  

    ret
;
;
StoreGlobal:
    mov dword [RunTaskEntry], RunTask
    mov dword [LoadTaskEntry], LoadTask
    mov dword [InitInterruptEntry], InitInterrupt
    mov dword [EnableTimerEntry], EnableTimer
    mov dword [SendEOIEntry], SendEOI  

    mov eax, dword [GdtPtr + 2]
    mov dword [GdtEntry], eax

    mov dword [GdtSize], GdtLen / 8

    mov eax, dword [IdtPtr + 2]
    mov dword [IdtEntry], eax

    mov dword [IdtSize], IdtLen / 8

    ret

[section .sfunc]
[bits 32]
;
;
Delay:
    %rep 5
    nop
    %endrep

    ret   
;
;
Init8259A:
    push ax 

    ; master
    ; ICW1
    mov al, 00010001B
    out MASTER_ICW1_PORT, al

    call Delay
    
    ; ICW2
    mov al, 0x20
    out MASTER_ICW2_PORT, al    

    call Delay
    
    ; ICW3
    mov al, 00000100B
    out MASTER_ICW3_PORT, al   

    call Delay    

    ; ICW4

    mov al, 00010001B
    out MASTER_ICW4_PORT, al   

    call Delay  

    ; slave
    ; ICW1
    mov al, 00010001B
    out SLAVE_ICW1_PORT, al  

    call Delay  

    ; ICW2
    mov al, 0x28
    out SLAVE_ICW2_PORT, al
    
    call Delay

    ; ICW3
    mov al, 00000010B
    out SLAVE_ICW3_PORT, al    

    call Delay   

    ; ICW4
    mov al, 00000001B
    out SLAVE_ICW4_PORT, al   

    call Delay   

    pop ax
    
    ret

    

; al --> IMR register value
; dx --> 8259A port
WriteIMR:
    out dx, al
    call Delay
    ret   

; dx --> 8259A
; return:
;     ax --> IMR register value
ReadIMR:
    in ax, dx
    call Delay

    ret

;
; dx --> 8259A port
WriteEOI:
    push ax
   
    mov al, 0x20
    out dx, al
    
    call Delay    

    pop ax   

    ret



[section .gfunc]
[bits 32]
;
;  parameter  ===> Task* pt
RunTask:
    push ebp

    mov ebp, esp   
    mov esp, [ebp + 8]    

    lldt word [esp + 96]
    ltr word [esp + 98]
    
    pop gs
    pop fs
    pop es
    pop ds   

    popad
  
    add esp, 4    

    mov dx, MASTER_IMR_PORT
    in ax, dx
    
    %rep 5
    nop
    %endrep
   
    and ax, 0xFE
    out dx, al
    
    %rep 5
    nop
    %endrep
   
    iret
    
; void LoadTask(Task* pt);
;
LoadTask:
    push ebp
    mov ebp, esp
    
    mov eax, [ebp + 8]
    
    lldt word [eax + 96]
    
    leave   

    ret      
;
;
InitInterrupt:
    push ebp
    mov ebp, esp
    
    push ax
    push dx
    
    call Init8259A   

    sti   

    mov ax, 0xFF
    mov dx, MASTER_IMR_PORT   

    call WriteIMR
    
    mov ax, 0xFF
    mov dx, SLAVE_IMR_PORT
    
    call WriteIMR
   
    pop dx
    pop ax    

    leave 

    ret



;
;
EnableTimer:
    push ebp
    mov ebp, esp    

    push ax
    push dx   

    mov dx, MASTER_IMR_PORT    

    call ReadIMR    

    and ax, 0xFE
    
    call WriteIMR   

    pop dx
    pop ax
    
    leave

    ret      

; void SendEOI(uint port);
;    port ==> 8259A port
SendEOI:
    push ebp
    mov ebp, esp   

    mov edx, [ebp + 8]   

    mov al, 0x20
    out dx, al
    
    call Delay
    
    leave

    ret 

    

[section .s32]
[bits 32]
CODE32_SEGMENT:
    mov ax, VideoSelector
    mov gs, ax   

    mov ax, Data32FlatSelector
    mov ds, ax
    mov es, ax
    mov fs, ax  

    mov ax, Data32FlatSelector
    mov ss, ax
    mov esp, BaseOfLoader
  
    jmp dword Code32FlatSelector : BaseOfKernel
   
;
;
DefaultHandlerFunc:
    iret
   
DefaultHandler    equ    DefaultHandlerFunc - $$
Code32SegLen    equ    $ - CODE32_SEGMENT

NoKernel db  "No KERNEL"	
NKLen    equ ($-NoKernel)
NoApp    db  "No KERNEL"	
NALen    equ ($-NoApp)

Buffer db  0

在实模式下加载 app 到  0xF000 地址处,加载 kernel 到 0xB000 地址处,跳转到保护模式后,初始化相关段寄存器,然后跳转到 kernel 处执行。

aentry.asm

%include "common.asm"

global _start
global AppModInit

extern AppMain
extern GetAppToRun
extern GetAppNum

[section .text]
[bits 32]
_start:
AppModInit:
    push ebp
    mov ebp, esp
    
    mov dword [GetAppToRunEntry], GetAppToRun
    mov dword [GetAppNumEntry], GetAppNum
    
    call AppMain
    
    leave
    
    ret

app.c

void AppMain()
{
	RegApp("TaskA", TaskA, 255);
	RegApp("TaskB", TaskB, 230);
	RegApp("TaskC", TaskC, 230);
	RegApp("TaskD", TaskD, 255);
}

task.c

static AppInfo* (*GetAppToRun)(uint index) = NULL;
static uint (*GetAppNum)() = NULL;

void TaskModInit()
{
	int i = 0;
    
    GetAppToRun = (void*)(*((uint*)GetAppToRunEntry));
    GetAppNum = (void*)(*((uint*)GetAppNumEntry));
    
    Queue_Init(&gFreeTaskNode);
    Queue_Init(&gReadyTask);
    Queue_Init(&gRunningTask);
    Queue_Init(&gWaitingTask);
    
    for(i = 0; i < MAX_TASK_NUM; i++)
    {
    	Queue_Add(&gFreeTaskNode, (QueueNode*)AddrOff(gTaskBuff, i));
    }
    
    SetDescValue(AddrOff(gGdtInfo.entry, GDT_TASK_TSS_INDEX), (uint)&gTSS, sizeof(gTSS)-1, DA_386TSS + DA_DPL0);
    
    InitTask(&(gIdleTask.task), 0, "IdleTask", IdleTask, 255);
    
    ReadyToRunning();
    CheckRunningTask();
    
}

kmain.c

#include "task.h"
#include "interrupt.h"
#include "screen.h"

void KMain()
{
	void (*AppModInit)() = (void*)BaseOfApp;

    int n = PrintString("D.T.OS\n");
    
    PrintString("GDT Entry: ");
    PrintIntHex((uint)gGdtInfo.entry);
    PrintChar('\n');
    
    PrintString("GDT Size: ");
    PrintIntDec((uint)gGdtInfo.size);
    PrintChar('\n');
    
    PrintString("IDT Entry: ");
    PrintIntHex((uint)gIdtInfo.entry);
    PrintChar('\n');
    
    PrintString("IDT Size: ");
    PrintIntDec((uint)gIdtInfo.size);
    PrintChar('\n');
    
    AppModInit();
    
    TaskModInit();
    
    IntModInit();
    
    LaunchTask();
    
}

makefile


.PHONY : all clean rebuild

KERNEL_SRC := kmain.c      \
              screen.c     \
              kernel.c     \
              utility.c    \
              task.c       \
              interrupt.c  \
              ihandler.c   \
              list.c       \
              queue.c      

APP_SRC :=    screen.c     \
              utility.c    \
              app.c

KERNEL_ADDR := B000
APP_ADDR    := F000

IMG := D.T.OS
IMG_PATH := /mnt/hgfs

DIR_DEPS := deps
DIR_EXES := exes
DIR_OBJS := objs

DIRS := $(DIR_DEPS) $(DIR_EXES) $(DIR_OBJS)

KENTRY_SRC := kentry.asm
AENTRY_SRC := aentry.asm
BLFUNC_SRC := blfunc.asm
BOOT_SRC   := boot.asm
LOADER_SRC := loader.asm
COMMON_SRC := common.asm

BOOT_OUT   := boot
LOADER_OUT := loader
KERNEL_OUT := kernel
KENTRY_OUT := $(DIR_OBJS)/kentry.o
APP_OUT    := app
AENTRY_OUT := $(DIR_OBJS)/aentry.o

KERNEL_EXE := kernel.out
KERNEL_EXE := $(addprefix $(DIR_EXES)/, $(KERNEL_EXE))

APP_EXE := app.out
APP_EXE := $(addprefix $(DIR_EXES)/, $(APP_EXE))


KERNEL_OBJS := $(KERNEL_SRC:.c=.o)
KERNEL_OBJS := $(addprefix $(DIR_OBJS)/, $(KERNEL_OBJS))
KERNEL_DEPS := $(KERNEL_SRC:.c=.dep)
KERNEL_DEPS := $(addprefix $(DIR_DEPS)/, $(KERNEL_DEPS))

APP_OBJS := $(APP_SRC:.c=.o)
APP_OBJS := $(addprefix $(DIR_OBJS)/, $(APP_OBJS))
APP_DEPS := $(APP_SRC:.c=.dep)
APP_DEPS := $(addprefix $(DIR_DEPS)/, $(APP_DEPS))

all : $(DIR_OBJS) $(DIR_EXES) $(IMG) $(BOOT_OUT) $(LOADER_OUT) $(KERNEL_OUT) $(APP_OUT)
	@echo "Build Success ==> D.T.OS!"
	
ifeq ("$(MAKECMDGOALS)", "all")
-include $(KERNEL_DEPS) $(APP_DEPS)
endif

ifeq ("$(MAKECMDGOALS)", "")
-include $(KERNEL_DEPS) & (APP_DEPS)
endif

$(IMG) :
	bximage $@ -q -fd -size=1.44
	
$(BOOT_OUT) : $(BOOT_SRC) $(BLFUNC_SRC)
	nasm $< -o $@
	dd if=$@ of=$(IMG) bs=512 count=1 conv=notrunc
	
$(LOADER_OUT) : $(LOADER_SRC) $(COMMON_SRC) $(BLFUNC_SRC)
	nasm $< -o $@
	sudo mount -o loop $(IMG) $(IMG_PATH)
	sudo cp $@ $(IMG_PATH)/$@
	sudo umount $(IMG_PATH)
	
$(KENTRY_OUT) : $(KENTRY_SRC) $(COMMON_SRC)
	nasm -f elf $< -o $@
    
$(KERNEL_OUT) : $(KERNEL_EXE)
	./elf2kobj -c$(KERNEL_ADDR) $< $@
	sudo mount -o loop $(IMG) $(IMG_PATH)
	sudo cp $@ $(IMG_PATH)/$@
	sudo umount $(IMG_PATH)
	
$(KERNEL_EXE) : $(KENTRY_OUT) $(KERNEL_OBJS)
	ld -s $^ -o $@
	
$(AENTRY_OUT) : $(AENTRY_SRC) $(COMMON_SRC)
	nasm -f elf $< -o $@
    
$(APP_OUT) : $(APP_EXE)
	./elf2kobj -c$(APP_ADDR) $< $@
	sudo mount -o loop $(IMG) $(IMG_PATH)
	sudo cp $@ $(IMG_PATH)/$@
	sudo umount $(IMG_PATH)
	
$(APP_EXE) : $(AENTRY_OUT) $(APP_OBJS)
	ld -s $^ -o $@
	
$(DIR_OBJS)/%.o : %.c
	gcc -fno-builtin -fno-stack-protector -c $(filter %.c, $^) -o $@

$(DIRS) :
	mkdir $@

ifeq ("$(wildcard $(DIR_DEPS))", "")
$(DIR_DEPS)/%.dep : $(DIR_DEPS) %.c
else
$(DIR_DEPS)/%.dep : %.c
endif
	@echo "Creating $@ ..."
	@set -e; \
	gcc -MM -E $(filter %.c, $^) | sed 's,\(.*\)\.o[ :]*,objs/\1.o $@ : ,g' > $@
	
clean :
	rm -fr $(IMG) $(BOOT_OUT) $(LOADER_OUT) $(KERNEL_OUT) $(APP_OUT) $(DIRS)
	
rebuild :
	@$(MAKE) clean
	@$(MAKE) all

aentry.asm 中将 AppModInit 定义为一个函数,kmain.c 将函数指针 AppModInit 设置为 0xF000,通过设置这个函数指针,可以调用 aentry.asm 中的 AppModInit 函数,AppModInit 函数用于将 app 的相关函数的入口地址通过共享内存来给内核提供接口,之后调用 AppMain(),来初始化任务。

task.c 定义两个函数指针,用于保存 app 的相关接口函数的地址。

这样内核和应用程序的代码分开编译,并且能正常正确的运行了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值