u-boot-1.1.6第1階段分析之start.S、lowlevel_init.S文件

来源:https://www.cnblogs.com/053179hu/archive/2018/07/15/9313548.html
-Advertisement-
Play Games

學習目標: U-boot屬於兩個階段的Bootloader,第一階段的文件為cpu/arm920t/start.S和board/smdk2410/lowlevel_init.S,前者是平臺相關的,後者是開發板相關的。U-boot的第一階段主要的任務是一些系統的初始化工作,從大的方面可以分為以下幾個部 ...


學習目標:

  1. 對start.S中每一行代碼,都有基本瞭解
  2. 通過對start.S文件分析,對ARM920T架構的CPU的啟動過程,有更清楚理解

U-boot屬於兩個階段的Bootloader,第一階段的文件為cpu/arm920t/start.S和board/smdk2410/lowlevel_init.S,前者是平臺相關的,後者是開發板相關的。U-boot的第一階段主要的任務是一些系統的初始化工作,從大的方面可以分為以下幾個部分:

①設置CPU的模式

②關閉看門狗

③關閉中斷

④關閉MMU、設置RAM時序

⑤代碼重定位、設置堆棧SP指針

⑥清除BSS段

⑦異常中斷處理

下麵對start.S文件做出詳細分析,研究每一部分內容如何實現。


 1 設置CPU的模式

110 reset:
111     /*
112      * set the cpu to SVC32 mode
113      */
114     mrs    r0,cpsr
115     bic    r0,r0,#0x1f
116     orr    r0,r0,#0xd3
117     msr    cpsr,r0

cpsr為當前狀態寄存器,它的格式如下所示:

cpsr是32位寄存器,寄存器[31:28]位為狀態標誌位,[27:8]位保留未被使用,[7:0]位為控制位。控制位中的第7位和第6位是用來設置是否使能中斷請求和快速中斷請求,第5位是設置CPU操作狀態,當設置為1處理器執行在Thumb狀態,為0時執行在ARM狀態,第0~4位一起用來決定CPU的工作模式,模式位具體說明如下圖所示:

由上圖可知我們把M[4:0]設置為0b10011時,CPU工作在管理模式下,下麵來分析代碼如何實現。

第114行,將當前程式狀態寄存器內容,複製到r0寄存器內

第115行,將r0寄存器內低5位清零

第116行,將r0寄存器內容與立即數0xd3進行或運算,並把運算結果保存到r0寄存器,CPSR位域和含義如下表所示:

CPSR位域 7 6 5 4 3 2 1 0
位域含義 I F T M4 M3 M2 M1 M0
0xd3 1 1 0 1 0 0 1 1

即:

bit[7]=1->設置I位為1->關閉中斷IQR,bit[6]=1->設置F位為1->關閉FIQ中斷

bit[4:0]=0b10011->設置CPU位SVC管理模式

第117行,將r0寄存器內容,複製到cpsr寄存器中


 2 關看門狗

119 /* turn off the watchdog */
120 #if defined(CONFIG_S3C2400)
121 # define pWTCON        0x15300000
122 # define INTMSK        0x14400008    /* Interupt-Controller base addresses */
123 # define CLKDIVN    0x14800014    /* clock divisor register */
124 #elif defined(CONFIG_S3C2410)
125 # define pWTCON        0x53000000
126 # define INTMSK        0x4A000008    /* Interupt-Controller base addresses */
127 # define INTSUBMSK    0x4A00001C
128 # define CLKDIVN    0x4C000014    /* clock divisor register */
129 #endif
130 
131 #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
132     ldr     r0, =pWTCON
133     mov     r1, #0x0
134     str     r1, [r0]

分析代碼之前,先來介紹如何查看CONFIG_S3C2400、CONFIG_S3C2410巨集是否定義。在start.S文件開頭部分,可以看到如下圖所示的頭文件的引用

打開config.h文件,可以看到config.h內容如下

所以#include <config.h>可以替換為#include <configs/smdk2410.h>,查看CONFIG_S3C2400、CONFIG_S3C2410巨集是否定義,在config/smdk2410.h文件中直接搜索即可。

(config.h文件是根據配置U-boot命令自動生成,關於U-boot配置命令介紹,可以參考這篇文章:https://www.cnblogs.com/053179hu/p/9266553.html)

下麵來分析代碼:

第120行,使用#if語句進行判斷是否定義了CONFIG_S3C2400這個巨集,若定義了這個巨集,則第121~123處語句有效

第124行,若120行#if語句為假,執行#elif處代碼,判斷是否定義CONFIG_S3C2410這個巨集,則125~128處語句有效

第131行,使用#if語句判斷是否定義CONFIG_S3C2400、CONFIG_S3C2410巨集的任何一個,若定義則執行下麵代碼

這裡我們引用的是smdk2410.h頭文件,在該頭文件中CONFIG_S3C2410巨集被定義,編譯時#elif下語句被編譯。125~128行是對硬體外設寄存器地址巨集定義,以pWTCON為例,編譯器進行預處理時遇到pWTCON即把它換為0x53000000,進行巨集定義的目的,是使編寫代碼更加方便。

        

第132行,將WTCON寄存器地址,複製到r0寄存器中

第133行,將立即數0移入寄存器r1中

第134行,將r1內容0,寫入到r0地址中,即WTCON寄存器地址

從硬體手冊可以看出當WTCON寄存器第0bit內容為0,即關閉定時器0複位功能,與上面代碼相吻合。


 3 關閉中斷

139     mov    r1, #0xffffffff
140     ldr    r0, =INTMSK
141     str    r1, [r0]
142 # if defined(CONFIG_S3C2410)
143     ldr    r1, =0x3ff
144     ldr    r0, =INTSUBMSK
145     str    r1, [r0]
146 # endif
147 
148     /* FCLK:HCLK:PCLK = 1:2:4 */
149    /* default FCLK is 120 MHz ! */
150     ldr    r0, =CLKDIVN
151     mov    r1, #3
152    str    r1, [r0]
153 #endif    /* CONFIG_S3C2400 || CONFIG_S3C2410 */        

 

第139~145行,向INMSK和INTSUBMSK寄存器相應為位寫1,屏蔽中斷源

第148~152,設置時鐘分頻繫數

4 關閉MMU、設置RAM時序

159 #ifndef CONFIG_SKIP_LOWLEVEL_INIT
160     bl    cpu_init_crit
161 #endif

bl是跳轉指令,除了包含b指令的單純的跳轉功能,在跳轉之前,還把r15寄存器=PC=CPU地址,賦值給r14=lr,然後跳轉到對應位置,等要做的事情執行完畢後,再用mov pc, lr使得cpu再跳轉回來,所以整個邏輯就是調用子程式的意思。

上面的代碼意思很清晰,就是當沒有定義CONFIG_SKIP_LOWLEVEL_INIT的時候,就跳轉到cpu_init_crit的位置,頭文件中未定義CONFIG_SKIP_LOWLEVEL_INIT,CPU將跳轉到cpu_init_crit處執行程式,cpu_init_crit入口地址處代碼如下:

240 #ifndef CONFIG_SKIP_LOWLEVEL_INIT
241 cpu_init_crit:
242     /*
243     * flush v4 I/D caches
244     */
245     mov    r0, #0
246     mcr    p15, 0, r0, c7, c7, 0    /* flush v3/v4 cache */
247     mcr    p15, 0, r0, c8, c7, 0    /* flush v4 TLB */
248
249     /*
250      * disable MMU stuff and caches
251      */
252     mrc    p15, 0, r0, c1, c0, 0
253     bic    r0, r0, #0x00002300    @ clear bits 13, 9:8 (--V- --RS)
254     bic    r0, r0, #0x00000087    @ clear bits 7, 2:0 (B--- -CAM)
255     orr    r0, r0, #0x00000002    @ set bit 2 (A) Align
256     orr    r0, r0, #0x00001000    @ set bit 12 (I) I-Cache
257     mcr    p15, 0, r0, c1, c0, 0
258 
259     /*
260      * before relocating, we have to setup RAM timing
261     * because memory timing is board-dependend, you will
262      * find a lowlevel_init.S in your board directory.
263      */
264     mov    ip, lr
265     bl    lowlevel_init
266     mov    lr, ip
267     mov    pc, lr
268 #endif /* CONFIG_SKIP_LOWLEVEL_INIT */

 MCR 指令用於將ARM 處理器寄存器中的數據傳送到協處理器寄存器中,格式為:

        MCR 協處理器編號,協處理器操作碼1,源寄存器,目的寄存器1,目的寄存器2,協處理器操作碼2。
        其中協處理器操作碼1 和協處理器操作碼2 為協處理器將要執行的操作,
        源寄存器為ARM 處理器的寄存器,目的寄存器1 和目的寄存器2 均為協處理器的寄存器。

第242~246行,使I/D cache失效: 協處理寄存器操作,將r0中的數據寫入到協處理器p15的c7中,c7對應cp15的cache控制寄存器

第247行,使TLB操作寄存器失效:將r0數據送到cp15的c8、c7中。C8對應TLB操作寄存器

第252行,將c1、c0的值寫入到r0中

第257行,將設置好的r0值寫入到協處理器p15的c1、c0中,關閉MMU

第264行,將lr寄存器內容保存到ip寄存器中,用於子程式調用返回

第265行,跳轉到lowlevel_init入口地址執行,lowlevel_init在lowlevel_init.S文件中,代碼如下:

133 lowlevel_init:
134     /* memory control configuration */
135     /* make r0 relative the current location so that it */
136    /* reads SMRDATA out of FLASH rather than memory ! */
137     ldr     r0, =SMRDATA
138     ldr    r1, _TEXT_BASE
139     sub    r0, r0, r1
140     ldr    r1, =BWSCON    /* Bus Width Status Controller */
141    add     r2, r0, #13*4
142 0:
143     ldr     r3, [r0], #4
144     str     r3, [r1], #4
145    cmp     r2, r0
146     bne     0b
147 
148     /* everything is fine now */
149     mov    pc, lr
150 
151     .ltorg
152 /* the literal pools origin */
153
154 SMRDATA:
155     .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
156     .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
157     .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
158     .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
159     .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
160     .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
161     .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
162     .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
163     .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
164     .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
165     .word 0x32
166     .word 0x30
167     .word 0x30

此處代碼實現記憶體控制器初始化,為後面代碼重定位做準備。

第137行,將保存設置記憶體控制器的參數的初始地址(連接地址),保存到r0寄存器中。

第138行,將程式連接的入口地址存入到r1寄存器中

第139行,r0=r0-r1,獲取保存設置記憶體控制器的參數的初始地址(此處代碼未進行定位,代碼處於載入地址中,獲取的是在存儲器存放的物理地址)

第140行,將記憶體控制器的第一個寄存器地址存到r1寄存器中

第141行,獲取保存設置記憶體控制器的參數的結束地址地址(此處代碼未進行定位,代碼處於載入地址中,獲取的是在存儲器存放的物理地址)

第142~146行,將標號SMRDATA地址處存放的參數,寫入到相應寄存器中,設置記憶體控制器工作方式

第149行,程式調用返回,返回調用節點

第266~267,程式調用返回,返回調用節點(PC寄存器內容為bl cpu_init_crit指令地址+4)

5 代碼重定位、設置堆棧SP指針

163 #ifndef CONFIG_SKIP_RELOCATE_UBOOT
164 relocate:                /* relocate U-Boot to RAM        */
165     adr    r0, _start        /* r0 <- current position of code   */
166     ldr    r1, _TEXT_BASE        /* test if we run from flash or RAM */
167     cmp     r0, r1                  /* don't reloc during debug         */
168    beq     stack_setup
169 
170     ldr    r2, _armboot_start
171     ldr    r3, _bss_start
172     sub    r2, r3, r2        /* r2 <- size of armboot            */
173     add    r2, r0, r2        /* r2 <- source end address         */
174 
175 copy_loop:
176     ldmia    r0!, {r3-r10}        /* copy from source address [r0]    */
177     stmia    r1!, {r3-r10}        /* copy to   target address [r1]    */
178     cmp    r0, r2            /* until source end addreee [r2]    */
179     ble    copy_loop
180 #endif    /* CONFIG_SKIP_RELOCATE_UBOOT */
181 
182     /* Set up the stack                            */
183 stack_setup:
184     ldr    r0, _TEXT_BASE        /* upper 128 KiB: relocated uboot   */
185     sub    r0, r0, #CFG_MALLOC_LEN    /* malloc area                      */
186     sub    r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */
187 #ifdef CONFIG_USE_IRQ
188     sub    r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
189 #endif
190     sub    sp, r0, #12        /* leave 3 words for abort-stack    */

第165行,獲取當前代碼存放起始地址,存入r0寄存器

第166行,獲取代碼連接的初始地址,存入r1寄存器

第167~168行,比較代碼當前存放初始地址和設置連接地址是否相等,如果代碼當前存放地址等於連接地址,則跳轉stack_setup入口地址,設置堆棧;否則,執行重定位操作

第170~173行,獲取U-boot代碼長度,併進長度值存入r2寄存器中

第175~179行,迴圈操作,將代碼從載入地址,複製到連接地址。

  ldmia r0!, {r3-r10}  從源地址[r0]讀取4個位元組到寄存器(低地址存入低編號寄存器,高地址存入高編號寄存器),每讀一次就更新一次r0地址 ,r0=r0+4

    存放形式 [r0]-->r3 r0=r0+4;[r0]-->r4 r0=r0+4;........................[r0]-->r10 r0=r0+4

       stmia r1!, {r3-r10}  拷貝寄存器r3-r10的值保存到 [r1]指明的地址(低地址存入低編號寄存器,高地址存入高編號寄存器),每寫一個位元組,r1=r1+4,

      存放形式 r3-->[r1]  r1=r1+4;r4-->[r1]  r1=r1+4;........................r10-->[r1]  r1=r1+4

第183~190行,設置堆棧,CFG_MALLOC_LEN 、CFG_GBL_DATA_SIZE、CONFIG_STACKSIZE_IRQ、CONFIG_STACKSIZE_FIQ等巨集在smdk2410.h中有定義,記憶體使用如下圖所示:

6 清除bss段

85 .globl _bss_start
86 _bss_start:
87     .word __bss_start
88 
89 .globl _bss_end
90 _bss_end:
91     .word _end
192 clear_bss:
193     ldr    r0, _bss_start        /* find start of bss segment        */
194     ldr    r1, _bss_end        /* stop here                        */
195     mov     r2, #0x00000000        /* clear                            */
196 
197 clbss_l:str    r2, [r0]        /* clear loop...                    */
198     add    r0, r0, #4
199     cmp    r0, r1
200     ble    clbss_l

第85~91行,_bss_start,_bss_end為標號地址中存放bss段起始地址和結束地址,_bss_start和_end在連接腳本中定義\u-boot-1.1.6\board\smdk2410\u-boot.lds,程式連接時動態確定。

第193~194行,把bss段起始地址存入r0寄存器中,結束地址存放到r1寄存器中。

第197~200行,迴圈操作,將bss段中的記憶體清零

7 跳轉到u-boot第二階段入口

223 ldr    pc, _start_armboot
224 
225 _start_armboot:    .word start_armboot

 初始化外設完成之後,程式跳轉到u-boot第二階段入口函數start_armboot。ldr pc,_start_armboot為絕對跳轉命令,pc值等於_start_armboot的連接地址,程式跳到SDRAM中執行,再辭之前程式都是在flash中運行的,絕對跳轉必須在初始SDRAM,執行代碼重定位之後才能進行。

8 異常中斷處理

ARM920T架構CPU異常向量表如下圖所示:

當CPU發生異常時,程式計數器跳到相應異常向量表地址處讀取指令。由上圖很容易看出,不同異常入口地址之間只有4個位元組,在這裡肯定不能存放異常處理函數。因此我們在中斷向量地址處存放相應跳轉指令,當發生異常時CPU跳到相應異常地址,讀取跳轉指令,跳轉到相應異常處理函數處執行異常處理。start.S代碼處理如下:

41 .globl _start
42 _start:    b       reset
43     ldr    pc, _undefined_instruction
44     ldr    pc, _software_interrupt
45     ldr    pc, _prefetch_abort
46     ldr    pc, _data_abort
47     ldr    pc, _not_used
48     ldr    pc, _irq
49     ldr    pc, _fiq
50 
51 _undefined_instruction:    .word undefined_instruction
52 _software_interrupt:    .word software_interrupt
53 _prefetch_abort:    .word prefetch_abort
54 _data_abort:        .word data_abort
55 _not_used:        .word not_used
56 _irq:            .word irq
57 _fiq:            .word fiq
58 
59     .balignl 16,0xdeadbeef

 globl是個關鍵字,意思很簡單,就是相當亍C語言中的extern,聲明此變數,並且告訴鏈接器此變數是全局的,外部可以訪問。 

第41行,是上電或者複位後執行第一題指令,通過b命令跳轉到reset地址處進行一系列初始化操作,reset地址標號後的代碼已經在上面分析了。

第43~57行,以_undefined_instruction為例,就是,此處分配了一個word=32bit=4位元組的地址空間,裡面存放的值是undefined_instruction。

而此處_undefined_instruction也就是該地址空間的地址了。用C語言來表達就是:

                             _undefined_instruction = &undefined_instruction

                             或 *_undefined_instruction = undefined_instruction

在後面的代碼,我們可以看到,undefined_instruction也是一個標號,即一個地址值,對應著就是在發生“未定義指令”的時候,系統所要去執行的代碼。(其他幾個對應的“軟體中斷”,“預取指錯誤”,“數據錯誤”,“未定義”,“(普通)中斷”,“快速中斷”,也是同樣的做法,跳轉到對應的位置執行對應的代碼。)

第59行,意思就是,接下來的代碼,都要16位元組對齊,不足之處,用0xdeadbeef填充。 

 

總結:uboot第一階段

1、完成了硬體設備初始化操作

2、為載入Bootloader的第二階段準備好RAM空間

3、實現代碼重定位

4、設置好棧,並跳轉到第二階段入口函數


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

-Advertisement-
Play Games
更多相關文章
  • .Net Core 使用 System.Drawing.Common 部署到CentOS上遇到的問題 ...
  • 在我們寫程式的時候,經常會需要判斷數據的是空值還是null值,基本上十個方法函數,八個要做這樣的判斷,因此我們很有必要拓展出來一個類來做監控,在這裡我們使用一個簡單地,可拓展的第三方組件:Ardalis.GuardClauses在這裡首先提一點,一般我們的一旦給一個實體類或者集合類初始化了之後,其值 ...
  • ASP.NET Core程式現在變得如同控制台(Console)程式一般,同樣通過Main方法啟動整個應用。而Main方法要做的事情很簡單,創建一個WebHostBuilder類,調用其Build方法生成一個WebHost類,最後啟動之。 實現代碼一目瞭然: 要想探尋其內部究竟做了哪些操作,則需要調 ...
  • 摘自:http://www.cnblogs.com/nianming/archive/2012/11/07/2757997.html Entity Framework Code First的預設行為是使用一系列約定將POCO類映射到表。然而,有時候,不能也不想遵循這些約定,那就需要重寫它們。重寫預設 ...
  • Entity Framework Code First與數據表之間的映射方式有兩種實現:Data Annotation和Fluent API。本文中採用創建Product類為例來說明tity Framework Code First屬性映射約定的具體方式。 1. 表名及所有者 在預設約定的情況下,E ...
  • 設置主鍵 modelBuilder.Entity<x>().HasKey(t => t.Name); 設置聯合主鍵 modelBuilder.Entity<x>().HasKey(t =>new{t.Name,t.ID} ); 取消資料庫欄位標識(取消自動增長) modelBuilder.Entit ...
  • 在/etc/rc.local文件中添加自啟動命令(其中一種方法) 1.案例,就用博主本人之前發的博文 “nginx + flask + uwsgi + centos + python3 搭建web項目”,把步驟6的語句添加到/etc/rc.local文件中 附:關於開機自啟動腳本我再列舉一種方法(方 ...
  • 1. 分配源地址、目的地址 函數裡面是源和目的的物理地址,函數返回的是源和目的的虛擬地址。 2. 源和目的的記憶體釋放 3. 和硬體打交道用到的是物理地址,比如把源和目的地址告訴DMA寄存器 4. 調用內核函數使用的一般是虛擬地址 完整程式見https://www.cnblogs.com/zhu-g5 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...