婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > 詳解go中panic源碼解讀

詳解go中panic源碼解讀

熱門標簽:壽光微信地圖標注 excel地圖標注分布數據 外呼系統顯本地手機號 涿州代理外呼系統 外呼系統用什么卡 評價高的400電話辦理 電話機器人軟件免費 百度地圖標注后傳給手機 阿克蘇地圖標注

panic源碼解讀

前言

本文是在go version go1.13.15 darwin/amd64上進行的

panic的作用

  • panic能夠改變程序的控制流,調用panic后會立刻停止執行當前函數的剩余代碼,并在當前Goroutine中遞歸執行調用方的defer
  • recover可以中止panic造成的程序崩潰。它是一個只能在defer中發揮作用的函數,在其他作用域中調用不會發揮作用;

舉個栗子

package main

import "fmt"

func main() {
	fmt.Println(1)
	func() {
		fmt.Println(2)
		panic("3")
	}()
	fmt.Println(4)
}

輸出

1
2
panic: 3

goroutine 1 [running]:
main.main.func1(...)
        /Users/yj/Go/src/Go-POINT/panic/main.go:9
main.main()
        /Users/yj/Go/src/Go-POINT/panic/main.go:10 +0xee

panic后會立刻停止執行當前函數的剩余代碼,所以4沒有打印出來

對于recover

  • panic只會觸發當前Goroutine的defer;
  • recover只有在defer中調用才會生效;
  • panic允許在defer中嵌套多次調用;
package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println(1)

	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	go func() {
		fmt.Println(2)
		panic("3")
	}()
	time.Sleep(time.Second)
	fmt.Println(4)
}

上面的栗子,因為recoverpanic不在同一個goroutine中,所以不會捕獲到

嵌套的demo

func main() {
	defer fmt.Println("in main")
	defer func() {
		defer func() {
			panic("3 panic again and again")
		}()
		panic("2 panic again")
	}()

	panic("1 panic once")
}

輸出

in main
panic: 1 panic once
        panic: 2 panic again
        panic: 3 panic again and again

goroutine 1 [running]:
...

多次調用panic也不會影響defer函數的正常執行,所以使用defer進行收尾工作一般來說都是安全的。

panic使用場景

  • error:可預見的錯誤
  • panic:不可預見的異常

需要注意的是,你應該盡可能地使用error,而不是使用panicrecover。只有當程序不能繼續運行的時候,才應該使用panicrecover機制。

panic有兩個合理的用例。

1、發生了一個不能恢復的錯誤,此時程序不能繼續運行。 一個例子就是 web 服務器無法綁定所要求的端口。在這種情況下,就應該使用 panic,因為如果不能綁定端口,啥也做不了。

2、發生了一個編程上的錯誤。 假如我們有一個接收指針參數的方法,而其他人使用 nil 作為參數調用了它。在這種情況下,我們可以使用panic,因為這是一個編程錯誤:用 nil 參數調用了一個只能接收合法指針的方法。

在一般情況下,我們不應通過調用panic函數來報告普通的錯誤,而應該只把它作為報告致命錯誤的一種方式。當某些不應該發生的場景發生時,我們就應該調用panic。

總結下panic的使用場景:

1、空指針引用

2、下標越界

3、除數為0

4、不應該出現的分支,比如default

5、輸入不應該引起函數錯誤

看下實現

先來看下_panic的結構

// _panic 保存了一個活躍的 panic
//
// 這個標記了 go:notinheap 因為 _panic 的值必須位于棧上
//
// argp 和 link 字段為棧指針,但在棧增長時不需要特殊處理:因為他們是指針類型且
// _panic 值只位于棧上,正常的棧指針調整會處理他們。
//
//go:notinheap
type _panic struct {
	argp      unsafe.Pointer // panic 期間 defer 調用參數的指針; 無法移動 - liblink 已知
	arg       interface{}    // panic的參數
	link      *_panic        // link 鏈接到更早的 panic
	recovered bool           // panic是否結束
	aborted   bool           // panic是否被忽略
}

link指向了保存在goroutine鏈表中先前的panic鏈表

gopanic

編譯器會將panic裝換成gopanic,來看下執行的流程:

1、創建新的runtime._panic并添加到所在Goroutine的_panic鏈表的最前面;

2、在循環中不斷從當前Goroutine 的_defer中鏈表獲取runtime._defer并調用runtime.reflectcall運行延遲調用函數;

3、調用runtime.fatalpanic中止整個程序;

// 預先聲明的函數 panic 的實現
func gopanic(e interface{}) {
	gp := getg()
	// 判斷在系統棧上還是在用戶棧上
	// 如果執行在系統或信號棧時,getg() 會返回當前 m 的 g0 或 gsignal
	// 因此可以通過 gp.m.curg == gp 來判斷所在棧
	// 系統棧上的 panic 無法恢復
	if gp.m.curg != gp {
		print("panic: ")
		printany(e)
		print("\n")
		throw("panic on system stack")
	}
	// 如果正在進行 malloc 時發生 panic 也無法恢復
	if gp.m.mallocing != 0 {
		print("panic: ")
		printany(e)
		print("\n")
		throw("panic during malloc")
	}
	// 在禁止搶占時發生 panic 也無法恢復
	if gp.m.preemptoff != "" {
		print("panic: ")
		printany(e)
		print("\n")
		print("preempt off reason: ")
		print(gp.m.preemptoff)
		print("\n")
		throw("panic during preemptoff")
	}
	// 在 g 鎖在 m 上時發生 panic 也無法恢復
	if gp.m.locks != 0 {
		print("panic: ")
		printany(e)
		print("\n")
		throw("panic holding locks")
	}

	// 下面是可以恢復的
	var p _panic
	p.arg = e
	// panic 保存了對應的消息,并指向了保存在 goroutine 鏈表中先前的 panic 鏈表
	p.link = gp._panic
	gp._panic = (*_panic)(noescape(unsafe.Pointer(p)))

	atomic.Xadd(runningPanicDefers, 1)

	for {
		// 開始逐個取當前 goroutine 的 defer 調用
		d := gp._defer
		// 沒有defer,退出循環
		if d == nil {
			break
		}

		// 如果 defer 是由早期的 panic 或 Goexit 開始的(并且,因為我們回到這里,這引發了新的 panic),
		// 則將 defer 帶離鏈表。更早的 panic 或 Goexit 將無法繼續運行。
		if d.started {
			if d._panic != nil {
				d._panic.aborted = true
			}
			d._panic = nil
			d.fn = nil
			gp._defer = d.link
			freedefer(d)
			continue
		}

		// 將deferred標記為started
		// 如果棧增長或者垃圾回收在 reflectcall 開始執行 d.fn 前發生
		// 標記 defer 已經開始執行,但仍將其保存在列表中,從而 traceback 可以找到并更新這個 defer 的參數幀

		// 標記defer是否已經執行
		d.started = true

		// 記錄正在運行的延遲的panic。
		// 如果在延遲調用期間有新的panic,那么這個panic
		// 將在列表中找到d,并將標記d._panic(此panic)中止。
		d._panic = (*_panic)(noescape(unsafe.Pointer(p)))

		p.argp = unsafe.Pointer(getargp(0))

		reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
		p.argp = nil

		// reflectcall沒有panic。刪除d
		if gp._defer != d {
			throw("bad defer entry in panic")
		}
		d._panic = nil
		d.fn = nil
		gp._defer = d.link

		// trigger shrinkage to test stack copy. See stack_test.go:TestStackPanic
		//GC()

		pc := d.pc
		sp := unsafe.Pointer(d.sp) // must be pointer so it gets adjusted during stack copy
		freedefer(d)
		if p.recovered {
			atomic.Xadd(runningPanicDefers, -1)

			gp._panic = p.link
			// 忽略的 panic 會被標記,但仍然保留在 g.panic 列表中
			// 這里將它們移出列表
			for gp._panic != nil  gp._panic.aborted {
				gp._panic = gp._panic.link
			}
			if gp._panic == nil { // 必須由 signal 完成
				gp.sig = 0
			}
			// 傳遞關于恢復幀的信息
			gp.sigcode0 = uintptr(sp)
			gp.sigcode1 = pc
			// 調用 recover,并重新進入調度循環,不再返回
			mcall(recovery)
			// 如果無法重新進入調度循環,則無法恢復錯誤
			throw("recovery failed") // mcall should not return
		}
	}

	// 消耗完所有的 defer 調用,保守地進行 panic
	// 因為在凍結之后調用任意用戶代碼是不安全的,所以我們調用 preprintpanics 來調用
	// 所有必要的 Error 和 String 方法來在 startpanic 之前準備 panic 字符串。
	preprintpanics(gp._panic)

	fatalpanic(gp._panic) // 不應該返回
	*(*int)(nil) = 0      // 無法觸及
}

// reflectcall 使用 arg 指向的 n 個參數字節的副本調用 fn。
// fn 返回后,reflectcall 在返回之前將 n-retoffset 結果字節復制回 arg+retoffset。
// 如果重新復制結果字節,則調用者應將參數幀類型作為 argtype 傳遞,以便該調用可以在復制期間執行適當的寫障礙。
// reflect 包傳遞幀類型。在 runtime 包中,只有一個調用將結果復制回來,即 cgocallbackg1,
// 并且它不傳遞幀類型,這意味著沒有調用寫障礙。參見該調用的頁面了解相關理由。
//
// 包 reflect 通過 linkname 訪問此符號
func reflectcall(argtype *_type, fn, arg unsafe.Pointer, argsize uint32, retoffset uint32)

梳理下流程

1、在處理panic期間,會先判斷當前panic的類型,確定panic是否可恢復;

  • 系統棧上的panic無法恢復
  • 如果正在進行malloc時發生panic也無法恢復
  • 在禁止搶占時發生panic也無法恢復
  • 在g鎖在m上時發生panic也無法恢復

2、可恢復的panicpaniclink指向goroutine鏈表中先前的panic鏈表;

3、循環逐個獲取當前goroutinedefer調用;

  • 如果defer是由早期panic或Goexit開始的,則將defer帶離鏈表,更早的panic或Goexit將無法繼續運行,也就是將之前的panic終止掉,將aborted設置為true,在下面執行recover時保證goexit不會被取消;
  • recovered會在gorecover中被標記,見下文。當recovered被標記為true時,recovery函數觸發Goroutine的調度,調度之前會準備好 sp、pc 以及函數的返回值;
  • 當延遲函數中recover了一個panic時,就會返回1,當runtime.deferproc函數的返回值是1時,編譯器生成的代碼會直接跳轉到調用方函數返回之前并執行runtime.deferreturn,跳轉到runtime.deferturn函數之后,程序就已經從panic恢復了正常的邏輯。而runtime.gorecover函數也能從runtime._panic結構中取出了調用panic時傳入的arg參數并返回給調用方。
// 在發生 panic 后 defer 函數調用 recover 后展開棧。然后安排繼續運行,
// 就像 defer 函數的調用方正常返回一樣。
func recovery(gp *g) {
	// Info about defer passed in G struct.
	sp := gp.sigcode0
	pc := gp.sigcode1

	// d's arguments need to be in the stack.
	if sp != 0  (sp  gp.stack.lo || gp.stack.hi  sp) {
		print("recover: ", hex(sp), " not in [", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n")
		throw("bad recovery")
	}

	// 使 deferproc 為此 d 返回
	// 這時候返回 1。調用函數將跳轉到標準的返回尾聲
	gp.sched.sp = sp
	gp.sched.pc = pc
	gp.sched.lr = 0
	gp.sched.ret = 1
	gogo(gp.sched)
}

recovery函數中,利用g中的兩個狀態碼回溯棧指針sp并恢復程序計數器pc到調度器中,并調用gogo重新調度g,將g恢復到調用recover函數的位置,goroutine繼續執行,recovery在調度過程中會將函數的返回值設置為1。調用函數將跳轉到標準的返回尾聲。

func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
	...

	// deferproc returns 0 normally.
	// a deferred func that stops a panic
	// makes the deferproc return 1.
	// the code the compiler generates always
	// checks the return value and jumps to the
	// end of the function if deferproc returns != 0.
	return0()
	// No code can go here - the C return register has
	// been set and must not be clobbered.
}

當延遲函數中recover了一個panic時,就會返回1,當runtime.deferproc函數的返回值是1時,編譯器生成的代碼會直接跳轉到調用方函數返回之前并執行runtime.deferreturn,跳轉到runtime.deferturn函數之后,程序就已經從panic恢復了正常的邏輯。而runtime.gorecover函數也能從runtime._panic結構中取出了調用panic時傳入的arg參數并返回給調用方。

gorecover

編譯器會將recover裝換成gorecover

如果recover被正確執行了,也就是gorecover,那么recovered將被標記成true

// go/src/runtime/panic.go
// 執行預先聲明的函數 recover。
// 不允許分段棧,因為它需要可靠地找到其調用者的棧段。
//
// TODO(rsc): Once we commit to CopyStackAlways,
// this doesn't need to be nosplit.
//go:nosplit
func gorecover(argp uintptr) interface{} {
	// 必須在 panic 期間作為 defer 調用的一部分在函數中運行。
	// 必須從調用的最頂層函數( defer 語句中使用的函數)調用。
	// p.argp 是最頂層 defer 函數調用的參數指針。
	// 比較調用方報告的 argp,如果匹配,則調用者可以恢復。
	gp := getg()
	p := gp._panic
	if p != nil  !p.recovered  argp == uintptr(p.argp) {
		// 標記recovered
		p.recovered = true
		return p.arg
	}
	return nil
}

在正常情況下,它會修改runtime._panicrecovered字段,runtime.gorecover函數中并不包含恢復程序的邏輯,程序的恢復是由runtime.gopanic函數負責。

gorecoverrecovered標記為true,然后gopanic就可以通過mcall調用recovery并重新進入調度循環

fatalpanic

runtime.fatalpanic實現了無法被恢復的程序崩潰,它在中止程序之前會通過runtime.printpanics打印出全部的panic消息以及調用時傳入的參數:

// go/src/runtime/panic.go
// fatalpanic 實現了不可恢復的 panic。類似于 fatalthrow,
// 如果 msgs != nil,則 fatalpanic 仍然能夠打印 panic 的消息
// 并在 main 在退出時候減少 runningPanicDeferss
//
//go:nosplit
func fatalpanic(msgs *_panic) {
	// 返回程序計數寄存器指針
	pc := getcallerpc()
	// 返回堆棧指針
	sp := getcallersp()
	// 返回當前G
	gp := getg()
	var docrash bool
	// 切換到系統棧來避免棧增長,如果運行時狀態較差則可能導致更糟糕的事情
	systemstack(func() {
		if startpanic_m()  msgs != nil {
			// 有 panic 消息和 startpanic_m 則可以嘗試打印它們

			// startpanic_m 設置 panic 會從阻止 main 的退出,
			// 因此現在可以開始減少 runningPanicDefers 了
			atomic.Xadd(runningPanicDefers, -1)

			printpanics(msgs)
		}

		docrash = dopanic_m(gp, pc, sp)
	})

	if docrash {
		// 通過在上述 systemstack 調用之外崩潰,調試器在生成回溯時不會混淆。
		// 函數崩潰標記為 nosplit 以避免堆棧增長。
		crash()
	}
	// 從系統推出
	systemstack(func() {
		exit(2)
	})

	*(*int)(nil) = 0 // not reached
}

// 打印出當前活動的panic
func printpanics(p *_panic) {
	if p.link != nil {
		printpanics(p.link)
		print("\t")
	}
	print("panic: ")
	printany(p.arg)
	if p.recovered {
		print(" [recovered]")
	}
	print("\n")
}

總結

引一段來自【panic 和recover】的總結

1、編譯器會負責做轉換關鍵字的工作;

1、將panicrecover分別轉換成runtime.gopanicruntime.gorecover

2、將defer轉換成runtime.deferproc函數;

3、在調用defer的函數末尾調用runtime.deferreturn函數;

2、在運行過程中遇到runtime.gopanic方法時,會從Goroutine的鏈表依次取出runtime._defer結構體并執行;

3、如果調用延遲執行函數時遇到了runtime.gorecover就會將_panic.recovered標記成true并返回panic的參數;

1、在這次調用結束之后,runtime.gopanic會從runtime._defer結構體中取出程序計數器pc和棧指針sp并調用runtime.recovery函數進行恢復程序;

2、runtime.recovery會根據傳入的pcsp跳轉回runtime.deferproc

3、編譯器自動生成的代碼會發現runtime.deferproc的返回值不為0,這時會跳回runtime.deferreturn并恢復到正常的執行流程;

4、如果沒有遇到runtime.gorecover就會依次遍歷所有的runtime._defer,并在最后調用runtime.fatalpanic中止程序、打印panic的參數并返回錯誤碼2

參考

【panic 和 recover】https://draveness.me/golang/docs/part2-foundation/ch05-keyword/golang-panic-recover/
【恐慌與恢復內建函數】https://golang.design/under-the-hood/zh-cn/part1basic/ch03lang/panic/
【Go語言panic/recover的實現】https://zhuanlan.zhihu.com/p/72779197
【panic and recover】https://eddycjy.gitbook.io/golang/di-6-ke-chang-yong-guan-jian-zi/panic-and-recover
【翻了源碼,我把 panic 與 recover 給徹底搞明白了】https://jishuin.proginn.com/p/763bfbd4ed8c

到此這篇關于詳解go中panic源碼解讀的文章就介紹到這了,更多相關go panic源碼內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • Go中recover與panic區別詳解
  • go panic時如何讓函數返回數據?
  • Golang捕獲panic堆棧信息的講解
  • go語言的panic和recover函數用法實例
  • go語言異常panic和恢復recover用法實例
  • GO語言異常處理機制panic和recover分析

標簽:汕頭 重慶 吐魯番 梅河口 欽州 銅川 蘭州 雞西

巨人網絡通訊聲明:本文標題《詳解go中panic源碼解讀》,本文關鍵詞  詳解,中,panic,源碼,解讀,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《詳解go中panic源碼解讀》相關的同類信息!
  • 本頁收集關于詳解go中panic源碼解讀的相關信息資訊供網民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    久久99精品久久久| 亚洲精品在线免费观看视频| 亚洲精品你懂的| 中文字幕一区二区三区四区| 欧美狂野另类xxxxoooo| 亚洲综合男人的天堂| 视频在线在亚洲| 国产成人aaa| 日韩精品一区二区三区蜜臀| 色94色欧美sute亚洲线路一ni| 日韩精品一区二区三区中文不卡| 精品国产乱子伦一区| 亚洲福利视频三区| 99久久久国产精品| 欧美成人一区二区三区在线观看| 亚洲日本成人在线观看| 一区二区三区在线免费播放| 欧美在线视频全部完| **性色生活片久久毛片| 看片的网站亚洲| 三级欧美韩日大片在线看| 制服丝袜亚洲网站| 国产黄人亚洲片| 久久成人羞羞网站| 欧美性猛交xxxx乱大交退制版| 亚洲精品成人在线| 视频一区二区欧美| 日本欧美在线看| 欧美日韩免费观看一区二区三区| 在线成人高清不卡| 亚洲一区二区av在线| 久久精品国产网站| 欧美视频中文一区二区三区在线观看| 欧美日韩三级一区二区| 国产麻豆一精品一av一免费| 亚洲高清免费在线| 成人app网站| 在线精品视频一区二区三四| 国产精品传媒入口麻豆| 国产精品911| 国产精品高潮呻吟| 亚洲一二三区在线观看| 精品久久久久av影院| 国产综合久久久久久鬼色| 精品免费国产二区三区| 亚洲黄色av一区| 精品理论电影在线观看| 伊人一区二区三区| 欧美绝品在线观看成人午夜影视| 亚洲婷婷综合色高清在线| 99在线视频精品| 日韩欧美一区二区视频| 99久久精品免费看国产| 亚洲丶国产丶欧美一区二区三区| 欧美人体做爰大胆视频| 久久er精品视频| 日韩片之四级片| 欧美日韩中文字幕精品| 六月丁香综合在线视频| 国产午夜一区二区三区| 91网上在线视频| 国内一区二区在线| 亚洲视频一区二区免费在线观看 | 久草这里只有精品视频| 亚洲成av人影院在线观看网| 国产精品影视在线观看| 综合久久综合久久| 欧美日韩国产片| 国产.欧美.日韩| 国产午夜精品美女毛片视频| 色视频成人在线观看免| 一区二区三区高清不卡| 7777精品伊人久久久大香线蕉 | 一道本成人在线| 亚洲综合激情小说| 久久精品一二三| 欧美卡1卡2卡| 日韩视频免费观看高清完整版| 91麻豆成人久久精品二区三区| 午夜伦欧美伦电影理论片| 国产精品网站在线播放| 日韩一区二区视频| 欧美吻胸吃奶大尺度电影| 高清av一区二区| 亚洲午夜久久久久久久久电影网| 欧美性猛交一区二区三区精品| a4yy欧美一区二区三区| 成人性视频网站| 国产精品自拍网站| 蜜乳av一区二区三区| 一区二区三区欧美久久| 亚洲视频小说图片| 调教+趴+乳夹+国产+精品| 亚洲综合男人的天堂| 亚洲欧美一区二区三区孕妇| 色欧美片视频在线观看| 国产成人午夜精品5599| 国产精品久久久久久久久免费樱桃| 精品日韩在线观看| 精品免费99久久| 色哟哟一区二区在线观看| 91精品蜜臀在线一区尤物| 色综合久久中文综合久久97 | 一区二区视频在线| 国产精品高潮呻吟久久| 久久综合五月天婷婷伊人| 精品日韩在线一区| 夜夜揉揉日日人人青青一国产精品| 亚洲蜜桃精久久久久久久| 91麻豆精品在线观看| 亚洲综合视频网| 日韩欧美在线不卡| 不卡电影免费在线播放一区| 一二三区精品视频| 日韩欧美精品在线视频| 国产精品一区二区不卡| 中文字幕一区在线观看视频| 欧美一区二区精美| 91在线你懂得| 9人人澡人人爽人人精品| 欧美在线免费观看亚洲| 国产不卡视频在线观看| 久久久久国产精品麻豆ai换脸 | 在线观看国产精品网站| 国产一区二区三区在线观看精品| 日韩精品五月天| 国产真实乱对白精彩久久| 欧美三级中文字幕| 国产亚洲综合av| 免费成人在线网站| 777色狠狠一区二区三区| 中文字幕免费不卡在线| 精品一区二区三区视频在线观看 | 欧美一区二区三区爱爱| 一区二区三区欧美在线观看| 婷婷国产在线综合| 色视频成人在线观看免| 国产日本欧美一区二区| 亚洲成av人片一区二区梦乃| 欧洲av一区二区嗯嗯嗯啊| 中文字幕一区二区三中文字幕 | 亚洲欧洲在线观看av| 国产精品911| 国产亚洲欧美激情| 成人短视频下载| 中文字幕在线播放不卡一区| 六月婷婷色综合| 精品久久久久久久久久久久久久久 | 欧美日韩日本视频| 亚洲午夜久久久久久久久电影院| 成人综合婷婷国产精品久久免费| 成人深夜视频在线观看| 1024国产精品| 97se亚洲国产综合自在线不卡 | 欧美日韩亚洲不卡| 日本午夜一本久久久综合| 久久色在线视频| 成人午夜激情视频| 午夜影视日本亚洲欧洲精品| 欧美精品自拍偷拍动漫精品| 国产精品18久久久久久vr| 亚洲乱码中文字幕| 3d动漫精品啪啪1区2区免费| 成人精品在线视频观看| 石原莉奈在线亚洲三区| 中文字幕一区二区三区四区 | 亚洲女厕所小便bbb| 欧美精品乱码久久久久久按摩| 国产成人亚洲综合a∨婷婷图片| 亚洲一区二区三区视频在线播放 | 亚洲美女偷拍久久| 欧美色男人天堂| 麻豆精品在线视频| 亚洲欧美一区二区视频| 91国产精品成人| 日韩中文字幕一区二区三区| 久久久久久久久久久久久女国产乱 | 欧美三片在线视频观看 | 国产伦精品一区二区三区免费| 337p粉嫩大胆噜噜噜噜噜91av| 亚洲欧洲另类国产综合| 欧美一区二区三区四区五区| 91搞黄在线观看| 欧美理论片在线| 欧美写真视频网站| 国内精品嫩模私拍在线| 亚洲一区二区欧美| 中文字幕在线观看一区| 中文字幕精品—区二区四季| 欧美一区二区精品在线| 欧美精品免费视频| 成人精品视频一区二区三区尤物| 毛片不卡一区二区| 国模冰冰炮一区二区| 国产伦精品一区二区三区免费迷 | 亚洲精品在线免费播放| 欧美日韩综合一区| 日韩久久免费av| 国产性做久久久久久| 亚洲成人资源网|