[自製操作系統] 第09回 載入內核

来源:https://www.cnblogs.com/Lizhixing/archive/2022/06/20/16393527.html
-Advertisement-
Play Games

目錄 一、前景回顧 二、用C語言編寫內核 三、載入內核 四、運行測試 一、前景回顧 本回開始,我們要開始編寫內核代碼了,在此之前,先梳理一下已經完成的工作。 藍色部分是目前已經完成的部分,黃色部分是本節將要實現的。 二、用C語言編寫內核 為什麼要用C語言來編寫內核呢,其實用彙編語言也可以實現,只是對 ...


目錄
一、前景回顧
二、用C語言編寫內核
三、載入內核
四、運行測試

 

一、前景回顧

  本回開始,我們要開始編寫內核代碼了,在此之前,先梳理一下已經完成的工作。
  
  藍色部分是目前已經完成的部分,黃色部分是本節將要實現的。

二、用C語言編寫內核

  為什麼要用C語言來編寫內核呢,其實用彙編語言也可以實現,只是對於我們來講,看C語言代碼肯定要比彙編語言更容易理解,看起來也沒那麼費勁。所以用C語言可以更加省事。

  先來看看我們內核代碼的最初形態,首先在項目路徑下新建一個project/kernel的目錄,以後我們內核相關的文件都存放於此,在該目錄下新建一個名為main.c的文件,在main.c中鍵入如下代碼:

1 int main(void)
2 {
3     while(1);
4     return 0;
5 }

  這就是我們的內核代碼,當然現在什麼都還沒有,就算內核成功載入進去也沒有什麼反應。這裡我們先實現一個自己的列印函數,在main函數中調用這個列印函數來列印出“HELLO KERNEL”的字元,這樣就能測試內核代碼運行是否成功。前面我們一直都是直接操作顯存段的記憶體來往屏幕上來列印字元,現在開始用C語言編程了,自然要封裝一個列印函數來列印字元。

  同樣,在項目路徑下新建另一個project/lib/kernel目錄,該目錄用來存放一些供內核使用的庫文件。在該目錄下新建名為print.S和print.h的文件,在此之前,我們在project/lib目錄下新建一個名為stdint.h的文件用來定義一些數據類型。代碼如下:

 1 #ifndef __LIB_STDINT_H__
 2 #define __LIB_STDINT_H__
 3 typedef signed char int8_t;
 4 typedef signed short int int16_t;
 5 typedef signed int int32_t;
 6 typedef signed long long int int64_t;
 7 typedef unsigned char uint8_t;
 8 typedef unsigned short int uint16_t;
 9 typedef unsigned int uint32_t;
10 typedef unsigned long long int uint64_t;
11 #endif
stdint.h
  1 TI_GDT         equ  0
  2 RPL0           equ  0
  3 SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0
  4 
  5 section .data
  6 put_int_buffer dq 0
  7 
  8 [bits 32]
  9 section .text
 10 ;-----------------------------------put_str--------------------------------------
 11 ;功能描述:put_str通過put_char來列印以0字元結尾的字元串
 12 ;----------------------------------------------------------------------------------
 13 global put_str
 14 put_str:
 15         push ebx
 16         push ecx
 17         xor ecx, ecx
 18         mov ebx, [esp + 12]
 19 .goon:
 20         mov cl, [ebx]
 21         cmp cl, 0
 22         jz .str_over
 23         push ecx
 24         call put_char
 25         add esp, 4
 26         inc ebx
 27         jmp .goon
 28 .str_over:
 29         pop ecx
 30         pop ebx
 31         ret
 32         
 33 ;--------------------------put_char-------------------------
 34 ;功能描述:把棧中的一個字元寫入到游標所在處
 35 ;---------------------------------------------------------------
 36 global put_char
 37 put_char:
 38         pushad                                         ;備份32位寄存器環境
 39         mov ax, SELECTOR_VIDEO  ;不能直接把立即數送入段寄存器中
 40         mov gs, ax
 41 
 42         ;----------------------獲取當前游標位置---------------------------------
 43         ;先獲取高8位
 44         mov dx, 0x03d4
 45         mov al, 0x0e
 46         out dx, al
 47         mov dx, 0x03d5
 48         in al, dx
 49         mov ah, al
 50 
 51         ;再獲取低8位
 52         mov dx, 0x03d4
 53         mov al, 0x0f
 54         out dx, al
 55         mov dx, 0x03d5
 56         in al, dx
 57 
 58         ;將游標位置存入bx
 59         mov bx, ax
 60 
 61         ;在棧中獲取待列印的字元
 62         mov ecx, [esp + 36]  ;pushad將8個32位寄存器都壓入棧中,再加上主調函數4位元組的返回地址,所以esp+36之後才是主調函數壓入的列印字元
 63         cmp cl, 0xd                 ;判斷該字元是否為CR(回車),CR的ASCII碼為0x0d
 64         jz .is_carriage_return
 65 
 66         cmp cl, 0xa                 ;判斷該字元是否為LF(換行),LF的ASCII碼為0x0a
 67         jz .is_line_feed
 68 
 69         cmp cl, 0x8                 ;判斷該字元是否為BS(空格),BS的ASCII碼為0x08
 70         jz .is_backspace
 71 
 72         jmp .put_other
 73 
 74 ;字元為BS(空格)的處理辦法
 75 .is_backspace:
 76         dec bx
 77         shl bx, 1
 78         mov byte [gs:bx], 0x20
 79         inc bx
 80         mov byte [gs:bx], 0x07
 81         shr bx, 1
 82         jmp set_cursor
 83 
 84 ;字元為CR(回車)以及LF(換行)的處理辦法
 85 .is_line_feed:
 86 .is_carriage_return:
 87         xor dx, dx
 88         mov ax, bx
 89         mov si, 80
 90         div si
 91         sub bx, dx
 92 
 93 ;CR(回車)符的處理結束
 94 .is_carriage_return_end:
 95         add bx, 80
 96         cmp bx, 2000
 97 ;LF(換行)符的處理結束
 98 .is_line_feed_end:
 99         jl set_cursor
100 
101 .put_other:
102         shl bx, 1
103         mov [gs:bx], cl
104         inc bx
105         mov byte [gs:bx], 0x07
106         shr bx, 1
107         inc bx
108         cmp bx, 2000
109         jl set_cursor
110 
111 .roll_screen:
112         cld
113         mov ecx, 960
114         mov esi, 0xc00b80a0
115         mov edi, 0xc00b8000
116         rep movsd
117         
118         mov ebx, 3840
119         mov ecx, 80
120 
121 .cls:
122         mov word [gs:ebx], 0x0720
123         add ebx, 2
124         loop .cls
125         mov bx, 1920
126 global set_cursor
127 set_cursor:
128         mov dx, 0x03d4
129         mov al, 0x0e
130         out dx, al
131         mov dx, 0x03d5
132         mov al, bh
133         out dx, al
134 
135         mov dx, 0x03d4
136         mov al, 0x0f
137         out dx, al
138         mov dx, 0x03d5
139         mov al, bl
140         out dx, al
141 .put_char_done:
142         popad
143         ret
144 ;-----------------------------------put_int--------------------------------------
145 ;功能描述:將小端位元組序的數字變成對應的ASCII後,倒置
146 ;輸入:棧中參數為待列印的數字
147 ;輸出:在屏幕中列印十六進位數字,並不會列印首碼0x
148 ;如列印十進位15時,只會列印f,而不是0xf
149 ;----------------------------------------------------------------------------------
150 global put_int
151 put_int:
152         pushad
153         mov ebp, esp
154         mov eax, [ebp + 36]
155         mov edx, eax
156         mov edi, 7
157         mov ecx, 8
158         mov ebx, put_int_buffer
159 
160 ;將32位數字按照16進位的形式從低位到高位逐個處理,共處理8個16進位數字
161 .16based_4bits:                   ; 每4位二進位是16進位數字的1位,遍歷每一位16進位數字
162         and edx, 0x0000000F               ; 解析16進位數字的每一位。and與操作後,edx只有低4位有效
163         cmp edx, 9                   ; 數字0~9和a~f需要分別處理成對應的字元
164         jg .is_A2F 
165         add edx, '0'                   ; ascii碼是8位大小。add求和操作後,edx低8位有效。
166         jmp .store
167 .is_A2F:
168         sub edx, 10                   ; A~F 減去10 所得到的差,再加上字元A的ascii碼,便是A~F對應的ascii碼
169         add edx, 'A'
170 
171 ;將每一位數字轉換成對應的字元後,按照類似“大端”的順序存儲到緩衝區put_int_buffer
172 ;高位字元放在低地址,低位字元要放在高地址,這樣和大端位元組序類似,只不過咱們這裡是字元序.
173 .store:
174 ; 此時dl中應該是數字對應的字元的ascii碼
175         mov [ebx+edi], dl               
176         dec edi
177         shr eax, 4
178         mov edx, eax 
179         loop .16based_4bits
180 
181 ;現在put_int_buffer中已全是字元,列印之前,
182 ;把高位連續的字元去掉,比如把字元000123變成123
183 .ready_to_print:
184         inc edi                   ; 此時edi退減為-1(0xffffffff),加1使其為0
185 .skip_prefix_0:  
186         cmp edi,8                   ; 若已經比較第9個字元了,表示待列印的字元串為全0 
187         je .full0 
188 ;找出連續的0字元, edi做為非0的最高位字元的偏移
189 .go_on_skip:   
190         mov cl, [put_int_buffer+edi]
191         inc edi
192         cmp cl, '0' 
193         je .skip_prefix_0               ; 繼續判斷下一位字元是否為字元0(不是數字0)
194         dec edi                   ;edi在上面的inc操作中指向了下一個字元,若當前字元不為'0',要恢復edi指向當前字元               
195         jmp .put_each_num
196 
197 .full0:
198         mov cl,'0'                   ; 輸入的數字為全0時,則只列印0
199 .put_each_num:
200         push ecx                   ; 此時cl中為可列印的字元
201         call put_char
202         add esp, 4
203         inc edi                   ; 使edi指向下一個字元
204         mov cl, [put_int_buffer+edi]           ; 獲取下一個字元到cl寄存器
205         cmp edi,8
206         jl .put_each_num
207         popad
208         ret
print.S
1 #ifndef  __LIB_KERNEL_PRINT_H
2 #define  __LIB_KERNEL_PRINT_H
3 #include "stdint.h"
4 void put_char(uint8_t char_asci);
5 void put_str(char *message);
6 void put_int(uint32_t num);
7 #endif
print.h

  最後輸入如下命令來編譯print.S:

nasm -f elf -o ./project/lib/kernel/print.o ./project/lib/kernel/print.S

  完善了列印函數後,我們現在可以在main函數中實現列印功能了,修改main.c文件:

1 #include "print.h"
2 int main(void)
3 {
4     put_str("HELLO KERNEL\n");
5     while(1);
6     return 0;
7 }

三、載入內核

  前面我們已經將內核代碼實現完成了,接下來按道理應該和前面一樣,將main.c文件編譯載入到硬碟中,隨後通過loader來讀取載入該文件,最終跳轉運行。的確也是如此,不過略有不同。請聽我慢慢講來。

  現在我們是main.c文件,不同於彙編代碼,我們接下來要使用gcc工具將main.c文件編譯成main.o文件:

gcc -m32 -I project/lib/kernel/ -c -fno-builtin project/kernel/main.c -o project/kernel/main.o

  它只是一個目標文件,也稱為重定位文件,重定位文件指的是文件裡面所用的符號還沒有安排地址,這些符號的地址將來是要與其他目標文件“組成”一個可執行文件時再重定位(編排地址),這裡的符號就是指的所調用的函數或使用的變數,看我們的main.c文件中,在main函數中調用了print.h中聲明的put_str函數,所以將來main.o文件需要和print.o文件一起組成可執行文件。

  如何“組成”呢?這裡的“組成”其實就是指的C語言程式變成可執行文件下的四步驟(預處理、編譯、彙編和鏈接)中的鏈接,Linux下使用的是ld命令來鏈接,我們是在Linux平臺下的,所以自然使用ld命令:

ld -m elf_i386 -Ttext 0xc0001500 -e main -o project/kernel/kernel.bin project/kernel/main.o project/lib/kernel/print.o

  最終生成可執行文件kernel.bin。它就是我們需要載入到硬碟里的那個文件。

  到這裡都和前面步驟一致,只是後面loader並不是單純的將kernel.bin文件拷貝到記憶體某處再跳轉執行。這是因為我們生成的kernel.bin文件的格式為elf,elf格式的文件,在文件最開始有一個名為elf格式頭的部分,該部分詳細包含了整個文件的信息,具體內容過多我這裡不再展開講,感興趣的朋友可以參考原書《操作系統真象還原》p213~222,或者百度。所以說如果我們只是單純地跳轉到該文件的載入處,那麼必定會出現問題,因為該文件的開始部分並不是可供CPU執行的程式,我們跳轉的地址應該是該文件的程式部分。這個地址在我們前面鏈接時已經指定為0xc0001500,因為我們前面已經開啟了分頁機制,所以實際上這個地址對應的是物理地址的0x1500處。

  接下來再修改loader.S文件,增加拷貝內核部分代碼以及拷貝函數代碼,為了便於閱讀,我將新代碼附在了之前的loader.S文件下,除此之外,boot.inc也有新增的內容。

  1 %include "boot.inc"
  2 section loader vstart=LOADER_BASE_ADDR
  3 LOADER_STACK_TOP equ LOADER_BASE_ADDR
  4 jmp loader_start
  5 
  6 ;構建gdt及其內部描述符
  7 GDT_BASE:        dd 0x00000000
  8                 dd 0x00000000
  9 CODE_DESC:       dd 0x0000FFFF
 10                 dd DESC_CODE_HIGH4
 11 DATA_STACK_DESC: dd 0x0000FFFF
 12                 dd DESC_DATA_HIGH4
 13 VIDEO_DESC:      dd 0x80000007
 14                 dd DESC_VIDEO_HIGH4
 15 
 16 GDT_SIZE  equ $-GDT_BASE
 17 GDT_LIMIT equ GDT_SIZE-1
 18 times 60 dq 0  ;此處預留60個描述符的空位
 19 
 20 SELECTOR_CODE  equ (0x0001<<3) + TI_GDT + RPL0
 21 SELECTOR_DATA  equ (0x0002<<3) + TI_GDT + RPL0
 22 SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0
 23 
 24 ;以下是gdt指針,前2個位元組是gdt界限,後4個位元組是gdt的起始地址
 25 gdt_ptr   dw GDT_LIMIT 
 26         dd GDT_BASE
 27 
 28 ;---------------------進入保護模式------------
 29 loader_start:
 30     ;一、打開A20地址線
 31     in al, 0x92
 32     or al, 0000_0010B
 33     out 0x92, al
 34     
 35     ;二、載入GDT
 36     lgdt [gdt_ptr]
 37 
 38     ;三、cr0第0位(pe)置1
 39     mov eax, cr0
 40     or eax, 0x00000001
 41     mov cr0, eax
 42     
 43     jmp dword SELECTOR_CODE:p_mode_start ;刷新流水線
 44 
 45     [bits 32]
 46     p_mode_start:
 47             mov ax, SELECTOR_DATA
 48             mov ds, ax
 49             mov es, ax
 50             mov ss, ax
 51             mov esp, LOADER_STACK_TOP
 52             mov ax, SELECTOR_VIDEO
 53             mov gs, ax
 54             
 55             mov byte [gs:160], 'p'
 56 
 57 ;------------------開啟分頁機制-----------------
 58     ;一、創建頁目錄表並初始化頁記憶體點陣圖
 59     call setup_page
 60 
 61     ;將描述符表地址及偏移量寫入記憶體gdt_ptr,一會兒用新地址重新載入
 62     sgdt [gdt_ptr]
 63     ;將gdt描述符中視頻段描述符中的段基址+0xc0000000
 64     mov ebx, [gdt_ptr + 2]
 65     or dword [ebx + 0x18 + 4], 0xc0000000
 66             
 67     ;將gdt的基址加上0xc0000000使其成為內核所在的高地址
 68     add dword [gdt_ptr + 2], 0xc0000000
 69 
 70     add esp, 0xc0000000  ;將棧指針同樣映射到內核地址
 71             
 72     ;二、將頁目錄表地址賦值給cr3
 73     mov eax, PAGE_DIR_TABLE_POS
 74     mov cr3, eax
 75             
 76     ;三、打開cr0的pg位
 77     mov eax, cr0
 78     or eax, 0x80000000
 79     mov cr0, eax
 80             
 81     ;在開啟分頁後,用gdt新的地址重新載入
 82     lgdt [gdt_ptr]
 83     mov byte [gs:160], 'H'
 84     mov byte [gs:162], 'E'
 85     mov byte [gs:164], 'L'
 86     mov byte [gs:166], 'L'
 87     mov byte [gs:168], 'O'
 88     mov byte [gs:170], ' '
 89     mov byte [gs:172], 'P'
 90     mov byte [gs:174], 'A'
 91     mov byte [gs:176], 'G'
 92     mov byte [gs:178], 'E'
 93 
 94 ;---------------------------------------------
 95 
 96 ;--------------------拷貝內核文件併進入kernel--------------------------
 97     mov eax, KERNEL_START_SECTOR              ;kernel.bin所在的扇區號 0x09
 98     mov ebx, KERNEL_BIN_BASE_ADDR             ;從磁碟讀出後,寫入到ebx指定的地址0x70000
 99     mov ecx, 200                              ;讀入的扇區數
100 
101     call rd_disk_m_32
102 
103     ;由於一直處在32位下,原則上不需要強制刷新,但是以防萬一還是加上
104     ;跳轉到kernel處
105     jmp SELECTOR_CODE:enter_kernel
106     
107     enter_kernel:
108         call kernel_init
109         mov esp, 0xc009f000               ;更新棧底指針
110         jmp KERNEL_ENTRY_POINT            ;內核地址0xc0001500
111         ;jmp $
112         ;---------------------將kernel.bin中的segment拷貝到指定的地址
113         kernel_init:
114             xor eax, eax
115             xor ebx, ebx   ;ebx記錄程式頭表地址
116             xor ecx, ecx    ;cx記錄程式頭表中的program header數量
117             xor edx, edx    ;dx記錄program header 尺寸,即e_phentsize
118 
119             ;偏移文件42位元組處的屬性是e_phentsize, 表示program header大小
120             mov dx, [KERNEL_BIN_BASE_ADDR + 42]
121             
122             ;偏移文件28位元組處的屬性是e_phoff
123             mov ebx, [KERNEL_BIN_BASE_ADDR + 28]
124 
125             add ebx, KERNEL_BIN_BASE_ADDR
126             mov cx, [KERNEL_BIN_BASE_ADDR + 44]
127     
128             .each_segment: 
129                     cmp byte [ebx + 0], PT_NULL
130                     je .PTNULL
131 
132             ;為函數memcpy壓入參數,參數是從右往左壓入
133             push dword [ebx + 16]
134             mov eax, [ebx + 4]
135             add eax, KERNEL_BIN_BASE_ADDR
136             push eax
137             push dword [ebx + 8]
138             call mem_cpy
139             add esp, 12
140 
141             .PTNULL:
142                     add ebx, edx
143                     loop .each_segment
144             ret
145 
146             ;-----------逐位元組拷貝mem_cpy(dst, src, size)
147             mem_cpy:
148                     cld
149                     push ebp
150                     mov ebp, esp
151                     push ecx
152                     mov edi, [ebp + 8]
153                     mov esi, [ebp + 12]
154                     mov ecx, [ebp + 16]
155                     rep movsb
156 
157                     pop ecx
158                     pop ebp
159                     ret 
160 ;---------------------------------------------------    
161 
162 ;--------------函數聲明------------------------
163     ;setup_page:(功能)設置分頁------------
164     setup_page:
165         ;先把頁目錄

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 大佬理解->Java集合之ArrayList 1、ArrayList的特點 存放的元素有序 元素不唯一(可以重覆) 隨機訪問快 插入刪除元素慢 非線程安全 2、底層實現 底層初始化,使用一個Object類型的空對象數組,初始長度為0; 源碼 //Object類型對象數組引用 transient Ob ...
  • 大佬的理解->《Java IO(五) -- 字元流進階及BufferedWriter,BufferedReader》 1、BufferedReader BufferedReader高效字元流讀取文件基本用法,自帶緩衝區,讀取文件效率高,支持逐行讀取; 1.1 初始化 BufferedReader(R ...
  • 游戲的世界精彩紛呈,有動作類、策略類、角色扮演類等諸多類型,還有很多難以分類的小游戲,讓人玩起來往往愛不釋手 ...
  • 大佬的理解->《Java IO(四) -- 字元流》 FileReader字元流讀取文件,更適合用於讀取文件,可以讀取中文 1、FileReader 1.1 初始化 FileReader(File file) FileReader(String fileName) 1.2 讀取文件內容 read() ...
  • 項目全部代碼地址:https://github.com/Tom-shushu/work-study.git (mqtt-emqt 項目) 先看我們最後實現的一個效果 1.手機端向主題 topic111 發送消息,並接收。(手機測試工具名稱:MQTT調試器) 2.控制台列印 MQTT基本簡介 MQTT ...
  • 大佬的理解->《Java IO(三) -- 位元組流》 1、FileInputStream 1.1 初始化 FileInputStream(String name) FileInputStream(File file) //直接通過文件地址初始化 FileInputStream fis = new i ...
  • 第二回 巧習得元素分類 子不知懷璧其罪 雲溪父親見狀看了看雲溪,臉上滲出意思冷汗,但遲疑一下就立即退了出去,匆匆忙忙的往右邊廚房趕,只留下了雲溪和這位神秘的老爺子。 雲溪瞠目結舌的看著悠然自得的喝著老爹泡的茶的老爺子,下意識說了一句:“老先生你怎麼這麼快,還知道我要來這裡”。 “方向,你一直在繞巷子 ...
  • 一、Iproute2簡介 Iproute2是一個在Linux下的高級網路管理工具軟體。實際上,它是通過rtnetlink sockets方式動態配置內核的一些小工具組成的,從Linux2.2內核開始,Alexey Kuznetsov 實現了通過rtnetlink sockets用來配置網路協議棧,它 ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...