日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

6.S081 附加Lab1 用户执行系统调用的过程(Trap)

發布時間:2024/3/24 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 6.S081 附加Lab1 用户执行系统调用的过程(Trap) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

6.S081 附加Lab011 用戶執行系統調用的過程(Trap)

文章目錄

    • 6.S081 附加Lab011 用戶執行系統調用的過程(Trap)
    • 0. 一些背景說明
    • 1. 進入內核前的準備write() -> ECALL
    • 2. 進入內核后的ECALL -> uservec()
    • 3. 保存用戶寄存器內容uservec()
    • 4. 處理trap——usertrap()
    • 5. usertrapret()
    • 6. userrret()

0. 一些背景說明

附加lab從011開始編號,下一個是012(為了避開lab10,lab11…)

注意,這里主要接 6.S081-5用戶空間和內核空間的切換–Trap機制

代碼流程:write() -> ECALL -> uservec()(Trampoline.s) -> usertrap() -> syscall() -> sys_write() -> syscall() -> usertrapret() -> usertrapret() ->ret

Trap的時候,我們需要做什么?(當前處于user mode,現在需要執行系統調用)

  • 保存32個用戶寄存器
  • 保存當前PC
  • mode切換成supervisor
  • SATP從user page table切換成kernel_pagetable
  • Stack Frame和Stack Pointer都需要改變,因為需要一個stack來調用kernel的函數
  • 跳入kernel

本實驗將主要關心的是,執行系統調用時計算機的狀態 – 可以用寄存器狀態來判斷,主要關心的寄存器有:

  • PC 程序計數器(Program Counter Register)

  • mode標志位(是supervisor mode 還是 user mode)

  • SATP(Supervisor Address Translation and Protection)寄存器,它包含了指向page table的物理內存地址

  • STVEC(Supervisor Trap Vector Base Address Register)寄存器,它指向了內核中處理trap的指令的起始地址。

  • SEPC(Supervisor Exception Program Counter)寄存器,在trap的過程中保存程序計數器的值。

  • SSRATCH(Supervisor Scratch Register)寄存器

后面的實驗內容和流程,主要是實驗內容參考-中文notes,實驗的流程和工具參考6.S081 Lab00 xv6啟動過程

總結一下:系統調用被刻意設計的看起來像是函數調用,但是背后的user/kernel轉換比函數調用要復雜的多。之所以這么復雜,很大一部分原因是要保持user/kernel之間的隔離性,內核不能信任來自用戶空間的任何內容。

1. 進入內核前的準備write() -> ECALL

打開sh.c,代碼如下,注意write(2, "$ ", 2);,它是我們接下來要追蹤的系統調用。-- shell 將 "$ " 通過 write系統調用輸出到文件描述符2。 – 注意,我這里其實原本是fprintf(2, "$ ");,需要修改成write ,然后make clean,然后再調試

int getcmd(char *buf, int nbuf) {// fprintf(2, "$ ");write(2, "$ ", 2);memset(buf, 0, nbuf);gets(buf, nbuf);if(buf[0] == 0) // EOFreturn -1;return 0; }int main(void) {static char buf[100];int fd;// Ensure that three file descriptors are open.while((fd = open("console", O_RDWR)) >= 0){if(fd >= 3){close(fd);break;}}// Read and run input commands.... }

進入調試

  • make qemu-gdb,并使用cpu個數 = 1(方便調試)
wc@r740:~/OS_experiment/xv6-riscv-fall19$ make CPUS=1 qemu-gdb *** Now run 'gdb' in another window. qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 1 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 -S -gdb tcp::26017
  • 新建窗口,打開gdb并用gdb遠程鏈接 qemu的gdb
wc@r740:~/OS_experiment/xv6-riscv-fall19$ riscv64-unknown-elf-gdb kernel/kernel # 一大堆輸出 ... (gdb) target remote localhost:26017 Remote debugging using localhost:26017 0x0000000000001000 in ?? ()
  • 實際上被調用的write函數的實現如下(usys.s中可以看到) – 還有很多系統調用都是這樣實現的,比如pipe, close, read, kill …
#define SYS_write 16.global write write:li a7, SYS_writeecallret

? ecall會讓我們跳轉到內核,kernel執行完之后會返回,然后執行ret,最終返回到shell。

  • 找到shell 的write對應的ecall地址(再shell.asm中找到如下代碼👇),可以看到ecall對應地址是0xd66
0000000000000d64 <write>: .global write write:li a7, SYS_writed64: 48c1 li a7,16ecalld66: 00000073 ecallretd6a: 8082 ret
  • 在0xd66處設置斷點,然后運行 (注意這里的display/i $pc在每次斷點的時候,都顯示下一條指令的反匯編)
Remote debugging using localhost:26017 0x0000000000001000 in ?? () (gdb) display/i $pc 1: x/i $pc => 0x1000: auipc t0,0x0 (gdb) b *0xd66 Breakpoint 1 at 0xd66 (gdb) c Continuing.Breakpoint 1, 0x0000000000000d66 in ?? () 1: x/i $pc => 0xd66: ecall
  • 可以看到,我們成功在shell的write對應的ecall處停下來了,然后我們刪除當前斷點,打印pc,打印全部32個用戶寄存器(info reg)。這里的a0, a1, a2是shell傳給write系統調用的參數(write(2, "$ ", 2)) ——所以a0是2(文件描述符),a1是字符串指針,a2是2(寫入字符數)。
(gdb) delete 1 (gdb) p $pc $1 = (void (*)()) 0xd66 (gdb) info reg (gdb) info reg ra 0x24 0x24 sp 0x3f80 0x3f80 gp 0x505050505050505 0x505050505050505 tp 0x505050505050505 0x505050505050505 t0 0x505050505050505 361700864190383365 t1 0x505050505050505 361700864190383365 t2 0x505050505050505 361700864190383365 fp 0x3fa0 0x3fa0 s1 0x13f8 5112 a0 0x3fffffe000 274877898752 a1 0x1280 4736 a2 0x2 2 a3 0x505050505050505 361700864190383365 a4 0x505050505050505 361700864190383365 a5 0x2 2 a6 0x505050505050505 361700864190383365 a7 0x10 16 s2 0x64 100 s3 0x20 32 s4 0x13fb 5115 s5 0x1380 4992 s6 0x505050505050505 361700864190383365 s7 0x505050505050505 361700864190383365 s8 0x505050505050505 361700864190383365 s9 0x505050505050505 361700864190383365 s10 0x505050505050505 361700864190383365 --Type <RET> for more, q to quit, c to continue without paging--c s11 0x505050505050505 361700864190383365 t3 0x505050505050505 361700864190383365 t4 0x505050505050505 361700864190383365 t5 0x505050505050505 361700864190383365 t6 0x505050505050505 361700864190383365 pc 0x3ffffff004 0x3ffffff004
  • 打印a1的內容,確實是"$ "
(gdb) p/c *($a1)@2 $7 = {36 '$', 0 '\000'}
  • 此外,寄存器可以看出,sp和pc還都比較接近0,說明現在運行在用戶態。(-- 說明是虛擬地址),因為物理地址,至少都是從0x80000(OS啟動處)開始的了。 --xv6中 kernel的虛擬地址和物理地址是一樣的。

  • 查看SATP寄存器(頁表地址)

  • 查看頁表:qemu界面,按ctrl + a然后按c可以進入qemu console, 輸入info mem可以查看頁表的,但是我這里看不到。

$ QEMU 4.1.0 monitor - type 'help' for more information (qemu) info mem unknown command: 'info mem'

尋找原因:在如下所示的路徑文件下,存在的代碼如下,說明我必須是I386機型,才能使用info mem,這里我進行了注釋的修改,最終make會報錯,因此后面關于page table的輸出,我都只能暫時省去了。

// /home/wc/OS_experiment/qemu-4.1.0/riscv64-softmmu/hmp-commands-info.h// changed by levi #if defined(TARGET_I386) { .name = "mem", .args_type = "", .params = "", .help = "show the active virtual memory mappings", .cmd = hmp_info_mem, }, #endif // { // .name = "mem", // .args_type = "", // .params = "", // .help = "show the active virtual memory mappings", // .cmd = hmp_info_mem, // },
  • page table的正確輸出如圖👇——后面幾頁地址很大,是因為位于彈簧床程序中。(這是trampoline page。trampoline page包含了內核的trap處理代碼)

2. 進入內核后的ECALL -> uservec()

  • 繼續執行,ecall
Breakpoint 1 at 0xd66 (gdb) c Continuing.Breakpoint 1, 0x0000000000000d66 in ?? () 1: x/i $pc => 0xd66: ecall (gdb) x/3i 0xd640xd64: li a7,16 => 0xd66: ecall0xd6a: ret (gdb) stepi 0x0000003ffffff004 in ?? () 1: x/i $pc => 0x3ffffff004: sd ra,40(a0)
  • 打印pc,發現已經是很大的地址了 – 說明進入了內核 根據現在的程序計數器,代碼正在trampoline page的最開始,這是用戶內存中一個非常大的地址,所以現在我們的指令正運行在內存的trampoline page中。
(gdb) p $pc $1 = (void (*)()) 0x3ffffff004
  • 查看當前的指令(查看的是pc - 4的內容):這些指令是內核在supervisor mode中將要執行的最開始的幾條指令,也是在trap機制中最開始要執行的幾條指令。
(gdb) x/6i 0x3ffffff0000x3ffffff000: csrrw a0,sscratch,a0 => 0x3ffffff004: sd ra,40(a0)0x3ffffff008: sd sp,48(a0)0x3ffffff00c: sd gp,56(a0)0x3ffffff010: sd tp,64(a0)0x3ffffff014: sd t0,72(a0)
  • 繼續查看寄存器的值,發現并沒有改變——因此在此之前,我們還不能往這些寄存器賦值,否則不能正確恢復
(gdb) info reg ra 0x24 0x24 sp 0x3f80 0x3f80 gp 0x505050505050505 0x505050505050505 tp 0x505050505050505 0x505050505050505 t0 0x505050505050505 361700864190383365 t1 0x505050505050505 361700864190383365 t2 0x505050505050505 361700864190383365 fp 0x3fa0 0x3fa0 s1 0x13f8 5112 a0 0x3fffffe000 274877898752 a1 0x1280 4736 a2 0x2 2 a3 0x505050505050505 361700864190383365 a4 0x505050505050505 361700864190383365 a5 0x2 2 a6 0x505050505050505 361700864190383365 a7 0x10 16 s2 0x64 100 s3 0x20 32 s4 0x13fb 5115 s5 0x1380 4992 s6 0x505050505050505 361700864190383365 s7 0x505050505050505 361700864190383365 s8 0x505050505050505 361700864190383365 s9 0x505050505050505 361700864190383365 s10 0x505050505050505 361700864190383365 --Type <RET> for more, q to quit, c to continue without paging--c s11 0x505050505050505 361700864190383365 t3 0x505050505050505 361700864190383365 t4 0x505050505050505 361700864190383365 t5 0x505050505050505 361700864190383365 t6 0x505050505050505 361700864190383365 pc 0x3ffffff004 0x3ffffff004
  • 進入qemu,查看page table,發現還沒改變👇。ecall并不會切換page table,這是ecall指令的一個非常重要的特點。 —— trap處理代碼必須存在于每一個user page table中,因為ecall并不會切換page table,我們需要在user page table中的某個地方來執行最初的內核代碼。而這個trampoline page,是由內核小心的映射到每一個user page table中,以使得當我們仍然在使用user page table時,內核在一個地方能夠執行trap機制的最開始的一些指令。——這里的控制是通過STVEC寄存器實現的,在從內核空間進入到用戶空間之前,內核會設置好STVEC寄存器指向內核希望trap代碼運行的位置。
(gdb) p/x $stvec $2 = 0x3ffffff000
  • 即使trampoline page是在用戶地址空間的user page table完成的映射,用戶代碼不能寫它,因為這些page對應的PTE并沒有設置PTE_u標志位。這也是為什么trap機制是安全的。

ecall指令都做了什么??

  • user mode -> supervisor mode
  • 將PC保存在了SEPC寄存器(調用ecall的pc)👇
  • (gdb) p/x $sepc $4 = 0xd66
  • ecall 會跳轉到STVEC寄存器指向的指令(trampoline page設置的trap內核代碼處,上面已經說過)
  • 根據0. 一些背景說明中的要求,我們只完成了橘色部分,紅色部分還需要別的函數/指令完成👇

    Trap的時候,我們需要做什么?(當前處于user mode,現在需要執行系統調用)

    • 保存32個用戶寄存器
    • 保存當前PC
    • mode切換成supervisor
    • SATP從user page table切換成kernel_pagetable
    • Stack Frame和Stack Pointer都需要改變,因為需要一個stack來調用kernel的函數
    • 跳入kernel

    為什么ecall 不切換pagetable?——切換page table的代價比較高,不用在不必要的場景切換page table。

    所以ecall的下一條指令的位置是STVEC指向的地址,也就是trampoline page的起始地址。(注,實際上ecall是CPU的指令,自然在gdb中看不到具體內容)

    3. 保存用戶寄存器內容uservec()

    接上面,由于我們的page table中存放了trampoline page(-- 這樣每個進程都有自己的trapframe page,并且此處的虛擬地址總是0x3ffffffe000

    trampframe存放的內容如下(proc.h 中的trapframe的結構體),剛開始有5個kernel實現存放在trapframe中的數據,后面是32個用戶寄存器的內容。

    // per-process data for the trap handling code in trampoline.S. // sits in a page by itself just under the trampoline page in the // user page table. not specially mapped in the kernel page table. // the sscratch register points here. // uservec in trampoline.S saves user registers in the trapframe, // then initializes registers from the trapframe's // kernel_sp, kernel_hartid, kernel_satp, and jumps to kernel_trap. // usertrapret() and userret in trampoline.S set up // the trapframe's kernel_*, restore user registers from the // trapframe, switch to the user page table, and enter user space. // the trapframe includes callee-saved user registers like s0-s11 because the // return-to-user path via usertrapret() doesn't return through // the entire kernel call stack. struct trapframe {/* 0 */ uint64 kernel_satp; // kernel page table/* 8 */ uint64 kernel_sp; // top of process's kernel stack/* 16 */ uint64 kernel_trap; // usertrap()/* 24 */ uint64 epc; // saved user program counter/* 32 */ uint64 kernel_hartid; // saved kernel tp/* 40 */ uint64 ra;/* 48 */ uint64 sp;/* 56 */ uint64 gp;/* 64 */ uint64 tp;/* 72 */ uint64 t0;/* 80 */ uint64 t1;/* 88 */ uint64 t2;/* 96 */ uint64 s0;/* 104 */ uint64 s1;/* 112 */ uint64 a0;/* 120 */ uint64 a1;/* 128 */ uint64 a2;/* 136 */ uint64 a3;/* 144 */ uint64 a4;/* 152 */ uint64 a5;/* 160 */ uint64 a6;/* 168 */ uint64 a7;/* 176 */ uint64 s2;/* 184 */ uint64 s3;/* 192 */ uint64 s4;/* 200 */ uint64 s5;/* 208 */ uint64 s6;/* 216 */ uint64 s7;/* 224 */ uint64 s8;/* 232 */ uint64 s9;/* 240 */ uint64 s10;/* 248 */ uint64 s11;/* 256 */ uint64 t3;/* 264 */ uint64 t4;/* 272 */ uint64 t5;/* 280 */ uint64 t6; };

    查看trampoline.S的代碼,第一行就是csrrw a0, sscratch, a0這個指令交換了a0和sscratch兩個寄存器的內容。(為了清晰我這里給出了trampoline.S的完整代碼——后續也會用到)

    #this is trampoline.S# code to switch between user and kernel space.## this code is mapped at the same virtual address# (TRAMPOLINE) in user and kernel space so that# it continues to work when it switches page tables.## kernel.ld causes this to be aligned# to a page boundary.#.section trampsec .globl trampoline trampoline: .align 4 .globl uservec uservec: ## trap.c sets stvec to point here, so# traps from user space start here,# in supervisor mode, but with a# user page table.## sscratch points to where the process's p->tf is# mapped into user space, at TRAPFRAME.## swap a0 and sscratch# so that a0 is TRAPFRAMEcsrrw a0, sscratch, a0# save the user registers in TRAPFRAMEsd ra, 40(a0)sd sp, 48(a0)sd gp, 56(a0)sd tp, 64(a0)sd t0, 72(a0)sd t1, 80(a0)sd t2, 88(a0)sd s0, 96(a0)sd s1, 104(a0)sd a1, 120(a0)sd a2, 128(a0)sd a3, 136(a0)sd a4, 144(a0)sd a5, 152(a0)sd a6, 160(a0)sd a7, 168(a0)sd s2, 176(a0)sd s3, 184(a0)sd s4, 192(a0)sd s5, 200(a0)sd s6, 208(a0)sd s7, 216(a0)sd s8, 224(a0)sd s9, 232(a0)sd s10, 240(a0)sd s11, 248(a0)sd t3, 256(a0)sd t4, 264(a0)sd t5, 272(a0)sd t6, 280(a0)# save the user a0 in p->tf->a0csrr t0, sscratchsd t0, 112(a0)# restore kernel stack pointer from p->tf->kernel_spld sp, 8(a0)# make tp hold the current hartid, from p->tf->kernel_hartidld tp, 32(a0)# load the address of usertrap(), p->tf->kernel_trapld t0, 16(a0)# restore kernel page table from p->tf->kernel_satpld t1, 0(a0)csrw satp, t1sfence.vma zero, zero# a0 is no longer valid, since the kernel page# table does not specially map p->tf.# jump to usertrap(), which does not returnjr t0.globl userret userret:# userret(TRAPFRAME, pagetable)# switch from kernel to user.# usertrapret() calls here.# a0: TRAPFRAME, in user page table.# a1: user page table, for satp.# switch to the user page table.csrw satp, a1sfence.vma zero, zero# put the saved user a0 in sscratch, so we# can swap it with our a0 (TRAPFRAME) in the last step.ld t0, 112(a0)csrw sscratch, t0# restore all but a0 from TRAPFRAMEld ra, 40(a0)ld sp, 48(a0)ld gp, 56(a0)ld tp, 64(a0)ld t0, 72(a0)ld t1, 80(a0)ld t2, 88(a0)ld s0, 96(a0)ld s1, 104(a0)ld a1, 120(a0)ld a2, 128(a0)ld a3, 136(a0)ld a4, 144(a0)ld a5, 152(a0)ld a6, 160(a0)ld a7, 168(a0)ld s2, 176(a0)ld s3, 184(a0)ld s4, 192(a0)ld s5, 200(a0)ld s6, 208(a0)ld s7, 216(a0)ld s8, 224(a0)ld s9, 232(a0)ld s10, 240(a0)ld s11, 248(a0)ld t3, 256(a0)ld t4, 264(a0)ld t5, 272(a0)ld t6, 280(a0)# restore user a0, and save TRAPFRAME in sscratchcsrrw a0, sscratch, a0# return to user mode and user pc.# usertrapret() set up sstatus and sepc.sret

    打印sscratch寄存器的內容,現在是2,其實這里就是a0之前的值,正如之前所說,write(2, "$ ", 2);函數調用的時候a0作為第一個傳入參數的保存者,保存的是文件描述符2。也就是說:在進入到user space之前,內核會將trapframe page的地址保存在這個寄存器中,也就是0x3fffffe000這個地址。更重要的是,RISC-V有一個指令允許交換任意兩個寄存器的值。而SSCRATCH寄存器的作用就是保存另一個寄存器的值,并將自己的值加載給另一個寄存器。,所以,現在的a0就是trapframe的首地址(0x3ffffffe000

    (gdb) p/x $sscratch $5 = 0x2 (gdb) p/x $a0 $6 = 0x3fffffe000

    所以trampoline.S的后續指令(第二三四五六七八…)都有意義了,也就是a0其實是trapframe的首地址,由第二條指令sd ra, 40(a0)可知,ra被保存在了trapframe + 40的位置…。注意代碼的最后(倒數第二條指令,又將a0和sscratch寄存器的內容互換了回去,然后執行sret就返回用戶空間了)

    • 然后注意trampoline中的第一條load指令,因為a0是trapframe,所以a0 + 8是kernel_sp(kernel的Stack Pointer)所以這條指令的作用是初始化Stack Pointer指向這個進程的kernel stack的最頂端。
    # restore kernel stack pointer from p->tf->kernel_sp ld sp, 8(a0)
    • 指向完這👆條指令之后,我們打印一下當前的Stack Pointer寄存器,這是這個進程的kernel stack。因為XV6在每個kernel stack下面放置一個guard page,所以kernel stack的地址都比較大。下一條指令是tp – 用來保存hartid(CPU編號) —— 保存當前運行在多核CPU的哪一個核上(因為我設置了單核,所以編號一定是0)。
    (gdb) p/x $sp $9 = 0x3fffffc000 (gdb) p/x $tp $10 = 0x0
    • 接下來是t0(usertrap的函數地址),t1(kernel_pagetable的地址)——實際上嚴格來說,t1的內容并不是kernel page table的地址,這是你需要向SATP寄存器寫入的數據。它包含了kernel page table的地址,但是移位了(注,詳見4.3),并且包含了各種標志位。
    (gdb) p/x $t0 $11 = 0x8000276a (gdb) p/x $t1 $12 = 0x505050505050505

    下一條指令是交換SATP和t1寄存器。這條指令執行完成之后,當前程序會從user page table切換到kernel page table?,F在我們在QEMU中打印page table,可以看出與之前的page table完全不一樣👇?!晒η袚Q了page table——切換到了kernel page table——有了kernel_pagetable我們就可以讀取kernel的data

    這里還有個問題,為什么代碼沒有崩潰?畢竟我們在內存中的某個位置執行代碼,程序計數器保存的是虛擬地址,如果我們切換了page table,為什么同一個虛擬地址不會通過新的page table尋址走到一些無關的page中?看起來我們現在沒有崩潰并且還在執行這些指令。有人來猜一下原因嗎?

    學生回答:因為我們還在trampoline代碼中,而trampoline代碼在用戶空間和內核空間都映射到了同一個地址。

    之所以叫trampoline page,是因為你某種程度在它上面“彈跳”了一下,然后從用戶空間走到了內核空間。

    這就是本科的時候,柏軍老師說的彈簧床可以防止程序“跑飛”(這里的彈簧床和本實驗的彈簧床有所不同)——柏軍老師指的是操作系統啟動的時候,用彈簧床程序引導bootloader去main()的首地址去執行(而不是直接用bootloader執行main),這里的“彈簧床”意思就是,當main()里面出現bug了,就再去彈簧床處執行(再次調用main()),這樣程序就不會因為一次bug而panic。

    最后一條指令是jr t0。執行了這條指令,我們就要從trampoline跳到內核的C代碼中。這條指令的作用是跳轉到t0指向的函數中。(前面已經說過,是usertrap函數),當然也可以打印一下:

    (gdb) stepi 0x0000003ffffff08e in ?? () 1: x/i $pc => 0x3ffffff08e: jr t0 (gdb) x/3i $t00x8000276a <usertrap>: addi sp,sp,-320x8000276c <usertrap+2>: sd ra,24(sp)0x8000276e <usertrap+4>: sd s0,16(sp)

    接下來我們就要以kernel stack,kernel page table跳轉到usertrap函數。

    4. 處理trap——usertrap()

    usertrap某種程度上存儲并恢復硬件狀態,但是它也需要檢查觸發trap的原因,以確定相應的處理方式。代碼如下。

    // // handle an interrupt, exception, or system call from user space. // called from trampoline.S // void usertrap(void) {int which_dev = 0;if((r_sstatus() & SSTATUS_SPP) != 0)panic("usertrap: not from user mode");// send interrupts and exceptions to kerneltrap(),// since we're now in the kernel.w_stvec((uint64)kernelvec);struct proc *p = myproc();// save user program counter.p->tf->epc = r_sepc();if(r_scause() == 8){// system callif(p->killed)exit(-1);// sepc points to the ecall instruction,// but we want to return to the next instruction.p->tf->epc += 4;// an interrupt will change sstatus &c registers,// so don't enable until done with those registers.intr_on();syscall();} else if((which_dev = devintr()) != 0){// ok} else if(r_scause() == 13 || r_scause() == 15){// printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);// printf(" sepc=%p stval=%p\n", r_sepc(), r_stval());uvmalloc(p->pagetable, PGROUNDDOWN(r_stval()), PGROUNDDOWN(r_stval()) + 4096);}else {printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);printf(" sepc=%p stval=%p\n", r_sepc(), r_stval());printf("page down:%d\n",PGROUNDDOWN(r_stval()));// printf("r :%d\n",r_scause());// int sz = 0;// while( !(r_stval()>=sz && r_stval()<sz+4096) )// {// sz = sz + 4096;// }// printf("sz:%d\n",sz);p->killed = 1;}if(p->killed)exit(-1);// give up the CPU if this is a timer interrupt.if(which_dev == 2)yield();usertrapret(); }

    它做的第一件事情是更改STVEC寄存器。取決于trap是來自于用戶空間還是內核空間,實際上XV6處理trap的方法是不一樣的。目前為止,我們只討論過當trap是由用戶空間發起時會發生什么。如果trap從內核空間發起,將會是一個非常不同的處理流程,因為從內核發起的話,程序已經在使用kernel page table。所以當trap發生時,程序執行仍然在內核的話,很多處理都不必存在。

    在內核中執行任何操作之前,usertrap中先將STVEC指向了kernelvec變量,這是內核空間trap處理代碼的位置,而不是用戶空間trap處理代碼的位置。

    然后來一步步分析代碼,注意看注釋部分

    // 找出當前正在運行的進程 -- 通過hartid (之前切換pagetable前已經保存到t0) struct proc *p = myproc(); // 把當前進程的pc保存到當前進程的trapframe(防止進程切換找不到了) // save user program counter. p->tf->epc = r_sepc();
    • 把當前進程的pc保存到當前進程的trapframe(防止進程切換找不到了);接下來檢查進程是否被killed(這里shell沒有被killed,所以可以繼續執行;記錄ret的地址 = 當前pc + 4——這樣我們會在ecall的下一條指令恢復,而不是重新執行ecall指令;打開中斷(因為XV6會在處理系統調用的時候使能中斷,這樣中斷可以更快的服務,有些系統調用需要許多時間處理。中斷總是會被RISC-V的trap硬件關閉,所以在這個時間點,我們需要顯式的打開中斷。);然后調用syscall。
    // 找出usertrap的原因,如果如果是8,那么是系統調用 if(r_scause() == 8){// system callif(p->killed)exit(-1);// sepc points to the ecall instruction,// but we want to return to the next instruction.p->tf->epc += 4;// an interrupt will change sstatus &c registers,// so don't enable until done with those registers.intr_on();syscall();}
    • syscall函數——根據預定義的函數編號,找到函數地址sys_write
    void syscall(void) {int num;struct proc *p = myproc();num = p->tf->a7;if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {p->tf->a0 = syscalls[num]();} else {printf("%d %s: unknown sys call %d\n",p->pid, p->name, num);p->tf->a0 = -1;} }
    • 運行sys_write(參數保存在a0,a1和a2),現在需要返回了
    • syscall之后再次返回usertrap函數,usertrap調用了一個函數usertrapret。

    5. usertrapret()

    處理返回用戶空間之前,內核要做的工作。

    • 首先關閉中斷。我們關閉中斷因為當我們將STVEC更新到指向用戶空間的trap處理代碼時,我們仍然在內核中執行代碼。如果這時發生了一個中斷,那么程序執行會走向用戶空間的trap處理代碼,即便我們現在仍然在內核中,出于各種各樣具體細節的原因,這會導致內核出錯。所以我們這里關閉中斷。
    • 在下一行我們設置了STVEC寄存器指向trampoline代碼,在那里最終會執行sret指令返回到用戶空間。位于trampoline代碼最后的sret指令會重新打開中斷。這樣,即使我們剛剛關閉了中斷,當我們在執行用戶代碼時中斷是打開的。
    • 接下來的幾行填入了trapframe的內容
      • kernel_pagetable
      • 當前用戶進程的kernel stack
      • usertrap地址——trampoline可以跳轉到這里
      • tp寄存器中的hartid
    // // return to user space // void usertrapret(void) {struct proc *p = myproc();// turn off interrupts, since we're switching// now from kerneltrap() to usertrap().intr_off();// send syscalls, interrupts, and exceptions to trampoline.Sw_stvec(TRAMPOLINE + (uservec - trampoline));// set up trapframe values that uservec will need when// the process next re-enters the kernel.p->tf->kernel_satp = r_satp(); // kernel page tablep->tf->kernel_sp = p->kstack + PGSIZE; // process's kernel stackp->tf->kernel_trap = (uint64)usertrap;p->tf->kernel_hartid = r_tp(); // hartid for cpuid()// set up the registers that trampoline.S's sret will use// to get to user space.// set S Previous Privilege mode to User.unsigned long x = r_sstatus();x &= ~SSTATUS_SPP; // clear SPP to 0 for user modex |= SSTATUS_SPIE; // enable interrupts in user modew_sstatus(x);// set S Exception Program Counter to the saved user pc.w_sepc(p->tf->epc);// tell trampoline.S the user page table to switch to.uint64 satp = MAKE_SATP(p->pagetable);// jump to trampoline.S at the top of memory, which // switches to the user page table, restores user registers,// and switches to user mode with sret.uint64 fn = TRAMPOLINE + (userret - trampoline);((void (*)(uint64,uint64))fn)(TRAPFRAME, satp); }
    • 要設置SSTATUS寄存器,這是一個控制寄存器。這個寄存器的SPP bit位控制了sret指令的行為,該bit為0表示下次執行sret的時候,我們想要返回user mode而不是supervisor mode。這個寄存器的SPIE bit位控制了,在執行完sret之后,是否打開中斷。因為我們在返回到用戶空間之后,我們的確希望打開中斷,所以這里將SPIE bit位設置為1。修改完這些bit位之后,我們會把新的值寫回到SSTATUS寄存器。我們在trampoline代碼的最后執行了sret指令。這條指令會將程序計數器設置成SEPC寄存器的值,所以現在我們將SEPC寄存器的值設置成之前保存的用戶程序計數器的值。在不久之前,我們在usertrap函數中將用戶程序計數器保存在trapframe中的epc字段。
    unsigned long x = r_sstatus();
    • 根據current user process 的頁表,設置SATP寄存器,準備切換到user 頁表。
    // tell trampoline.S the user page table to switch to. uint64 satp = MAKE_SATP(p->pagetable);
    • 計算好我們要jmp到的地址(trampoline中的userret函數 —— 這個函數包含了所有能將我們帶回到用戶空間的指令。)
    uint64 fn = TRAMPOLINE + (userret - trampoline);
    • 最后,就是執行userret函數(這個函數在trampoline中)
    ((void (*)(uint64,uint64))fn)(TRAPFRAME, satp);

    6. userrret()

    這個函數包含了所有能將我們帶回到用戶空間的指令。

    .globl userret userret:# userret(TRAPFRAME, pagetable)# switch from kernel to user.# usertrapret() calls here.# a0: TRAPFRAME, in user page table.# a1: user page table, for satp.# switch to the user page table.csrw satp, a1sfence.vma zero, zero# put the saved user a0 in sscratch, so we# can swap it with our a0 (TRAPFRAME) in the last step.ld t0, 112(a0)csrw sscratch, t0# restore all but a0 from TRAPFRAMEld ra, 40(a0)ld sp, 48(a0)ld gp, 56(a0)ld tp, 64(a0)ld t0, 72(a0)ld t1, 80(a0)ld t2, 88(a0)ld s0, 96(a0)ld s1, 104(a0)ld a1, 120(a0)ld a2, 128(a0)ld a3, 136(a0)ld a4, 144(a0)ld a5, 152(a0)ld a6, 160(a0)ld a7, 168(a0)ld s2, 176(a0)ld s3, 184(a0)ld s4, 192(a0)ld s5, 200(a0)ld s6, 208(a0)ld s7, 216(a0)ld s8, 224(a0)ld s9, 232(a0)ld s10, 240(a0)ld s11, 248(a0)ld t3, 256(a0)ld t4, 264(a0)ld t5, 272(a0)ld t6, 280(a0)# restore user a0, and save TRAPFRAME in sscratchcsrrw a0, sscratch, a0# return to user mode and user pc.# usertrapret() set up sstatus and sepc.sret
    • 首先是切換pagetable(從kernel pa tb到user pg tb)

    • 然后是將之前保存在trapframe中的registers還原。user page table也映射了trampoline page,所以程序還能繼續執行而不是崩潰。 (在這里a0是trapframe的地址)

    • 解釋 sfence.vma zero, zero是clear page table 的TLB

    • 重新打印所有寄存器(和調用前一致) (除了a0 —— a0 被write的返回值覆蓋了)

    • 打印pc可以看出來,現在已經回到用戶空間了 —— 以為pc很小,明顯是虛擬地址

    (gdb) p/x $pc $44 = 0xd6a

    最后總結一下,系統調用被刻意設計的看起來像是函數調用,但是背后的user/kernel轉換比函數調用要復雜的多。之所以這么復雜,很大一部分原因是要保持user/kernel之間的隔離性,內核不能信任來自用戶空間的任何內容。

    Trampoline page之所以叫trampoline page,是因為你某種程度在它上面“彈跳”了一下,然后從用戶空間走到了內核空間。從內核空間彈了一下,又走出來?!?strong>trampoline代碼在用戶空間和內核空間都映射到了同一個地址。這樣不會讓程序在user和kernel pagetable切換的時候崩潰。

    總結

    以上是生活随笔為你收集整理的6.S081 附加Lab1 用户执行系统调用的过程(Trap)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    久久久久www | 亚洲毛片久久 | 午夜三级大片 | 亚洲一二三区精品 | 9999激情| 天天操天天干天天玩 | 亚洲精品视频第一页 | 久久久久久久99 | 9999在线观看 | 午夜在线免费视频 | av一区二区三区在线播放 | 97视频人人澡人人爽 | 国产精品一区二区在线观看 | 国产色综合 | 国产91在线观看 | 成人高清av在线 | 在线影院av | 国产日韩欧美在线播放 | 中文字幕日韩在线播放 | 日日夜夜天天 | 视频二区在线 | 91视频观看免费 | 四虎影视成人永久免费观看视频 | 一区二区三区日韩在线 | 国产免费一区二区三区最新6 | 中文字幕黄网 | 国产精品久久久久久久久岛 | 欧美激情h | 成人av高清在线观看 | 久久成人麻豆午夜电影 | 亚洲精品美女久久久久 | 日韩在线观看视频免费 | 97色综合| 免费福利视频导航 | 日本韩国精品在线 | 免费国产亚洲视频 | 久草在线91| 五月婷婷综合久久 | 色资源网免费观看视频 | 日韩超碰在线 | 91精品中文字幕 | 91桃色在线免费观看 | 午夜视频色 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 欧美老女人xx | av九九| 成人黄色影片在线 | 国产黄色一级大片 | 五月婷婷.com | 色香蕉在线视频 | 色婷婷综合成人av | 日日摸日日添日日躁av | 午夜 在线 | 免费在线观看毛片网站 | 午夜成人免费电影 | 97超碰福利久久精品 | 狠狠干天天操 | 黄色免费网站 | 久久久久久网址 | 日韩最新在线视频 | 免费成视频 | 久久久网页 | 日韩成人精品一区二区 | 经典三级一区 | 国产流白浆高潮在线观看 | 天天干,天天干 | 日韩精品欧美专区 | 国产91精品一区二区麻豆亚洲 | 欧美日韩国产页 | 国产一区二区三区免费在线观看 | 亚洲视屏 | 亚洲精品av在线 | 人人干狠狠操 | 99精品国产在热久久 | 波多野结衣视频一区二区 | 天天插综合网 | 日韩 精品 一区 国产 麻豆 | 99热这里只有精品在线观看 | 国产精品国产毛片 | 日本最新一区二区三区 | 国产精品久久久久久久久久东京 | 在线导航福利 | 日韩久久午夜一级啪啪 | 成人在线电影观看 | 人人狠狠综合久久亚洲 | 久草在线视频在线观看 | 狠狠操操 | 国产91综合一区在线观看 | 久久久噜噜噜久久久 | 黄色av影视 | 97精品国自产拍在线观看 | 91精品在线视频观看 | 精品久久91| 超碰国产在线播放 | 婷婷在线色 | 日韩av电影中文字幕在线观看 | 国产精品av在线免费观看 | 狠狠地操 | 欧美成人影音 | 五月激情婷婷丁香 | 日韩字幕在线 | 午夜三级影院 | 久久婷婷五月综合色丁香 | 在线观看免费福利 | 欧美日一级片 | 久久污视频 | 欧美色图狠狠干 | 二区三区精品 | 国产 日韩 欧美 中文 在线播放 | 国产黄色一级片 | 日本特黄特色aaa大片免费 | 丁香婷婷在线 | 四虎成人精品永久免费av九九 | 久产久精国产品 | 国产成人精品一区二区三区网站观看 | 激情婷婷在线观看 | 最新国产一区二区三区 | 久久艹国产视频 | 免费三级大片 | 亚洲色图27p | 国产成人精品一区一区一区 | 久操操| www久久精品 | 人人澡人人模 | 久久久久久久久精 | 亚洲精品白浆高清久久久久久 | 国产96视频 | 69国产精品成人在线播放 | 色综合久久久 | 狠狠网亚洲精品 | 2024国产精品视频 | 国产小视频福利在线 | 久草在线视频网站 | 在线成人性视频 | 成人亚洲综合 | 最新av网站在线观看 | 国产999视频在线观看 | 国产精品久久久久毛片大屁完整版 | 亚洲最新av网址 | 久二影院 | 99精品免费久久久久久久久日本 | 日韩欧美精品在线观看 | 999久久久久 | 五月婷婷,六月丁香 | 日韩精品一区二区三区第95 | av在线专区 | 国产欧美中文字幕 | 国产小视频在线观看免费 | 免费看毛片在线 | 日本性生活一级片 | 激情丁香久久 | 亚洲三级网 | 久久九九精品久久 | 超薄丝袜一二三区 | 在线观看深夜福利 | av在线播放中文字幕 | 久久国产美女视频 | 欧美成年人在线视频 | 久久久久久国产一区二区三区 | 日韩高清精品免费观看 | 国产精品第54页 | 男女啪啪网站 | a午夜在线 | 伊人激情综合 | 亚洲一区欧美精品 | 中文字幕在线影院 | 97超碰在线资源 | 久久久久亚洲精品中文字幕 | 国产无区一区二区三麻豆 | 国产一区免费观看 | 国产理论一区二区三区 | 婷婷色伊人 | 国产色小视频 | 亚洲成人黄色网址 | 久久久久免费看 | 九九免费观看全部免费视频 | 久久免费高清视频 | 日韩有色| 久久爱影视i | 中文字幕亚洲字幕 | 午夜精品久久久久久久久久久久久久 | 国产精品久久久区三区天天噜 | 免费一级片视频 | 天天操网 | 久久99久久99久久 | 亚洲国产69 | 一区在线观看 | 91亚洲精品乱码久久久久久蜜桃 | 婷婷色站 | 美女在线观看网站 | 黄色毛片视频免费观看中文 | 91理论片午午伦夜理片久久 | 久久久久免费精品视频 | 二区三区在线 | 精品五月天 | 丝袜足交在线 | 国产第一二区 | 国产视频91在线 | 国产在线视频不卡 | 2022中文字幕在线观看 | 久久69精品 | 精品福利片 | 色偷偷人人澡久久超碰69 | 日韩精品一区二区在线 | 日韩欧美v | 色综合久久99 | 久久免费视频精品 | 91精品在线免费观看视频 | 天操夜夜操 | 国产中文字幕久久 | aaaaaa毛片| 开心激情五月网 | 91精品一区二区在线观看 | 色婷丁香 | 欧美日韩在线播放 | 97国产一区 | 中文字幕在线观看一区二区 | 久久首页 | 国产专区在线播放 | 91精品老司机久久一区啪 | 欧美一二三视频 | 久久综合五月婷婷 | 91尤物国产尤物福利在线播放 | 国产精品a成v人在线播放 | 国产美女在线免费观看 | 日韩黄在线观看 | 国产成人精品亚洲 | 欧美天天射 | 成人一级在线观看 | av在线超碰 | 人人草在线视频 | www天天干com | 黄网站色视频免费观看 | 2022久久国产露脸精品国产 | 欧美日韩性生活 | 五月激情综合婷婷 | 欧美精品一区二区在线播放 | 成人a视频在线观看 | 最近2019好看的中文字幕免费 | 亚洲欧洲av在线 | 中文字幕在线看视频国产中文版 | 伊人天堂av| 日韩欧美一区二区三区黑寡妇 | 波多野结衣资源 | 欧美日韩国产网站 | 亚洲精品视频免费看 | 欧美一区三区四区 | 欧美色噜噜| 一区二区三区电影 | 日韩高清免费在线 | 久久精品日本啪啪涩涩 | 2022久久国产露脸精品国产 | 福利在线看片 | 最新国产在线 | 91成人天堂久久成人 | 丁香六月久久综合狠狠色 | 久久久国产影视 | 天天操天天爽天天干 | 91视频免费视频 | 日本爱爱免费视频 | 黄色软件在线观看免费 | 精品国产精品国产偷麻豆 | 日韩二区在线观看 | 成人黄色小视频 | 91电影福利 | 91香蕉视频在线下载 | 六月激情丁香 | 丝袜+亚洲+另类+欧美+变态 | 91在线观看高清 | 伊人婷婷在线 | 久久99视频免费 | 国产中文字幕在线免费观看 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 久久精精品视频 | 欧美一二三专区 | 中文字幕在线影院 | 在线看一区 | 日韩精品不卡在线 | 久久草草影视免费网 | 日韩色区 | 视频福利在线观看 | 米奇四色影视 | 国产色啪| 亚洲成色777777在线观看影院 | 亚洲精品18日本一区app | 国产精品白丝av | 色久av| 激情婷婷丁香 | 国产一区二区三区免费视频 | av不卡在线看 | 天堂激情网| 欧美成人区| 日韩精品电影在线播放 | www色 | 深夜国产在线 | 国产精品6999成人免费视频 | 久久福利小视频 | 天天色天天操综合 | 国产一区二区不卡视频 | 在线观看国产区 | 中文字幕av全部资源www中文字幕在线观看 | 手机在线观看国产精品 | 黄色av一区二区 | 91av免费观看 | 亚洲天堂在线观看完整版 | 国产精品 国产精品 | 日韩理论在线观看 | 成年美女黄网站色大片免费看 | www黄色 | 色婷婷国产精品一区在线观看 | 久久久久久久久久伊人 | 日韩三级久久 | av日韩不卡| 精品视频在线免费 | 亚在线播放中文视频 | 蜜臀aⅴ精品一区二区三区 久久视屏网 | 成人va天堂 | 黄色一级在线视频 | 国产老熟| 美女网站在线看 | 久热色超碰 | 免费视频一二三区 | 欧美国产日韩在线观看 | 中文字幕一区二区三区在线播放 | 精品久久久久久综合 | 一级免费片 | 国产精品高清一区二区三区 | 成年人黄色av| a黄色影院 | 精品视频在线免费观看 | 91精品国产综合久久婷婷香蕉 | av天天色| 久久久久久久久影视 | 日本三级久久久 | 开心激情五月网 | 亚洲最新视频在线播放 | 99色婷婷 | 日韩在线观看一区二区 | 毛片基地黄久久久久久天堂 | 亚洲精品在线电影 | 婷婷激情av | 亚洲黄色av一区 | 色先锋资源网 | 免费久久精品视频 | 天天干天天插 | 欧美国产91 | 久久av影视 | 亚洲国产视频在线 | 波多野结衣精品在线 | 黄色三级免费观看 | 日韩动态视频 | 波多野结衣在线观看一区二区三区 | 中文字幕乱在线伦视频中文字幕乱码在线 | 午夜成人影视 | 99av在线视频 | 国产在线观看中文字幕 | 在线观看中文字幕一区二区 | 久久久免费看 | 国产特级毛片 | 欧美片网站yy| 天天干天天做 | 国产精品免费小视频 | 在线v片免费观看视频 | 天天干天天摸天天操 | 亚洲一区 av | 国产成人精品999 | 波多野结衣电影久久 | 亚洲成人黄色网址 | www.黄色片网站 | 五月婷婷在线视频观看 | 国产精品一区二区免费视频 | av电影免费在线播放 | 在线观看免费黄视频 | 69av视频在线 | 在线观看国产福利片 | 国产精品丝袜久久久久久久不卡 | av一区二区在线观看中文字幕 | 亚洲丁香日韩 | 亚洲欧美日韩一二三区 | 日韩av电影免费在线观看 | 国产一级视频在线 | 99精品国产aⅴ | 国产一区在线不卡 | 99这里只有久久精品视频 | 国产精品二区三区 | 超碰在线97免费 | 精品综合久久久 | 国产只有精品 | 99国产一区二区三精品乱码 | 欧美国产精品一区二区 | 久久五月天色综合 | 久久综合精品国产一区二区三区 | 91专区在线观看 | 久久免费视频在线观看 | 亚洲欧美va | 久久精品免视看 | 久久久受www免费人成 | 久久久91精品国产一区二区三区 | 国产91成人 | 天天干天天草 | a级国产乱理伦片在线播放 久久久久国产精品一区 | av蜜桃在线 | 九九热re | 国产视频 亚洲视频 | 激情五月网站 | 91在线产啪 | 婷婷久月| 久青草影院 | 日韩大片在线 | 久久久精品99 | 日日夜夜精品视频天天综合网 | 色黄久久久久久 | 国产精品自拍在线 | 少妇精品久久久一区二区免费 | 最近中文字幕在线 | 伊色综合久久之综合久久 | 成人永久免费 | 亚洲丝袜中文 | 青青草国产成人99久久 | 日韩中文字幕在线 | 香蕉视频91 | 91chinesexxx| 免费在线观看成人 | 久久系列| 久草免费电影 | 欧美精品成人在线 | 国产一区二区三区黄 | 久久久在线 | 国产一区二区三区高清播放 | 久久99久久99精品免视看婷婷 | 丁香婷婷电影 | 久久福利国产 | 久久毛片视频 | 亚洲美女视频在线观看 | 国产日韩欧美精品在线观看 | 探花视频在线观看 | 免费在线观看黄网站 | 午夜精品一区二区三区在线播放 | 午夜精品久久久久久99热明星 | 久久99精品久久久久婷婷 | 在线岛国av| 欧美最猛性xxxxx(亚洲精品) | 国产亚洲婷婷免费 | 人人爽人人香蕉 | 五月婷婷另类国产 | 成人午夜精品福利免费 | 成人91在线观看 | 97在线免费观看 | 中文字幕免费高清 | 91av视频观看 | 天天干夜夜爽 | 99re中文字幕 | 久久久久久久久久免费 | h视频在线看 | 亚洲 欧美变态 另类 综合 | 国产一区在线免费观看视频 | 国产成人a亚洲精品v | 久久69精品久久久久久久电影好 | 国产一区二区三区 在线 | 国产网站av | 91香蕉国产| 国产香蕉久久精品综合网 | 中文字幕日本特黄aa毛片 | 久久男人免费视频 | 久久伊人91 | 麻豆传媒视频在线免费观看 | 国产精品第 | 高清在线一区 | 美女视频久久久 | www日韩精品 | 久久精品网址 | 97看片网 | 国产亚洲高清视频 | 亚洲综合小说 | 麻豆传媒在线视频 | 国产精品免费观看在线 | 国产专区视频在线 | 欧美黑吊大战白妞欧美 | 亚洲黄色免费网站 | 亚洲精品伦理在线 | 国产美女精品久久久 | 日韩免费福利 | 黄色精品视频 | 国产欧美综合视频 | 99在线观看 | 久草在线视频精品 | 男女精品久久 | 免费三级黄色片 | 国产一级免费观看视频 | 久草视频手机在线 | 久久精品国产99国产 | 99国产在线观看 | 久久6精品 | 欧美人人 | 国产精品免费大片视频 | 一区二区三区免费在线观看视频 | 国产精品久久久久久久av大片 | 996久久国产精品线观看 | 日韩欧美在线观看一区二区三区 | 久久涩视频 | 久久婷婷精品 | 岛国精品一区二区 | 国产精品成人自产拍在线观看 | 天天做天天爱天天爽综合网 | av福利资源| 免费视频 三区 | 最近最新中文字幕视频 | 99精品视频在线看 | 精品国产精品国产偷麻豆 | 五月婷婷综合激情 | 久久九九免费视频 | 就色干综合 | 日韩 在线 | 国产九九在线 | 349k.cc看片app | 在线国产能看的 | 欧美性生活免费看 | 日韩欧美一区二区在线观看 | 91麻豆精品国产自产 | av网在线观看 | 国产成人一区二 | 99视频一区| 亚洲狠狠丁香婷婷综合久久久 | 免费在线黄网 | 中文字幕在线视频国产 | 麻豆精品视频在线观看免费 | 国产明星视频三级a三级点| 日韩丝袜视频 | 91精品国自产在线观看欧美 | 黄www在线观看 | 亚洲精品乱码久久久久久高潮 | 欧美日韩免费一区二区三区 | 成人黄在线观看 | 日韩中文字幕视频在线 | 成人激情开心网 | 免费一级日韩欧美性大片 | 国产国产人免费人成免费视频 | 国产一级在线播放 | 视频一区二区在线观看 | 国产中文字幕第一页 | 久久久国产在线视频 | 久久久亚洲精华液 | 日韩av看片| 黄色高清视频在线观看 | 欧美日韩在线视频观看 | 成人动漫一区二区三区 | 国产精品亚洲片夜色在线 | 精品久久网 | 日本韩国精品一区二区在线观看 | 欧美日韩高清一区二区 国产亚洲免费看 | 国产日韩精品在线观看 | 欧美激情精品久久 | 日韩免费大片 | 久久精品国产免费看久久精品 | 黄色成人毛片 | 香蕉视频在线播放 | 免费美女久久99 | 亚洲一区尤物 | sm免费xx网站| 精品免费视频 | 久久亚洲精品电影 | 免费看片亚洲 | 精品国产aⅴ一区二区三区 在线直播av | av性在线 | 草久久影院 | 丰满少妇在线观看资源站 | 成人国产精品免费观看 | 人人搞人人搞 | 欧美另类v | 天堂网一区二区 | www.国产在线 | 成人av免费在线看 | 成人免费中文字幕 | 嫩嫩影院理论片 | 成年人免费观看国产 | av在线收看| 黄色一级免费电影 | 国产成人精品999在线观看 | 99热在线精品观看 | 99视频在线看| 欧美亚洲国产日韩 | 免费高清av在线看 | 国产剧情在线一区 | 中文字幕一区二区三区视频 | 伊人www22综合色 | 狠狠色丁香婷婷综合久小说久 | 天天干天天摸天天操 | av一级黄| 在线一区观看 | 在线中文字幕网站 | av免费在线播放 | 成年人免费看 | 久久66热这里只有精品 | www国产亚洲精品久久网站 | 免费黄色网址大全 | 在线观看视频一区二区三区 | 色噜噜狠狠狠狠色综合久不 | 97视频在线观看视频免费视频 | 在线免费黄网站 | 免费看国产视频 | 99国产精品久久久久老师 | 日韩av进入 | 国产午夜小视频 | 黄色av大片| 在线观看福利网站 | 探花系列在线 | 久草综合在线观看 | 伊人国产视频 | 欧美视频99 | 欧美在线aaa | 久久久久欧美精品 | 美女视频黄,久久 | 久久av网| 亚洲视频在线免费看 | 91精品国产91久久久久久三级 | 日韩欧美一区二区三区在线观看 | 91亚洲国产成人久久精品网站 | 久久精品三级 | 国产精品第一 | 亚洲精品美女久久17c | 久久国产日韩 | 黄色在线成人 | 97电影院在线观看 | 国产高清在线永久 | 天天干夜夜干 | 中文字幕中文字幕在线中文字幕三区 | 久久中文欧美 | 97狠狠操| 国产精品高潮呻吟久久久久 | 免费福利视频导航 | 久久久久久97三级 | 中文字幕在线观看国产 | 色天天久久 | 欧美韩日精品 | 久久久久久久久久福利 | av在线播放快速免费阴 | 欧美日韩在线视频免费 | 中文字幕在线一区二区三区 | 久久久国产精品一区二区三区 | 欧美激情综合色综合啪啪五月 | 六月天综合网 | 97在线观看视频国产 | 久久久久久久免费看 | 中文字幕日本特黄aa毛片 | 色婷婷狠狠五月综合天色拍 | 国产日韩三级 | 日韩一区二区三区观看 | 日本中文字幕电影在线免费观看 | 四虎成人精品永久免费av九九 | 久久久久亚洲天堂 | 久久久蜜桃一区二区 | 亚洲综合精品在线 | 久久91久久久久麻豆精品 | 欧美三级在线播放 | 久久高清免费观看 | 中文字幕在线日亚洲9 | 天天操比 | 国产精品成人一区二区三区吃奶 | 国产精品久久久久久久av大片 | 午夜手机电影 | 国产人成在线视频 | 公开超碰在线 | 在线观看韩国av | 97av视频| 国产原创av片| 亚洲精品玖玖玖av在线看 | 麻豆精品传媒视频 | 在线成人一区二区 | 日本久久精品视频 | 又污又黄网站 | 最近2019好看的中文字幕免费 | 日韩精品视频一二三 | 中文字幕 国产视频 | 婷婷丁香六月天 | 日韩国产欧美在线播放 | 国产精品一区二区三区在线免费观看 | 在线免费av观看 | 操天天操| 1区2区视频 | 欧美 日韩精品 | 91亚洲成人| 99久久久久久国产精品 | 日韩欧美在线高清 | 精品国产视频在线观看 | 欧美日韩免费观看一区二区三区 | av大全在线| 精品视频在线免费观看 | 免费99视频| 日韩欧美久久 | 久久久久久久久久久国产精品 | 欧美一区二区三区不卡 | 国产99久久精品一区二区永久免费 | 久草在线高清视频 | 97超碰在线久草超碰在线观看 | 在线免费黄色av | 免费观看视频的网站 | 免费的国产精品 | 成人影视免费 | 国产一区二区三区视频在线 | 欧美日韩在线第一页 | 国产精品视频久久久 | 亚洲精品高清视频 | 久草在线看片 | 精品欧美一区二区精品久久 | 中文字幕精 | 久久精品这里都是精品 | 国产精品成久久久久 | 在线免费观看亚洲视频 | www国产精品com | 色综合天天射 | 欧美另类z0zx | 99爱精品在线 | 中国成人一区 | 99久久久精品 | 久久99九九99精品 | 日本精品视频一区二区 | 综合久久久久久 | 在线免费高清视频 | 九九热免费视频在线观看 | 精品国产一区二区三区久久久蜜月 | 中文成人字幕 | 久久久久久久久久久综合 | 久久久久综合精品福利啪啪 | 伊人国产女 | 国产一区二区综合 | 欧美日韩亚洲第一页 | 久久精品国产一区二区三区 | ,久久福利影视 | 九九免费精品视频在线观看 | av一区二区三区在线播放 | 国产精品久久99综合免费观看尤物 | 日韩有色 | 男女激情免费网站 | 欧美日韩在线观看视频 | 国产在线不卡精品 | 色www精品视频在线观看 | av在线播放快速免费阴 | 欧美日韩在线免费观看视频 | av直接看 | 91精品国产高清自在线观看 | 狠狠色丁香婷婷综合久久片 | 欧美精品中文字幕亚洲专区 | 美女网站黄在线观看 | 精品91视频| 久久婷婷网 | 成年人国产在线观看 | 久久精品99国产精品亚洲最刺激 | 天操夜夜操 | 在线精品视频在线观看高清 | 亚洲婷婷在线视频 | 国产精品18久久久久久vr | 国产又粗又猛又黄又爽视频 | 欧美福利在线播放 | 欧美一级在线 | 日韩二区三区在线 | 亚洲91视频 | 天天av综合网 | 激情婷婷 | 欧美在线一 | 久久视| 国产成人精品久久久久 | 一区二区三区免费网站 | 精品在线小视频 | 久久99精品国产 | 欧美一级特黄aaaaaa大片在线观看 | 91av福利视频 | 久久久久久久久久免费 | 色狠狠狠 | 男女激情免费网站 | 免费视频黄 | 五月婷在线 | av片无限看| 天天干,狠狠干 | 日韩欧美视频免费在线观看 | 99久久精品国产系列 | 精品一区三区 | 丁香九月激情 | 国产毛片久久久 | 深爱激情久久 | 日韩欧美综合视频 | 国产在线小视频 | 黄色大片中国 | 午夜久久视频 | 欧美大香线蕉线伊人久久 | 亚州日韩中文字幕 | 成人国产一区二区 | 国产成人久久精品亚洲 | 成人精品影视 | 日韩精品一区不卡 | 免费不卡中文字幕视频 | 久久精品精品 | 丁香婷婷激情啪啪 | 超碰国产人人 | 激情综合五月天 | 亚洲精品黄色片 | 国产精品一区二区三区在线 | 丁香婷婷深情五月亚洲 | 96精品视频| 日本最新高清不卡中文字幕 | 成年一级片 | 日韩日韩日韩日韩 | 久久久久观看 | 伊人视频| 欧美激情视频一区二区三区 | 免费看黄在线观看 | 日韩aⅴ视频| 激情五月av | 国产成a人亚洲精v品在线观看 | 日韩色中色 | 人人躁 | 97超碰在线播放 | 中文字幕在线久一本久 | 91人人澡人人爽人人精品 | 中文字幕在线日本 | 九九九在线 | 国产裸体bbb视频 | 国产一区在线免费观看视频 | 亚洲伊人成综合网 | 欧美最猛性xxxxx免费 | 国产精品美女久久久久久2018 | 日韩成年视频 | 欧美看片 | 日韩久久久久久久久久久久 | 日本中文一级片 | 亚洲三级黄色 | 日韩国产精品一区 | 久久久久网址 | 97超在线 | 亚洲va欧美va人人爽春色影视 | 夜夜操狠狠操 | 免费十分钟 | 美女中文字幕 | 色综合天天综合网国产成人网 | 黄色国产在线 | 国产一级视屏 | 在线视频1卡二卡三卡 | 开心综合网 | 91av播放 | 天堂网一区二区三区 | 国产精品乱码久久 | 久久久久久电影 | 日韩簧片在线观看 | 日本精品一区二区三区在线观看 | 2021国产精品 | 不卡视频一区二区三区 | 国产视频精品久久 | 免费看一级黄色 | 欧美一区二区日韩一区二区 | 久久99在线| 欧美一级免费高清 | 亚洲国产中文字幕在线视频综合 | 日本精品一区二区在线观看 | 在线观看电影av | 国产精品va视频 | 999久久久久久久久久久 | 亚洲伊人第一页 | 三级黄色a| 成人免费看电影 | 国产在线看 | 午夜久操 | 亚洲午夜久久久久久久久久久 | 在线a亚洲视频播放在线观看 | 免费大片黄在线 | 超碰免费97 | 最新高清无码专区 | 国产一区二区三区高清播放 | 免费在线观看成人av | 中文字幕之中文字幕 | 精品日韩在线一区 | 免费精品人在线二线三线 | 国产区av在线 | 欧美另类v | 黄色国产高清 | 久久成人在线视频 | 国产精品九九九九九 | 黄色大片免费播放 | 国产中文字幕大全 | 欧美激情综合五月色丁香 | 国产又粗又猛又黄又爽的视频 | 国产精品video爽爽爽爽 | 色爱区综合激月婷婷 | 97精品在线 | 丁香激情网| 又黄又爽又湿又无遮挡的在线视频 | 亚洲国产精品视频 | 亚洲一区美女视频在线观看免费 | 久久人人爽人人爽人人片 | 亚洲91av| 中文字幕在线观看一区二区三区 | 丁香六月五月婷婷 | 五月天婷亚洲天综合网鲁鲁鲁 | 一级精品视频在线观看宜春院 | 久久国产精品99久久久久 | 国产精品自产拍在线观看蜜 | 久久久综合九色合综国产精品 | 青草视频在线 | 99热这里只有精品国产首页 | 国产精品久久久久久一二三四五 | 免费国产黄线在线观看视频 | 欧美日韩精品在线一区二区 | 天天干天天做天天操 | 欧美三级免费 | 日韩欧美视频免费看 | 久久99精品久久久久久清纯直播 | av中文资源在线 | 色婷婷久久久综合中文字幕 | 色美女在线 | 99久久久久免费精品国产 | 草久久久 | 日韩成人免费观看 | 国产精品一区二区吃奶在线观看 | 国产精品久久久久久久久久了 | 91女神的呻吟细腰翘臀美女 | 欧美在线视频a | 免费观看全黄做爰大片国产 | www.黄色片网站| 国产成人av| 麻豆传媒视频观看 | 美女视频黄色免费 | 91九色蝌蚪国产 | 国产剧情av在线播放 | 日韩精品视频免费专区在线播放 | 91成人小视频 | 精品国产综合区久久久久久 | 欧美亚洲久久 | 青青草国产成人99久久 | 日本黄色大片儿 | 超碰人人在线 | 欧美乱码精品一区 | 狠狠做深爱婷婷综合一区 | 91麻豆精品国产 | 92精品国产成人观看免费 | 国产剧在线观看片 | 91视频久久久久 | 精品国产亚洲日本 | 在线看岛国av | 中文永久字幕 | 又色又爽又黄高潮的免费视频 | 天天躁日日 | a视频免费看 | 91久久精品日日躁夜夜躁国产 | 久草视频在线资源站 | 中文字幕av日韩 | 久久精品99国产国产 | 色综久久 | 热久久免费视频 | 国产精品久久久久久久久久久久午 | 精品国精品自拍自在线 | 激情综合网婷婷 | a在线观看免费视频 | 久久久网 | 国产美女主播精品一区二区三区 | 久久精品视频在线观看免费 | 三级免费黄色 | 91亚洲永久精品 | 国产美女网站视频 | 国产理论免费 | 欧美日韩色婷婷 | 天天插天天操天天干 | 日韩欧美视频免费观看 | 久久视频这里只有精品 | 精品久久久影院 | 国产在线播放一区二区三区 | 欧美一二三区在线观看 | 91麻豆精品91久久久久同性 | 亚洲,播放 | 91在线免费看片 | 91成版人在线观看入口 | 久久久高清视频 | 日韩精品在线视频 | 96在线| 久久综合九色综合欧美就去吻 | 国产精品久久久久久一二三四五 | 久草网在线视频 | 亚洲视频在线观看 | 欧美小视频在线 | 久久免费大片 | 精品亚洲男同gayvideo网站 | 久久99久国产精品黄毛片入口 | 国产综合片 | 婷婷色资源 | 99久久精品国产亚洲 | 久草在线视频国产 | 国产福利91精品张津瑜 | 成人一级在线观看 | 午夜在线看片 | 亚洲女裸体 | 免费人人干 | 91网在线 | 91精品1区2区 | 四虎在线免费观看 | 精品亚洲在线 | 亚洲一区尤物 | 色资源网在线观看 | 免费人成网 | 国产精品成人av久久 | 久久九九影院 | 国产精品久久婷婷六月丁香 |