1. 程式人生 > >作業系統實驗四實驗報告

作業系統實驗四實驗報告

實驗四:核心執行緒管理

練習1:分配並初始化一個程序控制塊

首先來看幾個比較重要的資料結構,kern/process/proc.h中定義的程序控制塊及kern/trap/trap.h中定義的中斷幀

struct proc_struct {
    enum proc_state state;                      // Process state
    int pid;                                    // Process ID
    int runs;                                   // the running times of Proces
uintptr_t kstack; // Process kernel stack volatile bool need_resched; // bool value: need to be rescheduled to release CPU? struct proc_struct *parent; // the parent process struct mm_struct *mm; // Process's memory management field
struct context context; // Switch here to run process即程序上下文 struct trapframe *tf; // Trap frame for current interrupt即中斷上下文 uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT) uint32_t flags; // Process flag
char name[PROC_NAME_LEN + 1]; // Process name list_entry_t list_link; // Process link list list_entry_t hash_link; // Process hash list } struct trapframe { /* below here is a struct of general registers and defined by software */ // 當中斷異常發生時,此處結構內的通用暫存器資訊由軟體負責壓棧儲存 struct pushregs tf_regs; /* below here are segement registers and defined by software */ // 當中斷異常發生時,此處的段暫存器資訊由軟體負責壓棧儲存 uint16_t tf_gs; uint16_t tf_padding0; uint16_t tf_fs; uint16_t tf_padding1; uint16_t tf_es; uint16_t tf_padding2; uint16_t tf_ds; uint16_t tf_padding3; uint32_t tf_trapno; /* below here defined by x86 hardware */ // 當中斷異常發生時,此處的資訊由硬體壓棧儲存 uint32_t tf_err; uintptr_t tf_eip; uint16_t tf_cs; uint16_t tf_padding4; uint32_t tf_eflags; /* below here only when crossing rings, such as from user to kernel, defined by hardware */ // 僅發生特權級改變時,此處額外的資訊由硬體壓棧儲存 uintptr_t tf_esp; uint16_t tf_ss; uint16_t tf_padding5; } struct context { uint32_t eip; uint32_t esp; uint32_t ebx; uint32_t ecx; uint32_t edx; uint32_t esi; uint32_t edi; uint32_t ebp; }

這裡可以看到struct contextstruct trapframe中有很多暫存器是一樣的,前者用於程序上下文切換,後者用於中斷上下文切換。注意這兩者的含義是不一樣的,在本實驗中一個程序開始執行需要系統進行初始化,此時tf被用來儲存中斷幀,而程序執行時是通過context來完成切換的,詳細見練習3的說明。

結構中一些重要的成員變數說明如下

  • mm記憶體管理資訊,包括記憶體對映列表、頁表指標等
  • state程序所處的狀態,有PROC_UNINITPROC_SLEEPINGPROC_RUNNABLEPROC_ZOMBIE四種,定義在enum proc_state
  • parent父程序,在所有程序中只有核心建立的第一個核心執行緒idleproc沒有父程序,核心根據父子關係建立樹形結構維護一些特殊的操作
  • context程序的上下文,儲存暫存器,用於程序切換
  • tf中斷幀,總是指向核心棧的某個位置,當程序發生中斷異常,從使用者跳到核心時,中斷幀記錄了程序在中斷前的狀態,由於允許中斷巢狀,因此ucore在核心棧上維護了tf鏈,保證tf總是能夠指向當前的trapframe
  • kstack每個執行緒都有核心棧,並且位於核心地址空間的不同位置,對於核心執行緒,該棧就是執行時程式使用的棧,對於普通程序,該棧就是發生特權級改變時需儲存被打斷硬體資訊的棧

另外為了管理系統中所有的程序控制塊,ucore維護了一些重要的全域性變數

  • static struct proc *current當前佔用處理機並處於執行狀態的程序控制塊指標
  • static list_entry_t hash_list[HASH_LIST_SIZE]所有程序控制塊的雜湊表,hash_link將基於pid連結入這個雜湊表

根據實驗手冊及程式碼的提示,練習1的程式碼如下

把proc進行初步初始化(即把proc_struct中的各個成員變數清零)。但有些成員變數設定了特殊的值

// alloc_proc - alloc a proc_struct and init all fields of proc_struct
static struct proc_struct *
alloc_proc(void) {
    struct proc_struct *proc = kmalloc(sizeof(struct proc_struct));
    if (proc != NULL) {
        proc->state = PROC_UNINIT;                 // 設定程序為初始態
        proc->pid = -1;                            // 設定程序pid的未初始化值
        proc->runs = 0;
        proc->kstack = 0;
        proc->need_resched = 0;
        proc->parent = NULL;
        proc->mm = NULL;
        memset(&(proc->context), 0, sizeof(struct context));
        proc->tf = NULL;
        proc->cr3 = boot_cr3;                      // 使用核心頁目錄表的基址
        proc->flags = 0;
        memset(proc->name, 0, PROC_NAME_LEN + 1);
    }
    return proc;
}

之後proc_init會進一步初始化idleproc核心執行緒

練習2:為新建立的核心執行緒分配資源

do_fork是建立執行緒的主要函式,kernel_thread通過呼叫do_fork函式來完成核心執行緒的建立工作,練習2中要求完成的do_fork函式主要完成了6項工作

  • 分配並初始化程序控制塊(alloc_proc
  • 分配並初始化核心棧(setup_stack
  • 根據clone_flag標誌複製或共享記憶體管理結構(copy_mm
  • 設定程序在核心正常執行和排程所需要的中斷幀和上下文(copy_thread
  • 把設定好的程序控制塊放入hash_listproc_list兩個全域性程序連結串列中
  • 將程序狀態設定為“就緒”態
/* do_fork -     parent process for a new child process
 * @clone_flags: used to guide how to clone the child process
 * @stack:       the parent's user stack pointer. if stack==0, It means to fork a kernel thread.
 * @tf:          the trapframe info, which will be copied to child process's proc->tf
 */
int
do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf) {
    int ret = -E_NO_FREE_PROC;
    struct proc_struct *proc;
    if (nr_process >= MAX_PROCESS) {
        goto fork_out;
    }
    ret = -E_NO_MEM;
    //LAB4:EXERCISE2 YOUR CODE
    /*
     * Some Useful MACROs, Functions and DEFINEs, you can use them in below implementation.
     * MACROs or Functions:
     *   alloc_proc:   create a proc struct and init fields (lab4:exercise1)
     *   setup_kstack: alloc pages with size KSTACKPAGE as process kernel stack
     *   copy_mm:      process "proc" duplicate OR share process "current"'s mm according clone_flags
     *                 if clone_flags & CLONE_VM, then "share" ; else "duplicate"
     *   copy_thread:  setup the trapframe on the  process's kernel stack top and
     *                 setup the kernel entry point and stack of process
     *   hash_proc:    add proc into proc hash_list
     *   get_pid:      alloc a unique pid for process
     *   wakeup_proc:  set proc->state = PROC_RUNNABLE
     * VARIABLES:
     *   proc_list:    the process set's list
     *   nr_process:   the number of process set
     */

    //    1. call alloc_proc to allocate a proc_struct
    //    2. call setup_kstack to allocate a kernel stack for child process
    //    3. call copy_mm to dup OR share mm according clone_flag
    //    4. call copy_thread to setup tf & context in proc_struct
    //    5. insert proc_struct into hash_list && proc_list
    //    6. call wakeup_proc to make the new child process RUNNABLE
    //    7. set ret vaule using child proc's pid
    if((proc = alloc_proc()) == NULL){
        goto fork_out;                              // 分配失敗,直接返回
    }
    if(setup_kstack(proc) != 0){
        goto bad_fork_cleanup_kstack;               // 堆疊初始化失敗,釋放已佔用的空間並返回
    }
    if(copy_mm(clone_flags, proc) != 0){
        goto bad_fork_cleanup_proc;                 // 複製或共享記憶體管理結構失敗,釋放已佔用的空間並返回
    }
    copy_thread(proc, stack, tf);                   // 複製中斷幀和上下文
    proc->pid = get_pid();                          // 分配pid
    hash_proc(proc);                                // 將新程序加入雜湊表
    list_add(&proc_list, &(proc->list_link));
    nr_process ++;
    wakeup_proc(proc);                              // 喚醒程序
    ret = proc->pid;                                // 返回程序pid

fork_out:
    return ret;

bad_fork_cleanup_kstack:
    put_kstack(proc);
bad_fork_cleanup_proc:
    kfree(proc);
    goto fork_out;
}

參考答案中有更多的考慮,不同部分及說明如下

    proc->parent = current;                         // 設定父程序為當前的程序
    bool intr_flag;                                 // 由於要操作全域性資料結構,而ucore允許中斷巢狀,為了避免出現執行緒安全問題,這裡需要利用到kern/sync/sync.h中的鎖,至於執行緒安全問題可以參考相關的博文
    local_intr_save(intr_flag);
    {
        proc->pid = get_pid();
        hash_proc(proc);
        list_add(&proc_list, &(proc->list_link));
        nr_process ++;
    }
    local_intr_restore(intr_flag);

練習3:閱讀程式碼,理解proc_run函式和它呼叫的函式如何完成程序切換

參考piazza上同學的單步調式

1、kern_init呼叫了proc_init函式,後者啟動了建立核心執行緒的步驟

  • idleproc = alloc_proc()通過kmalloc函式獲得了proc_struct作為第0個程序的控制塊,並初始化
  • proc_init函式對idleproc核心執行緒進行進一步初始化
idleproc->pid = 0;                                // 設定pid=0,即第0個核心執行緒
idleproc->state = PROC_RUNNABLE;                  // 設定狀態為可執行,等待處理機排程執行該程序
idleproc->kstack = (uintptr_t)bootstack;          // 直接將ucore啟動時的核心棧作為改執行緒的核心棧(以後其他執行緒的核心棧需要分配獲得)
idleproc->need_resched = 1;                       // 設定為可以被排程,根據cpu_idle函式,只要idleproc在執行且可以被排程,就立刻執行schedule函式切換到其他有用程序執行
set_proc_name(idleproc, "idle");                  // 執行緒命名為idle
nr_process ++;                                    // 執行緒數+1
current = idleproc;                               // 設定當前執行緒為idleproc
  • 呼叫pid = kernel_thread(init_main, "Hello world!!", 0)建立一個核心執行緒init_main

2、kernel_thread函式建立了核心執行緒的臨時中斷幀,並呼叫do_fork函式來進行進一步產生新的核心執行緒

// kernel_thread - create a kernel thread using "fn" function
// NOTE: the contents of temp trapframe tf will be copied to 
//       proc->tf in do_fork-->copy_thread function
int
kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags) {
    struct trapframe tf;                            // 臨時變數tf來儲存中斷幀,並傳遞給do_fork
    memset(&tf, 0, sizeof(struct trapframe));       // tf清零初始化
    tf.tf_cs = KERNEL_CS;                           // 設定程式碼段為核心程式碼段KERNEL_CS
    tf.tf_ds = tf.tf_es = tf.tf_ss = KERNEL_DS;     // 設定資料段為核心資料段KERNEL_DS
    tf.tf_regs.reg_ebx = (uint32_t)fn;
    tf.tf_regs.reg_edx = (uint32_t)arg;
    tf.tf_eip = (uint32_t)kernel_thread_entry;      // 設定入口為函式kernel_thread_entry(定義在kern/process/entry.S中),該函式主要為函式fn做準備
    return do_fork(clone_flags | CLONE_VM, 0, &tf); // 呼叫do_fork進一步完成建立工作,第二個引數為0代表建立的是核心執行緒
}
kernel_thread_entry:        # void kernel_thread(void)
    pushl %edx              # push arg 將fn的引數壓棧
    call *%ebx              # call fn  呼叫fn
    pushl %eax              # save the return value of fn(arg) 將fn的返回值壓棧
    call do_exit            # call do_exit to terminate current thread 退出執行緒

3、do_fork完成的工作在練習2中已充分說明,這裡詳細說明最重要的copy_thread函式

// copy_thread - setup the trapframe on the  process's kernel stack top and
//             - setup the kernel entry point and stack of process
static void
copy_thread(struct proc_struct *proc, uintptr_t esp, struct trapframe *tf) {
    proc->tf = (struct trapframe *)(proc->kstack + KSTACKSIZE) - 1;  // proc->kstack指向給程序分配的KSTACKPAGE大小的空間,加上大小之後就指向最高地址,然後強制型別轉換後-1即在記憶體頂部空出一塊trap frame大小的空間,用於複製傳進來的臨時幀
    *(proc->tf) = *tf;                                               // 複製臨時幀
    proc->tf->tf_regs.reg_eax = 0;
    proc->tf->tf_esp = esp;
    proc->tf->tf_eflags |= FL_IF;

    proc->context.eip = (uintptr_t)forkret;                          // 設定指令暫存器為forkret函式,則進入子程序後就會呼叫forkret
    proc->context.esp = (uintptr_t)(proc->tf);                       // 設定棧頂為核心棧存放了tf後的最大地址,即子程序棧頂從tf下面開始
}

隨後到do_fork完成所有工作,返回到kernel_thread再返回到proc_init再返回到kern_init

4、此時在kern_init後續有cpu_idleschedule進行程序排程

// cpu_idle - at the end of kern_init, the first kernel thread idleproc will do below works
void
cpu_idle(void) {
    while (1) {
        if (current->need_resched) {                                 // 顯然此時current = idleproc且已被設定為需要被排程,故進入schedule函式排程
            schedule();
        }
    }
}

5、schedule將返回之前建立的程序,並呼叫proc_run進行執行

// proc_run - make process "proc" running on cpu
// NOTE: before call switch_to, should load  base addr of "proc"'s new PDT
void
proc_run(struct proc_struct *proc) {
    if (proc != current) {
        bool intr_flag;                                              // 由於要切換程序,同樣要注意執行緒安全問題,加鎖
        struct proc_struct *prev = current, *next = proc;
        local_intr_save(intr_flag);
        {
            current = proc;                                          // 設定要切換的程序為接下來的正在執行程序
            load_esp0(next->kstack + KSTACKSIZE);                    // 修改TSS任務狀態棧,將TSS的ts_esp0指向下一個程序的堆疊空間,引數值參考3中的註釋
            lcr3(next->cr3);                                         // 修改頁表基址
            switch_to(&(prev->context), &(next->context));           // 切換,函式定義在kern\process\switch.S中
        }
        local_intr_restore(intr_flag);
    }
}

6、proc_run完成一些準備工作後,呼叫switch_to最終完成切換

高地址
--------------------
|arg1:next->context|
--------------------
|arg0:prev->context|
--------------------
|return address    |    --->esp
--------------------

switch_to:                      # switch_to(from, to)
    # save from's registers
    movl 4(%esp), %eax          # eax points to from [esp+4]即prev->context,將指向prev->context的指標放入eax中
    popl 0(%eax)                # save eip !popl 按照context的順序儲存原程序的各暫存器
    movl %esp, 4(%eax)          # save esp::context of from
    movl %ebx, 8(%eax)          # save ebx::context of from
    movl %ecx, 12(%eax)         # save ecx::context of from
    movl %edx, 16(%eax)         # save edx::context of from
    movl %esi, 20(%eax)         # save esi::context of from
    movl %edi, 24(%eax)         # save edi::context of from
    movl %ebp, 28(%eax)         # save ebp::context of from

高地址
--------------------
|arg1:next->context|
--------------------
|arg0:prev->context|   --->esp
--------------------

    # restore to's registers
    movl 4(%esp), %eax          # not 8(%esp): popped return address already  由於前面已經popl,故這裡不是8而是[esp+4]即next->context放入eax中
                                # eax now points to to 按照context的順序獲取新程序的各暫存器
    movl 28(%eax), %ebp         # restore ebp::context of to
    movl 24(%eax), %edi         # restore edi::context of to
    movl 20(%eax), %esi         # restore esi::context of to
    movl 16(%eax), %edx         # restore edx::context of to
    movl 12(%eax), %ecx         # restore ecx::context of to
    movl 8(%eax), %ebx          # restore ebx::context of to
    movl 4(%eax), %esp          # restore esp::context of to

    pushl 0(%eax)               # push eip 前面將原程序的返回地址彈出,現在將next->context的eip壓入棧
    ret

注意,新程序就是前面建立的init_main,參考3可以知道當時proc->context.eip = (uintptr_t)forkret,當switch_to返回時,把棧頂的內容賦值給EIP暫存器,此時跳轉到了forkret進行執行

7、forkret呼叫forkrets完成準備並最終進入init_main

// forkret -- the first kernel entry point of a new thread/process
// NOTE: the addr of forkret is setted in copy_thread function
//       after switch_to, the current proc will execute here.
static void
forkret(void) {
    forkrets(current->tf);
}

    # return falls through to trapret...
.globl __trapret
__trapret:
    # restore registers from stack
    popal

    # restore %ds, %es, %fs and %gs
    popl %gs
    popl %fs
    popl %es
    popl %ds

    # get rid of the trap number and error code
    addl $0x8, %esp
    iret                                  

.globl forkrets
forkrets:
    # set stack to this new process's trapframe
    movl 4(%esp), %esp                            #esp指向當前程序的中斷幀即esp指向current->tf
    jmp __trapret                                

注意參考3可以知道proc->context.esp = (uintptr_t)(proc->tf),而在6中switch_to最後壓入了proc->context.eip,故在forkrets中[esp+4]即指向context.esp,這裡就是中斷幀proc->tf,參考棧的內容、struct trapframe__trapret就會理解跳轉情況

switch_to之後的棧在記憶體中的存放:
高地址
-------------
|context.ebp|
|...........| 
-------------
|context.esp|   --->[esp+4] = proc->tf
-------------
|context.eip|   --->esp

struct trapframe {
    struct pushregs tf_regs;   // 通用暫存器,對應__trapret: popal
    uint16_t tf_gs;            // 對應popl %gs
    uint16_t tf_padding0;
    uint16_t tf_fs;            // 對應popl %fs
    uint16_t tf_padding1;
    uint16_t tf_es;            // 對應popl %es
    uint16_t tf_padding2;
    uint16_t tf_ds;            // 對應popl %ds
    uint16_t tf_padding3;
    uint32_t tf_trapno;        // [esp]此時的esp指向這裡
    /* below here defined by x86 hardware */
    uint32_t tf_err;           // [esp+4]
    uintptr_t tf_eip;          // [esp+8]對應addl $0x8, %esp
    uint16_t tf_cs;
    ...
};

iret返回時就會進入此時的esp所指向的proc->tf.tf_eip,在2中這個值被初始化為kernel_thread_entry

.text
.globl kernel_thread_entry
kernel_thread_entry:        # void kernel_thread(void)

    pushl %edx              # push arg
    call *%ebx              # call fn

    pushl %eax              # save the return value of fn(arg)
    call do_exit            # call do_exit to terminate current thread

由此進入init_main,當返回時呼叫do_exit完成所有過程

完成程式碼後使用make qemu檢視結果如下,程式正確

(THU.CST) os is loading ...

...省略部分輸出
check_alloc_page() succeeded!
check_pgdir() succeeded!
check_boot_pgdir() succeeded!
...省略部分輸出
kmalloc_init() succeeded!
check_vma_struct() succeeded!
...省略部分輸出
check_pgfault() succeeded!
check_vmm() succeeded.
...省略部分輸出
check_swap() succeeded!
++ setup timer interrupts
this initproc, pid = 1, name = "init"
To U: "Hello world!!".
To U: "en.., Bye, Bye. :)"
kernel panic at kern/process/proc.c:344:
    process exit!!.

Welcome to the kernel debug monitor!!
Type 'help' for a list of commands.

相關推薦

作業系統實驗實驗報告

實驗四:核心執行緒管理 練習1:分配並初始化一個程序控制塊 首先來看幾個比較重要的資料結構,kern/process/proc.h中定義的程序控制塊及kern/trap/trap.h中定義的中斷幀 struct proc_struct {

實驗實驗報告

std 兩個 png int font src 初始 一次 數組a 實驗結論 Part 1 數組將類型相同的一組數據在內存中連續存放,由實驗可看出數組中元素的內存地址是連續的,不同類型數據計算機為其分配的內存空間是不同的。 Part 2 定義一維數組a,須指明它包含

物聯網-wemos D1 Mini (esp8266)實驗 -- 實驗二中的丟失尋找器改進為手機控制水泵

 材料:              D1 Mini              1只        &nbs

20155217 實驗《Java面向對象程序設計》實驗報告

是我 system oca thead temporary activity 免費 nds 系統文件 20155217 實驗四《Java面向對象程序設計》實驗報告 一、實驗內容 1.基於Android Studio開發簡單的Android應用並部署測試; 2.了解Andro

實驗 圖的實現和應用 實驗報告 20162305

peek 有關 打印 隊列 廣度 dex 是否 深度優先 遍歷 實驗四 圖的實現和應用 實驗報告 20162305 實驗一 鄰接矩陣實現無向圖 實驗要求 用鄰接矩陣實現無向圖(邊和頂點都要保存),實現在包含添加和刪除結點的方法,添加和刪除邊的方法,size(),isEmp

2017-2018-2 20165315 實驗《Android程序設計》實驗報告

idt 目的 不同 目錄 stat pic AC 定義 fan 2017-2018-2 20165315 實驗四《Android程序設計》實驗報告 第24章:初識Android Android Studio項目的目錄樹 1 build:該目錄包含了自動生成的文件,這些

2017-2018-2 20165202 實驗《Android程序設計》實驗報告

delete 封面 enter header nexus options baseline flat 代碼實現 一、實驗報告封面 二、實驗內容 1.基於Android Studio開發簡單的Android應用並部署測試; 2.了解Android、組件、布局管理器的使用;

2017-2018-2 20165330 實驗《Android程序設計》實驗報告

long this lin line tool 運行結果截圖 undle TP Coding 下載與安裝Android Studio 下載地址:Download Android Studio 安裝教程參考Android開發簡易教程 實驗內容 任務一 參考《Java和A

20165229 實驗 《Android程序設計》 實驗報告

andro content toast getmenu length tex 啟示 urn 內容 20165229 實驗四 《Android程序設計》 實驗報告 實驗內容: 1.Android Stuidio的安裝測試 2.Activity測試 3.UI測試 4.布局測試

2017-2018-2 20165236 實驗《Android開發基礎》實驗報告

pan and 發表 rda RoCE AS .cn main 博客 2017-2018-2 20165236 實驗四《Android開發基礎》實驗報告 一、實驗報告封面 課程:Java程序設計 班級:1652班 姓名:郭金濤 學號:20

2017-2018-2 20165215 實驗《Android開發基礎》實驗報告

PE 對齊 ftw src 創建 信息 多個 界面 線性布局 2017-2018-2 20165215 實驗四《Android開發基礎》實驗報告 一、實驗報告封面 課程:Java程序設計 班級:1652班 姓名:張家佳 學號:20165215 指導教師:婁嘉鵬 實驗日期:2

20165328 實驗《Andriid應用開發》實驗報告

com and alt mage 開發 TP 技術分享 inf ID 20165328 實驗四《Andriid應用開發》實驗報告

20165307 實驗《Andriid應用開發》實驗報告

布局 main logs ava png 啟動 TP ID hello 1.實驗要求: 參考《Java和Android開發學習指南(第二版)(EPUBIT,Java for Android 2nd)》第二十四章 參考http://www.cnblogs.com/rocedu

2017-2018-2 20165218 實驗《Android開發基礎》實驗報告

信息 and 修改 cti 自己的 res 鏈接 學習 main 實驗三 Android開發基礎 課程:java程序設計 姓名:趙冰雨 學號:20165218 指導教師:婁嘉鵬 實驗日期:2018.4.14 實驗內容: 1.基於Android Studio開發簡單的And

學號 2017-2018-20172309 《程序設計與數據結構》實驗報告

日期 處理 技術分享 dia https 構建 new 解決 哈哈 學號 2017-2018-20172309 《程序設計與數據結構》實驗四報告 課程:《程序設計與數據結構》 班級: 1723 姓名: 王誌偉 學號:20172309 實驗教師:王誌強老師 實驗日期:2018

20172311 2017-2018-2 《程序設計與數據結構》實驗報告

對話框 軟件 感想 epub 活動 日期 logs ima 圖片 20172311 2017-2018-2 《程序設計與數據結構》實驗四報告 課程:《程序設計與數據結構》 班級: 1723 姓名: 趙曉海 學號:20172311 實驗教師:王誌強 實驗日期:2018年5月3

20172303 2017-2018-2 《程序設計與數據結構》實驗報告

打開 RoCE rep 結果 left android-s 遇到 IE 程序 20172303 2017-2018-2 《程序設計與數據結構》實驗四報告 課程:《程序設計與數據結構》 班級: 1723 姓名: 範雯琪 學號:20172303 實驗教師:王誌強 助教:張旭

2017-2018-2《程序設計與數據結構》實驗報告

結果 數據結構 dea 怎麽加密 遇到 相關 cipher and AR 學號 2017-2018-2 《程序設計與數據結構》實驗四報告 課程:《程序設計與數據結構》 班級: 1723 姓名: 彭霖 學號:20172312 實驗教師:王誌強 實驗日期:2018年5月16日

實驗 惡意程式碼技術 實驗報告

實驗報告 四   學   號  201521440029   中國人民公安大學 Chinese people’ public security university  

實驗報告

實驗名稱 實驗 4 [bx]和 loop 的使用 實驗日期 11/21學院 計軟院 專業 計科 年級 2017 級  4 班次 姓名  任心怡  學號  20171308152 一、實驗目的 1. 理解和掌握暫存器間接定址方式[bx] 2. 通過彙編指令 lo