色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux內核內存管理之內核非連續物理內存分配

jf_0tjVfeJz ? 來源:嵌入式ARM和Linux ? 2024-02-23 09:44 ? 次閱讀

1 非連續內存區的線性地址

2 非連續內存區的描述符

3 申請非連續物理內存區

4 釋放非連續內存區

5 vmalloc和kmalloc

我們已經知道,最好將虛擬地址映射到連續頁幀,從而更好地利用緩存并實現更低的平均內存訪問時間。然而,如果對內存區域的請求并不頻繁,那么考慮基于通過連續線性地址訪問非連續頁幀的分配方案是有意義的。該模式的主要優點是避免了外部碎片,而缺點是需要修改內核頁表。顯然,非連續內存區域的大小必須是4096的倍數。Linux使用非連續物理內存區的場景有幾種:(1)為swap區分配數據結構;(2)為模塊分配空間(參見附錄B);(3)或者為一些I/O驅動程序分配緩沖區。此外,非連續物理內存區還提供了另一種利用高端內存的方法。

1 非連續內存區的線性地址

要查找線性地址的空閑范圍,我們可以從PAGE_OFFSET開始的區域(通常是0xc0000000,3G→4G)。下圖展示了這1G的線性地址的使用方式:

這1G大小的線性地址的第一部分是映射前896M物理內存的線性地址;與直接映射的物理內存的結尾對應的線性地址存儲在high_memory變量中。

這1G大小的線性地址的最后部分是固定映射的線性地址。

從PKMAP_BASE線性地址開始,是高端內存頁幀的永久內核映射使用的線性地址空間。

余下的線性地址空間用作非連續內存區域的映射。在前896M的線性地址之后與第一個非連續內存空間插入一個安全樁(大小為8M的地址間隔,使用宏VMALLOC_OFFSET獲取該值),以便捕獲越界內存訪問。基于這個目的,后面每個非連續內存區域之間都插入一個4K大小的地址間隔。

c1082398-d172-11ee-a297-92fbcf53809c.png

圖8-8 內核地址空間的布局

VMALLOC_START宏定義了為非連續內存區保留的線性空間的起始地址,而VMALLOC_END定義了它的結束地址。

2 非連續內存區的描述符

每個非連續內存區都有一個類型為vm_struct的描述符進行表達,各個成員如下所示:

表8-13 vm_struct各個成員的描述

類型 名稱 描述
void * addr 該區域的第一個存儲單元的線性地址
unsigned long size 該區域的大小+4096(內存區域間的安全間隔)
unsigned long flags 映射的內存類型
struct page ** pages 指向頁描述符的nr_pages指針數組
unsigned int nr_pages 該區域填充的頁數
unsigned long phys_addr 設置為0,除非是創建的內存區用來映射硬件設備的I/O共享內存
struct vm_struct* next 指向下一個vm_struct結構

這些描述符通過“next”字段插入到一個簡單的列表中;列表中第一個元素的地址存儲在vmlist變量中。通過“vmlist_lock”讀/寫自旋鎖保護對該列表的訪問。flags字段標識該內存區映射的內存類型:VM_ALLOC用于通過vmalloc()獲得的頁面,VM_MAP用于通過vmap()映射的已經分配的頁面(參見下一節),VM_IOREMAP用于通過ioremap()映射的硬件設備的板載內存(參見第13章)。

get_vm_area()負責在VMALLOC_START和VMALLOC_END之間找一段空閑的連續線性地址。這個函數作用于兩個參數:size,要創建的內存區的字節數;flag,指定要創建的內存類型。執行以下步驟:

structvm_struct*__get_vm_area(unsignedlongsize,unsignedlongflags,
unsignedlongstart,unsignedlongend)
{
structvm_struct**p,*tmp,*area;
unsignedlongalign=1;
unsignedlongaddr;

//...省略
addr=ALIGN(start,align);

/*1.創建一個內核slab通用對象,
*保存vm_struct(一段虛擬內存的描述符)的內容
*/
area=kmalloc(sizeof(*area),GFP_KERNEL);
if(unlikely(!area))
returnNULL;

/*
*2.創建一個保護頁(4K大小的間隔)
*/
size+=PAGE_SIZE;
if(unlikely(!size)){
kfree(area);
returnNULL;
}

/*3.獲取用于寫入的vmlist_lock鎖,
*并掃描類型為vm_struct的描述符列表(也就是vmlist),
*查找至少包含size+4096的線性地址空間
*(4k是內存區域之間的安全間隔的大小)。
*/
write_lock(&vmlist_lock);
for(p=&vmlist;(tmp=*p)!=NULL;p=&tmp->next){
if((unsignedlong)tmp->addraddr+tmp->size>=addr)
addr=ALIGN(tmp->size+
(unsignedlong)tmp->addr,align);
continue;
}
if((size+addr)addr)
gotofound;
addr=ALIGN(tmp->size+(unsignedlong)tmp->addr,align);
if(addr>end-size)
gotoout;
}

found:
/*4.如果找到合適的一段線性地址空間,
*則初始化申請的描述符并釋放鎖,
*然后返回描述符的地址。
*/
area->next=*p;
*p=area;

area->flags=flags;
area->addr=(void*)addr;
area->size=size;
area->pages=NULL;
area->nr_pages=0;
area->phys_addr=0;
write_unlock(&vmlist_lock);

returnarea;

out:
/*5.釋放獲得的描述符,并釋放鎖,并返回NULL*/
write_unlock(&vmlist_lock);
kfree(area);
returnNULL;
}

3 申請非連續物理內存區

vmalloc()函數為內核分配了一個非連續物理內存。參數size表示請求內存的大小。如果函數能夠滿足請求,則返回新區域的初始線性地址;否則,它返回一個NULL指針:

void*vmalloc(unsignedlongsize)
{
structvm_struct*area;
structpage**pages;
unsignedintarray_size,i;

//將size按照4k對齊
size=(size+PAGE_SIZE-1)&PAGE_MASK;
//創建新的頁描述符并返回對應的線性地址
//標志是VM_ALLOC,表示非連續物理頁幀將被映射到一段線性地址空間
area=get_vm_area(size,VM_ALLOC);
if(!area)
returnNULL;

area->nr_pages=size>>PAGE_SHIFT;
array_size=(area->nr_pages*sizeof(structpage*));
//申請一個數組的物理內存對象,保存頁描述符指針數組
area->pages=pages=kmalloc(array_size,GFP_KERNEL);
if(!area_pages){
remove_vm_area(area->addr);
kfree(area);
returnNULL;
}
//將指針數組的元素清零。
memset(area->pages,0,array_size);
/*根據需要的內存頁數,重復調用alloc_page函數
*給每一個頁分配一個頁幀,并將相應的頁描述符存入數組中。
*注意,這兒使用數據保存頁描述符是非常有必要的,
*因為這些頁幀屬于高端內存,它們不用映射為線性地址。
*/
for(i=0;inr_pages;i++){
area->pages[i]=alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
if(!area->pages[i]){
area->nr_pages=i;
fail:vfree(area->addr);
returnNULL;
}
}
/*到這兒,我們已經獲得了一段線性地址空間;
*也獲得了一組非連續的物理頁幀。那么關鍵的一步就是,
*修改頁表項,將每個分配的頁幀與一個線性地址建立映射關系
*實現的函數就是map_vm_area
*/
if(map_vm_area(area,__pgprot(0x63),&pages))
gotofail;
returnarea->addr;
}

將線性地址與非連續物理頁幀建立映射關系由map_vm_area()函數實現,使用3個參數:

area:指向該內存區域的vm_struct描述符的指針。

prot:已分配頁幀的保護位。總是設為0x63,對應于Present、Accessed、Read/Write和Dirty

pages: 指向頁描述符指針數組的變量的地址(因此,struct page ***用作數據類型!)。

具體代碼如下所示:

intmap_vm_area(structvm_struct*area,pgprot_tprot,structpage***pages)
{
/*1.獲取線性地址的起始位置、結束位置*/
unsignedlongaddress=(unsignedlong)area->addr;
unsignedlongend=address+(area->size-PAGE_SIZE);
unsignedlongnext;
pgd_t*pgd;
interr=0;
inti;

/*2.使用pgd_offset_k宏在主內核PGD頁全局目錄中
*導出與該區域的初始線性地址相關的表項
*/
pgd=pgd_offset_k(address);

/*3.申請內核頁表自旋鎖*/
spin_lock(&init_mm.page_table_lock);
for(i=pgd_index(address);i<=?pgd_index(end-1);?i++)?{

????????/*?4.?為新內存分配PUD頁表中間目錄,
?????????*????并將其正確的物理地址寫入PGD目錄中
?????????*/
????????pud_t?*pud?=?pud_alloc(&init_mm,?pgd,?address);
????????if?(!pud)?{
????????????err?=?-ENOMEM;
????????????break;
????????}

????????/*?5.?分配與新PUD目錄關聯的所有頁表。
?????????*????map_area_pud將單個PUD所跨越的線性地址范圍的大小
?????????*????(如果啟用了PAE,則為常數2^30,否則為2^22)加到當前的
?????????*????address值上,并增加指向PGD的指針pgd。
?????????*????重復這個循環,直到所有指向非連續內存區的頁表項都設置好。
?????????*/
????????next?=?(address?+?PGDIR_SIZE)?&?PGDIR_MASK;
????????if?(next?end)
next=end;
if(map_area_pud(pud,address,next,prot,pages)){
err=-ENOMEM;
break;
}

address=next;
pgd++;
}

spin_unlock(&init_mm.page_table_lock);
flush_cache_vmap((unsignedlong)area->addr,end);
returnerr;
}

map_area_pud()對PUD指向的所有頁表也執行相似的循環:

do{
pmd_t*pmd=pmd_alloc(&init_mm,pud,address);
if(!pmd)
return-ENOMEM;
if(map_area_pmd(pmd,address,end-address,prot,pages))
return-ENOMEM;
address=(address+PUD_SIZE)&PUD_MASK;
pud++;
}while(address

map_area_pmd()對PMD指向的所有頁表也執行相似的循環:

do{
pte_t*pte=pte_alloc_kernel(&init_mm,pmd,address);
if(!pte)
return-ENOMEM;
if(map_area_pte(pte,address,end-address,prot,pages))
return-ENOMEM;
address=(address+PMD_SIZE)&PMD_MASK;
pmd++;
}while(address

pte_alloc_kernel()函數分配一個新頁表,并更新PMD頁中間目錄的對應表項。接下來,調用map_area_pte()為新頁表中的每一項分配物理頁幀。變量address的值增加2^22(正好是一個PMD頁表中一項跨越的線性地址范圍。

map_area_pte()主要工作是:

do{
structpage*page=**pages;
set_pte(pte,mk_pte(page,prot));
address+=PAGE_SIZE;
pte++;
(*pages)++;
}while(address

要映射的頁幀的頁描述符地址page從地址pages的變量所指向的數組項中讀取。新頁幀的物理地址通過set_pte和mk_pte宏寫入頁表。在為地址添加常數4096(頁幀的長度)后,重復這個循環。

注意,map_vm_area()還沒有修改當前進程的頁表。因此,當內核態的進程訪問非連續內存區域時,就會發生Page Fault,因為進程頁表中沒有該區域的映射關系。然而,Page Fault處理程序根據主內核頁表(即init_mm.pgdPGD頁全局目錄及其子頁表)檢查錯誤的線性地址;一旦處理器發現一個主內核頁表包含一個非空的地址項,它就把它的值復制到相應進程的頁表項中,然后恢復進程的正常執行。該機制在第9章的“頁面錯誤異常處理程序”一節中進行了描述。

除了vmalloc()之外,非連續內存區的分配還可以由vmalloc_32()完成。它與vmalloc()類似,但是只分配ZONE_NORMAL和ZONE_DMA內存區。

Linux v2.6還有一個vmap()函數,它映射已經在非連續內存區中分配的頁幀:本質上,這個函數接收一個指向頁描述符的指針數組作為它的參數,調用get_vm_area()來獲得一個新的vm_struct描述符,然后調用map_vm_area()來映射頁幀。因此,該函數類似于vmalloc(),但它不分配頁幀。

所以說,vmalloc和vmap的操作,大部分的邏輯是一樣的,比如從VMALLOC_START ~ VMALLOC_END非連續物理內存映射區之間查找并分配vmap_area。不同之處,在于vmap建立映射時,page是函數傳入進來的,而vmalloc是通過調用alloc_page接口向Buddy系統申請分配的。

4 釋放非連續內存區

vfree()函數釋放由vmalloc()或vmalloc_32()創建的非連續內存區域,而vunmap()函數釋放由vmap()創建的內存區域。兩個函數都有一個參數-待釋放區域的初始線性地址的地址;它們都依賴于__vunmap()函數來完成實際的工作。

__vunmap()函數接收兩個參數:要釋放的區域的初始線性地址的地址addr和標志deallocate_pages,如果在該區域中映射的頁幀應該被釋放到ZONE頁幀分配器(vfree()調用),則設置該標志,否則將被清除(vunmap()調用)。該函數的主要功能如下:

void__vunmap(void*addr,intdeallocate_pages)
{
//...省略
/*1.獲取vm_struct描述符的地址;
*解除非連續物理內存與線性地址在頁表中的映射關系。
*/
area=remove_vm_area(addr);
if(unlikely(!area)){
//...省略
return;
}

if(deallocate_pages){
inti;

/*
*2.掃描頁描述符指針數據;對數組每個元素調用__free_page(),
*將頁幀釋放回`ZONE`頁幀分配器中。
*/
for(i=0;inr_pages;i++){
//...省略
__free_page(area->pages[i]);
}

if(area->nr_pages>PAGE_SIZE/sizeof(structpage*))
vfree(area->pages);
else
/*釋放指針數組,因為它是從連續物理內存中申請的,
*所以調用kfree
*/
kfree(area->pages);
}

/*3.釋放vm_struct描述符*/
kfree(area);
return;
}

remove_vm_area()執行下面的循環:

structvm_struct*remove_vm_area(void*addr)
{
structvm_struct**p,*tmp;

//申請鎖
write_lock(&vmlist_lock);
/*搜索從addr開始的內核虛擬內存區域,
*找到要釋放的線性地址區域area
*/
for(p=&vmlist;(tmp=*p)!=NULL;p=&tmp->next){
if(tmp->addr==addr)
gotofound;
}
write_unlock(&vmlist_lock);
returnNULL;

found:
/*釋放area*/
unmap_vm_area(tmp);
*p=tmp->next;
write_unlock(&vmlist_lock);
returntmp;
}
write_lock(&vmlist_lock);
for(p=&vmlist;(tmp=*p);p=&tmp->next){
if(tmp->addr==addr){
unmap_vm_area(tmp);
*p=tmp->next;
break;
}
}
write_unlock(&vmlist_lock);
returntmp;

map_vm_area()函數的內容如下所示,執行與map_vm_area()函數相逆的過程:

address=area->addr;
end=address+area->size;
pgd=pgd_offset_k(address);

for(i=pgd_index(address);i<=?pgd_index(end-1);?i++)?{
????next?=?(address?+?PGDIR_SIZE)?&?PGDIR_MASK;
????if?(next?<=?address?||?next?>end)
next=end;
unmap_area_pud(pgd,address,next-address);
address=next;
pgd++;
}

繼而,unmap_area_pud()執行與map_area_pud()相逆的過程:

do{
unmap_area_pmd(pud,address,end-address);
address=(address+PUD_SIZE)&PUD_MASK;
pud++;
}while(address&&(address

unmap_area_pmd()執行與map_area_pmd()相逆的過程:

do{
unmap_area_pte(pmd,address,end-address);
address=(address+PMD_SIZE)&PMD_MASK;
pmd++;
}while(address

最后,unmap_area_pte()執行與map_area_pte()相逆的過程:

do{
pte_tpage=ptep_get_and_clear(pte);
address+=PAGE_SIZE;
pte++;
if(!pte_none(page)&&!pte_present(page))
printk("Whee...Swappedoutpageinkernelpagetable
");
}while(address

在循環的每次迭代中,pte指向的頁表項被ptep_get_and_clear宏設置為0。

至于vmalloc(),內核修改主內核頁全局目錄及其子頁表的項(參見第2章的“內核頁表”一節),但它保持進程頁表映射第4G的項不變。這很好,因為內核永遠不會回收基于主內核頁全局目錄的頁上目錄(PUD)、頁中間目錄(PMD)和頁表。

例如,假設內核態進程訪問了一個非連續內存區域,該內存區域隨后被釋放。進程的PGD項等于主內核PGD的相應項,這要歸功于第9章“Page Fault異常處理程序”一節中解釋的機制;它們指向相同的頁上目錄、頁中間目錄和頁表。unmap_area_pte()函數只清除頁表的項(不回收頁表本身)。由于頁表項為空,進程對釋放的非連續內存區域的進一步訪問將觸發Page fault。然而,處理程序會認為這樣的訪問是一個錯誤,因為主內核頁表不包括有效的項。

5 vmalloc和kmalloc

到現在,我們應該能清楚vmalloc和kmalloc的差異了吧,kmalloc會根據申請的大小來選擇基于slab分配器或者基于buddy系統來申請連續的物理內存。而vmalloc則是通過alloc_page申請order = 0的頁面,再映射到連續的虛擬空間中,物理地址不連續。此外vmalloc可以休眠,不應在中斷處理程序中使用。與vmalloc相比,kmalloc使用ZONE_DMA和ZONE_NORMAL空間,性能更快,缺點是連續物理內存空間的分配容易帶來碎片問題,讓碎片的管理變得困難。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 內核
    +關注

    關注

    3

    文章

    1372

    瀏覽量

    40277
  • Linux
    +關注

    關注

    87

    文章

    11292

    瀏覽量

    209328
  • 內存管理
    +關注

    關注

    0

    文章

    168

    瀏覽量

    14134

原文標題:Linux內核8.8-內存管理之內核非連續物理內存分配

文章出處:【微信號:嵌入式ARM和Linux,微信公眾號:嵌入式ARM和Linux】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Linux內存管理是什么,Linux內存管理詳解

    Linux內存管理 Linux內存管理是一個非常復雜的過程,主要分成兩個大的部分:
    的頭像 發表于 05-11 17:54 ?6039次閱讀
    <b class='flag-5'>Linux</b>的<b class='flag-5'>內存</b><b class='flag-5'>管理</b>是什么,<b class='flag-5'>Linux</b>的<b class='flag-5'>內存</b><b class='flag-5'>管理</b>詳解

    Linux內核之內存映射原理分析

    Linux 內核采用延遲分配物理內存的策略,在進程第一次訪問虛擬頁的時候,產生缺頁異常。如果是文件映射,那么
    發表于 07-21 17:06 ?2353次閱讀

    Linux內核物理內存組織結構詳解

    Linux內存管理子系統使用 節點(node)、區域(zone)和頁(page) 三級結構描述物理內存
    發表于 08-21 15:35 ?566次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>的<b class='flag-5'>物理</b><b class='flag-5'>內存</b>組織結構詳解

    Linux內核內存管理詳解

    內存管理的主要工作就是對物理內存進行組織,然后對物理內存
    發表于 08-31 14:46 ?779次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>的<b class='flag-5'>內存</b><b class='flag-5'>管理</b>詳解

    Linux內核內存規整總結

    分配需求,如下圖所示: 內存外部碎片導致實際占用物理頁不多,但是已無法申請>=4個頁連續內存,理想當中我們希望
    的頭像 發表于 11-11 11:17 ?1313次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b><b class='flag-5'>內存</b>規整總結

    Linux內核內存管理架構解析

    內存管理子系統可能是linux內核中最為復雜的一個子系統,其支持的功能需求眾多,如頁面映射、頁面分配、頁面回收、頁面交換、冷熱頁面、緊急頁面
    的頭像 發表于 01-04 09:24 ?653次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b><b class='flag-5'>內存</b><b class='flag-5'>管理</b>架構解析

    Linux內核內存管理之ZONE內存分配

    內核中使用ZONE分配器滿足內存分配請求。該分配器必須具有足夠的空閑頁幀,以便滿足各種內存大小請
    的頭像 發表于 02-21 09:29 ?890次閱讀

    Linux內存系統: Linux 內存分配算法

    的大小· 它們的物理地址是連續的· 頁塊大小相同4、如何分配 4M 以上內存?1) 為何限制大塊內存分配
    發表于 08-24 07:44

    Linux虛擬內存物理內存的深刻分析

    的對象。vmalloc機制vmalloc機制使得內核通過連續的線性地址來訪問連續物理頁框,這樣可以最大限度的使用高端
    發表于 05-31 08:00

    LINUX內核中的內存是如何進行分配

    size);size:待分配內存的大小,自動按頁對齊。默認在動態內存映射區分配分配內存在內核
    發表于 11-04 14:46

    LINUX源代碼分析-內存管理

    操作系統管理系統所有的物理空間, 現代大多數操作系統都采取多級管理, 即頁面級分配內核內存
    發表于 12-19 16:38 ?102次下載
    <b class='flag-5'>LINUX</b>源代碼分析-<b class='flag-5'>內存</b><b class='flag-5'>管理</b>

    內核內存分配常用函數使用

    在 VMALLOC_START~4GB之間,這段連續內存區映射到物理內存也可能是非連續的在
    發表于 04-02 14:32 ?994次閱讀

    鴻蒙內核源碼分析: 虛擬內存物理內存是怎么管理

    到哪是屬于什么段。這些值大小取決實際項目內存條的大小,不同的內存條,地址肯定會不一樣,所以必須由外部提供,鴻蒙內核采用了Linux的段管理
    發表于 11-23 11:45 ?19次下載
    鴻蒙<b class='flag-5'>內核</b>源碼分析: 虛擬<b class='flag-5'>內存</b>和<b class='flag-5'>物理</b><b class='flag-5'>內存</b>是怎么<b class='flag-5'>管理</b>的

    Linux內核深度解析》之內存地址空間

    內核空間提供了把頁劃分成小內存分配的塊分配器,提供分配內存的接口 kmalloc()和釋放
    的頭像 發表于 07-15 14:22 ?2303次閱讀

    Linux內核引導內存分配器的原理

    Linux內核引導內存分配器使用的是伙伴系統算法。這種算法是一種用于動態內存分配的高效算法,它將
    發表于 04-03 14:52 ?405次閱讀
    主站蜘蛛池模板: 国产婷婷午夜精品无码A片| 云南14学生真实初次破初视频| 强开少妇嫩苞又嫩又紧九色| 久久精品热99看二| 国产香蕉尹人视频在线| 东京热 百度影音| jk制服喷水| 99久久精品久久久| 99久久精品国产交换| 最新精品国产| 4399日本电影完整版在线观看免费 | 被同桌摸出水来了好爽的视频| 18禁无遮挡羞羞污污污污免费| 日本枯瘦娇小| 琪琪色在线播放| 全彩黄漫火影忍者纲手无遮挡| 欧美精品AV一区二区无码| 嫩草影院在线观看网站成人| 久久91精品国产91久久户| 日本在线免费| 人曽交Z00Z0OA片| 全彩无翼污之邪恶女教师| 青青伊人久久| 色色色999| 亚洲av欧美在我| 老师的丝袜脚| 就操成人网| 嫩小性性性xxxxbbbb| 欧美日韩北条麻妃一区二区| 青草久久精品亚洲综合专区| 入禽太深免费观看| 午夜理伦大片一级| 日韩av国产av欧美天堂社区| 青草影院天堂男人久久| 色琪琪丁香婷婷综合久久| 午夜免费小视频| 一本道高清码| a级毛片黄免费a级毛片| 一区二区三区无码被窝影院| 2020国产成人精品视频人| 不卡一区二区高清观看视频|