日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

Linux 阻塞和非阻塞IO 实验

發(fā)布時(shí)間:2023/12/10 linux 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux 阻塞和非阻塞IO 实验 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

  • 阻塞和非阻塞IO
    • 阻塞和非阻塞簡(jiǎn)介
    • 等待隊(duì)列
    • 輪詢
    • Linux 驅(qū)動(dòng)下的poll 操作函數(shù)
  • 阻塞IO 實(shí)驗(yàn)
    • 硬件原理圖分析
    • 實(shí)驗(yàn)程序編寫
    • 運(yùn)行測(cè)試
  • 非阻塞IO 實(shí)驗(yàn)
    • 硬件原理圖分析
    • 實(shí)驗(yàn)程序編寫
    • 運(yùn)行測(cè)試

阻塞和非阻塞IO 是Linux 驅(qū)動(dòng)開發(fā)里面很常見的兩種設(shè)備訪問模式,在編寫驅(qū)動(dòng)的時(shí)候一定要考慮到阻塞和非阻塞。本章我們就來學(xué)習(xí)一下阻塞和非阻塞IO,以及如何在驅(qū)動(dòng)程序中處理阻塞與非阻塞,如何在驅(qū)動(dòng)程序使用等待隊(duì)列和poll 機(jī)制。

阻塞和非阻塞IO

阻塞和非阻塞簡(jiǎn)介

這里的“IO”并不是我們學(xué)習(xí)STM32 或者其他單片機(jī)的時(shí)候所說的“GPIO”(也就是引腳)。這里的IO 指的是Input/Output,也就是輸入/輸出,是應(yīng)用程序?qū)︱?qū)動(dòng)設(shè)備的輸入/輸出操作。

阻塞:當(dāng)應(yīng)用程序?qū)υO(shè)備驅(qū)動(dòng)進(jìn)行操作的時(shí)候,如果不能獲取到設(shè)備資源,那么阻塞式IO 就會(huì)將應(yīng)用程序?qū)?yīng)的線程掛起,直到設(shè)備資源可以獲取為止。

線程的掛起操作實(shí)質(zhì)上就是線程進(jìn)入"非可執(zhí)行"狀態(tài)下,在這個(gè)狀態(tài)下CPU不會(huì)分給線程時(shí)間片,進(jìn)入這個(gè)狀態(tài)可以用來暫停一個(gè)線程的運(yùn)行。線程掛起后,可以通過重新喚醒線程來使之恢復(fù)運(yùn)行。cpu分配的線程片非常的短、同時(shí)也非常珍貴。避免資源的浪費(fèi)。

非阻塞:對(duì)于非阻塞IO,應(yīng)用程序?qū)?yīng)的線程不會(huì)掛起,它要么一直輪詢等待,直到設(shè)備資源可以使用,要么就直接放棄。阻塞式IO 如圖52.1.1.1所示:

圖52.1.1.1 中應(yīng)用程序調(diào)用read 函數(shù)從設(shè)備中讀取數(shù)據(jù),當(dāng)設(shè)備不可用或數(shù)據(jù)未準(zhǔn)備好的時(shí)候就會(huì)進(jìn)入到休眠態(tài)。等設(shè)備可用的時(shí)候就會(huì)從休眠態(tài)喚醒,然后從設(shè)備中讀取數(shù)據(jù)返回給應(yīng)用程序。非阻塞IO 如圖52.1.2 所示:

從圖52.1.1.2 可以看出,應(yīng)用程序使用非阻塞訪問方式從設(shè)備讀取數(shù)據(jù),當(dāng)設(shè)備不可用或數(shù)據(jù)未準(zhǔn)備好的時(shí)候會(huì)立即向內(nèi)核返回一個(gè)錯(cuò)誤碼,表示數(shù)據(jù)讀取失敗。應(yīng)用程序會(huì)再次重新讀取數(shù)據(jù),這樣一直往復(fù)循環(huán),直到數(shù)據(jù)讀取成功。

應(yīng)用程序可以使用如下所示示例代碼來實(shí)現(xiàn)阻塞訪問:

1 int fd; 2 int data = 0; 3 4 fd = open("/dev/xxx_dev", O_RDWR); /* 阻塞方式打開*/ 5 ret = read(fd, &data, sizeof(data)); /* 讀取數(shù)據(jù)*/

從示例代碼52.1.1.1 可以看出,對(duì)于設(shè)備驅(qū)動(dòng)文件的默認(rèn)讀取方式就是阻塞式的,所以我們前面所有的例程測(cè)試APP 都是采用阻塞IO。
如果應(yīng)用程序要采用非阻塞的方式來訪問驅(qū)動(dòng)設(shè)備文件,可以使用如下所示代碼:

1 int fd; 2 int data = 0; 3 4 fd = open("/dev/xxx_dev", O_RDWR | O_NONBLOCK); /* 非阻塞方式打開*/ 5 ret = read(fd, &data, sizeof(data)); /* 讀取數(shù)據(jù)*/

第4 行使用open 函數(shù)打開“/dev/xxx_dev”設(shè)備文件的時(shí)候添加了參數(shù)“O_NONBLOCK”,表示以非阻塞方式打開設(shè)備,這樣從設(shè)備中讀取數(shù)據(jù)的時(shí)候就是非阻塞方式的了。

等待隊(duì)列

1、等待隊(duì)列頭

阻塞訪問最大的好處就是當(dāng)設(shè)備文件不可操作的時(shí)候進(jìn)程可以進(jìn)入休眠態(tài),這樣可以將CPU 資源讓出來。但是,當(dāng)設(shè)備文件可以操作的時(shí)候就必須喚醒進(jìn)程,一般在中斷函數(shù)里面完成喚醒工作。Linux 內(nèi)核提供了等待隊(duì)列(wait queue)來實(shí)現(xiàn)阻塞進(jìn)程的喚醒工作,如果我們要在驅(qū)動(dòng)中使用等待隊(duì)列,必須創(chuàng)建并初始化一個(gè)等待隊(duì)列頭,等待隊(duì)列頭使用結(jié)構(gòu)體
wait_queue_head_t 表示,wait_queue_head_t 結(jié)構(gòu)體定義在文件include/linux/wait.h 中,結(jié)構(gòu)體內(nèi)容如下所示:

39 struct __wait_queue_head { 40 spinlock_t lock; 41 struct list_head task_list; 42 }; 43 typedef struct __wait_queue_head wait_queue_head_t;

定義好等待隊(duì)列頭以后需要初始化,使用init_waitqueue_head 函數(shù)初始化等待隊(duì)列頭,函數(shù)原型如下:

void init_waitqueue_head(wait_queue_head_t *q)

參數(shù)q 就是要初始化的等待隊(duì)列頭。
也可以使用宏DECLARE_WAIT_QUEUE_HEAD 來一次性完成等待隊(duì)列頭的定義的初始化。

2、等待隊(duì)列項(xiàng)
等待隊(duì)列頭就是一個(gè)等待隊(duì)列的頭部,每個(gè)訪問設(shè)備的進(jìn)程都是一個(gè)隊(duì)列項(xiàng),當(dāng)設(shè)備不可用的時(shí)候就要將這些進(jìn)程對(duì)應(yīng)的等待隊(duì)列項(xiàng)添加到等待隊(duì)列里面。結(jié)構(gòu)體wait_queue_t 表示等待隊(duì)列項(xiàng),結(jié)構(gòu)體內(nèi)容如下:

struct __wait_queue { unsigned int flags; void *private; wait_queue_func_t func; struct list_head task_list; }; typedef struct __wait_queue wait_queue_t;

使用宏DECLARE_WAITQUEUE 定義并初始化一個(gè)等待隊(duì)列項(xiàng),宏的內(nèi)容如下:

DECLARE_WAITQUEUE(name, tsk)

name 就是等待隊(duì)列項(xiàng)的名字,tsk 表示這個(gè)等待隊(duì)列項(xiàng)屬于哪個(gè)任務(wù)(進(jìn)程),一般設(shè)置為current ,在Linux 內(nèi)核中current 相當(dāng)于一個(gè)全局變量,表示當(dāng)前進(jìn)程。因此宏DECLARE_WAITQUEUE 就是給當(dāng)前正在運(yùn)行的進(jìn)程創(chuàng)建并初始化了一個(gè)等待隊(duì)列項(xiàng)。

3、將隊(duì)列項(xiàng)添加/移除等待隊(duì)列頭
當(dāng)設(shè)備不可訪問的時(shí)候就需要將進(jìn)程對(duì)應(yīng)的等待隊(duì)列項(xiàng)添加到前面創(chuàng)建的等待隊(duì)列頭中,只有添加到等待隊(duì)列頭中以后進(jìn)程才能進(jìn)入休眠態(tài)。當(dāng)設(shè)備可以訪問以后再將進(jìn)程對(duì)應(yīng)的等待隊(duì)列項(xiàng)從等待隊(duì)列頭中移除即可,等待隊(duì)列項(xiàng)添加API 函數(shù)如下:

void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

函數(shù)參數(shù)和返回值含義如下:
q:等待隊(duì)列項(xiàng)要加入的等待隊(duì)列頭。
wait:要加入的等待隊(duì)列項(xiàng)。
返回值:無。
等待隊(duì)列項(xiàng)移除API 函數(shù)如下:

void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

函數(shù)參數(shù)和返回值含義如下:
q:要?jiǎng)h除的等待隊(duì)列項(xiàng)所處的等待隊(duì)列頭。
wait:要?jiǎng)h除的等待隊(duì)列項(xiàng)。
返回值:無。
4、等待喚醒
當(dāng)設(shè)備可以使用的時(shí)候就要喚醒進(jìn)入休眠態(tài)的進(jìn)程,喚醒可以使用如下兩個(gè)函數(shù):

void wake_up(wait_queue_head_t *q) void wake_up_interruptible(wait_queue_head_t *q)

參數(shù)q 就是要喚醒的等待隊(duì)列頭,這兩個(gè)函數(shù)會(huì)將這個(gè)等待隊(duì)列頭中的所有進(jìn)程都喚醒。
wake_up 函數(shù)可以喚醒處于TASK_INTERRUPTIBLE 和TASK_UNINTERRUPTIBLE 狀態(tài)的進(jìn)程,而wake_up_interruptible 函數(shù)只能喚醒處于TASK_INTERRUPTIBLE 狀態(tài)的進(jìn)程。

5、等待事件
除了主動(dòng)喚醒以外,也可以設(shè)置等待隊(duì)列等待某個(gè)事件,當(dāng)這個(gè)事件滿足以后就自動(dòng)喚醒等待隊(duì)列中的進(jìn)程,和等待事件有關(guān)的API 函數(shù)如表52.1.2.1 所示:

函數(shù)描述
wait_event(wq, condition)等待以wq 為等待隊(duì)列頭的等待隊(duì)列被喚醒,前提是condition 條件必須滿足(為真),否則一直阻塞。此函數(shù)會(huì)將進(jìn)程設(shè)置為TASK_UNINTERRUPTIBLE 狀態(tài)
wait_event_timeout(wq, condition, timeout)功能和wait_event 類似,但是此函數(shù)可以添加超時(shí)時(shí)間,以jiffies 為單位。此函數(shù)有返回值,如果返回0 的話表示超時(shí)時(shí)間到,而且condition為假。為1 的話表示condition 為真,也就是條件滿足了。
wait_event_interruptible(wq, condition)與wait_event 函數(shù)類似,但是此函數(shù)將進(jìn)程設(shè)置為TASK_INTERRUPTIBLE,就是可以被信號(hào)打斷。
wait_event_interruptible_timeout(wq, condition, timeout)與wait_event_timeout 函數(shù)類似,此函數(shù)也將進(jìn)程設(shè)置為TASK_INTERRUPTIBLE,可以被信號(hào)打斷。

輪詢

如果用戶應(yīng)用程序以非阻塞的方式訪問設(shè)備,設(shè)備驅(qū)動(dòng)程序就要提供非阻塞的處理方式,也就是輪詢。poll、epoll 和select 可以用于處理輪詢,應(yīng)用程序通過select、epoll 或poll 函數(shù)來查詢?cè)O(shè)備是否可以操作,如果可以操作的話就從設(shè)備讀取或者向設(shè)備寫入數(shù)據(jù)。當(dāng)應(yīng)用程序調(diào)用select、epoll 或poll 函數(shù)的時(shí)候設(shè)備驅(qū)動(dòng)程序中的poll 函數(shù)就會(huì)執(zhí)行,因此需要在設(shè)備驅(qū)動(dòng)程序中編寫poll 函數(shù)。我們先來看一下應(yīng)用程序中使用的select、poll 和epoll 這三個(gè)函數(shù)。

1、select 函數(shù)
select 函數(shù)原型如下:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)

函數(shù)參數(shù)和返回值含義如下:
nfds:所要監(jiān)視的這三類文件描述集合中,最大文件描述符加1。
readfds、writefds 和exceptfds:這三個(gè)指針指向描述符集合,這三個(gè)參數(shù)指明了關(guān)心哪些描述符、需要滿足哪些條件等等,這三個(gè)參數(shù)都是fd_set 類型的,fd_set 類型變量的每一個(gè)位都代表了一個(gè)文件描述符。readfds 用于監(jiān)視指定描述符集的讀變化,也就是監(jiān)視這些文件是否可以讀取,只要這些集合里面有一個(gè)文件可以讀取那么seclect 就會(huì)返回一個(gè)大于0 的值表示文件可以讀取。如果沒有文件可以讀取,那么就會(huì)根據(jù)timeout 參數(shù)來判斷是否超時(shí)。可以將readfs設(shè)置為NULL,表示不關(guān)心任何文件的讀變化。writefds 和readfs 類似,只是writefs 用于監(jiān)視這些文件是否可以進(jìn)行寫操作。exceptfds 用于監(jiān)視這些文件的異常。

比如我們現(xiàn)在要從一個(gè)設(shè)備文件中讀取數(shù)據(jù),那么就可以定義一個(gè)fd_set 變量,這個(gè)變量要傳遞給參數(shù)readfds。當(dāng)我們定義好一個(gè)fd_set 變量以后可以使用如下所示幾個(gè)宏進(jìn)行操作:

void FD_ZERO(fd_set *set) void FD_SET(int fd, fd_set *set) void FD_CLR(int fd, fd_set *set) int FD_ISSET(int fd, fd_set *set)

FD_ZERO 用于將fd_set 變量的所有位都清零,FD_SET 用于將fd_set 變量的某個(gè)位置1,也就是向fd_set 添加一個(gè)文件描述符,參數(shù)fd 就是要加入的文件描述符。FD_CLR 用于將fd_set變量的某個(gè)位清零,也就是將一個(gè)文件描述符從fd_set 中刪除,參數(shù)fd 就是要?jiǎng)h除的文件描述
符。FD_ISSET 用于測(cè)試一個(gè)文件是否屬于某個(gè)集合,參數(shù)fd 就是要判斷的文件描述符。
timeout:超時(shí)時(shí)間,當(dāng)我們調(diào)用select 函數(shù)等待某些文件描述符可以設(shè)置超時(shí)時(shí)間,超時(shí)時(shí)間使用結(jié)構(gòu)體timeval 表示,結(jié)構(gòu)體定義如下所示:

struct timeval { long tv_sec; /* 秒*/ long tv_usec; /* 微妙*/ };

當(dāng)timeout 為NULL 的時(shí)候就表示無限期的等待。

返回值:0,表示的話就表示超時(shí)發(fā)生,但是沒有任何文件描述符可以進(jìn)行操作;-1,發(fā)生錯(cuò)誤;其他值,可以進(jìn)行操作的文件描述符個(gè)數(shù)。
使用select 函數(shù)對(duì)某個(gè)設(shè)備驅(qū)動(dòng)文件進(jìn)行讀非阻塞訪問的操作示例如下所示:

1 void main(void) 2 { 3 int ret, fd; /* 要監(jiān)視的文件描述符*/ 4 fd_set readfds; /* 讀操作文件描述符集*/ 5 struct timeval timeout; /* 超時(shí)結(jié)構(gòu)體*/ 6 7 fd = open("dev_xxx", O_RDWR | O_NONBLOCK); /* 非阻塞式訪問*/ 8 9 FD_ZERO(&readfds); /* 清除readfds */ 10 FD_SET(fd, &readfds); /* 將fd添加到readfds里面*/ 11 12 /* 構(gòu)造超時(shí)時(shí)間*/ 13 timeout.tv_sec = 0; 14 timeout.tv_usec = 500000; /* 500ms */ 15 16 ret = select(fd + 1, &readfds, NULL, NULL, &timeout); 17 switch (ret) { 18 case 0: /* 超時(shí)*/ 19 printf("timeout!\r\n"); 20 break; 21 case -1: /* 錯(cuò)誤*/ 22 printf("error!\r\n"); 23 break; 24 default: /* 可以讀取數(shù)據(jù)*/ 25 if(FD_ISSET(fd, &readfds)) { /* 判斷是否為fd文件描述符*/ 26 /* 使用read函數(shù)讀取數(shù)據(jù)*/ 27 } 28 break; 29 } 30 }

2、poll 函數(shù)
在單個(gè)線程中,select 函數(shù)能夠監(jiān)視的文件描述符數(shù)量有最大的限制,一般為1024,可以修改內(nèi)核將監(jiān)視的文件描述符數(shù)量改大,但是這樣會(huì)降低效率!這個(gè)時(shí)候就可以使用poll 函數(shù),poll 函數(shù)本質(zhì)上和select 沒有太大的差別,但是poll 函數(shù)沒有最大文件描述符限制,Linux 應(yīng)用程序中poll 函數(shù)原型如下所示:

int poll(struct pollfd *fds, nfds_t nfds, int timeout)

函數(shù)參數(shù)和返回值含義如下:
fds:要監(jiān)視的文件描述符集合以及要監(jiān)視的事件,為一個(gè)數(shù)組,數(shù)組元素都是結(jié)構(gòu)體pollfd類型的,pollfd 結(jié)構(gòu)體如下所示:

struct pollfd { int fd; /* 文件描述符*/ short events; /* 請(qǐng)求的事件*/ short revents; /* 返回的事件*/ };

fd 是要監(jiān)視的文件描述符,如果fd 無效的話那么events 監(jiān)視事件也就無效,并且revents返回0。events 是要監(jiān)視的事件,可監(jiān)視的事件類型如下所示:

POLLIN 有數(shù)據(jù)可以讀取。 POLLPRI 有緊急的數(shù)據(jù)需要讀取。 POLLOUT 可以寫數(shù)據(jù)。 POLLERR 指定的文件描述符發(fā)生錯(cuò)誤。 POLLHUP 指定的文件描述符掛起。 POLLNVAL 無效的請(qǐng)求。 POLLRDNORM 等同于POLLIN

revents 是返回參數(shù),也就是返回的事件,由Linux 內(nèi)核設(shè)置具體的返回事件。
nfds:poll 函數(shù)要監(jiān)視的文件描述符數(shù)量。
timeout:超時(shí)時(shí)間,單位為ms。
返回值:返回revents 域中不為0 的pollfd 結(jié)構(gòu)體個(gè)數(shù),也就是發(fā)生事件或錯(cuò)誤的文件描述符數(shù)量;0,超時(shí);-1,發(fā)生錯(cuò)誤,并且設(shè)置errno 為錯(cuò)誤類型。

使用poll 函數(shù)對(duì)某個(gè)設(shè)備驅(qū)動(dòng)文件進(jìn)行讀非阻塞訪問的操作示例如下所示:

1 void main(void) 2 { 3 int ret; 4 int fd; /* 要監(jiān)視的文件描述符*/ 5 struct pollfd fds; 6 7 fd = open(filename, O_RDWR | O_NONBLOCK); /* 非阻塞式訪問*/ 8 9 /* 構(gòu)造結(jié)構(gòu)體*/ 10 fds.fd = fd; 11 fds.events = POLLIN; /* 監(jiān)視數(shù)據(jù)是否可以讀取*/ 12 13 ret = poll(&fds, 1, 500); /* 輪詢文件是否可操作,超時(shí)500ms */ 14 if (ret) { /* 數(shù)據(jù)有效*/ 15 ...... 16 /* 讀取數(shù)據(jù)*/ 17 ...... 18 } else if (ret == 0) { /* 超時(shí)*/ 19 ...... 20 } else if (ret < 0) { /* 錯(cuò)誤*/ 21 ...... 22 } 23 }

3、epoll 函數(shù)
傳統(tǒng)的selcet 和poll 函數(shù)都會(huì)隨著所監(jiān)聽的fd 數(shù)量的增加,出現(xiàn)效率低下的問題,而且poll 函數(shù)每次必須遍歷所有的描述符來檢查就緒的描述符,這個(gè)過程很浪費(fèi)時(shí)間。為此,epoll應(yīng)運(yùn)而生,epoll 就是為處理大并發(fā)而準(zhǔn)備的,一般常常在網(wǎng)絡(luò)編程中使用epoll 函數(shù)。應(yīng)用程序需要先使用epoll_create 函數(shù)創(chuàng)建一個(gè)epoll 句柄,epoll_create 函數(shù)原型如下:

int epoll_create(int size)

函數(shù)參數(shù)和返回值含義如下:
size:從Linux2.6.8 開始此參數(shù)已經(jīng)沒有意義了,隨便填寫一個(gè)大于0 的值就可以。
返回值:epoll 句柄,如果為-1 的話表示創(chuàng)建失敗。
epoll 句柄創(chuàng)建成功以后使用epoll_ctl 函數(shù)向其中添加要監(jiān)視的文件描述符以及監(jiān)視的事件,epoll_ctl 函數(shù)原型如下所示:

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

函數(shù)參數(shù)和返回值含義如下:
epfd:要操作的epoll 句柄,也就是使用epoll_create 函數(shù)創(chuàng)建的epoll 句柄。
op:表示要對(duì)epfd(epoll 句柄)進(jìn)行的操作,可以設(shè)置為:

EPOLL_CTL_ADD 向epfd 添加文件參數(shù)fd 表示的描述符。 EPOLL_CTL_MOD 修改參數(shù)fd 的event 事件。 EPOLL_CTL_DEL 從epfd 中刪除fd 描述符。

fd:要監(jiān)視的文件描述符。
event:要監(jiān)視的事件類型,為epoll_event 結(jié)構(gòu)體類型指針,epoll_event 結(jié)構(gòu)體類型如下所示:

struct epoll_event { uint32_t events; /* epoll 事件*/ epoll_data_t data; /* 用戶數(shù)據(jù)*/ };

結(jié)構(gòu)體epoll_event 的events 成員變量表示要監(jiān)視的事件,可選的事件如下所示:

EPOLLIN 有數(shù)據(jù)可以讀取。 EPOLLOUT 可以寫數(shù)據(jù)。 EPOLLPRI 有緊急的數(shù)據(jù)需要讀取。 EPOLLERR 指定的文件描述符發(fā)生錯(cuò)誤。 EPOLLHUP 指定的文件描述符掛起。 EPOLLET 設(shè)置epoll 為邊沿觸發(fā),默認(rèn)觸發(fā)模式為水平觸發(fā)。 EPOLLONESHOT 一次性的監(jiān)視,當(dāng)監(jiān)視完成以后還需要再次監(jiān)視某個(gè)fd,那么就需要將 fd 重新添加到epoll 里面。

上面這些事件可以進(jìn)行“或”操作,也就是說可以設(shè)置監(jiān)視多個(gè)事件。
返回值:0,成功;-1,失敗,并且設(shè)置errno 的值為相應(yīng)的錯(cuò)誤碼。
一切都設(shè)置好以后應(yīng)用程序就可以通過epoll_wait 函數(shù)來等待事件的發(fā)生,類似select 函數(shù)。epoll_wait 函數(shù)原型如下所示:

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)

函數(shù)參數(shù)和返回值含義如下:
epfd:要等待的epoll。
events:指向epoll_event 結(jié)構(gòu)體的數(shù)組,當(dāng)有事件發(fā)生的時(shí)候Linux 內(nèi)核會(huì)填寫events,調(diào)用者可以根據(jù)events 判斷發(fā)生了哪些事件。
maxevents:events 數(shù)組大小,必須大于0。
timeout:超時(shí)時(shí)間,單位為ms。
返回值:0,超時(shí);-1,錯(cuò)誤;其他值,準(zhǔn)備就緒的文件描述符數(shù)量。
epoll 更多的是用在大規(guī)模的并發(fā)服務(wù)器上,因?yàn)樵谶@種場(chǎng)合下select 和poll 并不適合。當(dāng)設(shè)計(jì)到的文件描述符(fd)比較少的時(shí)候就適合用selcet 和poll,本章我們就使用sellect 和poll 這兩個(gè)函數(shù)。

Linux 驅(qū)動(dòng)下的poll 操作函數(shù)

當(dāng)應(yīng)用程序調(diào)用select 或poll 函數(shù)來對(duì)驅(qū)動(dòng)程序進(jìn)行非阻塞訪問的時(shí)候,驅(qū)動(dòng)程序file_operations 操作集中的poll 函數(shù)就會(huì)執(zhí)行。所以驅(qū)動(dòng)程序的編寫者需要提供對(duì)應(yīng)的poll 函數(shù),poll 函數(shù)原型如下所示:

unsigned int (*poll) (struct file *filp, struct poll_table_struct *wait)

函數(shù)參數(shù)和返回值含義如下:
filp:要打開的設(shè)備文件(文件描述符)。
wait:結(jié)構(gòu)體poll_table_struct 類型指針,由應(yīng)用程序傳遞進(jìn)來的。一般將此參數(shù)傳遞給poll_wait 函數(shù)。
返回值:向應(yīng)用程序返回設(shè)備或者資源狀態(tài),可以返回的資源狀態(tài)如下:

POLLIN 有數(shù)據(jù)可以讀取。 POLLPRI 有緊急的數(shù)據(jù)需要讀取。 POLLOUT 可以寫數(shù)據(jù)。 POLLERR 指定的文件描述符發(fā)生錯(cuò)誤。 POLLHUP 指定的文件描述符掛起。 POLLNVAL 無效的請(qǐng)求。 POLLRDNORM 等同于POLLIN,普通數(shù)據(jù)可讀

我們需要在驅(qū)動(dòng)程序的poll 函數(shù)中調(diào)用poll_wait 函數(shù),poll_wait 函數(shù)不會(huì)引起阻塞,只是將應(yīng)用程序添加到poll_table 中,poll_wait 函數(shù)原型如下:

void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)

參數(shù)wait_address 是要添加到poll_table 中的等待隊(duì)列頭,參數(shù)p 就是poll_table,就是file_operations 中poll 函數(shù)的wait 參數(shù)。

阻塞IO 實(shí)驗(yàn)

在上一章Linux 中斷實(shí)驗(yàn)中,我們直接在應(yīng)用程序中通過read 函數(shù)不斷的讀取按鍵狀態(tài),當(dāng)按鍵有效的時(shí)候就打印出按鍵值。這種方法有個(gè)缺點(diǎn),那就是imx6uirqApp 這個(gè)測(cè)試應(yīng)用程序擁有很高的CPU 占用率,大家可以在開發(fā)板中加載上一章的驅(qū)動(dòng)程序模塊imx6uirq.ko,然后以后臺(tái)運(yùn)行模式打開imx6uirqApp 這個(gè)測(cè)試軟件,命令如下:

./imx6uirqApp /dev/imx6uirq &

測(cè)試驅(qū)動(dòng)是否正常工作,如果驅(qū)動(dòng)工作正常的話輸入“top”命令查看imx6uirqApp 這個(gè)應(yīng)用程序的CPU 使用率,結(jié)果如圖52.2.1 所示:

從圖52.2.1 可以看出,imx6uirqApp 這個(gè)應(yīng)用程序的CPU 使用率竟然高達(dá)99.6%,這僅僅是一個(gè)讀取按鍵值的應(yīng)用程序,這么高的CPU 使用率顯然是有問題的!原因就在于我們是直接在while 循環(huán)中通過read 函數(shù)讀取按鍵值,因此imx6uirqApp 這個(gè)軟件會(huì)一直運(yùn)行,一直讀取按鍵值,CPU 使用率肯定就會(huì)很高。最好的方法就是在沒有有效的按鍵事件發(fā)生的時(shí)候,
imx6uirqApp 這個(gè)應(yīng)用程序應(yīng)該處于休眠狀態(tài),當(dāng)有按鍵事件發(fā)生以后imx6uirqApp 這個(gè)應(yīng)用程序才運(yùn)行,打印出按鍵值,這樣就會(huì)降低CPU 使用率,本小節(jié)我們就使用阻塞IO 來實(shí)現(xiàn)此功能。

硬件原理圖分析

本章實(shí)驗(yàn)硬件原理圖參考15.2 小節(jié)即可。

實(shí)驗(yàn)程序編寫

1、驅(qū)動(dòng)程序編寫
本實(shí)驗(yàn)對(duì)應(yīng)的例程路徑為:開發(fā)板光盤-> 2、Linux 驅(qū)動(dòng)例程-> 14_blockio。
本章實(shí)驗(yàn)我們?cè)谏弦徽碌摹?3_irq”實(shí)驗(yàn)的基礎(chǔ)上完成,主要是對(duì)其添加阻塞訪問相關(guān)的代碼。新建名為“14_blockio”的文件夾,然后在14_blockio 文件夾里面創(chuàng)建vscode 工程,工作區(qū)命名為“blockio”。將“13_irq”實(shí)驗(yàn)中的imx6uirq.c 復(fù)制到14_blockio 文件夾中,并重命名為blockio.c。接下來我們就修改blockio.c 這個(gè)文件,在其中添加阻塞相關(guān)的代碼,完成以后的
blockio.c 內(nèi)容如下所示(因?yàn)槭窃谏弦徽聦?shí)驗(yàn)的imx6uirq.c 文件的基礎(chǔ)上修改的,為了減少篇幅,下面的代碼有省略):

#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_gpio.h> #include <linux/semaphore.h> #include <linux/timer.h> #include <linux/of_irq.h> #include <linux/irq.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : block.c 作者 : 左忠凱 版本 : V1.0 描述 : 阻塞IO訪問 其他 : 無 論壇 : www.openedv.com 日志 : 初版V1.0 2019/7/26 左忠凱創(chuàng)建 ***************************************************************/ #define IMX6UIRQ_CNT 1 /* 設(shè)備號(hào)個(gè)數(shù) */ #define IMX6UIRQ_NAME "blockio" /* 名字 */ #define KEY0VALUE 0X01 /* KEY0按鍵值 */ #define INVAKEY 0XFF /* 無效的按鍵值 */ #define KEY_NUM 1 /* 按鍵數(shù)量 *//* 中斷IO描述結(jié)構(gòu)體 */ struct irq_keydesc {int gpio; /* gpio */int irqnum; /* 中斷號(hào) */unsigned char value; /* 按鍵對(duì)應(yīng)的鍵值 */char name[10]; /* 名字 */irqreturn_t (*handler)(int, void *); /* 中斷服務(wù)函數(shù) */ };/* imx6uirq設(shè)備結(jié)構(gòu)體 */ struct imx6uirq_dev{dev_t devid; /* 設(shè)備號(hào) */ struct cdev cdev; /* cdev */ struct class *class; /* 類 */struct device *device; /* 設(shè)備 */int major; /* 主設(shè)備號(hào) */int minor; /* 次設(shè)備號(hào) */struct device_node *nd; /* 設(shè)備節(jié)點(diǎn) */ atomic_t keyvalue; /* 有效的按鍵鍵值 */atomic_t releasekey; /* 標(biāo)記是否完成一次完成的按鍵,包括按下和釋放 */struct timer_list timer;/* 定義一個(gè)定時(shí)器*/struct irq_keydesc irqkeydesc[KEY_NUM]; /* 按鍵init述數(shù)組 */unsigned char curkeynum; /* 當(dāng)前init按鍵號(hào) */wait_queue_head_t r_wait; /* 讀等待隊(duì)列頭 */ };struct imx6uirq_dev imx6uirq; /* irq設(shè)備 *//* @description : 中斷服務(wù)函數(shù),開啟定時(shí)器 * 定時(shí)器用于按鍵消抖。* @param - irq : 中斷號(hào) * @param - dev_id : 設(shè)備結(jié)構(gòu)。* @return : 中斷執(zhí)行結(jié)果*/ static irqreturn_t key0_handler(int irq, void *dev_id) {struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;dev->curkeynum = 0;dev->timer.data = (volatile long)dev_id;mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10)); /* 10ms定時(shí) */return IRQ_RETVAL(IRQ_HANDLED); }/* @description : 定時(shí)器服務(wù)函數(shù),用于按鍵消抖,定時(shí)器到了以后* 再次讀取按鍵值,如果按鍵還是處于按下狀態(tài)就表示按鍵有效。* @param - arg : 設(shè)備結(jié)構(gòu)變量* @return : 無*/ void timer_function(unsigned long arg) {unsigned char value;unsigned char num;struct irq_keydesc *keydesc;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;num = dev->curkeynum;keydesc = &dev->irqkeydesc[num];value = gpio_get_value(keydesc->gpio); /* 讀取IO值 */if(value == 0){ /* 按下按鍵 */atomic_set(&dev->keyvalue, keydesc->value);}else{ /* 按鍵松開 */atomic_set(&dev->keyvalue, 0x80 | keydesc->value);atomic_set(&dev->releasekey, 1); /* 標(biāo)記松開按鍵,即完成一次完整的按鍵過程 */} /* 喚醒進(jìn)程 */if(atomic_read(&dev->releasekey)) { /* 完成一次按鍵過程 *//* wake_up(&dev->r_wait); */wake_up_interruptible(&dev->r_wait);} }/** @description : 按鍵IO初始化* @param : 無* @return : 無*/ static int keyio_init(void) {unsigned char i = 0;char name[10];int ret = 0;imx6uirq.nd = of_find_node_by_path("/key");if (imx6uirq.nd== NULL){printk("key node not find!\r\n");return -EINVAL;} /* 提取GPIO */for (i = 0; i < KEY_NUM; i++) {imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(imx6uirq.nd ,"key-gpio", i);if (imx6uirq.irqkeydesc[i].gpio < 0) {printk("can't get key%d\r\n", i);}}/* 初始化key所使用的IO,并且設(shè)置成中斷模式 */for (i = 0; i < KEY_NUM; i++) {memset(imx6uirq.irqkeydesc[i].name, 0, sizeof(name)); /* 緩沖區(qū)清零 */sprintf(imx6uirq.irqkeydesc[i].name, "KEY%d", i); /* 組合名字 */gpio_request(imx6uirq.irqkeydesc[i].gpio, name);gpio_direction_input(imx6uirq.irqkeydesc[i].gpio); imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(imx6uirq.nd, i); #if 0imx6uirq.irqkeydesc[i].irqnum = gpio_to_irq(imx6uirq.irqkeydesc[i].gpio); #endif}/* 申請(qǐng)中斷 */imx6uirq.irqkeydesc[0].handler = key0_handler;imx6uirq.irqkeydesc[0].value = KEY0VALUE;for (i = 0; i < KEY_NUM; i++) {ret = request_irq(imx6uirq.irqkeydesc[i].irqnum, imx6uirq.irqkeydesc[i].handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, imx6uirq.irqkeydesc[i].name, &imx6uirq);if(ret < 0){printk("irq %d request failed!\r\n", imx6uirq.irqkeydesc[i].irqnum);return -EFAULT;}}/* 創(chuàng)建定時(shí)器 */init_timer(&imx6uirq.timer);imx6uirq.timer.function = timer_function;/* 初始化等待隊(duì)列頭 */init_waitqueue_head(&imx6uirq.r_wait);return 0; }/** @description : 打開設(shè)備* @param - inode : 傳遞給驅(qū)動(dòng)的inode* @param - filp : 設(shè)備文件,file結(jié)構(gòu)體有個(gè)叫做private_data的成員變量* 一般在open的時(shí)候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。* @return : 0 成功;其他 失敗*/ static int imx6uirq_open(struct inode *inode, struct file *filp) {filp->private_data = &imx6uirq; /* 設(shè)置私有數(shù)據(jù) */return 0; }/** @description : 從設(shè)備讀取數(shù)據(jù) * @param - filp : 要打開的設(shè)備文件(文件描述符)* @param - buf : 返回給用戶空間的數(shù)據(jù)緩沖區(qū)* @param - cnt : 要讀取的數(shù)據(jù)長(zhǎng)度* @param - offt : 相對(duì)于文件首地址的偏移* @return : 讀取的字節(jié)數(shù),如果為負(fù)值,表示讀取失敗*/ static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {int ret = 0;unsigned char keyvalue = 0;unsigned char releasekey = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;#if 0/* 加入等待隊(duì)列,等待被喚醒,也就是有按鍵按下 */ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey)); if (ret) {goto wait_error;} #endifDECLARE_WAITQUEUE(wait, current); /* 定義一個(gè)等待隊(duì)列 */if(atomic_read(&dev->releasekey) == 0) { /* 沒有按鍵按下 */add_wait_queue(&dev->r_wait, &wait); /* 將等待隊(duì)列添加到等待隊(duì)列頭 */__set_current_state(TASK_INTERRUPTIBLE);/* 設(shè)置任務(wù)狀態(tài) */schedule(); /* 進(jìn)行一次任務(wù)切換 */if(signal_pending(current)) { /* 判斷是否為信號(hào)引起的喚醒 */ret = -ERESTARTSYS;goto wait_error;}__set_current_state(TASK_RUNNING); /* 將當(dāng)前任務(wù)設(shè)置為運(yùn)行狀態(tài) */remove_wait_queue(&dev->r_wait, &wait); /* 將對(duì)應(yīng)的隊(duì)列項(xiàng)從等待隊(duì)列頭刪除 */}keyvalue = atomic_read(&dev->keyvalue);releasekey = atomic_read(&dev->releasekey);if (releasekey) { /* 有按鍵按下 */ if (keyvalue & 0x80) {keyvalue &= ~0x80;ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));} else {goto data_error;}atomic_set(&dev->releasekey, 0);/* 按下標(biāo)志清零 */} else {goto data_error;}return 0;wait_error:set_current_state(TASK_RUNNING); /* 設(shè)置任務(wù)為運(yùn)行態(tài) */remove_wait_queue(&dev->r_wait, &wait); /* 將等待隊(duì)列移除 */return ret;data_error:return -EINVAL; }/* 設(shè)備操作函數(shù) */ static struct file_operations imx6uirq_fops = {.owner = THIS_MODULE,.open = imx6uirq_open,.read = imx6uirq_read, };/** @description : 驅(qū)動(dòng)入口函數(shù)* @param : 無* @return : 無*/ static int __init imx6uirq_init(void) {/* 1、構(gòu)建設(shè)備號(hào) */if (imx6uirq.major) {imx6uirq.devid = MKDEV(imx6uirq.major, 0);register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME);} else {alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);imx6uirq.major = MAJOR(imx6uirq.devid);imx6uirq.minor = MINOR(imx6uirq.devid);}/* 2、注冊(cè)字符設(shè)備 */cdev_init(&imx6uirq.cdev, &imx6uirq_fops);cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT);/* 3、創(chuàng)建類 */imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.class)) { return PTR_ERR(imx6uirq.class);}/* 4、創(chuàng)建設(shè)備 */imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.device)) {return PTR_ERR(imx6uirq.device);}/* 5、始化按鍵 */atomic_set(&imx6uirq.keyvalue, INVAKEY);atomic_set(&imx6uirq.releasekey, 0);keyio_init();return 0; }/** @description : 驅(qū)動(dòng)出口函數(shù)* @param : 無* @return : 無*/ static void __exit imx6uirq_exit(void) {unsigned i = 0;/* 刪除定時(shí)器 */del_timer_sync(&imx6uirq.timer); /* 刪除定時(shí)器 *//* 釋放中斷 */ for (i = 0; i < KEY_NUM; i++) {free_irq(imx6uirq.irqkeydesc[i].irqnum, &imx6uirq);}cdev_del(&imx6uirq.cdev);unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);device_destroy(imx6uirq.class, imx6uirq.devid);class_destroy(imx6uirq.class); }module_init(imx6uirq_init); module_exit(imx6uirq_exit); MODULE_LICENSE("GPL");

第32 行,修改設(shè)備文件名字為“blockio”,當(dāng)驅(qū)動(dòng)程序加載成功以后就會(huì)在根文件系統(tǒng)中出現(xiàn)一個(gè)名為“/dev/blockio”的文件。

第61 行,在設(shè)備結(jié)構(gòu)體中添加一個(gè)等待隊(duì)列頭r_wait,因?yàn)樵贚inux 驅(qū)動(dòng)中處理阻塞IO需要用到等待隊(duì)列。

第107~110 行,定時(shí)器中斷處理函數(shù)執(zhí)行,表示有按鍵按下,先在107 行判斷一下是否是一次有效的按鍵,如果是的話就通過wake_up 或者wake_up_interruptible 函數(shù)來喚醒等待隊(duì)列r_wait。

第168 行,調(diào)用init_waitqueue_head 函數(shù)初始化等待隊(duì)列頭r_wait。

第200~206 行,采用等待事件來處理read 的阻塞訪問,wait_event_interruptible 函數(shù)等待releasekey 有效,也就是有按鍵按下。如果按鍵沒有按下的話進(jìn)程就會(huì)進(jìn)入休眠狀態(tài),因?yàn)椴捎昧藈ait_event_interruptible 函數(shù),因此進(jìn)入休眠態(tài)的進(jìn)程可以被信號(hào)打斷。

第208~218 行,首先使用DECLARE_WAITQUEUE 宏定義一個(gè)等待隊(duì)列,如果沒有按鍵按下的話就使用add_wait_queue 函數(shù)將當(dāng)前任務(wù)的等待隊(duì)列添加到等待隊(duì)列頭r_wait 中。隨后調(diào)用__set_current_state 函數(shù)設(shè)置當(dāng)前進(jìn)程的狀態(tài)為TASK_INTERRUPTIBLE,也就是可以被信號(hào)打斷。接下來調(diào)用schedule 函數(shù)進(jìn)行一次任務(wù)切換,當(dāng)前進(jìn)程就會(huì)進(jìn)入到休眠態(tài)。如果有按
鍵按下,那么進(jìn)入休眠態(tài)的進(jìn)程就會(huì)喚醒,然后接著從休眠點(diǎn)開始運(yùn)行。在這里也就是從第213行開始運(yùn)行,首先通過signal_pending 函數(shù)判斷一下進(jìn)程是不是由信號(hào)喚醒的,如果是由信號(hào)喚醒的話就直接返回-ERESTARTSYS 這個(gè)錯(cuò)誤碼。如果不是由信號(hào)喚醒的(也就是被按鍵喚醒的)那么就在217 行調(diào)用__set_current_state 函數(shù)將任務(wù)狀態(tài)設(shè)置為TASK_RUNNING,然后在
218 行調(diào)用remove_wait_queue 函數(shù)將進(jìn)程從等待隊(duì)列中刪除。

使用等待隊(duì)列實(shí)現(xiàn)阻塞訪問重點(diǎn)注意兩點(diǎn):
①、將任務(wù)或者進(jìn)程加入到等待隊(duì)列頭,
②、在合適的點(diǎn)喚醒等待隊(duì)列,一般都是中斷處理函數(shù)里面。
2、編寫測(cè)試APP

本節(jié)實(shí)驗(yàn)的測(cè)試APP 直接使用第51.3.3 小節(jié)所編寫的imx6uirqApp.c,將imx6uirqApp.c 復(fù)制到本節(jié)實(shí)驗(yàn)文件夾下,并且重命名為blockioApp.c,不需要修改任何內(nèi)容。

運(yùn)行測(cè)試

1、編譯驅(qū)動(dòng)程序和測(cè)試APP
①、編譯驅(qū)動(dòng)程序
編寫Makefile 文件,本章實(shí)驗(yàn)的Makefile 文件和第四十章實(shí)驗(yàn)基本一樣,只是將obj-m 變量的值改為blockio.o,Makefile 內(nèi)容如下所示:

KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH := $(shell pwd)obj-m := blockio.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4 行,設(shè)置obj-m 變量的值為blockio.o。
輸入如下命令編譯出驅(qū)動(dòng)模塊文件:

make -j32

編譯成功以后就會(huì)生成一個(gè)名為“blockio.ko”的驅(qū)動(dòng)模塊文件。
②、編譯測(cè)試APP
輸入如下命令編譯測(cè)試noblockioApp.c 這個(gè)測(cè)試程序:

arm-linux-gnueabihf-gcc blockioApp.c -o blockioApp

編譯成功以后就會(huì)生成blcokioApp 這個(gè)應(yīng)用程序。

2、運(yùn)行測(cè)試
將上一小節(jié)編譯出來blockio.ko 和blockioApp 這兩個(gè)文件拷貝到rootfs/lib/modules/4.1.15目錄中,重啟開發(fā)板,進(jìn)入到目錄lib/modules/4.1.15 中,輸入如下命令加載blockio.ko 驅(qū)動(dòng)模塊:

depmod //第一次加載驅(qū)動(dòng)的時(shí)候需要運(yùn)行此命令 modprobe blockio.ko //加載驅(qū)動(dòng)

驅(qū)動(dòng)加載成功以后使用如下命令打開blockioApp 這個(gè)測(cè)試APP,并且以后臺(tái)模式運(yùn)行:

./blockioApp /dev/blockio &

按下開發(fā)板上的KEY0 按鍵,結(jié)果如圖52.2.3.1 所示:

當(dāng)按下KEY0 按鍵以后blockioApp 這個(gè)測(cè)試APP 就會(huì)打印出按鍵值。輸入“top”命令,查看blockioAPP 這個(gè)應(yīng)用APP 的CPU 使用率,如圖52.2.3.2 所示:

從圖52.2.3.2 可以看出,當(dāng)我們?cè)诎存I驅(qū)動(dòng)程序里面加入阻塞訪問以后,blockioApp 這個(gè)應(yīng)用程序的CPU 使用率從圖52.2.1 中的99.6%降低到了0.0%。大家注意,這里的0.0%并不是說blockioApp 這個(gè)應(yīng)用程序不使用CPU 了,只是因?yàn)槭褂寐侍×?#xff0c;CPU 使用率可能為0.00001%,但是圖52.2.3.2 只能顯示出小數(shù)點(diǎn)后一位,因此就顯示成了0.0%。

我們可以使用“kill”命令關(guān)閉后臺(tái)運(yùn)行的應(yīng)用程序,比如我們關(guān)閉掉blockioApp 這個(gè)后臺(tái)運(yùn)行的應(yīng)用程序。首先輸出“Ctrl+C”關(guān)閉top 命令界面,進(jìn)入到命令行模式。然后使用“ps”命令查看一下blockioApp 這個(gè)應(yīng)用程序的PID,如圖52.2.3.3 所示:

從圖圖52.2.3.3 可以看出,blockioApp 這個(gè)應(yīng)用程序的PID 為149,使用“kill -9 PID”即可“殺死”指定PID 的進(jìn)程,比如我們現(xiàn)在要“殺死”PID 為149 的blockioApp 應(yīng)用程序,可是使用如下命令:

kill -9 149

輸入上述命令以后終端顯示如圖52.2.3.4 所示:

從圖52.2.3.4 可以看出,“./blockioApp /dev/blockio”這個(gè)應(yīng)用程序已經(jīng)被“殺掉”了,在此輸入“ps”命令查看當(dāng)前系統(tǒng)運(yùn)行的進(jìn)程,會(huì)發(fā)現(xiàn)blockioApp 已經(jīng)不見了。這個(gè)就是使用kill命令“殺掉”指定進(jìn)程的方法。

非阻塞IO 實(shí)驗(yàn)

硬件原理圖分析

本章實(shí)驗(yàn)硬件原理圖參考15.2 小節(jié)即可。

實(shí)驗(yàn)程序編寫

1、驅(qū)動(dòng)程序編寫
本實(shí)驗(yàn)對(duì)應(yīng)的例程路徑為:開發(fā)板光盤-> 2、Linux 驅(qū)動(dòng)例程-> 15_noblockio。

本章實(shí)驗(yàn)我們?cè)?2.2 小節(jié)中的“14_blockio”實(shí)驗(yàn)的基礎(chǔ)上完成,上一小節(jié)實(shí)驗(yàn)我們已經(jīng)在驅(qū)動(dòng)中添加了阻塞IO 的代碼,本小節(jié)我們繼續(xù)完善驅(qū)動(dòng),加入非阻塞IO 驅(qū)動(dòng)代碼。新建名為“15_noblockio”的文件夾,然后在15_noblockio 文件夾里面創(chuàng)建vscode 工程,工作區(qū)命名為“noblockio”。將“14_blockio”實(shí)驗(yàn)中的blockio.c 復(fù)制到15_noblockio 文件夾中,并重命名為noblockio.c。接下來我們就修改noblockio.c 這個(gè)文件,在其中添加非阻塞相關(guān)的代碼,完成
以后的noblockio.c 內(nèi)容如下所示(因?yàn)槭窃谏弦恍」?jié)實(shí)驗(yàn)的blockio.c 文件的基礎(chǔ)上修改的,為了減少篇幅,下面的代碼有省略):

#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_gpio.h> #include <linux/semaphore.h> #include <linux/timer.h> #include <linux/of_irq.h> #include <linux/irq.h> #include <linux/wait.h> #include <linux/poll.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : noblock.c 作者 : 左忠凱 版本 : V1.0 描述 : 非阻塞IO訪問 其他 : 無 論壇 : www.openedv.com 日志 : 初版V1.0 2019/7/26 左忠凱創(chuàng)建 ***************************************************************/ #define IMX6UIRQ_CNT 1 /* 設(shè)備號(hào)個(gè)數(shù) */ #define IMX6UIRQ_NAME "noblockio" /* 名字 */ #define KEY0VALUE 0X01 /* KEY0按鍵值 */ #define INVAKEY 0XFF /* 無效的按鍵值 */ #define KEY_NUM 1 /* 按鍵數(shù)量 *//* 中斷IO描述結(jié)構(gòu)體 */ struct irq_keydesc {int gpio; /* gpio */int irqnum; /* 中斷號(hào) */unsigned char value; /* 按鍵對(duì)應(yīng)的鍵值 */char name[10]; /* 名字 */irqreturn_t (*handler)(int, void *); /* 中斷服務(wù)函數(shù) */ };/* imx6uirq設(shè)備結(jié)構(gòu)體 */ struct imx6uirq_dev{dev_t devid; /* 設(shè)備號(hào) */ struct cdev cdev; /* cdev */ struct class *class; /* 類 */struct device *device; /* 設(shè)備 */int major; /* 主設(shè)備號(hào) */int minor; /* 次設(shè)備號(hào) */struct device_node *nd; /* 設(shè)備節(jié)點(diǎn) */ atomic_t keyvalue; /* 有效的按鍵鍵值 */atomic_t releasekey; /* 標(biāo)記是否完成一次完成的按鍵,包括按下和釋放 */struct timer_list timer;/* 定義一個(gè)定時(shí)器*/struct irq_keydesc irqkeydesc[KEY_NUM]; /* 按鍵init述數(shù)組 */unsigned char curkeynum; /* 當(dāng)前init按鍵號(hào) */wait_queue_head_t r_wait; /* 讀等待隊(duì)列頭 */ };struct imx6uirq_dev imx6uirq; /* irq設(shè)備 *//* @description : 中斷服務(wù)函數(shù),開啟定時(shí)器 * 定時(shí)器用于按鍵消抖。* @param - irq : 中斷號(hào) * @param - dev_id : 設(shè)備結(jié)構(gòu)。* @return : 中斷執(zhí)行結(jié)果*/ static irqreturn_t key0_handler(int irq, void *dev_id) {struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;dev->curkeynum = 0;dev->timer.data = (volatile long)dev_id;mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10)); /* 10ms定時(shí) */return IRQ_RETVAL(IRQ_HANDLED); }/* @description : 定時(shí)器服務(wù)函數(shù),用于按鍵消抖,定時(shí)器到了以后* 再次讀取按鍵值,如果按鍵還是處于按下狀態(tài)就表示按鍵有效。* @param - arg : 設(shè)備結(jié)構(gòu)變量* @return : 無*/ void timer_function(unsigned long arg) {unsigned char value;unsigned char num;struct irq_keydesc *keydesc;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;num = dev->curkeynum;keydesc = &dev->irqkeydesc[num];value = gpio_get_value(keydesc->gpio); /* 讀取IO值 */if(value == 0){ /* 按下按鍵 */atomic_set(&dev->keyvalue, keydesc->value);}else{ /* 按鍵松開 */atomic_set(&dev->keyvalue, 0x80 | keydesc->value);atomic_set(&dev->releasekey, 1); /* 標(biāo)記松開按鍵,即完成一次完整的按鍵過程 */} /* 喚醒進(jìn)程 */if(atomic_read(&dev->releasekey)) { /* 完成一次按鍵過程 *//* wake_up(&dev->r_wait); */wake_up_interruptible(&dev->r_wait);} }/** @description : 按鍵IO初始化* @param : 無* @return : 無*/ static int keyio_init(void) {unsigned char i = 0;char name[10];int ret = 0;imx6uirq.nd = of_find_node_by_path("/key");if (imx6uirq.nd== NULL){printk("key node not find!\r\n");return -EINVAL;} /* 提取GPIO */for (i = 0; i < KEY_NUM; i++) {imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(imx6uirq.nd ,"key-gpio", i);if (imx6uirq.irqkeydesc[i].gpio < 0) {printk("can't get key%d\r\n", i);}}/* 初始化key所使用的IO,并且設(shè)置成中斷模式 */for (i = 0; i < KEY_NUM; i++) {memset(imx6uirq.irqkeydesc[i].name, 0, sizeof(name)); /* 緩沖區(qū)清零 */sprintf(imx6uirq.irqkeydesc[i].name, "KEY%d", i); /* 組合名字 */gpio_request(imx6uirq.irqkeydesc[i].gpio, name);gpio_direction_input(imx6uirq.irqkeydesc[i].gpio); imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(imx6uirq.nd, i); #if 0imx6uirq.irqkeydesc[i].irqnum = gpio_to_irq(imx6uirq.irqkeydesc[i].gpio); #endif}/* 申請(qǐng)中斷 */imx6uirq.irqkeydesc[0].handler = key0_handler;imx6uirq.irqkeydesc[0].value = KEY0VALUE;for (i = 0; i < KEY_NUM; i++) {ret = request_irq(imx6uirq.irqkeydesc[i].irqnum, imx6uirq.irqkeydesc[i].handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, imx6uirq.irqkeydesc[i].name, &imx6uirq);if(ret < 0){printk("irq %d request failed!\r\n", imx6uirq.irqkeydesc[i].irqnum);return -EFAULT;}}/* 創(chuàng)建定時(shí)器 */init_timer(&imx6uirq.timer);imx6uirq.timer.function = timer_function;/* 初始化等待隊(duì)列頭 */init_waitqueue_head(&imx6uirq.r_wait);return 0; }/** @description : 打開設(shè)備* @param - inode : 傳遞給驅(qū)動(dòng)的inode* @param - filp : 設(shè)備文件,file結(jié)構(gòu)體有個(gè)叫做private_data的成員變量* 一般在open的時(shí)候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。* @return : 0 成功;其他 失敗*/ static int imx6uirq_open(struct inode *inode, struct file *filp) {filp->private_data = &imx6uirq; /* 設(shè)置私有數(shù)據(jù) */return 0; }/** @description : 從設(shè)備讀取數(shù)據(jù) * @param - filp : 要打開的設(shè)備文件(文件描述符)* @param - buf : 返回給用戶空間的數(shù)據(jù)緩沖區(qū)* @param - cnt : 要讀取的數(shù)據(jù)長(zhǎng)度* @param - offt : 相對(duì)于文件首地址的偏移* @return : 讀取的字節(jié)數(shù),如果為負(fù)值,表示讀取失敗*/ static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {int ret = 0;unsigned char keyvalue = 0;unsigned char releasekey = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;if (filp->f_flags & O_NONBLOCK) { /* 非阻塞訪問 */if(atomic_read(&dev->releasekey) == 0) /* 沒有按鍵按下,返回-EAGAIN */return -EAGAIN;} else { /* 阻塞訪問 *//* 加入等待隊(duì)列,等待被喚醒,也就是有按鍵按下 */ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey)); if (ret) {goto wait_error;}}keyvalue = atomic_read(&dev->keyvalue);releasekey = atomic_read(&dev->releasekey);if (releasekey) { /* 有按鍵按下 */ if (keyvalue & 0x80) {keyvalue &= ~0x80;ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));} else {goto data_error;}atomic_set(&dev->releasekey, 0);/* 按下標(biāo)志清零 */} else {goto data_error;}return 0;wait_error:return ret; data_error:return -EINVAL; }/** @description : poll函數(shù),用于處理非阻塞訪問* @param - filp : 要打開的設(shè)備文件(文件描述符)* @param - wait : 等待列表(poll_table)* @return : 設(shè)備或者資源狀態(tài),*/ unsigned int imx6uirq_poll(struct file *filp, struct poll_table_struct *wait) {unsigned int mask = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;poll_wait(filp, &dev->r_wait, wait); /* 將等待隊(duì)列頭添加到poll_table中 */if(atomic_read(&dev->releasekey)) { /* 按鍵按下 */mask = POLLIN | POLLRDNORM; /* 返回PLLIN */}return mask; }/* 設(shè)備操作函數(shù) */ static struct file_operations imx6uirq_fops = {.owner = THIS_MODULE,.open = imx6uirq_open,.read = imx6uirq_read,.poll = imx6uirq_poll, };/** @description : 驅(qū)動(dòng)入口函數(shù)* @param : 無* @return : 無*/ static int __init imx6uirq_init(void) {/* 1、構(gòu)建設(shè)備號(hào) */if (imx6uirq.major) {imx6uirq.devid = MKDEV(imx6uirq.major, 0);register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME);} else {alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);imx6uirq.major = MAJOR(imx6uirq.devid);imx6uirq.minor = MINOR(imx6uirq.devid);}/* 2、注冊(cè)字符設(shè)備 */cdev_init(&imx6uirq.cdev, &imx6uirq_fops);cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT);/* 3、創(chuàng)建類 */imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.class)) { return PTR_ERR(imx6uirq.class);}/* 4、創(chuàng)建設(shè)備 */imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.device)) {return PTR_ERR(imx6uirq.device);}/* 5、始化按鍵 */atomic_set(&imx6uirq.keyvalue, INVAKEY);atomic_set(&imx6uirq.releasekey, 0);keyio_init();return 0; }/** @description : 驅(qū)動(dòng)出口函數(shù)* @param : 無* @return : 無*/ static void __exit imx6uirq_exit(void) {unsigned i = 0;/* 刪除定時(shí)器 */del_timer_sync(&imx6uirq.timer); /* 刪除定時(shí)器 *//* 釋放中斷 */ for (i = 0; i < KEY_NUM; i++) {free_irq(imx6uirq.irqkeydesc[i].irqnum, &imx6uirq);}cdev_del(&imx6uirq.cdev);unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);device_destroy(imx6uirq.class, imx6uirq.devid);class_destroy(imx6uirq.class); } module_init(imx6uirq_init); module_exit(imx6uirq_exit); MODULE_LICENSE("GPL");

第32 行,修改設(shè)備文件名字為“noblockio”,當(dāng)驅(qū)動(dòng)程序加載成功以后就會(huì)在根文件系統(tǒng)中出現(xiàn)一個(gè)名為“/dev/noblockio”的文件。

第202~204 行,判斷是否為非阻塞式讀取訪問,如果是的話就判斷按鍵是否有效,也就是判斷一下有沒有按鍵按下,如果沒有的話就返回-EAGAIN。

第241~ 252 行,imx6uirq_poll 函數(shù)就是file_operations 驅(qū)動(dòng)操作集中的poll 函數(shù),當(dāng)應(yīng)用程序調(diào)用select 或者poll 函數(shù)的時(shí)候imx6uirq_poll 函數(shù)就會(huì)執(zhí)行。第246 行調(diào)用poll_wait 函數(shù)將等待隊(duì)列頭添加到poll_table 中,第

248~250 行判斷按鍵是否有效,如果按鍵有效的話就向應(yīng)用程序返回POLLIN 這個(gè)事件,表示有數(shù)據(jù)可以讀取。

第259 行,設(shè)置file_operations 的poll 成員變量為imx6uirq_poll。

2、編寫測(cè)試APP
新建名為noblockioApp.c 測(cè)試APP 文件,然后在其中輸入如下所示內(nèi)容:

#include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" #include "poll.h" #include "sys/select.h" #include "sys/time.h" #include "linux/ioctl.h" /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : noblockApp.c 作者 : 左忠凱 版本 : V1.0 描述 : 非阻塞訪問測(cè)試APP 其他 : 無 使用方法 :./blockApp /dev/blockio 打開測(cè)試App 論壇 : www.openedv.com 日志 : 初版V1.0 2019/9/8 左忠凱創(chuàng)建 ***************************************************************//** @description : main主程序* @param - argc : argv數(shù)組元素個(gè)數(shù)* @param - argv : 具體參數(shù)* @return : 0 成功;其他 失敗*/ int main(int argc, char *argv[]) {int fd;int ret = 0;char *filename;struct pollfd fds;fd_set readfds;struct timeval timeout;unsigned char data;if (argc != 2) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR | O_NONBLOCK); /* 非阻塞訪問 */if (fd < 0) {printf("Can't open file %s\r\n", filename);return -1;}#if 0/* 構(gòu)造結(jié)構(gòu)體 */fds.fd = fd;fds.events = POLLIN;while (1) {ret = poll(&fds, 1, 500);if (ret) { /* 數(shù)據(jù)有效 */ret = read(fd, &data, sizeof(data));if(ret < 0) {/* 讀取錯(cuò)誤 */} else {if(data)printf("key value = %d \r\n", data);} } else if (ret == 0) { /* 超時(shí) *//* 用戶自定義超時(shí)處理 */} else if (ret < 0) { /* 錯(cuò)誤 *//* 用戶自定義錯(cuò)誤處理 */}} #endifwhile (1) { FD_ZERO(&readfds);FD_SET(fd, &readfds);/* 構(gòu)造超時(shí)時(shí)間 */timeout.tv_sec = 0;timeout.tv_usec = 500000; /* 500ms */ret = select(fd + 1, &readfds, NULL, NULL, &timeout);switch (ret) {case 0: /* 超時(shí) *//* 用戶自定義超時(shí)處理 */break;case -1: /* 錯(cuò)誤 *//* 用戶自定義錯(cuò)誤處理 */break;default: /* 可以讀取數(shù)據(jù) */if(FD_ISSET(fd, &readfds)) {ret = read(fd, &data, sizeof(data));if (ret < 0) {/* 讀取錯(cuò)誤 */} else {if (data)printf("key value=%d\r\n", data);}}break;} }close(fd);return ret; }

第52~73 行,這段代碼使用poll 函數(shù)來實(shí)現(xiàn)非阻塞訪問,在while 循環(huán)中使用poll 函數(shù)不斷的輪詢,檢查驅(qū)動(dòng)程序是否有數(shù)據(jù)可以讀取,如果可以讀取的話就調(diào)用read 函數(shù)讀取按鍵數(shù)據(jù)。
第75~101 行,這段代碼使用select 函數(shù)來實(shí)現(xiàn)非阻塞訪問。

運(yùn)行測(cè)試

1、編譯驅(qū)動(dòng)程序和測(cè)試APP
①、編譯驅(qū)動(dòng)程序
編寫Makefile 文件,本章實(shí)驗(yàn)的Makefile 文件和第四十章實(shí)驗(yàn)基本一樣,只是將obj-m 變量的值改為noblockio.o,Makefile 內(nèi)容如下所示:

1 KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek ...... 4 obj-m := noblockio.o ...... 11 clean: 12 $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4 行,設(shè)置obj-m 變量的值為noblockio.o。
輸入如下命令編譯出驅(qū)動(dòng)模塊文件:

make -j32

編譯成功以后就會(huì)生成一個(gè)名為“noblockio.ko”的驅(qū)動(dòng)模塊文件。
②、編譯測(cè)試APP
輸入如下命令編譯測(cè)試noblockioApp.c 這個(gè)測(cè)試程序:

arm-linux-gnueabihf-gcc noblockioApp.c -o noblockioApp

編譯成功以后就會(huì)生成noblcokioApp 這個(gè)應(yīng)用程序。
2、運(yùn)行測(cè)試
將上一小節(jié)編譯出來noblockio.ko 和noblockioApp 這兩個(gè)文件拷貝到
rootfs/lib/modules/4.1.15 目錄中,重啟開發(fā)板,進(jìn)入到目錄lib/modules/4.1.15 中,輸入如下命令
加載blockio.ko 驅(qū)動(dòng)模塊:

depmod //第一次加載驅(qū)動(dòng)的時(shí)候需要運(yùn)行此命令 modprobe noblockio.ko //加載驅(qū)動(dòng)

驅(qū)動(dòng)加載成功以后使用如下命令打開noblockioApp 這個(gè)測(cè)試APP,并且以后臺(tái)模式運(yùn)行:

./noblockioApp /dev/noblockio &

按下開發(fā)板上的KEY0 按鍵,結(jié)果如圖52.3.3.1 所示:

當(dāng)按下KEY0 按鍵以后noblockioApp 這個(gè)測(cè)試APP 就會(huì)打印出按鍵值。輸入“top”命令,查看noblockioAPP 這個(gè)應(yīng)用APP 的CPU 使用率,如圖52.3.3.2 所示:

從圖52.3.3.2 可以看出,采用非阻塞方式讀處理以后,noblockioApp 的CPU 占用率也低至0.0%,和圖52.2.3.2 中的blockioApp 一樣,這里的0.0%并不是說noblockioApp 這個(gè)應(yīng)用程序不使用CPU 了,只是因?yàn)槭褂寐侍×?#xff0c;而圖中只能顯示出小數(shù)點(diǎn)后一位,因此就顯示成了0.0%。
如果要“殺掉”處于后臺(tái)運(yùn)行模式的noblockioApp 這個(gè)應(yīng)用程序,可以參考52.2.3 小節(jié)講解的方法。

總結(jié)

以上是生活随笔為你收集整理的Linux 阻塞和非阻塞IO 实验的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

最新av在线播放 | 国内精品久久久久久久97牛牛 | 国产精品综合在线观看 | 日韩成人黄色av | 国产在线高清视频 | 美女网站在线播放 | 三级黄免费看 | 日韩经典一区二区三区 | 91亚洲精品久久久蜜桃网站 | 91精品伦理 | 久久亚洲综合国产精品99麻豆的功能介绍 | 91精品国产自产在线观看 | 久久久久久久久久久免费 | 黄色资源网站 | 精品国产一区二区在线 | 天天射天天干天天操 | 久产久精国产品 | 欧美一级电影在线观看 | 天天操夜夜想 | 不卡视频在线看 | 久久99久久99免费视频 | 91亚洲国产成人 | 国产日韩三级 | 免费看黄色大全 | 久久最新 | 久久色在线观看 | 久久国产经典视频 | 四虎国产精 | 国模视频一区二区 | 久久精品一二三区白丝高潮 | 探花视频在线观看 | 精品一区二区三区久久 | 2017狠狠干| 在线观看中文字幕 | 青春草视频 | 天天做天天爱天天爽综合网 | 97色免费视频 | 天天操比 | 91精品麻豆 | 日韩高清在线观看 | 成人动漫精品一区二区 | 久久久久久久久久免费视频 | 色在线视频网 | av久久在线 | 特级西西人体444是什么意思 | 97av影院| 久久久久久久免费看 | 久久噜噜少妇网站 | 九九热在线精品 | 欧美日韩精品在线免费观看 | 国外调教视频网站 | 国产高清不卡在线 | 91av视频播放 | 国产毛片久久 | 国产精品久久久久一区二区三区 | 999久久精品 | 日韩黄色大片在线观看 | 国产极品尤物在线 | 久草在线视频网站 | 免费看片成人 | 亚洲国产网站 | 国产精品永久久久久久久久久 | 成人福利在线观看 | 色婷婷六月天 | 日韩电影中文字幕在线 | 日韩中文幕| 中文字幕在线字幕中文 | 国产又粗又猛又黄视频 | 九九九热精品免费视频观看网站 | 亚洲国产小视频在线观看 | 中中文字幕av在线 | 精品视频在线观看 | 欧美一级片免费在线观看 | 97天堂| 色999视频 | 亚洲精品国产精品国自 | 色999视频 | www.夜色321.com | 人人插人人爱 | 精品美女久久 | 久久艹久久| 97精品国产97久久久久久粉红 | 久久一久久| 天天天天天天天操 | 91在线免费观看国产 | 奇米777777| 久久艹人人 | 国产免费不卡 | 欧洲亚洲国产视频 | 美女国内精品自产拍在线播放 | 日韩欧美视频免费观看 | 国产在线精品一区二区 | 一区二区三区在线不卡 | 中文字幕免费高清 | 中文字幕日韩高清 | 亚洲一区日韩在线 | 成人av免费在线观看 | 久久tv | 日韩高清精品免费观看 | 久久免费在线视频 | 色婷婷88av视频一二三区 | 国产麻豆传媒 | 国产成人一区二区三区影院在线 | 国产精品99精品久久免费 | 激情网色 | 日韩久久精品一区二区三区下载 | 在线免费观看麻豆视频 | 欧美性一级观看 | 波多野结衣理论片 | 亚洲欧美精品一区二区 | 国产精品v a免费视频 | 日本精品中文字幕在线观看 | 91久久国产综合精品女同国语 | 欧美日本一二三 | 天天干天天操天天射 | 天天色棕合合合合合合 | 四虎国产精品免费观看视频优播 | 日韩毛片在线播放 | 亚洲激情在线 | 一级欧美一级日韩 | 九色琪琪久久综合网天天 | 欧美一级黄色视屏 | 国产成人三级在线 | 青青河边草免费观看完整版高清 | 午夜12点 | 18国产精品白浆在线观看免费 | 99视频精品免费观看, | 91视频在线免费看 | 成人av一区二区在线观看 | 中文字幕视频免费观看 | 视色网站 | 色综合婷婷久久 | 成人av一区二区在线观看 | 啪嗒啪嗒免费观看完整版 | 久久精品视频网 | 国产网红在线观看 | 91香蕉视频在线 | 国产成人性色生活片 | 91亚洲视频在线观看 | 亚洲一区二区麻豆 | 日韩毛片久久久 | 国产一级一级国产 | 色婷婷www| 九七在线视频 | 中文字幕久久网 | 在线婷婷| 黄色天堂在线观看 | 国产一级片免费观看 | 亚洲播放一区 | 国产高清免费观看 | 特级a老妇做爰全过程 | 日韩欧美一区二区三区视频 | 在线精品国产 | 久草在线99| 国产涩涩网站 | 国产精品久久电影观看 | 福利视频导航网址 | 中文字幕超清在线免费 | 天天干天天草 | 日韩精品大片 | 久久不见久久见免费影院 | 国产精品黄色 | 91精品国自产在线观看欧美 | 婷婷av在线 | 成人国产精品电影 | 国产精品不卡视频 | 五月激情亚洲 | 一区二区三区精品在线视频 | 国产一级在线观看 | 久久视频在线视频 | 国产91九色视频 | 国产剧情在线一区 | 精品久久久成人 | 国产一区二区高清视频 | 中文字幕丝袜制服 | 日韩午夜在线 | 天天干,夜夜爽 | 国产成人精品三级 | 日韩欧美电影在线观看 | 四虎4hu永久免费 | 亚洲精品男女 | 99精品国产在热久久下载 | 婷婷久久久 | 日韩精品久久一区二区三区 | 亚洲成人家庭影院 | 国产九九热视频 | 天天操天天色天天射 | av线上看| 色噜噜在线观看 | 五月激情丁香婷婷 | 国内精品久久久久久久久久久久 | 色婷婷97 | 亚洲视频免费在线 | 精品视频久久久久久 | 亚洲aaa级 | 国产福利91精品张津瑜 | 国产在线观看中文字幕 | av网站在线观看免费 | 黄a网站 | 精品久久久久久亚洲综合网站 | 欧美日韩91 | www久久国产 | 在线观看日韩免费视频 | 午夜精品久久久久久中宇69 | 福利视频导航网址 | 97视频久久久 | 欧美精品乱码久久久久 | 久久不卡电影 | 天天干天天操天天爱 | 国产精品男女视频 | 亚洲国内在线 | 久久综合色一综合色88 | 黄色一级在线免费观看 | 奇米网8888 | 成人app在线免费观看 | 国产亚洲精品日韩在线tv黄 | 亚洲免费av电影 | 一本一本久久a久久精品牛牛影视 | 91中文在线 | 免费看污片 | 超碰97在线人人 | av色影院| 国产xxxx做受性欧美88 | 国内亚洲精品 | 国产系列 在线观看 | 911国产精品 | 国产成人精品av在线观 | 国内精品久久久久久久影视麻豆 | av在线之家电影网站 | 在线观看午夜av | 久久视频在线观看免费 | 久草在线视频在线观看 | 人人插人人看 | 欧美一区二区三区在线观看 | 欧美日韩一区二区免费在线观看 | 国产精品初高中精品久久 | 国产精品乱码高清在线看 | 久久久久国产成人精品亚洲午夜 | 国产视频 亚洲视频 | 色狠狠操 | 国产在线播放不卡 | 四虎影视久久久 | 国产精品嫩草影院99网站 | 美女网站视频久久 | 中文字幕资源在线 | 激情综合六月 | 美女免费黄网站 | 色婷婷狠狠五月综合天色拍 | 亚洲精区二区三区四区麻豆 | 热久久国产 | 麻豆免费看片 | 国产aaa大片 | 国产精品一区久久久久 | 国产成人三级三级三级97 | 色五月色开心色婷婷色丁香 | 91正在播放| av中文字幕网址 | 激情文学丁香 | av免费看看 | 97久久精品午夜一区二区 | av电影免费在线看 | 中文字幕一区二区三区乱码不卡 | 久草网站在线 | 在线不卡视频 | 国产在线无 | 中文字幕乱偷在线 | 摸bbb搡bbb搡bbbb | 免费av片在线 | 天天做天天爱夜夜爽 | 欧美国产日韩在线视频 | 国产91学生 | 操操操人人人 | 黄影院| 亚洲一区二区三区四区精品 | 色七七亚洲影院 | 国产视频2区| 四虎影视国产精品免费久久 | 中文字幕日韩在线播放 | 一区二区三区在线免费观看视频 | 99久久99久久精品国产片果冰 | 天天操夜夜操天天射 | 亚洲h在线播放在线观看h | 国产精品久久久久国产a级 激情综合中文娱乐网 | 亚洲欧美视频在线播放 | 国产精品黄色av | 精品视频久久久久久 | 91亚·色| 99亚洲精品在线 | 日韩伦理一区二区三区av在线 | 外国av网| 日韩精品一区在线播放 | 久久久亚洲精华液 | 国产欧美最新羞羞视频在线观看 | 午夜视频99 | 日韩区视频 | 热久久99这里有精品 | 国产区精品在线 | 在线国产99 | 精品一区二区在线观看 | 日韩一区二区三区免费视频 | 国产精品毛片一区二区 | 成人av免费电影 | 国产黄色大片 | 国产精品高潮呻吟久久av无 | 日韩精品视频免费 | 美女av免费| 又黄又刺激的网站 | 久热只有精品 | 国产小视频福利在线 | 成人啊 v| 国产美女精品久久久 | 成年人在线免费看视频 | 伊人看片 | 欧美一区二区三区特黄 | 国内外激情视频 | 免费a视频在线观看 | 夜夜夜影院 | 日韩精品亚洲专区在线观看 | 亚洲精品在线观看网站 | 久久精品免费播放 | 久草网站在线观看 | 成人在线小视频 | av大全在线 | 射射射综合网 | 天天视频色版 | 久久久黄视频 | 久久无码av一区二区三区电影网 | 一本色道久久精品 | 免费在线观看av不卡 | 国产一区二区三区高清播放 | 日本中文在线 | 精品国产一区二区久久 | 99在线观看免费视频精品观看 | 97品白浆高清久久久久久 | 国产一区二区手机在线观看 | 黄色网址在线播放 | 欧美成人精品在线 | 日韩一区二区三 | 少妇搡bbbb搡bbb搡忠贞 | 国产精品女同一区二区三区久久夜 | 久久五月婷婷丁香 | 欧美激情另类文学 | 久久精品中文字幕一区二区三区 | 91资源在线视频 | 日日夜夜精品 | 久久久精品欧美一区二区免费 | 色美女在线 | 成人影视免费 | 性色av免费看 | 亚洲视频在线免费看 | 狠狠狠色狠狠色综合 | 精品视频一区在线观看 | 免费午夜av | 精品久久91 | 91精品入口| 深夜免费福利在线 | 日本精品视频一区二区 | 久久激情影院 | 国产99久久99热这里精品5 | 久久成人免费电影 | 日韩在线观看中文 | 超碰97中文| 一区二区精品在线 | 免费涩涩网站 | 六月色丁香 | 在线观看91精品国产网站 | 亚洲国产欧美在线人成大黄瓜 | 日韩免费区 | 国产剧情一区二区 | 狠狠狠狠狠操 | 欧美a级片网站 | 国产大片免费久久 | 国产亲近乱来精品 | 天天干天天做天天爱 | 成人av中文字幕 | 免费在线观看国产精品 | 国产精品久久久99 | 播五月综合| 亚洲高清视频在线播放 | 九九免费在线观看视频 | 天天色天天 | 国产69久久久欧美一级 | 亚洲综合视频网 | 成人在线播放免费观看 | 成人毛片100免费观看 | 一区二区中文字幕在线观看 | 中文字幕久久亚洲 | 丝袜护士aⅴ在线白丝护士 天天综合精品 | 久草线| av不卡网站 | 天堂av影院 | 精品一区 在线 | 六月丁香六月婷婷 | 91精品资源| 中文字幕在线播放av | 99在线精品免费视频九九视 | 特级黄色一级 | 中文字幕免费一区二区 | 99久久久久| 久久草av | 天天干人人| 久久成人久久 | 人人爽人人爽人人爽学生一级 | 97视频免费在线看 | 国产在线观看一 | 视频一区久久 | 日韩视频三区 | 日本女人逼| 日本中文字幕免费观看 | 超碰精品在线 | 成人动漫视频在线 | 久久夜色电影 | 激情视频一区 | 91成年人网站 | 黄色毛片在线观看 | 国产亚洲在线 | 开心激情久久 | 亚洲专区在线播放 | 免费99精品国产自在在线 | 亚洲精品视频在线观看视频 | 国产精品久久久久久久久免费 | 色小说在线 | 99日精品 | 午夜久久福利视频 | 国产成人精品久久久久 | 91麻豆精品国产91久久久更新时间 | 久久这里精品视频 | 国产馆在线播放 | 色婷婷狠 | 久久毛片网 | 免费观看性生交大片3 | 最近中文字幕免费视频 | 伊人色综合网 | 久久久久久久国产精品影院 | 六月丁香综合 | 久久er99热精品一区二区三区 | 成人免费看电影 | 日韩av一区二区三区四区 | 国产黄色电影 | 天天综合网天天 | 伊人夜夜 | 91成人在线观看高潮 | 久久中文视频 | 久久综合视频网 | 国产精品成人在线观看 | 亚洲理论视频 | 在线观看国产福利片 | 国产亚洲视频系列 | 2022国产精品视频 | 日日噜噜噜噜夜夜爽亚洲精品 | 国产不卡在线视频 | 国产精品99久久久久 | 国产精品毛片久久蜜 | 在线天堂中文www视软件 | 黄色一级大片在线免费看国产一 | 国产精品美女久久久久久 | 狠狠地日 | 色综合久久中文字幕综合网 | 国产精品久久久久一区二区国产 | 美女免费黄网站 | 久久久久电影 | 免费一级片视频 | 欧美一区二区三区四区夜夜大片 | 亚洲高清在线观看视频 | 亚洲最快最全在线视频 | 天天插天天爱 | 亚洲男男gaygay无套同网址 | 一级黄色免费网站 | 一区二区国产精品 | 久久99亚洲精品久久久久 | 国产成人精品av在线观 | 国产精品欧美精品 | 国产精品永久久久久久久www | 天天爽天天做 | 色偷偷男人的天堂av | av网站在线观看免费 | 国产在线视频一区二区三区 | 精品视频在线观看 | 国产高清综合 | 国产精品不卡一区 | 精品电影一区二区 | 91最新在线视频 | 黄色h在线观看 | 欧美精品乱码99久久影院 | 亚洲成aⅴ人在线观看 | 午夜国产福利视频 | 黄色资源在线观看 | 91精品免费在线视频 | 久草视频免费在线观看 | 精品在线观看一区二区三区 | 欧美成人h版 | 最新日韩精品 | 97视频在线观看播放 | 中文字幕亚洲精品日韩 | 成片人卡1卡2卡3手机免费看 | 视频一区二区国产 | 超碰人人干人人 | 狠狠色狠狠综合久久 | 婷婷综合av | 精品美女国产在线 | 中国一级片在线播放 | 六月激情 | 精品一区电影国产 | 在线观看国产日韩欧美 | 婷婷丁香色 | 九九九在线观看视频 | 91黄色小视频| 成人国产精品一区 | 国产精品欧美久久久久天天影视 | 久久99免费观看 | 高清国产午夜精品久久久久久 | 久久99九九99精品 | 国产日韩欧美在线一区 | 久久综合久久久 | 久久艹免费 | 91自拍视频在线观看 | 天天操天天干天天综合网 | 人人爽人人爽av | 九九精品毛片 | 国产二区电影 | 天天天干天天射天天天操 | 色网免费观看 | 97成人精品区在线播放 | 最新av免费在线观看 | 国产精品成人免费精品自在线观看 | 日韩理论电影在线观看 | 久久精品网址 | adn—256中文在线观看 | 国产又黄又爽无遮挡 | 国产精品国产三级国产 | 精品黄色在线观看 | 欧美天天综合 | 国产亚洲在 | 国产一区二区在线播放 | 国产成人精品久久久久蜜臀 | sm免费xx网站 | 91精品国产一区二区在线观看 | 日韩乱色精品一区二区 | 欧美天天干 | av片在线看| 久久精品成人热国产成 | 六月丁香婷婷久久 | 久久视频精品在线 | 91色偷偷| 在线观看视频国产一区 | 亚洲欧美日韩在线看 | 国产区网址| 国产麻豆精品久久一二三 | 草久久久久久久 | 亚洲欧美成人综合 | 国产一区二区不卡在线 | 日韩av成人在线观看 | 91麻豆精品久久久久久 | 日韩在线免费观看视频 | 网站在线观看日韩 | 亚洲国产三级在线观看 | 亚洲日韩中文字幕在线播放 | 在线国产不卡 | 成年人视频在线观看免费 | 久草精品资源 | 久久最新网址 | 国产精品毛片久久 | 国产精品99久久久精品免费观看 | 97国产小视频 | 国产亚洲精品久久 | 欧美色操 | 日韩黄色网络 | 亚洲国内精品在线 | 91视频黄色 | 亚洲在线视频播放 | 人人爽久久涩噜噜噜网站 | 国产黄色片在线免费观看 | 中文字幕免费看 | 九九视频在线观看视频6 | 日韩三级视频在线观看 | 日日夜夜狠狠操 | 精品国产伦一区二区三区 | 成人在线一区二区 | 欧美国产日韩一区二区三区 | 色天天 | 国产一区二区久久久 | 日韩av视屏 | 亚洲人av免费网站 | 一区二区国产精品 | 国产精品久久久av久久久 | 波多野结衣电影一区二区三区 | 久久精品国产精品亚洲 | 国产一级免费视频 | 69视频国产| 国产一区精品在线 | 日韩欧美国产激情在线播放 | 91色欧美 | 99r国产精品| 91亚洲网 | 偷拍福利视频一区二区三区 | 热久久影视 | 九九九九热精品免费视频点播观看 | 欧美成人精品欧美一级乱 | 91手机电影| 狠狠色伊人亚洲综合成人 | 一区精品在线 | 麻豆国产视频下载 | 日韩动态视频 | 日韩久久精品一区二区三区 | 九九精品久久 | 国产日韩欧美精品在线观看 | 婷香五月 | 亚洲乱码国产乱码精品天美传媒 | 99久久婷婷国产 | 96久久欧美麻豆网站 | 黄色视屏在线免费观看 | 日韩v在线 | 免费av网址在线观看 | 天天躁日日躁狠狠躁av麻豆 | 免费三及片| 久久综合九色综合欧美就去吻 | 成年人在线看片 | 久久黄色精品视频 | 五月综合激情婷婷 | 午夜久久久久久久久久影院 | 亚洲精品久久久久久国 | 精品国产免费观看 | 在线观看中文 | 在线激情小视频 | 日日爽日日操 | 日本黄区免费视频观看 | 亚洲九九精品 | 日韩精品中文字幕一区二区 | 亚洲精品国产视频 | 一区二区视频免费在线观看 | 久久久久国产一区二区三区 | 色天天综合网 | 三级动态视频在线观看 | 成人蜜桃网 | 欧美日韩观看 | 美女在线国产 | 日韩一二三区不卡 | 国产精品免费久久久久影院仙踪林 | 一区中文字幕 | 久久精品中文字幕免费mv | 综合伊人av | 久章草在线观看 | 欧美另类老妇 | 中文字幕一区二区三区视频 | 久久精品香蕉 | 色欧美日韩 | av一级片网站 | 蜜臀91丨九色丨蝌蚪老版 | 白丝av免费观看 | 国产小视频你懂的 | 国精产品999国精产 久久久久 | 色中射 | 99热精品久久 | 99久热精品 | 国产成人亚洲在线观看 | 国产尤物视频在线 | 天天射夜夜爽 | 欧美性做爰猛烈叫床潮 | 国产精品免费在线 | 亚洲最新av网站 | 免费视频久久久久 | 天天草天天干天天射 | 香蕉视频在线看 | 国产日韩欧美在线影视 | 91黄色视屏 | 久久综合久久久 | 天天av在线播放 | 免费看v片 | 在线观看国产v片 | 色全色在线资源网 | 欧美激情视频在线免费观看 | 国产a国产 | 五月婷婷香蕉 | 日韩高清免费电影 | 免费观看日韩av | 免费观看国产成人 | 国产一性一爱一乱一交 | 黄网站色 | 狠狠久久伊人 | 九九有精品 | 97国产在线视频 | 午夜国产一区二区三区四区 | 亚洲精品国产精品乱码在线观看 | av中文天堂在线 | 成年人视频在线免费观看 | av在线播放中文字幕 | 96看片| 精品96久久久久久中文字幕无 | 国产精选在线 | 九九爱免费视频 | 久久不卡国产精品一区二区 | 成年人看片网站 | 日本久久成人 | 热99在线 | 国产二区视频在线观看 | 黄色片网站大全 | 一区二区 不卡 | 精品国产一区二区三区久久久蜜月 | 97色涩 | 国产麻豆视频免费观看 | 欧美日韩综合在线 | 91资源在线免费观看 | 天天射天天干天天插 | 在线视频观看国产 | 久久久国产精品成人免费 | 99热9| 午夜视频在线观看网站 | 深爱激情婷婷网 | 91重口视频| 亚洲一区欧美激情 | 色九九在线 | 波多野结衣视频一区 | 久久久久久久久久久久久国产精品 | 91桃色视频 | 韩国av一区二区三区在线观看 | 国产男女爽爽爽免费视频 | 亚洲第一av在线播放 | 欧美日韩中文在线 | 国产不卡在线观看 | 国产精品精品国产 | 精品在线一区二区三区 | 91麻豆免费看 | 九九九九色 | 久久黄网站 | 欧美日韩视频网站 | 成人在线视频免费看 | 麻豆视频在线免费观看 | 日韩精品一区二 | www黄色软件 | 看全黄大色黄大片 | 在线观看涩涩 | 999毛片| 亚洲性少妇性猛交wwww乱大交 | 国产午夜三级一二三区 | 久久在线免费视频 | 久久成人亚洲欧美电影 | 久久久精品视频成人 | 99这里只有精品99 | 国产视频精品在线 | 亚洲区精品 | 天天曰 | 日韩在线视频线视频免费网站 | 日本三级国产 | 91精品免费看 | 国产精在线| 综合久久一本 | 日本久久高清视频 | 人人添人人澡人人澡人人人爽 | 中文字幕乱在线伦视频中文字幕乱码在线 | 五月天激情开心 | 色吧av色av| 99热国产在线 | 麻豆成人在线观看 | 欧美成人中文字幕 | 天天操天天摸天天射 | 国产精品99久久99久久久二8 | 久久国产免费看 | 韩国精品福利一区二区三区 | 青青河边草观看完整版高清 | 在线观看视频日韩 | 91成年视频 | 国产精品久久久av | 在线视频日韩精品 | 国产亚洲观看 | 精品国产伦一区二区三区观看体验 | 黄色av电影在线观看 | 久久色在线观看 | 91看片在线免费观看 | 亚洲黄色在线播放 | 久久综合久久综合这里只有精品 | 激情视频在线观看网址 | 九九九九热精品免费视频点播观看 | 2018好看的中文在线观看 | 人人射人人爽 | 麻豆小视频在线观看 | 一区二区 精品 | 粉嫩av一区二区三区四区在线观看 | 久草爱 | 日本亚洲国产 | 国外成人在线视频网站 | 一区二区男女 | 五月婷网 | 日日操夜 | 日韩电影中文字幕在线 | 综合精品久久久 | 久久久麻豆视频 | 在线小视频国产 | 四虎永久免费在线观看 | 国产精品久久一 | 天天天干夜夜夜操 | 久久激情电影 | www久久精品 | 精品人妖videos欧美人妖 | 久久久久久国产精品美女 | 日韩一级电影网站 | 天天操人人要 | 婷婷丁香av | 国产视频一区二区三区在线 | 欧美精品一级视频 | 久久天天躁狠狠躁亚洲综合公司 | 91精品国产成人观看 | 欧美日韩在线免费观看 | 天天干天天射天天操 | 日本中文字幕在线播放 | 国产精品资源 | 久草在线视频看看 | 久久国产亚洲 | 精品亚洲一区二区三区 | 免费观看www7722午夜电影 | 亚洲国产成人av网 | 日本狠狠干 | 婷婷六月丁香激情 | 亚洲在线视频免费 | 日日夜夜精品视频 | 久久深夜福利免费观看 | 久久亚洲专区 | 亚洲,国产成人av | 色综久久 | 国产精品嫩草影院9 | 久久精品久久久久电影 | 欧美精品一区在线 | 久久国产精品二国产精品中国洋人 | 黄色资源网站 | 天天色影院| 日日添夜夜添 | 国产三级精品在线 | 在线成人一区二区 | 国产精品女人网站 | 国产视频综合在线 | 免费av免费观看 | 黄p网站在线观看 | 丰满少妇一级 | 91av视频在线播放 | av激情五月| 中文在线免费看视频 | 日韩影视精品 | 午夜精品久久久久久中宇69 | 日批视频在线 | www.com.黄| 国产精品久久久久一区二区国产 | 久久超碰网 | 综合国产在线观看 | 国产明星视频三级a三级点| 激情av五月婷婷 | 999亚洲国产996395 | 日韩精品在线视频 | 亚洲欧洲一区二区在线观看 | 久操中文字幕在线观看 | 国产女v资源在线观看 | 在线国产精品一区 | 99精品国产99久久久久久福利 | 日本精品在线视频 | 九色在线视频 | 99在线精品免费视频九九视 | 免费污片| 国产亚洲精品中文字幕 | 久久视频这里有精品 | 成人av电影免费在线观看 | av亚洲产国偷v产偷v自拍小说 | 日本黄色一级电影 | 一区二区三区在线不卡 | 欧美日在线观看 | 色资源二区在线视频 | 国产精品久久久久久一二三四五 | 欧美色插| 天天干天天拍天天操天天拍 | 色婷婷亚洲精品 | 99久久综合国产精品二区 | 亚洲精品中文字幕视频 | 日韩精品不卡在线观看 | 国产精品一区二区在线看 | 在线激情小视频 | 午夜性福利 | 在线观看黄网站 | 在线视频 一区二区 | 欧美日韩国产精品一区二区亚洲 | 韩国av一区二区三区在线观看 | 99久久国产免费免费 | 日韩在线观看av | 国产精品久久久久久久久岛 | 色婷婷综合久久久久中文字幕1 | 国产精品一区二区麻豆 | 色天堂在线视频 | 天天躁日日躁狠狠躁 | 成人夜晚看av | 碰超在线97人人 | 婷婷播播网 | 成人午夜在线电影 | 91久久精品日日躁夜夜躁国产 | 国产精品嫩草69影院 | 97色噜噜 | 激情视频久久 | 亚洲国产中文字幕 | 免费看黄电影 | 日韩av免费观看网站 | 国产精品久久久久久吹潮天美传媒 | 国产123av | 久久97超碰 | 草久视频在线观看 | 天天干天天色2020 | 男女激情免费网站 | 97超碰免费在线 | 一区二区三区播放 | 国产精品亚洲精品 | 久草久草在线观看 | 色婷婷视频网 | 狠狠干天天操 | 成人a免费 | 国产一区二区视频在线 | 日韩中文幕 | 日韩中字在线观看 | 五月婷婷开心 | 国产精品 日韩精品 | 亚洲精品黄色片 | 中文字幕在线观看第一区 | 中文在线a√在线 | av在线最新 | 色综合久久久久综合体桃花网 | 中文字幕资源在线 | 免费av视屏| 三级在线视频观看 | 精品91久久久久 | 日韩欧美精选 | 色999视频 | 国产视频导航 | 中文字幕精品一区 | 久草| 九九热精品视频在线观看 | 国产又粗又猛又黄又爽视频 | 超碰97国产 | 日韩一级成人av | 亚洲欧洲视频 | 日韩天天干 | 99精品一区 | 91av在线免费观看 | 国产伦精品一区二区三区无广告 | 中文字幕在线一二 | 国产福利在线 | 一区二区三区免费在线观看 | 亚洲激情视频在线 | 国产99久久久欧美黑人 | 91精品视频网站 | 99在线观看 | 偷拍视频一区 | 久久久国产精品成人免费 | 91女人18片女毛片60分钟 | 人人爽人人插 | 亚洲精品国偷拍自产在线观看蜜桃 | 亚洲 欧美变态 另类 综合 | av免费在线观看1 | 久久精品国产免费看久久精品 | 精品国产一区二区三区久久 | 精品国产免费av | 久久免费大片 | 丁香综合激情 | 一区二区网 | 人人射人人澡 | 亚洲狠狠操 | 九九免费在线观看视频 | 成人永久免费 | 天天爽天天搞 | 美女网站黄在线观看 | 中文资源在线官网 | 天天看天天操 | 激情视频免费观看 | 亚洲国产精品一区二区尤物区 | 国产精品久久麻豆 | 天天操天天干天天操天天干 | 久久免费a| 欧美日韩国产在线精品 | 1000部国产精品成人观看 | 久久成人午夜视频 | 蜜臀av性久久久久蜜臀aⅴ流畅 | 久久久久久久久精 | 97成人在线视频 | 亚洲狠狠婷婷综合久久久 | 久久久久久久久久久影视 | 亚洲成av人片在线观看无 | 亚洲精品一区二区久 | 激情丁香综合五月 | 色婷婷激情五月 | 91私密保健| 欧美日韩一区久久 | 狠狠的操你 | 99一区二区三区 | 五月综合激情 | 国产一区二区三区在线免费观看 | 国产又黄又硬又爽 | 国产精品2019 | 又色又爽又黄高潮的免费视频 | 亚洲在线免费视频 | 久久激情综合网 | 日本超碰在线 | 国产综合香蕉五月婷在线 | 欧洲一区二区在线观看 | 欧美精品一区二区在线播放 | 国产一级片一区二区三区 | 久久女教师 |