RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

你知道你寫的代碼是怎樣跑起來的嗎(下)

jf_78858299 ? 來源:開發(fā)內(nèi)功修煉 ? 作者:開發(fā)內(nèi)功修煉 ? 2023-05-05 14:36 ? 次閱讀

四、execve 加載用戶程序

具體加載可執(zhí)行文件的工作是由 execve 系統(tǒng)調(diào)用來完成的。

該系統(tǒng)調(diào)用會讀取用戶輸入的可執(zhí)行文件名,參數(shù)列表以及環(huán)境變量等開始加載并運(yùn)行用戶指定的可執(zhí)行文件。該系統(tǒng)調(diào)用的位置在 fs/exec.c 文件中。

//file:fs/exec.c
SYSCALL_DEFINE3(execve, const char __user *, filename, ...)
{
 struct filename *path = getname(filename);
 do_execve(path->name, argv, envp)
 ...
}

int do_execve(...)
{
 ...
 return do_execve_common(filename, argv, envp);
}

execve 系統(tǒng)調(diào)用到了 do_execve_common 函數(shù)。我們來看這個函數(shù)的實現(xiàn)。

//file:fs/exec.c
static int do_execve_common(const char *filename, ...)
{
 //linux_binprm 結(jié)構(gòu)用于保存加載二進(jìn)制文件時使用的參數(shù)
 struct linux_binprm *bprm;

 //1.申請并初始化 brm 對象值
 bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
 bprm->file = ...;
 bprm->filename = ...;
 bprm_mm_init(bprm)
 bprm->argc = count(argv, MAX_ARG_STRINGS);
 bprm->envc = count(envp, MAX_ARG_STRINGS);
 prepare_binprm(bprm);
 ...

 //2.遍歷查找合適的二進(jìn)制加載器
 search_binary_handler(bprm);
}

這個函數(shù)中申請并初始化 brm 對象的具體工作可以用下圖來表示。

在這個函數(shù)中,完成了一下三塊工作。

第一、使用 kzalloc 申請 linux_binprm 內(nèi)核對象。該內(nèi)核對象用于保存加載二進(jìn)制文件時使用的參數(shù)。在申請完后,對該參數(shù)對象進(jìn)行各種初始化。

第二、在 bprm_mm_init 中會申請一個全新的 mm_struct 對象,準(zhǔn)備留著給新進(jìn)程使用。

第三、給新進(jìn)程的棧申請一頁的虛擬內(nèi)存空間,并將棧指針記錄下來。

第四、讀取二進(jìn)制文件頭 128 字節(jié)。

我們來看下初始化棧的相關(guān)代碼。

//file:fs/exec.c
static int __bprm_mm_init(struct linux_binprm *bprm)
{
 bprm->vma = vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
 vma->vm_end = STACK_TOP_MAX;
 vma->vm_start = vma->vm_end - PAGE_SIZE;
 ...

 bprm->p = vma->vm_end - sizeof(void *);
}

在上面這個函數(shù)中申請了一個 vma 對象(表示虛擬地址空間里的一段范圍),vm_end 指向了 STACK_TOP_MAX(地址空間的頂部附近的位置),vm_start 和 vm_end 之間留了一個 Page 大小。 也就是說默認(rèn)給棧申請了 4KB 的大小 。最后把棧的指針記錄到 bprm->p 中。

另外再看下 prepare_binprm,在這個函數(shù)中,從文件頭部讀取了 128 字節(jié)。之所以這么干,是為了讀取二進(jìn)制文件頭為了方便后面判斷其文件類型。

//file:include/uapi/linux/binfmts.h
#define BINPRM_BUF_SIZE 128

//file:fs/exec.c
int prepare_binprm(struct linux_binprm *bprm)
{
 ......
 memset(bprm->buf, 0, BINPRM_BUF_SIZE);
 return kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);
}

在申請并初始化 brm 對象值完后,最后使用 search_binary_handler 函數(shù)遍歷系統(tǒng)中已注冊的加載器,嘗試對當(dāng)前可執(zhí)行文件進(jìn)行解析并加載。

在 3.1 節(jié)我們介紹了系統(tǒng)所有的加載器都注冊到了 formats 全局鏈表里了。函數(shù) search_binary_handler 的工作過程就是遍歷這個全局鏈表,根據(jù)二進(jìn)制文件頭中攜帶的文件類型數(shù)據(jù)查找解析器。找到后調(diào)用解析器的函數(shù)對二進(jìn)制文件進(jìn)行加載。

//file:fs/exec.c
int search_binary_handler(struct linux_binprm *bprm)
{
 ...
 for (try=0; try<2; try++) {
  list_for_each_entry(fmt, &formats, lh) {
   int (*fn)(struct linux_binprm *) = fmt->load_binary;
   ...
   retval = fn(bprm);

   //加載成功的話就返回了
   if (retval >= 0) {
    ...
    return retval;
   }
   //加載失敗繼續(xù)循環(huán)以嘗試加載
   ...
  }
 }
}

在上述代碼中的 list_for_each_entry 是在遍歷 formats 這個全局鏈表,遍歷時判斷每一個鏈表元素是否有 load_binary 函數(shù)。有的話就調(diào)用它嘗試加載。

回憶一下 3.1 注冊可執(zhí)行文件加載程序,對于 ELF 文件加載器 elf_format 來說, load_binary 函數(shù)指針指向的是 load_elf_binary。

//file:fs/binfmt_elf.c
static struct linux_binfmt elf_format = {
 .module  = THIS_MODULE,
 .load_binary = load_elf_binary,
 ......
};

那么加載工作就會進(jìn)入到 load_elf_binary 函數(shù)中來進(jìn)行。這個函數(shù)很長,可以說所有的程序加載邏輯都在這個函數(shù)中體現(xiàn)了。我根據(jù)這個函數(shù)的主要工作,分成以下 5 個小部分來給大家介紹。

在介紹的過程中,為了表達(dá)清晰,我會稍微調(diào)一下源碼的位置,可能和內(nèi)核源碼行數(shù)順序會有所不同。

4.1 ELF 文件頭讀取

在 load_elf_binary 中首先會讀取 ELF 文件頭。

文件頭中包含一些當(dāng)前文件格式類型等數(shù)據(jù),所以在讀取完文件頭后會進(jìn)行一些合法性判斷。如果不合法,則退出返回。

//file:fs/binfmt_elf.c
static int load_elf_binary(struct linux_binprm *bprm)
{
 //4.1 ELF 文件頭解析
 //定義結(jié)構(gòu)題并申請內(nèi)存用來保存 ELF 文件頭
 struct {
  struct elfhdr elf_ex;
  struct elfhdr interp_elf_ex;
 } *loc;
 loc = kmalloc(sizeof(*loc), GFP_KERNEL);

 //獲取二進(jìn)制頭
 loc->elf_ex = *((struct elfhdr *)bprm->buf);

 //對頭部進(jìn)行一系列的合法性判斷,不合法則直接退出
 if (loc->elf_ex.e_type != ET_EXEC && ...){
  goto out;
 }
 ...
}

4.2 Program Header 讀取

在 ELF 文件頭中記錄著 Program Header 的數(shù)量,而且在 ELF 頭之后緊接著就是 Program Header Tables。所以內(nèi)核接下來可以將所有的 Program Header 都讀取出來。

//file:fs/binfmt_elf.c
static int load_elf_binary(struct linux_binprm *bprm)
{
 //4.1 ELF 文件頭解析

 //4.2 Program Header 讀取
 // elf_ex.e_phnum 中保存的是 Programe Header 數(shù)量
 // 再根據(jù) Program Header 大小 sizeof(struct elf_phdr)
 // 一起計算出所有的 Program Header 大小,并讀取進(jìn)來
 size = loc->elf_ex.e_phnum * sizeof(struct elf_phdr);
 elf_phdata = kmalloc(size, GFP_KERNEL);
 kernel_read(bprm->file, loc->elf_ex.e_phoff,
     (char *)elf_phdata, size);
 
 ...
}

4.3 清空父進(jìn)程繼承來的資源

在 fork 系統(tǒng)調(diào)用創(chuàng)建出來的進(jìn)程中,包含了不少原進(jìn)程的信息,如老的地址空間,信號表等等。這些在新的程序運(yùn)行時并沒有什么用,所以需要清空處理一下。

具體工作包括初始化新進(jìn)程的信號表,應(yīng)用新的地址空間對象等。

//file:fs/binfmt_elf.c
static int load_elf_binary(struct linux_binprm *bprm)
{
 //4.1 ELF 文件頭解析
 //4.2 Program Header 讀取

 //4.3 清空父進(jìn)程繼承來的資源
 retval = flush_old_exec(bprm);
 ...

 current->mm->start_stack = bprm->p;
}

在清空完父進(jìn)程繼承來的資源后(當(dāng)然也就使用上了新的 mm_struct 對象),這之后,直接將前面準(zhǔn)備的進(jìn)程棧的地址空間指針設(shè)置到了 mm 對象上。這樣將來棧就可以被使用了。

4.4 執(zhí)行 Segment 加載

接下來,加載器會將 ELF 文件中的 LOAD 類型的 Segment 都加載到內(nèi)存里來。使用 elf_map 在虛擬地址空間中為其分配虛擬內(nèi)存。最后合適地設(shè)置虛擬地址空間 mm_struct 中的 start_code、end_code、start_data、end_data 等各個地址空間相關(guān)指針。

我們來看下具體的代碼:

//file:fs/binfmt_elf.c
static int load_elf_binary(struct linux_binprm *bprm)
{
 //4.1 ELF 文件頭解析
 //4.2 Program Header 讀取
 //4.3 清空父進(jìn)程繼承來的資源

 //4.4 執(zhí)行 Segment 加載過程
 //遍歷可執(zhí)行文件的 Program Header
 for(i = 0, elf_ppnt = elf_phdata;
  i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {

  //只加載類型為 LOAD 的 Segment,否則跳過
  if (elf_ppnt->p_type != PT_LOAD)
   continue;
  ...

  //為 Segment 建立內(nèi)存 mmap, 將程序文件中的內(nèi)容映射到虛擬內(nèi)存空間中
  //這樣將來程序中的代碼、數(shù)據(jù)就都可以被訪問了
  error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
    elf_prot, elf_flags, 0);

  //計算 mm_struct 所需要的各個成員地址
  start_code = ...;
  start_data = ...
  end_code = ...;
  end_data = ...;
  ...
 }

 current->mm->end_code = end_code;
 current->mm->start_code = start_code;
 current->mm->start_data = start_data;
 current->mm->end_data = end_data;
 ...
}

其中 load_bias 是 Segment 要加載到內(nèi)存里的基地址。這個參數(shù)有這么幾種可能

  • 值為 0,就是直接按照 ELF 文件中的地址在內(nèi)存中進(jìn)行映射
  • 值為對齊到整數(shù)頁的開始,物理文件中可能為了可執(zhí)行文件的大小足夠緊湊,而不考慮對齊的問題。但是操作系統(tǒng)在加載的時候為了運(yùn)行效率,需要將 Segment 加載到整數(shù)頁的開始位置處。

4.5 數(shù)據(jù)內(nèi)存申請&堆初始化

因為進(jìn)程的數(shù)據(jù)段需要寫權(quán)限,所以需要使用 set_brk 系統(tǒng)調(diào)用專門為數(shù)據(jù)段申請?zhí)摂M內(nèi)存。

//file:fs/binfmt_elf.c
static int load_elf_binary(struct linux_binprm *bprm)
{
 //4.1 ELF 文件頭解析
 //4.2 Program Header 讀取
 //4.3 清空父進(jìn)程繼承來的資源
 //4.4 執(zhí)行 Segment 加載過程
 //4.5 數(shù)據(jù)內(nèi)存申請&堆初始化
 retval = set_brk(elf_bss, elf_brk);
 ......
}

在 set_brk 函數(shù)中做了兩件事情:第一是為數(shù)據(jù)段申請?zhí)摂M內(nèi)存,第二是將進(jìn)程堆的開始指針和結(jié)束指針初始化一下。

//file:fs/binfmt_elf.c
static int set_brk(unsigned long start, unsigned long end)
{
 //1.為數(shù)據(jù)段申請?zhí)摂M內(nèi)存
 start = ELF_PAGEALIGN(start);
 end = ELF_PAGEALIGN(end);
 if (end > start) {
  unsigned long addr;
  addr = vm_brk(start, end - start);
 }

 //2.初始化堆的指針
 current->mm->start_brk = current->mm->brk = end;
 return 0;
}

因為程序初始化的時候,堆上還是空的。所以堆指針初始化的時候,堆的開始地址 start_brk 和結(jié)束地址 brk 都設(shè)置成了同一個值。

4.6 跳轉(zhuǎn)到程序入口執(zhí)行

在 ELF 文件頭中記錄了程序的入口地址。如果是非動態(tài)鏈接加載的情況,入口地址就是這個。

但是如果是動態(tài)鏈接,也就是說存在 INTERP 類型的 Segment,由這個動態(tài)鏈接器先來加載運(yùn)行,然后再調(diào)回到程序的代碼入口地址。

# readelf --program-headers helloworld
......
Program Headers:
  Type           Offset             VirtAddr           PhysAddr
     FileSiz            MemSiz              Flags  Align
  INTERP         0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
     0x000000000000001c 0x000000000000001c  R      0x1
   [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

對于是動態(tài)加載器類型的,需要先將動態(tài)加載器(本文示例中是 ld-linux-x86-64.so.2 文件)加載到地址空間中來。

加載完成后再計算動態(tài)加載器的入口地址。這段代碼我展示在下面了,沒有耐心的同學(xué)可以跳過。反正只要知道這里是計算了一個程序的入口地址就可以了。

//file:fs/binfmt_elf.c
static int load_elf_binary(struct linux_binprm *bprm)
{
 //4.1 ELF 文件頭解析
 //4.2 Program Header 讀取
 //4.3 清空父進(jìn)程繼承來的資源
 //4.4 執(zhí)行 Segment 加載
 //4.5 數(shù)據(jù)內(nèi)存申請&堆初始化
 //4.6 跳轉(zhuǎn)到程序入口執(zhí)行

 //第一次遍歷 program header table
 //只針對 PT_INTERP 類型的 segment 做個預(yù)處理
 //這個 segment 中保存著動態(tài)加載器在文件系統(tǒng)中的路徑信息
 for (i = 0; i < loc->elf_ex.e_phnum; i++) {
  ...
 }

 //第二次遍歷 program header table, 做些特殊處理
 elf_ppnt = elf_phdata;
 for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++){
  ...
 }

 //如果程序中指定了動態(tài)鏈接器,就把動態(tài)鏈接器程序讀出來
 if (elf_interpreter) {
  //加載并返回動態(tài)鏈接器代碼段地址
  elf_entry = load_elf_interp(&loc->interp_elf_ex,
      interpreter,
      &interp_map_addr,
      load_bias);
  //計算動態(tài)鏈接器入口地址
  elf_entry += loc->interp_elf_ex.e_entry;
 } else {
  elf_entry = loc->elf_ex.e_entry;
 }

 //跳轉(zhuǎn)到入口開始執(zhí)行
 start_thread(regs, elf_entry, bprm->p);
 ...
}

五、總結(jié)

看起來簡簡單單的一行 helloworld 代碼,但是要想把它運(yùn)行過程理解清楚可卻需要非常深厚的內(nèi)功的。

本文首先帶領(lǐng)大家認(rèn)識和理解了二進(jìn)制可運(yùn)行 ELF 文件格式。在 ELF 文件中是由四部分組成,分別是 ELF 文件頭 (ELF header)、Program header table、Section 和 Section header table。

Linux 在初始化的時候,會將所有支持的加載器都注冊到一個全局鏈表中。對于 ELF 文件來說,它的加載器在內(nèi)核中的定義為 elf_format,其二進(jìn)制加載入口是 load_elf_binary 函數(shù)。

一般來說 shell 進(jìn)程是通過 fork + execve 來加載并運(yùn)行新進(jìn)程的。執(zhí)行 fork 系統(tǒng)調(diào)用的作用是創(chuàng)建一個新進(jìn)程出來。不過 fork 創(chuàng)建出來的新進(jìn)程的代碼、數(shù)據(jù)都還是和原來的 shell 進(jìn)程的內(nèi)容一模一樣。要想實現(xiàn)加載并運(yùn)行另外一個程序,那還需要使用到 execve 系統(tǒng)調(diào)用。

在 execve 系統(tǒng)調(diào)用中,首先會申請一個 linux_binprm 對象。在初始化 linux_binprm 的過程中,會申請一個全新的 mm_struct 對象,準(zhǔn)備留著給新進(jìn)程使用。還會給新進(jìn)程的棧準(zhǔn)備一頁(4KB)的虛擬內(nèi)存。還會讀取可執(zhí)行文件的前 128 字節(jié)。

接下來就是調(diào)用 ELF 加載器的 load_elf_binary 函數(shù)進(jìn)行實際的加載。大致會執(zhí)行如下幾個步驟:

  • ELF 文件頭解析
  • Program Header 讀取
  • 清空父進(jìn)程繼承來的資源,使用新的 mm_struct 以及新的棧
  • 執(zhí)行 Segment 加載,將 ELF 文件中的 LOAD 類型的 Segment 都加載到虛擬內(nèi)存中
  • 為數(shù)據(jù) Segment 申請內(nèi)存,并將堆的起始指針進(jìn)行初始化
  • 最后計算并跳轉(zhuǎn)到程序入口執(zhí)行

當(dāng)用戶進(jìn)程啟動起來以后,我們可以通過 proc 偽文件來查看進(jìn)程中的各個 Segment。

# cat /proc/46276/maps
00400000-00401000 r--p 00000000 fd:01 396999                             /root/work_temp/helloworld
00401000-00402000 r-xp 00001000 fd:01 396999                             /root/work_temp/helloworld
00402000-00403000 r--p 00002000 fd:01 396999                             /root/work_temp/helloworld
00403000-00404000 r--p 00002000 fd:01 396999                             /root/work_temp/helloworld
00404000-00405000 rw-p 00003000 fd:01 396999                             /root/work_temp/helloworld
01dc9000-01dea000 rw-p 00000000 00:00 0                                  [heap]
7f0122fbf000-7f0122fc1000 rw-p 00000000 00:00 0 
7f0122fc1000-7f0122fe7000 r--p 00000000 fd:01 1182071                    /usr/lib64/libc-2.32.so
7f0122fe7000-7f0123136000 r-xp 00026000 fd:01 1182071                    /usr/lib64/libc-2.32.so
......
7f01231c0000-7f01231c1000 r--p 0002a000 fd:01 1182554                    /usr/lib64/ld-2.32.so
7f01231c1000-7f01231c3000 rw-p 0002b000 fd:01 1182554                    /usr/lib64/ld-2.32.so
7ffdf0590000-7ffdf05b1000 rw-p 00000000 00:00 0                          [stack]
......

雖然本文非常的長,但仍然其實只把大體的加載啟動過程串了一下。如果你日后在工作學(xué)習(xí)中遇到想搞清楚的問題,可以順著本文的思路去到源碼中尋找具體的問題,進(jìn)而幫助你找到工作中的問題的解。

最后提一下,細(xì)心的讀者可能發(fā)現(xiàn)了,本文的實例中加載新程序運(yùn)行的過程中其實有一些浪費(fèi),fork 系統(tǒng)調(diào)用首先將父進(jìn)程的很多信息拷貝了一遍,而 execve 加載可執(zhí)行程序的時候又是重新賦值的。所以在實際的 shell 程序中,一般使用的是 vfork。其工作原理基本和 fork 一致,但區(qū)別是會少拷貝一些在 execve 系統(tǒng)調(diào)用中用不到的信息,進(jìn)而提高加載性能。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11292

    瀏覽量

    209322
  • 代碼

    關(guān)注

    30

    文章

    4779

    瀏覽量

    68521
  • helloworld
    +關(guān)注

    關(guān)注

    0

    文章

    13

    瀏覽量

    4365
收藏 人收藏

    評論

    相關(guān)推薦

    MotorControl Workbench生成的代碼是開環(huán)的嗎,為什么電機(jī)跑起來很容易受到外力導(dǎo)致停機(jī)?

    請問各位高手 MotorControl Workbench 生成的代碼是開環(huán)的嗎?為什么我的電機(jī)跑起來很容易受到外力導(dǎo)致停機(jī),我想讓它不停機(jī),請問有什么好的辦法嗎 ?
    發(fā)表于 03-21 07:12

    請問HVMotorCtrl+PfcKit_v1.7/HVPM_sensorless_2833x代碼能不能讓電機(jī)跑起來?需要修改哪些參數(shù)?

    ,現(xiàn)在想測試一下代碼能不能讓電機(jī)跑起來,從level1——level6,不知道從哪個level可以讓電機(jī)跑起來,聽說比較危險,不知道需要改什
    發(fā)表于 06-13 05:19

    請問stm32f103工程代碼如何在stm32f407芯片上跑起來?

    如題:1、stm32f103工程代碼如何在stm32f407芯片上跑起來?2、要做哪些修改?
    發(fā)表于 09-04 09:27

    STM32芯片用KEIL4載程序后要按復(fù)位鍵才能跑起來

    STM32芯片用KEIL4載程序后要按復(fù)位鍵才能跑起來?大神可否詳細(xì)截圖解說下怎么設(shè)置一可以解決此問題。。
    發(fā)表于 12-14 08:45

    如何讓的ESP32跑起來

    ESP32是了國內(nèi)樂鑫科技推出的Wifi&藍(lán)牙物聯(lián)網(wǎng)MCU,而最近項目正好在用ESP32,所以我們今天就來分享,如何讓的ESP32跑起來,并應(yīng)用于更多實際項目。1ESP32簡...
    發(fā)表于 07-16 06:57

    怎樣讓自己編譯的uboot跑起來

    小目標(biāo):讓自己編譯的uboot跑起來參考:wiki.friendlyarm.com/wiki/index.php/NanoPi_NEO首先熟悉一板子和開發(fā)流程。維基主要參考《使用全志原廠BSP
    發(fā)表于 11-08 06:37

    程序能跑起來就是很好的c代碼

    程序能跑起來并不見得代碼就是很好的c代碼了,衡量代碼的好壞應(yīng)該從以下幾個方面來添加鏈接描述看:海風(fēng)教育投訴1,
    發(fā)表于 11-23 08:00

    如何讓u-boot跑起來

    如何讓u-boot跑起來?
    發(fā)表于 01-26 08:26

    如何讓的ESP32跑起來

    ESP32是了國內(nèi)樂鑫科技推出的Wifi&藍(lán)牙物聯(lián)網(wǎng)MCU,而最近項目正好在用ESP32,所以我們今天就來分享,如何讓的ESP32跑起來,并應(yīng)用于更多實際項目。1ESP32簡介ESP32
    發(fā)表于 02-10 06:25

    STM32如何區(qū)分程序跑起來用的是HSE還是HSI呢?

    方法去區(qū)別HSE和HSI的話,我的問題就來了:燒到030f4并測到晶振有起振的程序(main死循環(huán)),燒到030rb上,晶振不起振了,但是通過仿真發(fā)現(xiàn)程序還在跑的。所以問一大家,是怎么確定HSE跑起來了?由于程序會認(rèn)為改錯配
    發(fā)表于 05-05 10:47

    Zynq 7015 linux跑起來之導(dǎo)入之BOOT.bin生成詳解

    本文主要介紹Zynq 7015 linux跑起來之導(dǎo)入之BOOT.bin生成,具體的跟隨小編一起來了解一
    的頭像 發(fā)表于 06-27 10:01 ?7473次閱讀

    FreeRTOS_003 _讓系統(tǒng)在板子上跑起來

    FreeRTOS_003_讓系統(tǒng)在板子上跑起來
    的頭像 發(fā)表于 03-14 11:25 ?2767次閱讀
    FreeRTOS_003 _讓系統(tǒng)在板子上<b class='flag-5'>跑起來</b>

    windows安裝ubuntu并讓pioneer1應(yīng)用程序跑起來的過程

    本文介紹在windows安裝ubuntu并且讓pioneer1的應(yīng)用程序跑起來的全過程。雖然安裝ubuntu不是本文重點,但是還是啰嗦地一遍吧。
    的頭像 發(fā)表于 10-23 10:41 ?2355次閱讀
    windows安裝ubuntu并讓pioneer1應(yīng)用程序<b class='flag-5'>跑起來</b>的過程

    代碼是如何跑起來的?

    今天我們來思考一個簡單的問題,一個程序是如何在 Linux 上執(zhí)行起來的?
    的頭像 發(fā)表于 12-08 15:50 ?886次閱讀

    知道代碼怎樣跑起來的嗎(上)

    今天我們來思考一個簡單的問題,一個程序是如何在 Linux 上執(zhí)行起來的? 我們就拿全宇宙最簡單的 Hello World 程序來舉例。
    的頭像 發(fā)表于 05-05 14:36 ?525次閱讀
    <b class='flag-5'>你</b><b class='flag-5'>知道</b><b class='flag-5'>你</b><b class='flag-5'>寫</b>的<b class='flag-5'>代碼</b>是<b class='flag-5'>怎樣</b><b class='flag-5'>跑起來</b>的嗎(上)
    RM新时代网站-首页