999精品在线视频,手机成人午夜在线视频,久久不卡国产精品无码,中日无码在线观看,成人av手机在线观看,日韩精品亚洲一区中文字幕,亚洲av无码人妻,四虎国产在线观看 ?

嵌入式系統基于動態命令的軟件局部更新技術

2023-02-17 01:54:22王宜懷許粲昊
計算機應用與軟件 2023年1期
關鍵詞:功能

劉 強 王宜懷 許粲昊

(蘇州大學計算機科學與技術學院 江蘇 蘇州 215000)

0 引 言

嵌入式系統的開發與應用已經不再滿足于局限在微控制器(Microcontroller Unit,MCU)中的固定功能和固定運行流程,而寄希望于通過串口等通信方式臨時調整MCU的功能與流程。一種普遍的解決方案是在MCU的中斷處理程序中,根據串口接收的不同命令字,轉向多個不同功能的程序分支。但這些程序分支本質上仍然是預先駐留在微控制器中無法改動的功能,而并非新功能。隨著需求不斷增多,為了利用有限的硬件資源實現更多的內容,軟件開放性設計的需求也越來越明顯[1]。并且每當有新的功能需求時,除了要對軟件進行擴充,還必須要對軟件進行整體更新,特別是在遠程更新時,由于通信的不穩定,往往會要求更多的備份存儲空間和更高的傳輸帶寬,Steger等[2]的工作中強調了更新時的指定區域和指定網絡就是為了在高數據量的情況下保證穩定性。此外傳統的“停機—更新軟件版本—重啟”的應用軟件更新方法勢必會影響系統的可用性[3]。現有的嵌入式設備的遠程更新模式仍然是以“BootLoader+應用程序”為主,并由BootLoader對應用程序進行復位和整體更新[4]。應用程序在運行時的更新仍然是軟件工程中一個具有挑戰性的問題[5]。

王婷等[1]通過采用“APP1(帶BootLoader)+APP2”的方式,提出了一種軟件重構技術,實現了在不影響APP1正常運行的情況下更新APP2。但這本質上并不是軟件的局部更新,APP1與APP2都有各自的中斷向量表,其本質還是整體更新,兩者的共性并沒有得到充分利用,且與Boyer等[6]的工作一樣,在更新后系統仍然需要重啟,并沒有很好地解決問題。謝國珍等[7]和Hayden等[8]都提出了一種面向C語言的動態更新技術,但謝國珍等把更多的精力放在了整體程序版本的一致性上,并且比單個函數更小的更新單元也是不必要的;而Hayden等同樣為了面向具有大量狀態的系統做了相當多的措施,不適用于嵌入式系統。Chen等[9]提出了一種較為實用的動態軟件更新框架,但框架的諸多技術因素更多面向大型計算機考慮,針對堆棧、函數活動級別等因素的魯棒性處理在嵌入式場景下可能反而會使得整體的框架占據更多的空間。總結發現,目前對于軟件工程動態更新的研究更多是致力于軟件整體的重構和模組化,如Mahdi等[10]和Varghese等[11]的研究,而忽略了嵌入式資源受限的場景下對單一功能添加的需求。

因此,針對嵌入式系統的特殊性,同時受到Tao等[12]提出的動態配置智能算法DC-IA的啟發,本文提出一種基于動態命令的軟件局部更新技術。動態命令(Dynamic Command,DC)的提出和使用,可以實現在不影響MCU正常運行的前提下,將臨時的一段代碼動態地注入MCU并運行,這一段臨時的具有一定功能的代碼就稱為動態命令,多個動態命令同時使用同一塊存儲空間。這樣使得MCU從自身固有代碼的功能中解放出來,可以運行更加豐富的動態命令,并且由于動態命令的數量基本不受存儲空間限制,且代碼注入技術占用的內存小[13],空間資源的問題也得到了解決。這樣,當嵌入式系統中需要增加一個新的功能,或是現有的軟件需要做部分的調整,都可以通過動態命令實現軟件的局部更新,數據傳輸時的存儲空間和帶寬問題也隨之解決。并且,本文提出的軟件局部更新技術可以更新系統中固有的軟件,極大程度上保證了系統長期的自我維護能力。

動態命令的實現主要存在三個要解決的問題。(1) 動態命令的正確性問題,動態命令的執行不能夠使MCU出現致命錯誤而影響MCU固有功能,即動態命令的正確性如何保證;(2) 動態命令獲取的問題,動態命令如何產生并被正確獲取;(3) 動態命令執行的問題,獲取到的動態命令如何、何時被MCU執行。

1 動態命令技術的基本思想

MCU所執行的功能取決于其自身存儲空間內所存放的機器碼,這些機器碼是高級語言經由編譯器生成,再經過寫入工具寫入至MCU中的。動態命令技術的實質就是MCU可以通過串口等通信方式接收非自身最初所存儲的機器碼并執行。為了實現這一目的,需要對MCU的系統工程Porigin做一定程度的改造。

首先,為了存儲和運行動態命令,在MCU內需要預設一個空函數Fdc(可以包含若干個子函數,以下統稱為Fdc),并開辟一塊新的FLASH空間Sdc用于存儲這個函數。Fdc是后續動態命令的載體,Sdc為動態命令的存儲空間,此外還需預留調用Fdc和維護Sdc的相關代碼,這樣,得到新的MCU系統工程Pnew,將其編譯后的機器碼Hnew寫入MCU并開始運行。其次,為了獲取動態命令,需要利用MCU系統工程Pnew的副本Pcopy,在Pcopy的Fdc中編寫期望MCU臨時執行的功能,編譯工程Pcopy后得到工程的機器碼Hcopy,從中篩選出Fdc對應的機器碼Hdc,Hdc即為要獲取的動態命令。最后,為了向被寫入了Hnew的MCU發送動態命令(即Hdc),以串行通信這一通信方式為例,借助編寫好的PC機軟件,向MCU的串口發送Hdc,并在希望執行這一動態命令時,發送相關的調用Fdc的命令字(已在Pnew中預留)。

這樣,即便MCU最初并未存儲Hdc這段機器碼,也可以在這樣的機制下獲取Hdc并運行,如圖1所示。

圖1 動態命令體系

2 技術細節

接下來從正確性、獲取和執行三個方面闡述動態命令的技術細節。

2.1 動態命令的正確性

為了減少動態命令Hdc的傳輸和存儲成本,也為了動態命令函數代碼Fdc的編寫更加方便,Fdc允許使用工程Pcopy內所有原有構件的對外函數、全局變量和宏定義,但由此也帶來了動態命令的正確性問題。動態命令的正確性問題主要有兩個方面。

1) 調用的正確性。調用的正確性是指動態命令函數代碼Fdc調用工程Pcopy和Fdc自身定義的內容時,Fdc經由編譯器轉化為Hdc注入工程Pnew后仍然能夠正確的調用Pnew中對應于Pcopy的內容和Fdc自身定義的內容。

函數和變量的調用主要有絕對地址調用和相對地址調用兩種方式。絕對地址調用的方式對調用者的地址沒有要求,因此不存在正確性問題。Fdc對自身定義的內容采用相對調用,而相對地址不因Fdc的存儲地址而改變,因而Fdc對自身定義的內容的相對調用不存在正確性問題。為了滿足Fdc中涉及相對地址的調用在Pnew和Pcopy中保持一致,Pcopy必須為Pnew的副本。這樣,在Fdc未動態注入Pnew前,因為在Pnew和Pcopy中預留給Fdc存儲空間的起始地址和空間大小相同,Pnew和Pcopy中除Fdc以外的內容將完全一致,且Hnew和Hcopy中除Hdc以外的內容也將完全一致。這樣在Fdc動態注入Pnew后,Pnew和Pcopy中的所有內容將完全一致,相對調用將不會存在問題。

可以得出一個結論:Pnew和Pcopy中除Fdc以外的內容保持一致,且Hnew和Hcopy中除Hdc以外的內容也保持一致,這是保證調用正確性的重要前提。以下簡稱差異的唯一性。

但此外,還存在一些特殊情況需要給出使用限制。

(1) 靜態內聯函數的使用。內聯函數是用來建議編譯器對一些特殊函數進行內聯擴展的。內聯擴展是指編譯器會將指定的函數體插入并取代每一處調用該函數的地方,從而節省了每次調用函數帶來的額外時間開支(保護現場等)。因此,內聯函數在本質上是一種用空間換取時間的程序優化方案。

在嵌入式系統的編程中,由于空間資源的稀缺,芯片的內核頭文件中一般不使用內聯函數,而都是使用靜態內聯函數,關鍵字一般為“_ _STATIC_INLINE”。這是因為,如果不加以靜態約束,則表示該函數有可能會被其他編譯單元所調用,所以一定會產生函數本身的代碼;以靜態約束后,一般可以使得最終生成的機器碼文件變小,這是因為多數的編譯器會將未使用過的靜態內聯函數直接從機器碼文件中刪除。

此外,在嵌入式系統使用的多數編譯器中,靜態內聯函數不是面向單個調用語句一一展開的,而是對于一個源文件的所有調用語句,會將內聯函數的代碼放置在一塊空間內,該源文件所有靜態內聯函數的調用,都使用指向這塊空間的同一絕對地址Asi。這個Asi一般在工程主程序代碼的前部,因此,在某個源文件第一次調用內聯函數時,這個內聯函數的調用語句會使得對應的Asi后的所有程序代碼(相比較于未調用內聯函數時的程序代碼)向后偏移一段位置。這個偏移的長度取決于被調用的內聯函數的大小。

接下來討論靜態內聯函數在動態命令中的使用帶來的問題。在工程Pcopy的函數Fdc中加入調用一個靜態內聯函數的語句,Pcopy生成的機器碼Hcopy與Pnew生成的機器碼Hnew顯然會存在差異。但由于靜態內聯函數的使用,會使得Hnew和Hcopy中除Hdc以外的內容也存在差異,不再滿足差異的唯一性,且這些額外差異是函數Fdc正確運行所需要的環境。這樣,當Hdc動態注入工程Pcopy(即Hcopy)時,函數Fdc將無法正確運行,產生難以預計的錯誤。

為了兼顧動態命令的正確性與豐富性,針對靜態內聯函數的使用給出了一個方案:將動態命令所有可能會使用到的靜態內聯函數,在創建Pnew時就新建一個構件,將所有靜態內聯函數加上一層函數封裝,動態命令在使用靜態內聯函數時,調用對應的函數封裝即可。由于這一層函數封裝的存在,被封裝的靜態內聯函數和該封裝函數在動態命令調用該靜態內聯函數之前,就已經存在于最終的機器碼Hnew中了。因此,不論Fdc調用靜態內聯函數與否,Pcopy最終生成的機器碼Hcopy與Hnew的差異都存在且僅存在于Hdc段,滿足了差異的唯一性。

(2) 變量和常量的使用。函數Fdc中定義的局部變量和常量,與工程內的所有其他局部變量和常量一樣,使用同一個主堆棧指針,因此在Fdc編寫結束后,只要工程Pnew可以正確編譯出Hnew,就無須考慮普通局部變量和常量空間溢出的問題,在Hdc注入Hcopy后,也是可以正常運行的。然而對于靜態變量和特殊的常量,需要給出使用限制。

靜態變量是以關鍵字static修飾的、定義時賦初值的量,其初值被存儲在程序空間的.data段(位于FLASH),一般由工程內啟動文件中的系統復位函數在MCU系統時鐘初始化后,從FLASH中被拷貝至RAM。因此雖然靜態變量實際使用的地址在RAM中,但本質上的初值來源于FLASH,如果在函數Fdc中定義靜態變量,差異的唯一性將被破壞,因為額外的FLASH空間也被修改,因而在函數Fdc中不可以使用靜態變量。

此外,一些特殊的常量也不允許直接使用。主要有以下兩種情況:① 對長度超過1的數組,初始化時賦初值;② 調用函數時,以字符串常量傳參。這兩種情況下的初值和字符串常量,一般都是存儲在程序空間中的.rodata段(Read only data),同樣位于FLASH,程序對于這些變量直接使用其FLASH的地址。所以和靜態變量一樣,這兩種情況中的常量無法使用于函數Fdc,因為差異的唯一性也將被破壞。

對于這些常量的使用,有一種替代的解決方案。對長度超過1的數組,避免初始化時賦初值,改為在初始化后逐個元素賦初值。對于傳參時使用的字符串常量,也改為以數組名傳參,數組的賦值方式同上。

(3) 子程調用。函數Fdc可以包含若干個子函數,為保證差異的一致性,每一個子函數都要存儲在與函數Fdc相同的存儲空間。且由于動態命令的運行是從空間Sdc的首地址開始的,所以主函數Fdc必須為第一個定義的函數,其余子函數只能在其前部進行聲明,在其后進行定義。

至此,調用的正確性得到了保證。

2) 固有程序運行的正確性。當Fdc涉及對Pnew固有內容的更改時,Pcopy不因對應內容的更改而影響其固有的運行流程,即固有程序的正確運行不因動態命令的執行而受影響。

動態命令可以通過直接操縱FLASH來修改MCU中的固有代碼,以此來實現對部分程序的更新等功能。這個場景下涉及的機器碼不再局限在動態命令自身,而是整個工程的所有代碼,需要綜合考慮存儲空間和調用的正確性問題。由于這種工程層次上的修改涉及各個方面,很難做到以一套規定來規避所有可能的問題,因此固有程序運行的正確性主要依賴于測試。即在Fdc動態注入正式使用的MCU前,在測試板上寫入Pnew工程,在Fdc動態注入測試板的Pnew后,觀察現象,多次測試無誤后,再投入正式版中使用。

2.2 動態命令獲取的問題

動態命令的獲取主要分為兩個方面。(1) 如何從Pnew的機器碼文件Hnew中獲取動態命令Fdc對應的機器碼Hdc;(2) 運行工程Pcopy的MCU如何獲取機器碼Hdc。

(1)Pnew中動態命令的獲取。要從Pnew的機器碼文件Hnew中獲取Hdc,本質上是要從機器碼文件中提取指定程序段的機器碼。機器碼文件的每一行都有著相應的地址信息,這樣對于已知地址的程序,提取機器碼就十分簡單了。這樣問題就轉化為了如何確定指定程序的實際地址。一些編譯器在通過設置之后可以產生工程的lst文件,lst文件給出了工程內幾乎所有的程序代碼及其對應的地址和匯編代碼等內容。這樣就得到了一種普適的方案:在lst文件中查找指定的程序對應的起始地址,根據得到的起始地址在機器碼文件中找到對應的機器碼片段。

又由上文可知,為了保證差異的一致性,在程序中為動態命令開辟了固定地址和大小的一段空間,這個操作可由鏈接文件實現,在定義函數Fdc及其子函數時,只需利用關鍵字_ _attribute_ _((section()))和開辟的空間名,即可將指定的函數放置在這塊開辟的空間中。由此,固定了動態命令程序代碼存儲的地址,即為這塊空間的起始地址。這樣只需要在機器碼文件中查找這一固定地址空間的機器碼,就可以實現從機器碼文件Hnew中直接獲取Hdc了。

(2)Pcopy中動態命令的獲取。要想使得MCU獲取一段新的機器碼,必然要通過一定的通信方式,本文采用串行通信將MCU與PC機的程序相連。這樣在MCU的串行中斷處理函數中,增加接收和寫入動態命令機器碼的程序代碼,PC機的上位機程序就可以將準備好的動態命令機器碼發送至MCU,并由MCU接收和寫入在Pcopy中Fdc對應的FLASH空間Sdc。至此Pcopy便成功獲取了動態命令。

2.3 動態命令執行的問題

動態命令執行的問題也包含兩個方面,(1) 動態命令何時被執行;(2) 動態命令如何被執行。

對于動態命令何時被執行,本文采用的方式是由上位機發送執行動態命令的指令。這是為了兼顧一些需要在指定時間運行動態命令的情況,這樣即便有立即運行動態命令的需求,也只需要在發送完動態命令后,緊跟執行動態命令的指令即可。

有了前文的鋪墊,對于運行著Pcopy的MCU,在接收并寫入了上位機發來的動態命令后,即便Pcopy中原本的函數Fdc是一個空函數,但在Hdc被MCU寫入Sdc后,函數Fdc對應的FLASH空間已經被動態命令取代而不再是空函數。此時為了執行動態命令,只需在MCU的串行中斷處理程序中,加入接收執行動態命令指令的程序代碼并加入對函數Fdc的調用即可。

3 基于動態命令的軟件局部更新技術

基于動態命令的軟件局部更新技術實質上是三種不同動態命令的應用,分別是用于添加功能的動態命令、修改固有軟件的動態命令、綜合的動態命令。

3.1 用于添加功能的動態命令

在現有的嵌入式系統有新的功能需求時,本文的軟件局部更新技術所使用的動態命令就用于功能添加,即動態命令對應的函數即為新功能需求的函數。這一類應用較為初級,遵循前文中給出的規則即可實現。需要注意的是,這種情況下的新功能需求不能夠改動現有程序。

3.2 修改固有軟件的動態命令

在固有程序的軟件有某(幾)個函數需要修改時,動態命令便使用于軟件修改。這種情況的主要思想是在動態命令中借助FLASH在線編程技術來實現對目標函數的擦除和寫入,而目標函數的機器碼由動態命令中開辟的數組存儲,因而要求修改后的函數總大小必須小于動態命令的存儲空間。主要方法如下。

首先在工程Pcopy中對目標函數進行修改并編譯,比較修改前后的函數大小。若函數大小不變,則直接提取機器碼交由動態命令進行相關處理;若修改后的目標函數變小,則在Pcopy修改后的目標函數末尾補足NOP語句,直至與原函數一致,提取后交由動態命令處理;若修改后的目標函數變大,在動態命令函數后新建一個函數(共同使用動態命令的空間),將修改后的函數體復制至新函數,新函數要與目標函數的傳參保持一致,目標函數清空后改為調用動態命令中的新函數,并在目標函數的末尾補足NOP語句,直至與原函數大小一致,再次編譯后提取目標函數的機器碼交由動態命令處理,此時的動態命令包含了動態命令函數和一個新函數,目標函數的真正實現是由這個新函數來完成的。

特別地,由于動態命令具有修改固有軟件的能力,這也表明了本文提出的局部更新技術可以更新其自身的啟動代碼。能否更新固有的啟動代碼,標志著一個動態更新系統是否有著長期的自我維護能力[5]。

3.3 綜合的動態命令

特別地,在嵌入式系統有新的功能需求且這個需求需要修改固有軟件時,只需結合前兩種情況的處理方式即可。這里以新的功能需求需要借助新的中斷服務例程為例。

首先在動態命令空間中,位于動態命令函數后要新建一個函數用于編寫需要的中斷服務例程。其次,將動態命令分為兩部分(這個劃分可以通過不同的傳參來實現),第一部分是對系統的中斷向量表的改寫和中斷的初始化,第二部分是具體的新功能。其中對系統中斷向量表的改寫即為將對應的中斷指針指向動態命令中的中斷服務例程。這樣,在動態命令發送完畢后,只需以對應的傳參調用動態命令來修改中斷向量表并初始化,然后切換參數調用新功能即可正常地觸發新中斷了。

4 實驗對比分析

為了說明軟件局部更新技術的優勢,以下進行兩項對比。

4.1 軟件功能擴展的能力對比

將軟件局部更新技術與傳統的增加程序分支并整體更新的技術進行對比。將十段不同功能的程序代碼通過兩種技術分別加入相同的工程并執行,并由此對比兩者的優劣。

本文以TI公司MSP432P401R(簡稱MSP432)作為硬件平臺,硬件編程語言選用C語言,編譯器選用GNU V7.2.1;PC機軟件選用C#語言;PC機軟件與MCU的通信方式選用串行通信。MSP432基于ARM Cortex-M4F內核,最大運行速率48 MHz,Flash大小為256 KB,RAM空間為64 KB[14]。

首先對傳統方法進行實驗。通過編譯生成的map文件可知,在這十段代碼分支加入串行中斷處理函數前后,串行中斷處理函數EUSCIA0_IRQHandler的占用空間增加了3 264 B。

接下來通過軟件局部更新技術將這十段代碼依次注入工程。再將這十段程序依次填充函數Fdc(工程中命名為dynamic_command),并獲取了它們對應的機器碼Hdc1-Hdc10,其中最長的機器碼占據空間為242 B。因此,可以在鏈接文件中將動態命令空間Sdc的大小調整為400 B,以此減少不必要的開銷。

至此,動態命令的程序代碼所產生的存儲空間的開銷為400 B,但這不是軟件局部更新技術帶來的全部空間開銷,還要將接收、寫入和執行動態命令的代碼(以下簡稱動態命令固有程序)產生的存儲空間的開銷也計算入。這部分程序開銷可計算出為1 020 B。因此采用軟件局部更新技術將這十段不同功能的程序代碼注入工程所帶來的總開銷為1 420 B。可以發現這一開銷遠小于傳統的方式,如表1所示。

表1 不同方案下的Flash空間開銷

因此,對于同等數量和大小的擴展功能,采用軟件局部更新帶來的存儲空間的開支更小。并且,對用同樣大小的存儲空間,采用軟件局部更新技術可擴展的功能理論上來說是不受限制的,僅需滿足各個功能的大小都可以被這同一塊存儲空間接收即可。此外,不同于整體更新技術,軟件局部更新技術的每一次功能擴展僅帶來單個功能存儲空間大小的數據傳輸,且整個系統無須重啟復位即可立即調用這一擴展功能。

4.2 綜合的技術特點對比

接下來將軟件局部更新技術與整體更新、增量更新、王婷等的軟件重構、郭宗芝等[15]使用的VxWorks操作系統和Chen等的動態軟件更新框架做對比。

與整體更新相比,局部更新無須重啟,且對數據傳輸和存儲空間的需求都較低,更新時間更短,且擁有自我維護的能力。而與增量更新相比,除了增量更新要求重新設計整體框架來優化每一次程序對比以外,程序的一致性對比也會帶來額外的數據傳輸和更新時間。王婷等的軟件重構技術與整體更新類似,更新單位較大因此對存儲空間和數據傳輸的要求也較高,更新時間也較長,且更新后需要重啟。Chen等的動態軟件更新,框架和VxWorks操作系統的缺陷則在于為了實現動態更新,其自身的框架較為復雜,不適用于資源受限的嵌入式系統。

5 結 語

針對嵌入式系統開發對功能靈活性日益增加的需求與嵌入式系統存儲空間受限的矛盾,本文提出一種基于動態命令的軟件局部更新技術,可以在存儲空間受限的情況下極大程度地滿足嵌入式系統開發的靈活性。針對動態命令的正確性問題,對靜態內聯函數、靜態變量、常量、子函數和固有程序給出了詳細的說明和限制,并且闡述了動態命令獲取和執行的方式。通過實驗對比分析可知,在程序靈活性需求越高的場合,軟件局部更新技術的表現將越為出色。

猜你喜歡
功能
拆解復雜功能
鐘表(2023年5期)2023-10-27 04:20:44
也談詩的“功能”
中華詩詞(2022年6期)2022-12-31 06:41:24
基層弄虛作假的“新功能取向”
當代陜西(2021年21期)2022-01-19 02:00:26
深刻理解功能關系
鉗把功能創新實踐應用
關于非首都功能疏解的幾點思考
基于PMC窗口功能實現設備同步刷刀功能
懷孕了,凝血功能怎么變?
媽媽寶寶(2017年2期)2017-02-21 01:21:24
“簡直”和“幾乎”的表達功能
中西醫結合治療甲狀腺功能亢進癥31例
主站蜘蛛池模板: 国产成人久视频免费| 日本道中文字幕久久一区| 91精品国产自产91精品资源| 久久精品无码中文字幕| 熟妇丰满人妻| 亚洲人成在线免费观看| 亚洲手机在线| 日韩毛片基地| 亚洲Av综合日韩精品久久久| 在线观看国产黄色| 亚洲第一成年免费网站| 综合人妻久久一区二区精品 | 国产成人免费手机在线观看视频| 18禁高潮出水呻吟娇喘蜜芽| www.91中文字幕| 丰满少妇αⅴ无码区| 国产高清免费午夜在线视频| 日韩A∨精品日韩精品无码| 四虎永久免费在线| 久久综合激情网| 国产网友愉拍精品| 欧美.成人.综合在线| 欧美一区二区三区欧美日韩亚洲 | 久久综合色播五月男人的天堂| 在线欧美日韩| 國產尤物AV尤物在線觀看| 色综合激情网| 欧美精品高清| 尤物特级无码毛片免费| 久久精品欧美一区二区| 亚洲综合婷婷激情| 99久久人妻精品免费二区| 国产日本欧美亚洲精品视| 免费在线成人网| 国产精品综合久久久| 69av免费视频| 亚洲综合一区国产精品| 精品久久久久久久久久久| 国产福利免费视频| 亚洲欧洲综合| 无码专区国产精品一区| 国产欧美另类| 亚洲嫩模喷白浆| 亚洲男人的天堂视频| 国产精品女熟高潮视频| 色偷偷综合网| 欧美午夜视频| 国产打屁股免费区网站| 成人精品区| 天天色综合4| 秋霞一区二区三区| 亚洲无线国产观看| 91精品专区国产盗摄| 99这里只有精品免费视频| 77777亚洲午夜久久多人| 亚洲人在线| 日韩专区第一页| 亚洲AⅤ综合在线欧美一区| 免费观看精品视频999| 无码中字出轨中文人妻中文中| 日本午夜视频在线观看| 免费中文字幕在在线不卡| 国产成人资源| 久久精品中文字幕免费| 日本人又色又爽的视频| 国产精彩视频在线观看| 欧美在线视频a| 麻豆国产在线不卡一区二区| 日韩精品亚洲精品第一页| 午夜福利视频一区| 国产高清在线丝袜精品一区| 88av在线| 日本91视频| 国产在线观看一区二区三区| 一级毛片免费高清视频| 美女视频黄频a免费高清不卡| 亚洲成a人片| 欧美另类图片视频无弹跳第一页| 在线精品欧美日韩| 国产欧美又粗又猛又爽老| 欧美天天干| 日本黄色a视频|