日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

返璞归真的Linux BFS调度器

發布時間:2025/3/21 linux 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 返璞归真的Linux BFS调度器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
自Linux 2.6以來(嚴格說應該是2.5),O(n)調度器被人們認為是一種千年之前就應該拋棄的東西被重重的甩開了,此后出現了O(1),CFS等,再也沒人提起O(n)了。說實話,Linux的調度器遠比標準Unix的來得復雜,因為Linux被用于不同的場合,從手機一直到大型服務器,跨度如此之大就需要兼各種情況,你既要使網絡服務器的吞吐量達到最大,又要使交互體驗更佳,然而有時候吞吐量和延遲卻是魚與熊掌的關系...
??????? O(n)被徹底遺忘,某種程度上反映了人們的思維誤區,那就是“解決問題的方案隨著問題的復雜化而復雜,殊不知方案復雜度的增加很多在于方案本身而不是問題”。走了這么多年,人們已然忘記了一樣東西在剛出現時是什么樣子了,隨著時間的流逝,任何事物都會面目全非,于是人們“站在巨人的肩上”使其越來越復雜,最終崩潰...在路上,或者在崩潰前夕,如果能停下來尋找一下本源,返璞歸真的東西也許更好,Con Kolivas研發了BFS調度器,正是循著這樣的思路進行的,于是他為自己的調度器起了一個十分具有諷刺意義的名字:Brain Fuck ...

0.小手段

計算機操作系統不是神,它們沒有靈魂,然而作為一款通用操作系統,很多設計者都希望其兼顧所有的情況,既要滿足桌面交互的低延遲需求,又要滿足大吞吐量,同時又要支持N多個處理器,因此就需要做負載均衡...作為一個通用的調度器的設計,為了照顧這么多種情況,你不得不引入一些小巧的手段來專門照顧這些另類的需求。

1.調度器回顧

O(n)調度器

Linux的初始版本使用的是O(n)調度器,它采用單一鏈表,用遍歷比較的方式,采用冒泡算法進行pick-next,下課的原因在于:
a.O(n)中的n嚇倒了很多人,大家都不喜歡這個n,因為它的時間復雜度會隨著進程數量的增加而增加
b.單一的隊列,進程等待時間可能過長,造成饑餓
c.多處理器的出現,為了避免頻繁鎖定
d.它太簡單了?玩弄高深的人不喜歡簡單的東西??

O(1)調度器

2.6內核采用了O(1)調度器,該調度器引入了每CPU的優先級隊列組,每組隊列包含active和expire兩個隊列,采用啟發式算法動態調整優先級,盡可能的進行時間片補償和懲罰等動態計算,多CPU之間的優先級隊列負載均衡。下課的原因有兩點:1.啟發式算法,補償/懲罰機制過于復雜,導致算法本身消耗巨大;2.有了更好的CFS調度器。

類“多級反饋隊列”調度器

此調度器只以patch的形式存在,在并入mainline之前即被CFS取代。采用類似4.4BSD的傳統UNIX調度算法,其優點在于免饑餓以及大吞吐量,這也正是UNIX的特點,缺點在于延遲不穩定,仍然需要一些“小手段”來“玷污”良好的設計。

CFS調度器

該調度器于2.6.23被并入,旨在消除一切的“小手段”,采用完全公平的策略進行調度,引入了虛擬時鐘的概念,進程的優先級以及性別(IO?Calc?交互性?)完全映射到虛擬時鐘的速率上,基于虛擬時鐘的補償性調度。理論很美,然而為了支持SMP以及交互進程要求日益提高,仍然引入了“小手段”-睡眠補償,調度域負載均衡等。

接下來的調度器

一定要以一種徹底的方式消除那些“小手段”,“小手段”的引入完全是“兼顧所有情況”這種“魚與熊掌可兼得”的思想造成的。如果我們回顧上述的調度器,就會發現從O(1)開始,基本都是拆東墻補西墻的方式,解決一個問題從而引入另一個問題。照此以往,事情會越來越復雜的。如果嘗試在O(n)調度器的基礎上優化,可能會更好,因為O(n)調度器最純粹。

2.BFS調度器的引入

給出一個圖片

前些天突然在網上看到了下面的圖片

后來發現該圖片是BFS調度器的引子,太具有諷刺意義了。

可配置型調度器的需求

為了避免小手段,那就要徹底拋棄“魚與熊掌可兼得”的思想,采用“一種調度器只適用于一種場景”的新思路。如此我們可以設計多種調度器,在安裝操作系統的時候可以由管理員進行配置,比如我們將其用于桌面,那么就使用“交互調度器”,如果用于路由器,那就使用“大吞吐調度器”...消除了兼顧的要求,調度器設計起來就更佳簡單和純粹了。
??????? 面對需要大吞吐量的網絡操作系統,我們有傳統的UNIX調度器,然而面對日益桌面化的操作系統比如Android手機,我們是否能摒棄那種大而全的調度策略呢?Con Kolivas老大設計出的BFS調度器就是為桌面交互式應用量身打造的。

問題在哪?

Linux 2.6內核實現了那么多的調度器,然而其效果總是有美中不足的地方,到底問題出在哪里?事實上,Linux 2.6的各種調度器的實現都不是完全按照理論完成的,其中都添加了一些小手段。比如雖然CFS號稱支持大于2048的CPU個數,然而實際應用中,效果未必好,因為CFS調度器繼承了O(1)調度器的load_balance特性,因此在那么多處理器之間進行基于調度域的load_balance,鎖定以及獨占的代價將會十分大,從而抵消了每CPU隊列帶來的消除鎖定的優勢。
??????? 總之,這些調度器太復雜了,而且越來越復雜,將80%的精力消耗在了20%的場景中。實際上,做設計不要聯想,完全依照我們目前所知道的和所遇到的來,在可用性和效率上被證明是明智的,當然不考慮太多的可擴展性。

回到O(n)調度器

BFS調度器用一句話來總結就是“回到了O(n)調度器”,它在O(n)調度器的基礎上進行了優化,而沒有引入看起來很好的O(1)調度器,這就是其實質。O(n)調度器有什么不好么?有的。大不了就是遍歷的時間太長,BFS根據實際的測試數據忽略之;每個處理器都要鎖定整個隊列,BFS改之,做到這些既可,這才叫基于O(n)調度器的優化而不是徹底顛覆O(n)調度器而引入O(1)調度器-當然前提是桌面環境。如果說能回到原始的O(n)調度器進行修改使之重新發揮其作用而不是徹底拋棄它,這才是最佳的做法,反之,如果我們把問題的解決方案搞的越來越復雜,最終就是陷入一個泥潭而不可自拔。要知道方案復雜性的積累是一個笛卡兒積式的積累,你必須考慮到每一種排列組合才能,當你做不到這一點的時候,你就需要返璞歸真。

BFS調度器的原理

BFS的原理十分簡單,其實質正是使用了O(1)調度器中的位圖的概念,所有進程被安排到103個queue中,各個進程不是按照優先級而是按照優先級區間被排列到各自所在的區間,每一個區間擁有一個queue,如下圖所示:

內核在pick-next的時候,按照O(1)調度器的方式首先查找位圖中不為0的那個queue,然后在該queue中執行O(n)查找,查找到virtual deadline(如下所述)最小的那個進程投入執行。過程很簡單,就像流水一樣。之所以規劃103個隊列而不是一個完全是為了進程按照其性質而分類,這個和每CPU沒有任何關系,將進程按照其性質(RT?優先級?)分類而不是按照CPU分類是明智之舉。內核中只有一個“103隊列”,m個CPU和“103隊列”完全是一個“消費者-生產者”的關系。O(1)調度器,內核中擁有m(CPU個數)個“消費者-生產者”的關系,每一個CPU附帶一個“生產者(140隊列組)”。
????????只有統一的,單一的“消費者-生產者”的關系才能做到調度的公平,避免了多個關系之間踢皮球現象,這是事實。在結構單一,功能確定且硬件簡單的系統中,正確的調度器架構如下圖所示:

在結構單一,功能確定且硬件簡單的系統中,不正確的調度器架構如下圖所示:

BFS調度器初始版本的鏈表的非O(n)遍歷

BFS調度器的發展歷程中也經歷了一個為了優化性能而引入“小手段”的時期,該“小手段”是如此合理,以至于每一個細節都值得品味,現表述如下:
大家都知道,遍歷一個鏈表的時間復雜度是O(n),然而這只是遍歷的開銷,在BFS調度器中,遍歷的目的其實就是pick-next,如果該鏈表某種意義上是預排序的,那么pick-next的開銷可以減少到接近O(1)。BFS如何做到的呢?我們首先看一下virtual deadline的概念
virtual deadline(VD)
VD=jiffies + (prio_ratio * rr_interval)

其中prio_ratio為進程優先級,rr_interval為一個Deadline,表示該進程在最多多久內被調度,鏈表中的每一個entry代表一個進程,都有一個VD與之相關。VD的存在使得entry在鏈表的位置得以預排序,這里的預排序指的是vitrual deadline expire的影響下的預排序,BFS和O(n)的差別就在于這個expire,由于這個expire在,一般都會在遍歷的途中遇到VD expire,進而不需要O(n)。基于VD的O(n)和基于優先級的O(n)是不同的,其區別在于根據上述的計算公式,VD是單調向前的,而優先級幾乎是不怎么變化的,因此基于VD的O(n)調度器某種程度上和基于紅黑樹的CFS是一樣的,VD也正類似于CFS中的虛擬時鐘,只是數據結構不同而已,BFS用鏈表實現,CFS用紅黑樹實現。
??????? 其實,O(n)并沒有那么可怕,特別是在桌面環境中,你倒是有多少進程需要調度呢?理論上O(n)會隨著進程數量的增加而效率降低,然而桌面環境下實際上沒有太多的進程需要被調度,所以采用了BFS而拋棄了諸多小手段的調度器效果會更好些。理論上,CFS或者O(1)可以支持SMP下的諸多進程調度的高效性,然而,桌面環境下,第一,SMP也只是2到4個處理器,進程數也大多不超過1000個,進程在CPU之間蹦來蹦去,很累,何必殺雞用牛刀呢?瓶頸不是雞,而是殺雞的刀,是吧!

pick-next算法

BFS的pick-next算法對于SCHED_ISO進程依照以下的原則進行:
a.依照FIFO原則進行,不再遍歷鏈表
BFS的pick-next算法對于SCHED_NORMAL或者SCHED_IDLEPRIO進程依照以下的原則進行:
a.遍歷運行鏈表,比較每一個entry的VD,找出最小的entry,從鏈表中刪除,投入運行
b.如果發現有entry的VD小于當前的jiffers,則停止遍歷,取出該entry,投入運行--小手段
以上的原則可以總結為“最小最負最優先”原則。作者一席話如下:
BFS has 103 priority queues. 100 of these are dedicated to the static priority
of realtime tasks, and the remaining 3 are, in order of best to worst priority,
SCHED_ISO (isochronous), SCHED_NORMAL, and SCHED_IDLEPRIO (idle priority
scheduling). When a task of these priorities is queued, a bitmap of running
priorities is set showing which of these priorities has tasks waiting for CPU
time. When a CPU is made to reschedule, the lookup for the next task to get
CPU time is performed in the following way:

First the bitmap is checked to see what static priority tasks are queued. If
any realtime priorities are found, the corresponding queue is checked and the
first task listed there is taken (provided CPU affinity is suitable) and lookup
is complete. If the priority corresponds to a SCHED_ISO task, they are also
taken in FIFO order (as they behave like SCHED_RR). If the priority corresponds
to either SCHED_NORMAL or SCHED_IDLEPRIO, then the lookup becomes O(n). At this
stage, every task in the runlist that corresponds to that priority is checked
to see which has the earliest set deadline, and (provided it has suitable CPU
affinity) it is taken off the runqueue and given the CPU. If a task has an
expired deadline, it is taken and the rest of the lookup aborted (as they are
chosen in FIFO order).

Thus, the lookup is O(n) in the worst case only, where n is as described
earlier, as tasks may be chosen before the whole task list is looked over.

使用virtual deadline,類似于CFS的virtual runtime的概念,然而不要紅黑樹,而采用了雙向鏈表來實現,因為紅黑樹的插入效率不如鏈表插入效率,在pick-next算法上雖然紅黑樹占優勢,然而由于VD expire的存在也使得pick-next不再是O(n)了。
??????? BFS初始版本的小手段的意義在于減少O(n)遍歷比較時間復雜度帶來的恐懼。

去除了小手段的BFS調度器

最終將小手段去除是重要的,否則BFS最終還是會陷入類似O(1),CFS等復雜化的泥潭里面不可自拔,因此在后續的patch中,BFS去除了上述的小手段,用統一的O(n)復雜度來pick-next,畢竟前面已經說了O(n)在特定環境下并不是問題的關鍵,該patch在2.6.31.14-bfs318-330test.patch中體現。

隊列外部執行

BFS調度器和CFS是一樣的,都是隊列外執行進程的,這樣可以減少鎖爭用帶來的性能問題。再列出作者的一席話:
BFS has one single lock protecting the process local data of every task in the
global queue. Thus every insertion, removal and modification of task data in the
global runqueue needs to grab the global lock. However, once a task is taken by
a CPU, the CPU has its own local data copy of the running process' accounting
information which only that CPU accesses and modifies (such as during a
timer tick) thus allowing the accounting data to be updated lockless. Once a
CPU has taken a task to run, it removes it from the global queue. Thus the
global queue only ever has, at most,

????(number of tasks requesting cpu time) - (number of logical CPUs) + 1

tasks in the global queue. This value is relevant for the time taken to look up
tasks during scheduling. This will increase if many tasks with CPU affinity set
in their policy to limit which CPUs they're allowed to run on if they outnumber
the number of CPUs. The +1 is because when rescheduling a task, the CPU's
currently running task is put back on the queue. Lookup will be described after
the virtual deadline mechanism is explained.
??????? 在schedule核心函數中,使用return_task來把prev進程重新入隊,在earliest_deadline_task這個pick-next中,使用take_task將選中的next從隊列取出,從而實現隊列外執行。

綜上的結論

從上面的論述,我們絲毫沒有看到有任何的諸如“SMP負載均衡”,“CPU親和力”,“補償”,“懲罰”之類的字眼,是的,這些字眼在BFS中完全不需要,BFS也正是摒棄了這些字眼才獲得成功的,畢竟在一個一般人使用的桌面操作系統中,沒有這么多的套套,大多數人使用的就是一個只有一個到兩個處理器核心的系統,難道有必要搞什么調度域么?難道有必要搞什么NUMA么?需求決定一切,面對大型服務器,有UNIX的機制站在那里,而如果我們想把Linux推廣到每一個掌上設備,那就沒必要復制UNIX的那套了,BFS完全可以完美的搞定一切。小手段的去除,說明BFS調度器的發展方向起碼是正確的。
??????? BFS對SMP的支持如何呢?答案是它僅僅支持少量CPU的SMP體系,別忘了BFS的應用場合。因為在調度過程中需要一個遍歷所有CPU的O(m)復雜度的計算,這就明確告訴人們,別指望BFS使用在擁有4096個CPU的系統上,正如沒人用這種系統看視頻一樣,那樣的話,還是乖乖使用CFS吧。

展示一些代碼

BFS調度器思想很簡單:集中精力做好一件事,適應一種場景,代碼同樣十分簡單,因此即使貼上代碼整個文章也不會顯得過于冗長,你再也看不到諸如load_balance或者for_each_domain之類的東西了,至于CPU cache的親和力智能判斷,如果你非要做,那么就自己調用sched_setaffinity系統調用設置吧,把一個線程或者一組相關的進程設置到一個或者一組共享Cache的CPU上,讓內核這些,在進程不那么多,CPU個數不那么多,沒有NUMA的系統上,真的太累了。

a.進程插入

當有一個進程要插入運行隊列的時候,調度器要遍歷所有的CPU,看一下是否能搶占某個CPU上正在運行的進程,這種策略是十分合理的。BFS調度器將所有的CPU作為一個執行者,其執行的資源就是唯一的運行隊列。如果不能搶占任何進程,那就將喚醒的進程相插入到相應隊列的末尾位置,等待deadline的調度:
static inline void enqueue_task(struct task_struct *p) { if (idleprio_task(p) && !rt_task(p)) { if (idleprio_suitable(p)) p->prio = p->normal_prio; else p->prio = NORMAL_PRIO; } if (iso_task(p) && !rt_task(p)) { if (isoprio_suitable()) p->prio = p->normal_prio; else p->prio = NORMAL_PRIO; } __set_bit(p->prio, grq.prio_bitmap); list_add_tail(&p->run_list, grq.queue + p->prio); sched_info_queued(p); }

b.進程喚醒

BFS省略了復雜的負載均衡機制,因此try_to_wake_up瘦身了很多很多。BFS不做調度域負載均衡,其理由是“在決定將進程投入運行的時候,消耗一點點計算來讓其自選CPU”。全局的看待整個CPU集合才是王道,所謂的負載均衡并不是說每個CPU上排隊待運行進程數量的均衡,而是讓每個CPU都動起來。因此唯一需要均衡的地方就是搶占,如果不能搶占任何CPU上的當前進程,那么就排入到全局的BFS隊列中,這是個全局的隊列而不是每CPU隊列,因此對各個CPU機會是均等的,當CPU需要運行一個進程的時候,讓其自己來這個全局隊列中挑選一個最值得運行的,而在這里,所有的CPU的挑選策略是相同的!
static int try_to_wake_up(struct task_struct *p, unsigned int state) { unsigned long flags; int success = 0; long old_state; struct rq *rq; rq = time_task_grq_lock(p, &flags); old_state = p->state; if (!(old_state & state)) goto out_unlock; if (queued_or_running(p)) goto out_running; activate_task(p, rq); //調用enqueue_task try_preempt(p, rq); //這里有一個O(m)時間復雜度的計算,m為cpu的個數 success = 1; out_running: trace_sched_wakeup(rq, p, success); p->state = TASK_RUNNING; out_unlock: task_grq_unlock(&flags); return success; } 我們沒有看到關于任何關于CPU親和力的“小手段”,在不多于16個CPU的系統上,完全沒有必要考慮CPU親和力帶來的巨大性能影響,如今的處理器都是微封裝的,大多數的桌面處理器都使用共享Cache,因此為了榨取那么一點點“一級緩存命中”所帶來的性能提升而增加如此復雜的針對于CPU親和力的負載均衡代碼,得不償失!

c.進程調度的pick-next

在BFS中,首先要調度的是搶占的進程,如果有進程搶占其它正在運行的進程,那么將它投入運行:
static inline struct task_struct *earliest_deadline_task(struct rq *rq, struct task_struct *idle) { unsigned long long_deadline, shortest_deadline; struct task_struct *edt, *p; unsigned int cpu = rq->cpu; struct list_head *queue; int idx = 0; if (rq->preempt_next) { //rq->preempt_next在try_preempt中設置 if (likely(task_queued(rq->preempt_next) && cpu_isset(cpu, rq->preempt_next->cpus_allowed))) { edt = rq->preempt_next; goto out_take; } } retry: idx = find_next_bit(grq.prio_bitmap, PRIO_LIMIT, idx); queue = &grq.queue[idx]; if (idx < MAX_RT_PRIO) { /* We found rt tasks */ list_for_each_entry(p, queue, run_list) { if (cpu_isset(cpu, p->cpus_allowed)) { edt = p; goto out_take; } } /* More rt tasks, we couldn't take the lower prio ones */ ++idx; goto retry; } /* No rt tasks, find earliest deadline task */ edt = idle; if (unlikely(idx >= PRIO_LIMIT)) { /* All rt tasks but none suitable for this cpu */ goto out; } long_deadline = shortest_deadline = longest_deadline() * 2 + 1; list_for_each_entry(p, queue, run_list) { unsigned long deadline_diff; /* Make sure cpu affinity is ok */ if (!cpu_isset(cpu, p->cpus_allowed)) continue; deadline_diff = p->deadline - jiffies; /* Normalise all old deadlines and cope with jiffy wrap. */ if (deadline_diff > long_deadline) deadline_diff = 0; /* Select the earliest deadline task now */ //簡單的冒泡比較 if (edt == idle || deadline_diff < shortest_deadline) { shortest_deadline = deadline_diff; edt = p; } } if (edt == idle) { if (idx < IDLE_PRIO) { /* Haven't checked for SCHED_IDLEPRIO tasks yet */ idx++; goto retry; } goto out; } out_take: take_task(rq, edt); out: return edt; }

d.時間片用盡

略......

自己的體驗

費了九牛二虎之力,編譯好了帶有BFS的內核(實際上只是在2.6.32內核上打上了一個patch,然后重新編譯內核而已,然而這個工作量不小,你可以試試...),基于Debian的桌面版本,硬件是在垃圾場淘的巨老無比的P4,效果十分可觀,起碼和Mac OS一樣了,由此我再也不把Redhat的桌面看作可有可無的部分了。以前我總是編輯/etc/inittab文件將其啟動到level 3,然后用ssh登錄操作之,現在我也可以直接進level 5了,這就是BFS的效果。順帶說一句,如果你使用很高檔或者比較高檔的硬件,那么這個效果就不明顯了,學子們注意了,別較真兒
??????? 聽說Android使用了該調度器,我還沒有嘗試,因為我沒有預算買設備。

3.更多的寓意

寫這篇文章的目的在于引出一些寓意,而不是介紹BFS調度器本身。該寓意在于,歷史得看問題或許會更好。埋沒在歷史深處的事務并不一定是過時的,它可能是最能反映問題本質的,不要將其置之不顧,時不時地回頭看一眼,有的時候,它會給你更多的思路。

?? ? ?? 所謂的操作系統進程調度就是為了讓所有的CPU都動起來,這是一門藝術而不僅僅是一門技術。你既不能讓CPU空閑,又不能讓某些進程排隊等待時間過長,這是一個CPU利用率和對待進程公平性的游戲。這才是問題的本質,而這個問題的本質在不同的硬件配置以及使用場景下又有不同的表現,因此調度器為了迎合這種差異巨大的不同的表現不可能做成一個統一的,只有具體問題具體分析才能得到良好的表現



?本文轉自 dog250 51CTO博客,原文鏈接:http://blog.51cto.com/dog250/1268976

總結

以上是生活随笔為你收集整理的返璞归真的Linux BFS调度器的全部內容,希望文章能夠幫你解決所遇到的問題。

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

主站蜘蛛池模板: 精品免费国产一区二区三区四区 | 综合激情久久 | 亚洲无在线观看 | 亚洲天堂手机在线观看 | 亚洲不卡免费视频 | 婷婷色图| 国产精品波多野结衣 | 欧美日韩免费在线视频 | 日本特黄网站 | 免费av资源 | 免费的黄色的网站 | 国产精品xxxxxx | ⅹxxxxhd亚洲日本hd老师 | 久久久久久久久久久久久久久久久 | 国产成人在线观看 | 看黄色的网站 | 日本黄色一级视频 | 一区精品二区国产 | 久免费一级suv好看的国产 | 日本三级中文 | 丝袜视频在线 | www黄色在线观看 | 欧美色xxxxx 日本精品一区二区三区四区的功能 | 免费麻豆国产一区二区三区四区 | 99香蕉视频 | 美女脱了裤子让男人捅 | 一卡二卡三卡四卡 | 国产毛片精品国产一区二区三区 | 黑人中文字幕一区二区三区 | 美国美女黄色片 | 亚洲精久| 怡红院国产 | 国产精品亚洲一区二区 | 亚洲黄网站在线观看 | 色999视频 | 久久精品视频在线免费观看 | 欧美福利视频一区二区 | 人妻熟妇又伦精品视频a | 国产精品白浆一区二小说 | 国产高中女学生第一次 | porn亚洲| 欧美视频在线一区 | 夜色综合网 | 中文字幕123| 欧美精品久久久久性色 | 成人免费看视频 | 金鱼妻日剧免费观看完整版全集 | 国产精品伦一区二区三级古装电影 | 国产成人在线视频网站 | 国产精品九九九 | 在线观看日韩国产 | 国产亚洲欧美日韩精品一区二区三区 | 精品无码国产av一区二区三区 | 99ri国产在线| 国产福利视频一区 | 美女隐私无遮挡免费 | 国产视频一区在线播放 | 久久久久久无码精品人妻一区二区 | 国产欧美日 | 日韩成人在线免费视频 | 精品日韩中文字幕 | 韩国av在线播放 | 新国产视频 | 亚洲欧洲日韩av | 亚洲欧美日韩国产 | 亚洲色图25p | 欧美一区二区三区爱爱 | 99精品久久久久久久 | 男女那个视频 | 欧美一级片在线播放 | 777视频在线观看 | 最新毛片网 | 亚洲一区av | 第一毛片 | 精品无码在线视频 | 加勒比精品 | 爽爽影院免费观看 | 精品二三区 | 波多野结衣高清视频 | 99福利影院 | 老司机福利精品 | 亚洲人性生活视频 | 91精产国品一二三区在线观看 | 亚洲精品久久久蜜桃 | 欧洲视频一区二区 | 亚洲三区视频 | 天天爱天天射 | 欧美性动态图 | 日本特黄一级大片 | 一级片大全 | 久久午夜场 | 日韩精品视频在线 | 国产精品77777 | 日日干天天射 | 国产精品国产精品国产专区不卡 | 西西44rtwww国产精品 | 午夜一级免费 | 奇米影视在线视频 | 久草视频免费看 |