亚洲综合原千岁中文字幕_国产精品99久久久久久久vr_无码人妻aⅴ一区二区三区浪潮_成人h动漫精品一区二区三

主頁 > 知識庫 > 詳解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、可恢復的panic,paniclink指向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麻豆tv| a级精品九九九大片免费看| 午夜欧美成人香蕉剧场| 九九干| 青青久久网| 国产网站免费视频| 国产成+人+综合+亚洲不卡| 99久久精品国产高清一区二区| 天天色色网| 国产韩国精品一区二区三区| 国产一区二区精品久久| 九九热国产视频| 一a一级片| 色综合久久天天综线观看| 国产91精品露脸国语对白| 欧美夜夜骑 青草视频在线观看完整版 久久精品99无色码中文字幕 欧美日韩一区二区在线观看视频 欧美中文字幕在线视频 www.99精品 香蕉视频久久 | 色综合久久天天综合| 日日日夜夜操| 黄视频网站在线看| 欧美日本免费| 久久99中文字幕久久| 午夜欧美成人香蕉剧场| 成人免费网站久久久| 精品视频一区二区三区免费| 国产美女在线观看| 99久久精品国产免费| 亚洲第一页乱| 久草免费资源| 九九久久99综合一区二区| 欧美电影免费| 韩国三级视频网站| 美女免费精品视频在线观看| 精品毛片视频| 午夜在线观看视频免费 成人| 久久99欧美| 国产一区二区精品| 美国一区二区三区| 亚洲精品中文字幕久久久久久| 亚欧成人乱码一区二区| 国产麻豆精品| a级精品九九九大片免费看| 免费一级片在线| 国产成a人片在线观看视频| 日本在线不卡视频| 久草免费资源| 国产91精品系列在线观看| 日韩专区一区| 好男人天堂网 久久精品国产这里是免费 国产精品成人一区二区 男人天堂网2021 男人的天堂在线观看 丁香六月综合激情 | 午夜欧美成人久久久久久| 精品在线观看一区| 日韩免费在线| 亚欧成人乱码一区二区| 高清一级片| 高清一级淫片a级中文字幕| 99久久精品国产麻豆| 国产麻豆精品免费密入口| 久久国产精品只做精品| 日韩av片免费播放| 一本高清在线| 可以免费看毛片的网站| 你懂的日韩| 韩国三级一区| 韩国三级视频网站| 日本特黄特黄aaaaa大片 | 成人高清视频免费观看| 亚洲精品影院| 国产亚洲免费观看| 日韩专区一区| 91麻豆精品国产片在线观看| 韩国三级香港三级日本三级la | 国产网站在线| 亚飞与亚基在线观看| 国产a一级| 亚欧成人乱码一区二区| 国产精品免费精品自在线观看| 国产国产人免费视频成69堂| 青青久久网| 九九精品影院| 中文字幕一区二区三区 精品| 麻豆午夜视频| 久久精品欧美一区二区| 久久久成人网| 精品久久久久久中文字幕2017| 美女免费毛片| 韩国三级香港三级日本三级| 黄视频网站免费看| 国产91丝袜在线播放0| 精品国产三级a| 一本高清在线| 国产麻豆精品免费密入口| 日日夜夜婷婷| 可以免费看污视频的网站| 日日日夜夜操| 九九精品在线| 亚洲不卡一区二区三区在线| 国产不卡高清| 欧美大片毛片aaa免费看| 日韩一级黄色大片| 国产精品123| 国产成a人片在线观看视频| 欧美激情一区二区三区视频 | 免费一级片在线| 青草国产在线观看| 可以免费看毛片的网站| 日韩免费片| 午夜欧美成人久久久久久| 日日爽天天| 久久99中文字幕久久| 精品国产一区二区三区久久久蜜臀 | 国产91素人搭讪系列天堂| 成人高清视频在线观看| a级精品九九九大片免费看| 四虎影视久久久免费| 久久精品免视看国产明星| 欧美爱色| 日日夜夜婷婷| 成人免费观看视频| 国产伦精品一区三区视频| 国产麻豆精品| 亚洲女人国产香蕉久久精品| 精品视频在线观看一区二区| 亚洲天堂在线播放| 国产亚洲免费观看| 99久久精品国产麻豆| 久久精品成人一区二区三区| 日本免费区| 国产福利免费视频| 91麻豆精品国产自产在线| 日韩专区一区| 国产视频一区在线| 国产极品精频在线观看| 你懂的日韩| 国产欧美精品| 欧美另类videosbestsex久久| 国产一区国产二区国产三区| 国产伦精品一区二区三区在线观看| 成人免费高清视频| 午夜在线亚洲男人午在线| 欧美日本韩国| 久久精品人人做人人爽97| 国产一区免费观看| 成人免费观看的视频黄页| 日本在线www| 国产成人精品综合久久久| 国产极品精频在线观看| 日韩在线观看免费| 一级女性全黄久久生活片| 可以在线看黄的网站| 成人a级高清视频在线观看| 欧美爱爱动态| 日韩专区在线播放| 欧美18性精品| 韩国三级视频在线观看| 免费国产在线观看| 四虎影视久久久| 99久久精品国产高清一区二区 | 韩国毛片免费大片| 国产欧美精品| 香蕉视频一级| 精品毛片视频| 91麻豆tv| 日韩字幕在线| 一级女性全黄久久生活片| 精品国产香蕉伊思人在线又爽又黄| 国产一区二区福利久久| 国产网站免费在线观看| 国产一区二区精品尤物| 99色视频在线| 国产视频久久久| 高清一级做a爱过程不卡视频| 日本免费看视频| 国产成人啪精品视频免费软件| 九九久久99综合一区二区| 黄色免费三级| 99久久网站| 99久久精品国产高清一区二区 | 日韩中文字幕一区| 高清一级片| 国产一区二区精品在线观看| 日韩欧美一二三区| 久久福利影视| 美女被草网站| 精品久久久久久影院免费| 亚洲第一色在线| 天天色色色| 国产国产人免费视频成69堂| 青草国产在线| 黄色免费网站在线| 国产高清视频免费| 日本久久久久久久 97久久精品一区二区三区 狠狠色噜噜狠狠狠狠97 日日干综合 五月天婷婷在线观看高清 九色福利视频 | 一级女人毛片人一女人| 免费一级片网站| 欧美国产日韩精品| 日本免费乱理伦片在线观看2018| 99热热久久| 黄视频网站免费观看| 免费一级生活片|