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

主頁 > 知識庫 > 一文讀懂go中semaphore(信號量)源碼

一文讀懂go中semaphore(信號量)源碼

熱門標簽:宿遷便宜外呼系統代理商 不封卡外呼系統 仙桃400電話辦理 上海極信防封電銷卡價格 重慶慶云企業400電話到哪申請 寧波語音外呼系統公司 湛江crm外呼系統排名 鄭州智能語音電銷機器人價格 地圖標注免費定制店

運行時信號量機制 semaphore

前言

最近在看源碼,發現好多地方用到了這個semaphore

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

作用是什么

下面是官方的描述

// Semaphore implementation exposed to Go.
// Intended use is provide a sleep and wakeup
// primitive that can be used in the contended case
// of other synchronization primitives.
// Thus it targets the same goal as Linux's futex,
// but it has much simpler semantics.
//
// That is, don't think of these as semaphores.
// Think of them as a way to implement sleep and wakeup
// such that every sleep is paired with a single wakeup,
// even if, due to races, the wakeup happens before the sleep.

// 具體的用法是提供 sleep 和 wakeup 原語
// 以使其能夠在其它同步原語中的競爭情況下使用
// 因此這里的 semaphore 和 Linux 中的 futex 目標是一致的
// 只不過語義上更簡單一些
//
// 也就是說,不要認為這些是信號量
// 把這里的東西看作 sleep 和 wakeup 實現的一種方式
// 每一個 sleep 都會和一個 wakeup 配對
// 即使在發生 race 時,wakeup 在 sleep 之前時也是如此 

上面提到了和futex作用一樣,關于futex

futex(快速用戶區互斥的簡稱)是一個在Linux上實現鎖定和構建高級抽象鎖如信號量和POSIX互斥的基本工具

Futex 由一塊能夠被多個進程共享的內存空間(一個對齊后的整型變量)組成;這個整型變量的值能夠通過匯編語言調用CPU提供的原子操作指令來增加或減少,并且一個進程可以等待直到那個值變成正數。Futex 的操作幾乎全部在用戶空間完成;只有當操作結果不一致從而需要仲裁時,才需要進入操作系統內核空間執行。這種機制允許使用 futex 的鎖定原語有非常高的執行效率:由于絕大多數的操作并不需要在多個進程之間進行仲裁,所以絕大多數操作都可以在應用程序空間執行,而不需要使用(相對高代價的)內核系統調用。

go中的semaphore作用和futex目標一樣,提供sleepwakeup原語,使其能夠在其它同步原語中的競爭情況下使用。當一個goroutine需要休眠時,將其進行集中存放,當需要wakeup時,再將其取出,重新放入調度器中。

例如在讀寫鎖的實現中,讀鎖和寫鎖之前的相互阻塞喚醒,就是通過sleepwakeup實現,當有讀鎖存在的時候,新加入的寫鎖通過semaphore阻塞自己,當前面的讀鎖完成,在通過semaphore喚醒被阻塞的寫鎖。

寫鎖

// 獲取互斥鎖
// 阻塞等待所有讀操作結束(如果有的話)
func (rw *RWMutex) Lock() {
	...
	// 原子的修改readerCount的值,直接將readerCount減去rwmutexMaxReaders
	// 說明,有寫鎖進來了,這在上面的讀鎖中也有體現
	r := atomic.AddInt32(rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
	// 當r不為0說明,當前寫鎖之前有讀鎖的存在
	// 修改下readerWait,也就是當前寫鎖需要等待的讀鎖的個數 
	if r != 0  atomic.AddInt32(rw.readerWait, r) != 0 {
		// 阻塞當前寫鎖
		runtime_SemacquireMutex(rw.writerSem, false, 0)
	}
	...
}

通過runtime_SemacquireMutex對當前寫鎖進行sleep

讀鎖釋放

// 減少讀操作計數,即readerCount--
// 喚醒等待寫操作的協程(如果有的話)
func (rw *RWMutex) RUnlock() {
	...
	// 首先通過atomic的原子性使readerCount-1
	// 1.若readerCount大于0, 證明當前還有讀鎖, 直接結束本次操作
	// 2.若readerCount小于0, 證明已經沒有讀鎖, 但是還有因為讀鎖被阻塞的寫鎖存在
	if r := atomic.AddInt32(rw.readerCount, -1); r  0 {
		// 嘗試喚醒被阻塞的寫鎖
		rw.rUnlockSlow(r)
	}
	...
}

func (rw *RWMutex) rUnlockSlow(r int32) {
	...
	// readerWait--操作,如果readerWait--操作之后的值為0,說明,寫鎖之前,已經沒有讀鎖了
	// 通過writerSem信號量,喚醒隊列中第一個阻塞的寫鎖
	if atomic.AddInt32(rw.readerWait, -1) == 0 {
		// 喚醒一個寫鎖
		runtime_Semrelease(rw.writerSem, false, 1)
	}
}

寫鎖處理完之后,調用runtime_Semrelease來喚醒sleep的寫鎖

幾個主要的方法

go/src/sync/runtime.go中,定義了這幾個方法

// Semacquire等待*s > 0,然后原子遞減它。
// 它是一個簡單的睡眠原語,用于同步
// library and不應該直接使用。
func runtime_Semacquire(s *uint32)

// SemacquireMutex類似于Semacquire,用來阻塞互斥的對象
// 如果lifo為true,waiter將會被插入到隊列的頭部
// skipframes是跟蹤過程中要省略的幀數,從這里開始計算
// runtime_SemacquireMutex's caller.
func runtime_SemacquireMutex(s *uint32, lifo bool, skipframes int)

// Semrelease會自動增加*s并通知一個被Semacquire阻塞的等待的goroutine
// 它是一個簡單的喚醒原語,用于同步
// library and不應該直接使用。
// 如果handoff為true, 傳遞信號到隊列頭部的waiter
// skipframes是跟蹤過程中要省略的幀數,從這里開始計算
// runtime_Semrelease's caller.
func runtime_Semrelease(s *uint32, handoff bool, skipframes int)

具體的實現是在go/src/runtime/sema.go

//go:linkname sync_runtime_Semacquire sync.runtime_Semacquire
func sync_runtime_Semacquire(addr *uint32) {
	semacquire1(addr, false, semaBlockProfile, 0)
}

//go:linkname sync_runtime_Semrelease sync.runtime_Semrelease
func sync_runtime_Semrelease(addr *uint32, handoff bool, skipframes int) {
	semrelease1(addr, handoff, skipframes)
}

//go:linkname sync_runtime_SemacquireMutex sync.runtime_SemacquireMutex
func sync_runtime_SemacquireMutex(addr *uint32, lifo bool, skipframes int) {
	semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile, skipframes)
}

如何實現

sudog 緩存

semaphore的實現使用到了sudog,我們先來看下

sudog 是運行時用來存放處于阻塞狀態的goroutine的一個上層抽象,是用來實現用戶態信號量的主要機制之一。 例如當一個goroutine因為等待channel的數據需要進行阻塞時,sudog會將goroutine及其用于等待數據的位置進行記錄, 并進而串聯成一個等待隊列,或二叉平衡樹。

// sudogs are allocated from a special pool. Use acquireSudog and
// releaseSudog to allocate and free them.
type sudog struct {
	// 以下字段受hchan保護
	g *g

	// isSelect 表示 g 正在參與一個 select, so
	// 因此 g.selectDone 必須以 CAS 的方式來獲取wake-up race.
	isSelect bool
	next  *sudog
	prev  *sudog
	elem  unsafe.Pointer // 數據元素(可能指向棧)

	// 以下字段不會并發訪問。
	// 對于通道,waitlink只被g訪問。
	// 對于信號量,所有字段(包括上面的字段)
	// 只有當持有一個semroot鎖時才被訪問。
	acquiretime int64
	releasetime int64
	ticket  uint32
	parent  *sudog //semaRoot 二叉樹
	waitlink *sudog // g.waiting 列表或 semaRoot
	waittail *sudog // semaRoot
	c   *hchan // channel
}

sudog的獲取和歸還,遵循以下策略:

1、獲取,首先從per-P緩存獲取,對于per-P緩存,如果per-P緩存為空,則從全局池抓取一半,然后取出per-P緩存中的最后一個;

2、歸還,歸還到per-P緩存,如果per-P緩存滿了,就把per-P緩存的一半歸還到全局緩存中,然后歸還sudogper-P緩存中。

acquireSudog

1、如果per-P緩存的內容沒達到長度的一般,則會從全局額緩存中抓取一半;

2、然后返回把per-P緩存中最后一個sudog返回,并且置空;

// go/src/runtime/proc.go
//go:nosplit
func acquireSudog() *sudog {
	// Delicate dance: 信號量的實現調用acquireSudog,然后acquireSudog調用new(sudog)
	// new調用malloc, malloc調用垃圾收集器,垃圾收集器在stopTheWorld調用信號量
	// 通過在new(sudog)周圍執行acquirem/releasem來打破循環
	// acquirem/releasem在new(sudog)期間增加m.locks,防止垃圾收集器被調用。

	// 獲取當前 g 所在的 m
	mp := acquirem()
	// 獲取p的指針
	pp := mp.p.ptr()
	if len(pp.sudogcache) == 0 {
		lock(sched.sudoglock)
		// 首先,嘗試從中央緩存獲取一批數據。
		for len(pp.sudogcache)  cap(pp.sudogcache)/2  sched.sudogcache != nil {
			s := sched.sudogcache
			sched.sudogcache = s.next
			s.next = nil
			pp.sudogcache = append(pp.sudogcache, s)
		}
		unlock(sched.sudoglock)
		// 如果中央緩存中沒有,新分配
		if len(pp.sudogcache) == 0 {
			pp.sudogcache = append(pp.sudogcache, new(sudog))
		}
	}
	// 取緩存中最后一個
	n := len(pp.sudogcache)
	s := pp.sudogcache[n-1]
	pp.sudogcache[n-1] = nil
	// 將剛取出的在緩存中移除
	pp.sudogcache = pp.sudogcache[:n-1]
	if s.elem != nil {
		throw("acquireSudog: found s.elem != nil in cache")
	}
	releasem(mp)
	return s
}

releaseSudog

1、如果per-P緩存滿了,就歸還per-P緩存一般的內容到全局緩存;

2、然后將回收的sudog放到per-P緩存中。

// go/src/runtime/proc.go
//go:nosplit
func releaseSudog(s *sudog) {
	if s.elem != nil {
		throw("runtime: sudog with non-nil elem")
	}
	if s.isSelect {
		throw("runtime: sudog with non-false isSelect")
	}
	if s.next != nil {
		throw("runtime: sudog with non-nil next")
	}
	if s.prev != nil {
		throw("runtime: sudog with non-nil prev")
	}
	if s.waitlink != nil {
		throw("runtime: sudog with non-nil waitlink")
	}
	if s.c != nil {
		throw("runtime: sudog with non-nil c")
	}
	gp := getg()
	if gp.param != nil {
		throw("runtime: releaseSudog with non-nil gp.param")
	}
	// 避免重新安排到另一個P
	mp := acquirem() // avoid rescheduling to another P
	pp := mp.p.ptr()
	// 如果緩存滿了
	if len(pp.sudogcache) == cap(pp.sudogcache) {
		// 將本地高速緩存的一半傳輸到中央高速緩存
		var first, last *sudog
		for len(pp.sudogcache) > cap(pp.sudogcache)/2 {
			n := len(pp.sudogcache)
			p := pp.sudogcache[n-1]
			pp.sudogcache[n-1] = nil
			pp.sudogcache = pp.sudogcache[:n-1]
			if first == nil {
				first = p
			} else {
				last.next = p
			}
			last = p
		}
		lock(sched.sudoglock)
		last.next = sched.sudogcache
		sched.sudogcache = first
		unlock(sched.sudoglock)
	}
	// 歸還sudog到`per-P`緩存中
	pp.sudogcache = append(pp.sudogcache, s)
	releasem(mp)
}

semaphore

// go/src/runtime/sema.go
// 用于sync.Mutex的異步信號量。

// semaRoot擁有一個具有不同地址(s.elem)的sudog平衡樹。
// 每個sudog都可以依次(通過s.waitlink)指向一個列表,在相同地址上等待的其他sudog。
// 對具有相同地址的sudog內部列表進行的操作全部為O(1)。頂層semaRoot列表的掃描為O(log n),
// 其中,n是阻止goroutines的不同地址的數量,通過他們散列到給定的semaRoot。
type semaRoot struct {
	lock mutex
	// waiters的平衡樹的根節點
	treap *sudog
	// waiters的數量,讀取的時候無所
	nwait uint32
}

// Prime to not correlate with any user patterns.
const semTabSize = 251

var semtable [semTabSize]struct {
	root semaRoot
	pad [cpu.CacheLinePadSize - unsafe.Sizeof(semaRoot{})]byte
}

poll_runtime_Semacquire/sync_runtime_SemacquireMutex

// go/src/runtime/sema.go
//go:linkname poll_runtime_Semacquire internal/poll.runtime_Semacquire
func poll_runtime_Semacquire(addr *uint32) {
	semacquire1(addr, false, semaBlockProfile, 0)
}
//go:linkname sync_runtime_SemacquireMutex sync.runtime_SemacquireMutex
func sync_runtime_SemacquireMutex(addr *uint32, lifo bool, skipframes int) {
	semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile, skipframes)
}


func semacquire1(addr *uint32, lifo bool, profile semaProfileFlags, skipframes int) {
	// 判斷這個goroutine,是否是m上正在運行的那個
	gp := getg()
	if gp != gp.m.curg {
		throw("semacquire not on the G stack")
	}

	// *addr -= 1
	if cansemacquire(addr) {
		return
	}

	// 增加等待計數
	// 再試一次 cansemacquire 如果成功則直接返回
	// 將自己作為等待者入隊
	// 休眠
	// (等待器描述符由出隊信號產生出隊行為)

	// 獲取一個sudog
	s := acquireSudog()
	root := semroot(addr)
	t0 := int64(0)
	s.releasetime = 0
	s.acquiretime = 0
	s.ticket = 0
	if profilesemaBlockProfile != 0  blockprofilerate > 0 {
		t0 = cputicks()
		s.releasetime = -1
	}
	if profilesemaMutexProfile != 0  mutexprofilerate > 0 {
		if t0 == 0 {
			t0 = cputicks()
		}
		s.acquiretime = t0
	}
	for {
		lock(root.lock)
		// 添加我們自己到nwait來禁用semrelease中的"easy case"
		atomic.Xadd(root.nwait, 1)
		// 檢查cansemacquire避免錯過喚醒
		if cansemacquire(addr) {
			atomic.Xadd(root.nwait, -1)
			unlock(root.lock)
			break
		}
		// 任何在 cansemacquire 之后的 semrelease 都知道我們在等待(因為設置了 nwait),因此休眠

		// 隊列將s添加到semaRoot中被阻止的goroutine中
		root.queue(addr, s, lifo)
		// 將當前goroutine置于等待狀態并解鎖鎖。
		// 通過調用goready(gp),可以使goroutine再次可運行。
		goparkunlock(root.lock, waitReasonSemacquire, traceEvGoBlockSync, 4+skipframes)
		if s.ticket != 0 || cansemacquire(addr) {
			break
		}
	}
	if s.releasetime > 0 {
		blockevent(s.releasetime-t0, 3+skipframes)
	}

	// 歸還sudog
	releaseSudog(s)
}

func cansemacquire(addr *uint32) bool {
	for {
		v := atomic.Load(addr)
		if v == 0 {
			return false
		}
		if atomic.Cas(addr, v, v-1) {
			return true
		}
	}
}

sync_runtime_Semrelease

// go/src/runtime/sema.go
//go:linkname sync_runtime_Semrelease sync.runtime_Semrelease
func sync_runtime_Semrelease(addr *uint32, handoff bool, skipframes int) {
	semrelease1(addr, handoff, skipframes)
}

func semrelease1(addr *uint32, handoff bool, skipframes int) {
	root := semroot(addr)
	atomic.Xadd(addr, 1)

	// Easy case:沒有等待者
	// 這個檢查必須發生在xadd之后,以避免錯過喚醒
	if atomic.Load(root.nwait) == 0 {
		return
	}

	// Harder case: 找到等待者,并且喚醒
	lock(root.lock)
	if atomic.Load(root.nwait) == 0 {
		// 該計數已被另一個goroutine占用,
		// 因此無需喚醒其他goroutine。
		unlock(root.lock)
		return
	}

	// 搜索一個等待著然后將其喚醒
	s, t0 := root.dequeue(addr)
	if s != nil {
		atomic.Xadd(root.nwait, -1)
	}
	unlock(root.lock)
	if s != nil { // 可能會很慢,因此先解鎖
		acquiretime := s.acquiretime
		if acquiretime != 0 {
			mutexevent(t0-acquiretime, 3+skipframes)
		}
		if s.ticket != 0 {
			throw("corrupted semaphore ticket")
		}
		if handoff  cansemacquire(addr) {
			s.ticket = 1
		}
		// goready(s.g, 5) 
		// 標記 runnable,等待被重新調度
		readyWithTime(s, 5+skipframes)
	}
}

摘自"同步原語"的一段總結

這一對 semacquire 和 semrelease 理解上可能不太直觀。 首先,我們必須意識到這兩個函數一定是在兩個不同的 M(線程)上得到執行,否則不會出現并發,我們不妨設為 M1 和 M2。 當 M1 上的 G1 執行到 semacquire1 時,如果快速路徑成功,則說明 G1 搶到鎖,能夠繼續執行。但一旦失敗且在慢速路徑下 依然搶不到鎖,則會進入 goparkunlock,將當前的 G1 放到等待隊列中,進而讓 M1 切換并執行其他 G。 當 M2 上的 G2 開始調用 semrelease1 時,只是單純的將等待隊列的 G1 重新放到調度隊列中,而當 G1 重新被調度時(假設運氣好又在 M1 上被調度),代碼仍然會從 goparkunlock 之后開始執行,并再次嘗試競爭信號量,如果成功,則會歸還 sudog。

參考

【同步原語】https://golang.design/under-the-hood/zh-cn/part2runtime/ch06sched/sync/
【Go并發編程實戰--信號量的使用方法和其實現原理】https://juejin.cn/post/6906677772479889422
【Semaphore】https://github.com/cch123/golang-notes/blob/master/semaphore.md
【進程同步之信號量機制(pv操作)及三個經典同步問題】https://blog.csdn.net/SpeedMe/article/details/17597373

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

您可能感興趣的文章:
  • 詳解Django之admin組件的使用和源碼剖析
  • 利用Vue.js+Node.js+MongoDB實現一個博客系統(附源碼)
  • python django事務transaction源碼分析詳解
  • 詳細分析Android中實現Zygote的源碼
  • 可以查詢google排名的asp源碼

標簽:儋州 海南 電子產品 安康 物業服務 西雙版納 青海 遼寧

巨人網絡通訊聲明:本文標題《一文讀懂go中semaphore(信號量)源碼》,本文關鍵詞  一文,讀懂,中,semaphore,信號,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《一文讀懂go中semaphore(信號量)源碼》相關的同類信息!
  • 本頁收集關于一文讀懂go中semaphore(信號量)源碼的相關信息資訊供網民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    激情综合色综合久久| 成人美女视频在线看| 91丨porny丨蝌蚪视频| 欧美精品一区二区三区高清aⅴ| 亚洲成人777| 在线视频观看一区| 亚洲色图欧洲色图| 黄色成人免费在线| 国产日韩成人精品| 粉嫩在线一区二区三区视频| 26uuu国产一区二区三区 | 26uuu色噜噜精品一区二区| 国产午夜精品美女毛片视频| 国产suv精品一区二区6| 欧美极品另类videosde| 成人永久aaa| 久久久久久9999| 成人午夜激情视频| 国产精品人成在线观看免费| 国产精品一区二区久激情瑜伽| 国产精品久久久久久亚洲伦| 成人爱爱电影网址| 中文字幕一区二区三区乱码在线| 成人av在线网| 亚洲色图都市小说| 91福利在线免费观看| 久久久噜噜噜久久人人看| 韩国理伦片一区二区三区在线播放 | 国产一区二区三区黄视频| www国产精品av| 国产福利一区二区三区| 中文字幕一区二区视频| 911精品国产一区二区在线| 久久精品72免费观看| 国产色91在线| 欧美一级一区二区| 99久久久精品| 久久se这里有精品| 一区二区三区不卡在线观看| 精品国产乱码久久久久久夜甘婷婷 | 日韩三级精品电影久久久| 成人国产精品免费观看动漫| 日韩av在线播放中文字幕| 亚洲国产岛国毛片在线| 欧美精品v国产精品v日韩精品| 国产精品综合av一区二区国产馆| 一区二区三区日韩欧美精品| 久久奇米777| 欧美精品欧美精品系列| 色欧美片视频在线观看| 懂色av一区二区三区免费观看| 五月天一区二区三区| 最新不卡av在线| 国产亚洲美州欧州综合国| 欧美三片在线视频观看| 色哟哟国产精品| 不卡在线观看av| 国产一区二区成人久久免费影院| 亚洲成人动漫av| 亚洲综合色丁香婷婷六月图片| 国产欧美视频在线观看| 久久久影视传媒| 日韩午夜在线影院| 91精品国产aⅴ一区二区| 欧美体内she精高潮| 一本色道久久综合精品竹菊| 97精品久久久午夜一区二区三区| 成人丝袜高跟foot| 国产精品一色哟哟哟| 精品亚洲国内自在自线福利| 日韩国产欧美在线播放| 午夜伦欧美伦电影理论片| 亚洲黄色尤物视频| 亚洲另类中文字| 亚洲三级免费观看| 亚洲免费观看高清完整| 亚洲欧美激情在线| 一区二区视频在线| 亚洲综合成人在线视频| 亚洲午夜久久久久久久久久久| 亚洲美女少妇撒尿| 亚洲自拍偷拍麻豆| 一区二区三区在线看| 一区二区免费看| 亚洲二区视频在线| 免费一级片91| 久草中文综合在线| 看电视剧不卡顿的网站| 国产精品一卡二| 成人丝袜高跟foot| 欧美又粗又大又爽| 777色狠狠一区二区三区| 欧美一区二区视频免费观看| 精品国产一区二区三区四区四| 日本一区二区三级电影在线观看 | 国产精品久久久爽爽爽麻豆色哟哟 | 久久99久久99精品免视看婷婷 | 天天操天天综合网| 美女看a上一区| 国产精品中文字幕日韩精品| 风间由美一区二区三区在线观看 | 91麻豆精品国产91久久久久久久久 | 亚洲精品网站在线观看| 亚洲成人三级小说| 极品少妇xxxx精品少妇偷拍| 懂色中文一区二区在线播放| 欧洲一区二区三区免费视频| 欧美一级日韩一级| 中文字幕第一区二区| 亚洲综合清纯丝袜自拍| 激情综合亚洲精品| 色伊人久久综合中文字幕| 日韩欧美一级二级三级| 国产精品欧美经典| 亚洲成人一区二区| 粉嫩av亚洲一区二区图片| 欧美亚州韩日在线看免费版国语版| 欧美一区欧美二区| 国产精品的网站| 丝袜美腿亚洲综合| 风间由美性色一区二区三区| 欧美精品一二三| 1000部国产精品成人观看| 蜜臀久久久久久久| 99r精品视频| 精品国产不卡一区二区三区| 亚洲美女偷拍久久| 国产精品性做久久久久久| 欧美日韩在线不卡| 国产精品人成在线观看免费| 奇米四色…亚洲| 色美美综合视频| 久久欧美一区二区| 日韩综合小视频| 91成人国产精品| 亚洲国产成人一区二区三区| 青青草精品视频| 欧洲激情一区二区| 国产精品情趣视频| 国产成人午夜高潮毛片| 日韩视频123| 日韩成人dvd| 欧美日韩视频不卡| 亚洲欧美日韩一区二区| 国产伦精品一区二区三区视频青涩 | 成人一区二区三区| 日韩免费电影一区| 日本不卡视频在线观看| 欧美在线啊v一区| 国产精品美女久久久久aⅴ国产馆 国产精品美女久久久久av爽李琼 国产精品美女久久久久高潮 | 亚洲国产精品久久久男人的天堂| 国产老妇另类xxxxx| 欧美成人一区二区三区| 亚洲国产精品一区二区www在线| 波多野洁衣一区| 日本一区二区免费在线| 国产成人综合在线| 国产日韩v精品一区二区| 国产精品18久久久久久久久久久久 | 久久精品国产99国产精品| 3d成人动漫网站| 亚洲大片精品永久免费| 欧美日韩一二三区| 午夜国产精品影院在线观看| 欧美精品在线视频| 图片区小说区国产精品视频 | 日韩欧美高清在线| 美日韩一区二区| 亚洲精品一区二区三区蜜桃下载| 久久99在线观看| 久久久午夜电影| 成人av网站在线| 亚洲天堂精品视频| 欧美三级午夜理伦三级中视频| 亚洲高清不卡在线| 欧美一卡二卡在线观看| 美女网站视频久久| 久久只精品国产| 99r精品视频| 日日摸夜夜添夜夜添国产精品| 欧美一区国产二区| 国产美女精品在线| 中文字幕日韩av资源站| 日本高清视频一区二区| 日本女优在线视频一区二区| 日韩欧美在线123| 国产成人一区二区精品非洲| 18成人在线视频| 欧美精品第1页| 国产一区二区三区蝌蚪| 综合久久久久久| 51久久夜色精品国产麻豆| 国产精品亚洲人在线观看| 亚洲柠檬福利资源导航| 欧美片网站yy| 粉嫩蜜臀av国产精品网站| 亚洲第一久久影院| 久久久精品日韩欧美| 色综合天天在线| 捆绑紧缚一区二区三区视频|