亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C > 正文

解析Linux內核的基本的模塊管理與時間管理操作

2020-01-26 14:43:01
字體:
來源:轉載
供稿:網友

內核模塊管理
Linux設備驅動會以內核模塊的形式出現,因此學會編寫Linux內核模塊編程是學習linux設備驅動的先決條件。

Linux內核的整體結構非常龐大,其包含的組件非常多。我們把需要的功能都編譯到linux內核,以模塊方式擴展內核功能。

先來看下最簡單的內核模塊

#include <linux/init.h> #include <linux/module.h>   static int __init hello_init(void) {     printk(KERN_ALERT "Hello world! %s, %d/n", __FILE__, __LINE__);     return 0; }  static void __exit hello_exit(void) {      printk(KERN_ALERT "Hello world! %s, %d/n", __FILE__, __LINE__); }  module_init(hello_init); module_exit(hello_exit);  MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mikcy Liu"); MODULE_DESCRIPTION("A simple Module"); MODULE_ALIAS("a simple module"); 

頭文件init.h包含了宏_init和_exit,它們允許釋放內核占用的內存。
module_init()和hello_exit()是模塊編程中最基本也是必須的兩個函數。
module_init()是驅動程序初始化的入口點。
hello_exit是模塊的退出和清理函數。此處可以做所有終止該驅動程序時相關的清理工作。

內核模塊中用于輸出的函數式內核空間的printk()而非用戶空間的printf(),printk()的用法和printf()相似,但前者可定義輸出級別。printk()可作為一種最基本的內核調試手段

前者可以定義輸出級別,在 <內核目錄>/include/linux/kernel.h中

#define KERN_EMERG   "<0>"  /* system is unusable          */ #define KERN_ALERT   "<1>"  /* action must be taken immediately   */ #define KERN_CRIT    "<2>"  /* critical conditions         */ #define KERN_ERR    "<3>"  /* error conditions           */ #define KERN_WARNING  "<4>"  /* warning conditions          */ #define KERN_NOTICE   "<5>"  /* normal but significant condition   */ #define KERN_INFO    "<6>"  /* informational            */ #define KERN_DEBUG   "<7>"  /* debug-level messages         */ 

未設定級別的,在<內核目錄>/kernel/printk.c中定義

/* printk's without a loglevel use this.. */ #define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */  #define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */ 

只有當printk打印信息時的loglevel小于DEFAULT_CONSOLE_LOGLEVEL的值(優先級高于console loglevel),這些信息才會被打印到console上。

模塊聲明與描述

  •    在linux模塊中,我們可以使用
  •    MODULE_LICENSE(license)   //定義模塊的license,一般為GPL,或相關公司的license
  •    MODULE_AUTHOR             //模塊的作者
  •    MODULE_DESCRIPTION        //對模塊程序的描述,string
  •    MODULE_VERSION            //版本
  •    MODULE_DEVICE_TABLE       //模塊程序所支持的設備,string
  •    MODULE_ALIAS              //別名
  •    MODULE_PARM(var,type)     //模塊參數

模塊編譯
首先看看Makefile文件:

obj-m := hello.o KERNEL_BUILD := /lib/modules/$(shell uname -r)/build all:     make -C $(KERNEL_BUILD) M=$(shell pwd) modules clean:     -rm -rf *.o *.ko *.mod.c .*.cmd *.order *.symvers .tmpversions KERNELBUILD :=/lib/modules/$(shell uname -r)/

build是編譯內核模塊需要的Makefile的路徑,Ubuntu下是/lib/modules/2.6.31-14-generic/build

如果是Arm平臺的開發板,則-C選項指定的位置(即內核源代碼目錄),其中保存有內核的頂層Makefile文件.

make -C $(KERNEL_BUILD) M=$(shell pwd) modules 編譯內核模塊。-C 將工作目錄轉到KERNEL_BUILD,調用該目錄下的Makefile,并向這個Makefile傳遞參數M的值是$(shell pwd) modules。

M=選項讓該makefile在構造modules目標之前返回到模塊源代碼目錄。然后modules目標指向obj-m變量中設定的模塊

執行make命令開始編譯模塊,生成hello.ko,執行make clean可清除編譯產生的文件。

1、添加模塊

   insmod hello.ko

2、查看模塊

  lsmod | grep hello

   lsmod命令實際上讀取并分析/proc/modules文件,也可以cat /proc/modules文件

   在模塊所在目錄下執行

    modinfo  hello.ko可以查看模塊信息,如下所示

filename:    hello.koalias:     a simple moduledescription:  A simple Moduleauthor:     Mikcy Liulicense:    GPLsrcversion:   875C95631F4F336BBD4216Cdepends:    vermagic:    3.5.0-17-generic SMP mod_unload modversions 686

3、刪除模塊

 rmmod hello


模塊加載函數

Linux內核模塊加載函數一般以__init標識聲明,典型的模塊加載函數的形式如下:

static int __init initialization_function(void) {    //初始化代碼  }  module_init(initialization_function); 

    模塊加載函數必須以“module_init(函數名)”的形式指定。它返回整形值,若初始化成功,應返回0。而在初始化失敗時。應該返回錯誤編碼。

    在linux內核里,錯誤編碼是一個負值,在<linux/errno.h>中定義,包含-ENODEV、-ENOMEM之類的符號值。返回相應的錯誤編碼是種非常好的習慣,因為只有這樣,用戶程序才可以利用perror等方法把它們轉換成有意義的錯誤信息字符串。

    在linux2.6內核中,所有標識為__init的函數在連接的時候都會放在.init.text(這是module_init宏在目標代碼中增加的一個特殊區段,用于說明內核初始化函數的所在位置)這個區段中,此外,所有的__init函數在區段.initcall.init中還保存著一份函數指針,在初始化時內核會通過這些函數指針調用這些__init函數,并在初始化完成后釋放init區段(包括.init.text和.initcall.init等)。所以大家應注意不要在結束初始化后仍要使用的函數上使用這個標記。

模塊卸載函數

Linux內核卸載模塊函數一般以__exit標識聲明,典型的模塊卸載函數的形式如下:

static void __exit cleanup_function(void) {    //釋放代碼  }  module_exit(cleanup_function); 

模塊卸載函數在模塊卸載時被調用,不返回任何值,必須以”module_exit(函數名)”的形式來指定
與__init一樣__exit也可以使對應函數在運行完成后自動回收內存。

一般來說,模塊卸載函數完成與模塊加載函數相反的功能:
如果模塊加載函數注冊了 XXX模塊,則模塊卸載函數應注銷XXX。
若模塊加載函數動體申請了內存,則模塊卸載函數應釋放該內存。
若模塊加載函數申請了硬件資源,則模塊卸載函數應釋放這些硬件資源。
若模塊加載函數開啟了硬件,則模塊卸載函數應關閉硬件。


內核時間管理
(1)內核中的時間概念
    時間管理在linux內核中占有非常重要的作用。
    相對于事件驅動而言,內核中有大量函數是基于時間驅動的。
    有些函數是周期執行的,比如每10毫秒刷新一次屏幕;
    有些函數是推后一定時間執行的,比如內核在500毫秒后執行某項任務。
    要區分:
    *絕對時間和相對時間
    *周期性產生的事件和推遲執行的事件
    周期性事件是由系統系統定時器驅動的
 
(2)HZ值
    內核必須在硬件定時器的幫助下才能計算和管理時間。
    定時器產生中斷的頻率稱為節拍率(tick rate)。
    在內核中指定了一個變量HZ,內核初始化的時候會根據這個值確定定時器的節拍率。
    HZ定義在<asm/param.h>,在i386平臺上,目前采用的HZ值是1000。
    也就是時鐘中斷每秒發生1000次,周期為1毫秒。即:
    #define HZ 1000
 
    注意!HZ不是個固定不變的值,它是可以更改的,可以在內核源代碼配置的時候輸入。
    不同的體系結構其HZ值是不一樣的,比如arm就采用100。
    如果在驅動中要使用系統的中斷頻率,直接使用HZ,而不要用100或1000
 
 
    a.理想的HZ值
        i386的HZ值一直采用100,直到2.5版后才改為1000。
        提高節拍率意味著時鐘中斷產生的更加頻繁,中斷處理程序也會更頻繁地執行。
 
        帶來的好處有:
        *內核定時器能夠以更高的頻率和更高的準確度運行
        *依賴定時器執行的系統調用,比如poll()和select(),運行的精度更高
        *提高進程搶占的準確度
        (縮短了調度延時,如果進程還剩2ms時間片,在10ms的調度周期下,進程會多運行8ms。
        由于耽誤了搶占,對于一些對時間要求嚴格的任務會產生影響)
 
        壞處有:
        *節拍率要高,系統負擔越重。
        中斷處理程序將占用更多的處理器時間。
 
 (3)jiffies
    全局變量jiffies用于記錄系統啟動以來產生的節拍的總數。
    啟動時,jiffies初始化為0,此后每次時鐘中斷處理程序都會增加該變量的值。
    這樣,系統啟動后的運行時間就是jiffies/HZ秒
 
    jiffies定義于<linux/jiffies.h>中:
    extern unsigned long volatile jiffies;
 
    jiffies變量總是為unsigned long型。
    因此在32位體系結構上是32位,而在64位體系上是64位。
    對于32位的jiffies,如果HZ為1000,49.7天后會溢出。
    雖然溢出的情況不常見,但程序在檢測超時時仍然可能因為回繞而導致錯誤。
    linux提供了4個宏來比較節拍計數,它們能正確地處理節拍計數回繞。 
 
 

  #include <linux/jiffies.h>   #define time_after(unknown, known)    // unknow > known   #define time_before(unknown, known)   // unknow < known   #define time_after_eq(unknown, known)  // unknow >= known   #define time_before_eq(unknown, known)  // unknow <= known  

    unknown通常是指jiffies,known是需要對比的值(常常是一個jiffies加減后計算出的相對值)
 
    例: 

  unsigned long timeout = jiffies + HZ/2; /* 0.5秒后超時 */   ...   if(time_before(jiffies, timeout)){     /* 沒有超時,很好 */   }else{     /* 超時了,發生錯誤 */ 

     
    time_before可以理解為如果在超時(timeout)之前(before)完成
 
 
    *系統中還聲明了一個64位的值jiffies_64,在64位系統中jiffies_64和jiffies是一個值。
    可以通過get_jiffies_64()獲得這個值。
 
    *使用 

  u64 j2;     j2 = get_jiffies_64(); 

 
 (4)獲得當前時間
    驅動程序中一般不需要知道墻鐘時間(也就是年月日的時間)。但驅動可能需要處理絕對時間。
    為此,內核提供了兩個結構體,都定義在<linux/time.h>: 

a.

  struct timeval {    time_t tv_sec; /* seconds */    suseconds_t tv_usec; /* microseconds */   }; 

    較老,但很流行。采用秒和毫秒值,保存了1970年1月1日0點以來的秒數 

b.

  struct timespec {    time_t tv_sec; /* seconds */    long tv_nsec; /* nanoseconds */   }; 

    較新,采用秒和納秒值保存時間。
 
    c.do_gettimeofday()
        該函數用通常的秒或微秒來填充一個指向struct timeval的指針變量,原型如下: 

    #include <linux/time.h>     void do_gettimeofday(struct timeval *tv);  

    d.current_kernel_time()
        該函數可用于獲得timespec 
       

#include <linux/time.h>     struct timespec current_kernel_time(void);


確定時間的延遲執行 
    設備驅動程序經常需要將某些特定代碼延遲一段時間后執行,通常是為了讓硬件能完成某些任務。
    長于定時器周期(也稱為時鐘嘀嗒)的延遲可以通過使用系統時鐘完成,而非常短的延時則通過軟件循環的方式完成
     
 
(1)短延時
    對于那些最多幾十個毫秒的延遲,無法借助系統定時器。
    系統通過軟件循環提供了下面的延遲函數: 

  #include <linux/delay.h>   /* 實際在<asm/delay.h> */   void ndelay(unsigned long nsecs); /*延遲納秒 */   void udelay(unsigned long usecs); /*延遲微秒 */   void mdelay(unsigned long msecs); /*延遲毫秒 */ 

 
    這三個延遲函數均是忙等待函數,在延遲過程中無法運行其他任務。
 
(2)長延時
    a.在延遲到期前讓出處理器 

    while(time_before(jiffies, j1))       schedule(); 

        在等待期間可以讓出處理器,但系統無法進入空閑模式(因為這個進程始終在進行調度),不利于省電。
 
    b.超時函數 

    #include <linux/sched.h>     signed long schedule_timeout(signed long timeout); 

 
    使用方式: 

  set_current_state(TASK_INTERRUPTIBLE);   schedule_timeout(2*HZ); /* 睡2秒 */ 

    進程經過2秒后會被喚醒。如果不希望被用戶空間打斷,可以將進程狀態設置為TASK_UNINTERRUPTIBLE。

#include <linux/init.h> #include <linux/module.h> #include <linux/time.h> #include <linux/sched.h> #include <linux/delay.h>  static int __init test_init(void) {   set_current_state(TASK_INTERRUPTIBLE);   schedule_timeout(5 * HZ);   printk(KERN_INFO "Hello Micky/n");   return 0; }  static void __exit test_exit(void) { }  module_init(test_init); module_exit(test_exit);  MODULE_LICENSE("GPL"); MODULE_AUTHOR("Micky Liu"); MODULE_DESCRIPTION("Test for delay"); 


(3)等待隊列
    使用等待隊列也可以實現長延遲。
    在延遲期間,當前進程在等待隊列中睡眠。
    進程在睡眠時,需要根據所等待的事件鏈接到某一個等待隊列。
 
    a.聲明等待隊列
        等待隊列實際上就是一個進程鏈表,鏈表中包含了等待某個特定事件的所有進程。 

    #include <linux/wait.h>     struct __wait_queue_head {       spinlock_t lock;       struct list_head task_list;     };     typedef struct __wait_queue_head wait_queue_head_t; 

 
        要想把進程加入等待隊列,驅動首先要在模塊中聲明一個等待隊列頭,并將它初始化。
 
        靜態初始化 
     

    DECLARE_WAIT_QUEUE_HEAD(name); 

 
        動態初始化 

      wait_queue_head_t my_queue;       init_waitqueue_head(&my_queue); 

 
    b.等待函數
        進程通過調用下面函數可以在某個等待隊列中休眠固定的時間: 

    #include <linux/wait.h>     long wait_event_timeout(wait_queue_head_t q,condition, long timeout);     long wait_event_interruptible_timeout(wait_queue_head_t q, condition, long timeout);


        調用這兩個函數后,進程會在給定的等待隊列q上休眠,但會在超時(timeout)到期時返回。
        如果超時到期,則返回0,如果進程被其他事件喚醒,則返回剩余的時間數。
        如果沒有等待條件,則將condition設為0
 
        使用方式: 

    wait_queue_head_t wait;     init_waitqueue_head(&wait);     wait_event_interruptible_timeout(wait, 0, 2*HZ);     /*當前進程在等待隊列wait中睡2秒 */ 

 
 
 
(4)內核定時器
    還有一種將任務延遲執行的方法是采用內核定時器。
    與前面幾種延遲方法不同,內核定時器并不會阻塞當前進程,
    啟動一個內核定時器只是聲明了要在未來的某個時刻執行一項任務,當前進程仍然繼續執行。
    不要用定時器完成硬實時任務
 
    定時器由結構timer_list表示,定義在<linux/timer.h> 

  struct timer_list{     struct list_head entry; /* 定時器鏈表 */     unsigned long expires; /* 以jiffies為單位的定時值 */     spinlock_t lock;     void(*function)(unsigned long); /* 定時器處理函數 */     unsigned long data; /* 傳給定時器處理函數的參數 */   } 

 
    內核在<linux/timer.h>中提供了一系列管理定時器的接口。
 
    a.創建定時器 

    struct timer_list my_timer; 

 
    b.初始化定時器 

    init_timer(&my_timer);     /* 填充數據結構 */     my_timer.expires = jiffies + delay;     my_timer.data = 0;     my_timer.function = my_function; /*定時器到期時調用的函數*/ 

 
    c.定時器的執行函數
        超時處理函數的原型如下: 

    void my_timer_function(unsigned long data); 

        可以利用data參數用一個處理函數處理多個定時器??梢詫ata設為0
 
    d.激活定時器 

    add_timer(&my_timer); 

        定時器一旦激活就開始運行。
 
    e.更改已激活的定時器的超時時間 

    mod_timer(&my_timer, jiffies+ney_delay); 

        可以用于那些已經初始化但還沒激活的定時器,
        如果調用時定時器未被激活則返回0,否則返回1。
        一旦mod_timer返回,定時器將被激活。
 
    f.刪除定時器 

    del_timer(&my_timer); 

        被激活或未被激活的定時器都可以使用,如果調用時定時器未被激活則返回0,否則返回1。
        不需要為已經超時的定時器調用,它們被自動刪除
 
    g.同步刪除 

  del_time_sync(&my_timer); 

    在smp系統中,確保返回時,所有的定時器處理函數都退出。不能在中斷上下文使用。 
   

#include <linux/init.h> #include <linux/module.h> #include <linux/time.h> #include <linux/sched.h> #include <linux/delay.h> #include <linux/timer.h>  struct timer_list my_timer;  static void timer_handler(unsigned long arg) {   printk(KERN_INFO "%s %d Hello Micky! arg=%lu/n",__func__, __LINE__, arg ); }  static int __init test_init(void) {   init_timer(&my_timer);    my_timer.expires = jiffies + 5 * HZ;   my_timer.function = timer_handler;   my_timer.data = 10;   add_timer(&my_timer);    return 0; }  static void __exit test_exit(void) {   del_timer(&my_timer); }  module_init(test_init); module_exit(test_exit);  MODULE_LICENSE("GPL"); MODULE_AUTHOR("Micky Liu"); MODULE_DESCRIPTION("Test for timer"); #include <linux/init.h> #include <linux/module.h> #include <linux/time.h> #include <linux/sched.h> #include <linux/delay.h> #include <linux/timer.h>  struct timer_list my_timer;  static void timer_handler(unsigned long arg) {   printk(KERN_INFO "%s %d Hello Micky! arg=%lu/n",__func__, __LINE__, arg ); }  static int __init test_init(void) {   init_timer(&my_timer);    //my_timer.expires = jiffies + 5 * HZ;   my_timer.function = timer_handler;   my_timer.data = 10;    //add_timer(&my_timer);   mod_timer(&my_timer, jiffies + 5 * HZ);    return 0; }  static void __exit test_exit(void) {   del_timer(&my_timer); }  module_init(test_init); module_exit(test_exit);  MODULE_LICENSE("GPL"); MODULE_AUTHOR("Micky Liu"); MODULE_DESCRIPTION("Test for timer"); 

 
不確定時間的延遲執行
(1)什么是不確定時間的延遲
    前面介紹的是確定時間的延遲執行,但在寫驅動的過程中經常遇到這種情況:
    用戶空間程序調用read函數從設備讀數據,但設備中當前沒有產生數據。
    此時,驅動的read函數默認的操作是進入休眠,一直等待到設備中有了數據為止。
 
    這種等待就是不定時的延遲,通常采用休眠機制來實現。
 
 
(2)休眠
    休眠是基于等待隊列實現的,前面我們已經介紹過wait_event系列函數,
    但現在我們將不會有確定的休眠時間。
 
    當進程被置入休眠時,會被標記為特殊狀態并從調度器的運行隊列中移走。
    直到某些事件發生后,如設備接收到數據,則將進程重新設為運行態并進入運行隊列進行調度。
    休眠函數的頭文件是<linux/wait.h>,具體的實現函數在kernel/wait.c中。
 
    a.休眠的規則
        *永遠不要在原子上下文中休眠
        *當被喚醒時,我們無法知道睡眠了多少時間,也不知道醒來后是否獲得了我們需要的資源
        *除非知道有其他進程會在其他地方喚醒我們,否則進程不能休眠
 
    b.等待隊列的初始化
        見前文
 
    c.休眠函數
        linux最簡單的睡眠方式為wait_event宏。該宏在實現休眠的同時,檢查進程等待的條件。
 
        A. 

void wait_event(        wait_queue_head_t q,        int condition);  

        B. 

int wait_event_interruptible(wait_queue_head_t q, int condition); 

            q: 是等待隊列頭,注意是采用值傳遞。
            condition: 任意一個布爾表達式,在條件為真之前,進程會保持休眠。
            注意!進程需要通過喚醒函數才可能被喚醒,此時需要檢測條件。
            如果條件滿足,則被喚醒的進程真正醒來;
            如果條件不滿足,則進程繼續睡眠。
 
 
    d.喚醒函數
        當我們的進程睡眠后,需要由其他的某個執行線程(可能是另一個進程或中斷處理例程)喚醒。
        喚醒函數:
            #include <linux/wait.h>
            1.

 void wake_up(         wait_queue_head_t *queue); 

 
            2. 

void wake_up_interruptible(         wait_queue_head_t *queue); 

 
        wake_up會喚醒等待在給定queue上的所有進程。
        而wake_up_interruptible喚醒那些執行可中斷休眠的進程。
        實踐中,約定做法是在使用wait_event時使用wake_up,而使用wait_event_interruptible時使用wake_up_interruptible。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
中文字幕综合在线| 欧美大片第1页| 96国产粉嫩美女| 亚洲人成电影网站色| 日韩av电影院| 欧美精品xxx| 欧美激情在线狂野欧美精品| 国模叶桐国产精品一区| 亚洲国产精品久久精品怡红院| 欧美日韩精品在线播放| 久久精品国产一区二区电影| 97国产精品久久| 欧美乱妇高清无乱码| 日韩资源在线观看| 色偷偷av一区二区三区| 91精品久久久久久久久久久久久久| 久久久免费av| 欧美中文字幕在线观看| 91视频九色网站| 在线观看国产精品91| 久久精品夜夜夜夜夜久久| 久久久久久香蕉网| 亚洲精品动漫100p| 欧美午夜激情在线| 欧美在线视频观看免费网站| 久久夜精品va视频免费观看| 羞羞色国产精品| 亚洲美女在线视频| 欧美午夜美女看片| 97香蕉久久超级碰碰高清版| 色综合久久中文字幕综合网小说| 亚洲精品国产综合区久久久久久久| 国产精品v片在线观看不卡| 中文字幕日本欧美| 精品一区二区三区电影| 久久免费视频在线观看| 18一19gay欧美视频网站| 色综合视频一区中文字幕| 亚洲精品国产精品国自产在线| 日韩成人av一区| 欧美亚洲成人xxx| 国产精品影片在线观看| 久久乐国产精品| 中文字幕不卡av| 亚洲摸下面视频| 亚洲视频日韩精品| 国产日本欧美一区| 性日韩欧美在线视频| 亚洲福利视频久久| 日韩麻豆第一页| 日韩中文理论片| 欧美色视频日本版| 久久琪琪电影院| 97在线视频免费播放| 国产va免费精品高清在线观看| 国产成人综合一区二区三区| 日韩精品中文字幕久久臀| 国产精品美女久久久久久免费| 欧美成人剧情片在线观看| 日韩欧美视频一区二区三区| 精品偷拍一区二区三区在线看| 亚洲精品suv精品一区二区| 成人精品久久久| 国产午夜精品视频免费不卡69堂| 久久久91精品国产一区不卡| 亚洲视频电影图片偷拍一区| 亚洲欧美国产日韩中文字幕| 8x拔播拔播x8国产精品| 精品国产一区二区三区久久久| 国产日本欧美视频| 亚洲欧美第一页| 欧美性猛交99久久久久99按摩| 蜜月aⅴ免费一区二区三区| 日韩av三级在线观看| 国产精品丝袜白浆摸在线| 国产精品欧美一区二区| 九九热精品视频国产| 欧美福利视频网站| 国产99在线|中文| 日本高清不卡的在线| 国产亚洲人成网站在线观看| 亚洲片国产一区一级在线观看| 91久久精品国产91性色| 亚洲国产一区二区三区四区| 久久久久久久久综合| 久久久久久国产精品久久| 久久精品夜夜夜夜夜久久| 91精品国产高清自在线看超| 日韩视频免费在线观看| 一区二区三区视频观看| 最新国产成人av网站网址麻豆| 欧美孕妇与黑人孕交| 久久亚洲精品国产亚洲老地址| 欧美激情一区二区三区高清视频| 成人免费淫片aa视频免费| 精品一区精品二区| 国产不卡一区二区在线播放| 国产精品入口尤物| 亚洲一区二区国产| 日韩av免费在线看| 国产欧美最新羞羞视频在线观看| 久久成人精品视频| 日韩国产高清污视频在线观看| 亚洲国产日韩欧美在线99| 久久99国产综合精品女同| 国产欧洲精品视频| 亚洲一区免费网站| 在线激情影院一区| 亚洲专区在线视频| 欧美黑人性视频| 久久久久久91香蕉国产| 欧美猛交ⅹxxx乱大交视频| 亚洲精品wwwww| 久久这里只有精品视频首页| 欧美天堂在线观看| 欧美黑人巨大精品一区二区| 欧美做受高潮1| 久久久精品日本| 欧美视频在线观看免费网址| 日韩电影免费观看在线| 亚洲一区二区中文字幕| 一色桃子一区二区| 日韩av免费在线看| 欧美性xxxx极品hd欧美风情| 久久99精品视频一区97| 国产成人亚洲综合91| 米奇精品一区二区三区在线观看| 91性高湖久久久久久久久_久久99| 欧美激情奇米色| 91久久嫩草影院一区二区| 国产精品欧美激情在线播放| 国产一区深夜福利| 91精品免费久久久久久久久| 2025国产精品视频| 97成人精品视频在线观看| 一个人www欧美| 欧美综合国产精品久久丁香| 色婷婷综合久久久久中文字幕1| 日韩电影在线观看中文字幕| 亚洲精品美女在线观看播放| 欧美激情第6页| 国产精品夜色7777狼人| 久久精品免费播放| 国产精品网红福利| 久久影视电视剧免费网站| 午夜精品一区二区三区在线视频| 国产免费一区视频观看免费| 日韩电影第一页| 国产va免费精品高清在线观看| 欧美激情国内偷拍| 成人有码在线视频| 日韩免费av一区二区| 亚洲国产91色在线| 亚洲国产精品女人久久久| 亚洲美女av黄| 精品国产乱码久久久久久婷婷| www.日韩av.com| 欧美激情精品久久久久久大尺度| 欧美高清激情视频| 亚洲视频视频在线| 欧美裸体男粗大视频在线观看| 久久亚洲一区二区三区四区五区高| 日韩有码在线电影| 亚洲aⅴ日韩av电影在线观看|