精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

聊聊跨進程共享內存的內部工作原理

開發 前端
共享內存本質上共享的是內核對象 struct file,通過在不同的進程之間使用同一個 struct file 來實現的共享。當然也得需要在虛擬內存對象 vma 帶上 VM_SHARED 標記來支持。

大家好,我是飛哥!

在 Linux 系統的進程虛擬內存中,一個重要的特性就是不同進程的地址空間是隔離的。A 進程的地址 0x4000 和 B 進程的 0x4000 之間沒有任何關系。這樣確確實實是讓各個進程的運行時互相之間的影響降到了最低。某個進程有 bug 也只能自己崩潰,不會影響其它進程的運行。

但是有時候我們想要跨進程傳遞一些數據。因為進程虛擬內存地址是隔離的。所以目前業界最常用的做法是讓進程之間通過 127.0.0.1 或者是 Unix Domain Socket 等本機網絡手段進行數據的傳輸。這個方案在傳輸的數據量較小的時候工作是很不錯的。

但如果進程間想共享的數據特別大,比如說幾個 GB,那如果使用網絡 IO 方案的話,就會涉及到大量的內存拷貝的開銷,導致比較低的程序性能。這是可以采用進程間共享內存的方法來在通信時避免內存拷貝。

那么問題來了,不同進程之間的虛擬地址是隔離的,共享內存又是如何突破這個限制的呢?我們今天就來深入地了解下共享內存的內部工作原理。

一、共享內存的使用方式

共享內存發送方進程的開發基本過程是調用 memfd_create 創建一個內存文件。然后通過 mmap 系統調用為這個內存文件申請一塊共享內存。然后這個內存文件就可以寫入數據了。最后把這個文件的句柄通過 Unix Domain Socket 的方式給接收方進程發送過去。

下面是發送方的核心代碼。

int main(int argc, char **argv) {
 // 創建內存文件
 fd = memfd_create("Server memfd", ...);

 // 為內存文件申請 MAP_SHARED 類型的內存
 shm = mmap(NULL, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

 // 向共享內存中寫入數據
 sprintf(shm, "這段內容是保存在共享內存里的,接收方和發送方都能根據自己的fd訪問到這塊內容");

 // 把共享內存文件的句柄給接收方進程發送過去
 struct msghdr msgh;
 *((int *) CMSG_DATA(CMSG_FIRSTHDR(&msgh))) = fd;
 sendmsg(conn, &msgh, 0);
 ......
}

共享內存接收方的工作過程是先用 Unix Domain Socket 連接上服務器,然后使用 recvmsg 就可以收到發送方發送過來的文件句柄。

int main(int argc, char **argv) {
 // 通過 Unix Domain Socket 連接發送方
 connect(conn, (struct sockaddr *)&address, sizeof(struct sockaddr_un));

 // 通過連接取出發送方發送過來的內存文件句柄
 int size = recvmsg(conn, &msgh, 0);
 fd = *((int *) CMSG_DATA(cmsgh));

 // 讀取共享文件中的內容
 shm = mmap(NULL, shm_size, PROT_READ, MAP_PRIVATE, fd, 0);
 printf("共享內存中的文件內容是: %s\n", shm);
 ......
}

這樣這兩個進程都各自有一個文件句柄,在底層上是指向同一個內存文件的。這樣就實現了發送方和接收方之間的內存文件共享了。

圖片圖片

但我們上面介紹的是開發基本過程。按照我們開發內功修煉公眾號的風格,這還不算完,我們是要把它最底層的原理真正的弄通透才算的。所以接下來我們再深入地分析 memfd_create、 mmap、以及 Unix Domain socket sendmsg 和 recvmsg 的底層工作原理,來看看它們是如何配合來實現跨進程共享內存的。

二、共享內存文件原理

在發送方發送文件之前,需要先通過 memfd_create 來創建一個內存文件,然后再使用 mmap 為其分配內存。

2.1 創建內存文件

其中 memfd_create 函數是一個系統調用。內核中它的主要邏輯有兩個,一是調用 get_unused_fd_flags 申請一個沒使用過的文件句柄,二是調用 shmem_file_setup 創建一個共享內存文件。

圖片圖片

我們來看 memfd_create 的源碼。

// file:mm/memfd.c
SYSCALL_DEFINE2(memfd_create,
  const char __user *, uname,
  unsigned int, flags)
{
 ...
 // 申請一個未使用過的文件句柄
 fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0);

 // 創建一個共享內存的文件
 file = shmem_file_setup(name, 0, VM_NORESERVE);

 fd_install(fd, file);
 return fd;
}

其中在 shmem_file_setup 函數中又調用了 __shmem_file_setup。

// file:mm/shmem.c
static struct file *__shmem_file_setup(struct vfsmount *mnt, const char *name, ...)
{
 ...
 // 申請一個 inode
 inode = shmem_get_inode(mnt->mnt_sb, NULL, S_IFREG | S_IRWXUGO, 0,
    flags);
 inode->i_flags |= i_flags;
 inode->i_size = size;

 ...
 // 創建一個文件
 res = alloc_file_pseudo(inode, mnt, name, O_RDWR,
    &shmem_file_operations);
 return res;
}

我們都知道磁盤文件在內核的實現中是由 inode 和 struct file 對象一起組成的。其實共享內存文件也一樣,__shmem_file_setup 中就是先申請了一個 inode,然后再調用 alloc_file_pseudo 創建一個文件。值得注意的是,這個文件并非是磁盤上的文件,而只是在內存里的。

2.2 mmap申請內存

mmap 也是一個系統調用,注意我們在開篇處調用它的時候傳入的第三個 flag 參數是 MAP_SHARED。這表示的是要通過 mmap 申請一塊跨進程可共享的內存出來。mmap 的實現入口在 arch/x86/kernel/sys_x86_64.c

//file:arch/x86/kernel/sys_x86_64.c
SYSCALL_DEFINE6(mmap, unsigned long, addr, ...)
{
 return ksys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
}

接下來的這個函數的調用鏈路如下

SYSCALL_DEFINE6(mmap
-> ksys_mmap_pgoff
---> vm_mmap_pgoff
------> do_mmap_pgoff
--------> do_mmap

在 do_mmap 函數中,對輸入的 MAP_SHARED 進行了處理。

//file:mm/mmap.c
unsigned long do_mmap(struct file *file, unsigned long addr,
   unsigned long len, unsigned long prot,
   unsigned long flags, vm_flags_t vm_flags,
   unsigned long pgoff, unsigned long *populate,
   struct list_head *uf)
{
 struct mm_struct * mm = current->mm;
 ...

 // 如果包含 MAP_SHARED,則對要申請的虛擬內存設置一個 VM_SHARED
 switch (flags & MAP_TYPE) {
  case MAP_SHARED:
  case MAP_SHARED_VALIDATE:
   vm_flags |= VM_SHARED | VM_MAYSHARE; 
   ... 
 } 
 ... 

 addr = mmap_region(file, addr, len, vm_flags, pgoff, uf);
 ......
}

如果 flag 包含了 MAP_SHARED,則對要申請的虛擬內存設置一個 VM_SHARED。該標記指明的是要申請一個可以跨進程共享的內存塊。接下來進入 mmap_region 中申請虛擬內存。

//file:mm/mmap.c
unsigned long mmap_region(struct file *file, ...)
{
 struct mm_struct *mm = current->mm;
 ......

 // 申請虛擬內存vma
 vma = vm_area_alloc(mm);

 // vma初始化
 vma->vm_start = addr;
 vma->vm_end = addr + len;
 vma->vm_flags = vm_flags;
 vma->vm_page_prot = vm_get_page_prot(vm_flags);
 vma->vm_pgoff = pgoff;
 ......

 // 加入到進程的虛擬內存 vma 鏈表中來
 vma_link(mm, vma, prev, rb_link, rb_parent);
}

進程的虛擬內存地址空間在內核底層中就是由這樣一個個的 vma 來組成的。每一個 vma 都聲明的是進程虛擬地址中的某一段地址范圍已經分配出去了。在 mmap_region 函數中申請了 vma,并在內核中將其管理了起來。

這里注意我們在申請共享內存的時候,給 vma 是帶了 VM_SHARED 標記的。帶了這個標記的 vma和普通的虛擬內存不一樣。后面在發生缺頁中斷申請物理內存的時候,在不同的進程間是可以對應到同一塊物理內存的。所以可以實現進程間的共享。

圖片圖片

所以真正讓進程之間可以共享內存的是這個帶 VM_SHARED 的 vma。

三、發送方發送文件句柄

發送方在使用 memfd_create 創建出來內存文件,并用 mmap 為其申請可跨進程共享的內存后。接著就可以通過 Unix Domain Socket 中對應的 sendmsg 方法將這個共享內存文件的句柄發送出來。如下是發送的代碼示例。

static void send_fd(int conn, int fd) {
    struct msghdr msgh;
    struct iovec iov;
    ...

    // 把文件句柄放到消息中來
    *((int *) CMSG_DATA(CMSG_FIRSTHDR(&msgh))) = fd;

    // 發送出去
    sendmsg(conn, &msgh, 0);
}

sendmsg 又是一個內核提供的系統調用,它位于 net/socket.c 文件中。

//file:net/socket.c
SYSCALL_DEFINE3(sendmsg, int, fd, struct user_msghdr __user *, msg, unsigned int, flags)
{
 return __sys_sendmsg(fd, msg, flags, true);
}

該函數的調用路徑如下

SYSCALL_DEFINE3(sendmsg, ...)
-> __sys_sendmsg
---> ___sys_sendmsg
-----> ____sys_sendmsg
-------> sock_sendmsg
---------> sock_sendmsg_nosec
-----------> unix_stream_sendmsg

在 unix_stream_sendmsg 中執行了真正的發送。

//file:net/unix/af_unix.c 
static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, ...)
{
 // 把文件描述符指向的文件信息復制到 scm_cookie 中
 struct scm_cookie scm;
 scm_send(sock, msg, &scm, false);

 // 不斷構建數據包發送,直到發送完畢
    while (sent < len) {
     // 申請一塊緩存區
     skb = sock_alloc_send_pskb(sk, size - data_len, data_len,
        msg->msg_flags & MSG_DONTWAIT, &err,
        get_order(UNIX_SKB_FRAGS_SZ));

     // 拷貝數據到 skb
     err = unix_scm_to_skb(&scm, skb, !fds_sent);
     err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, size);
     
     // 直接把 skb 放到對端的接收隊列中
     skb_queue_tail(&other->sk_receive_queue, skb);
  
  //發送完畢回調
  other->sk_data_ready(other);
  sent += size;
     ...
    }
}

在 unix_stream_sendmsg 中申請了個 skb 緩存區,然后把要發送的文件句柄等數據都塞到里面,最后調用 skb_queue_tail 直接把 skb 放到 Unix Domain Socket 連接另一端的接收隊列中了。

這里注意文件句柄只有在當前進程內才是有意義的。如果直接發送 fd 出去,接收方是沒有辦法使用的。所以在 scm_send 函數中,重要的邏輯是把 fd 對應的 struct file 的指針給找了出來,放到待發送的數據里面了。只有 file 這種內核級的對象接收方才能使用。

scm_send
-> __scm_send
---> scm_fp_copy

在 scm_fp_copy 中根據 fd 把 file 給找了出來。它的指針會被放到發送數據中

//file:net/core/scm.c
static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
{
 ...
 //把每一個要發送的 fd 對應的 file 給找出來
 for (i=0; i< num; i++)
 {
  int fd = fdp[i];
  struct file *file;

  if (fd < 0 || !(file = fget_raw(fd)))
   return -EBADF;
  *fpp++ = file;
  fpl->count++;
 }
}

四、接收方接收文件

接下來接收方就可以通過 recvmsg 來接收發送方發送過來的文件了。recvmsg 系統會調用到 unix_stream_read_generic 中,然后在這個函數中把 skb 給取出來。

圖片圖片

下面是接收函數核心 unix_stream_read_generic 的源碼。

//file:net/unix/af_unix.c
static int unix_stream_read_generic(struct unix_stream_read_state *state,
        bool freezable)
{
 do {
  // 拿出一個 skb
  last = skb = skb_peek(&sk->sk_receive_queue);
  ...
 }
 ...
 if (state->msg)
  scm_recv(sock, state->msg, &scm, flags);
 return copied ? : err;
}

在 skb 拿出來后,還需要調用 scm_recv 來把 skb 中包含的文件給找出來。在 scm_recv 中調用 scm_detach_fds。

//file:net/core/scm.c
void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
{

 for (i = 0; i < fdmax; i++) {
  err = receive_fd_user(scm->fp->fp[i], cmsg_data + i, o_flags);
  if (err < 0)
   break;
 }
 ...
}

在 scm->fp->fp[i] 中包含的是發送方發送過來的 struct file 指針。這樣文件就取出來了。當然 struct file 是個內核態的對象,用戶沒有辦法使用。所以還需要再為其在新的進程中申請一個文件句柄,然后返回。本文來自公眾號「開發內功修煉」。

//file:fs/file.c
int __receive_fd(struct file *file, int __user *ufd, unsigned int o_flags)
{
 //申請一個新的文件描述符
 new_fd = get_unused_fd_flags(o_flags);
 ...

 //關聯文件
 fd_install(new_fd, get_file(file));
 return new_fd;
}

五、總結

共享內存發送方進程的開發過程基本分 memfd_create 創建內存文件、mmap 申請共享內存、Unix Domain Socket 發送文件句柄三步。

  • 第一步,memfd_create 系統調用的主要邏輯有兩個,一是調用 get_unused_fd_flags 申請一個沒使用過的文件句柄,二是調用 shmem_file_setup 創建一個共享內存文件。
  • 第二步,mmap 系統調用在調用它的時候傳入的第三個 flag 參數是 MAP_SHARED,該參數是申請一塊跨進程可共享訪問的物理內存。
  • 第三步,接著通過 Unix Domain Socket 中對應的 sendmsg 方法將這個共享內存文件的句柄發送出去。在發送時,把文件句柄對應的 struct file 指針找到并放到要封裝的 skb 數據包中了。

接收方進程的主要實現原理是 recvmsg 系統調用。在這個系統調用中,內核會把發送方發送過來的 struct file 指針取出來,然后再在當前進程下為其申請一個新的文件句柄。這個文件句柄返回給用戶進程后,用戶進程就可以用它來和另外一個進程共享地訪問同一塊內存了。

總體來看,共享內存本質上共享的是內核對象 struct file,通過在不同的進程之間使用同一個 struct file 來實現的共享。當然也得需要在虛擬內存對象 vma 帶上 VM_SHARED 標記來支持。

責任編輯:武曉燕 來源: 開發內功修煉
相關推薦

2023-09-08 09:12:57

內存緩存圖像

2023-09-27 12:22:50

Kafka架構

2025-01-10 00:00:10

2022-06-06 07:58:52

勒索軟件惡意軟件解密

2018-01-12 14:35:00

Linux進程共享內存

2024-10-15 10:59:18

Spring MVCJava開發

2012-02-01 13:42:19

2019-12-06 11:18:07

LinuxCPU架構

2024-07-08 10:56:34

Rust進程內存

2024-05-09 09:55:08

2021-08-11 14:31:52

鴻蒙HarmonyOS應用

2021-10-06 20:23:08

Linux共享內存

2022-09-21 18:06:10

Python內存管理

2012-06-29 13:54:11

Java內存原型

2024-02-29 09:28:19

2023-09-19 22:47:39

Java內存

2024-12-23 15:05:29

2024-06-11 09:22:51

2021-01-07 07:53:10

JavaScript內存管理

2009-06-23 11:49:22

跨進程消息鉤子VB.NET
點贊
收藏

51CTO技術棧公眾號

精品国产亚洲一区二区在线观看 | 99国产精品久久久久久久| 亚洲电影av在线| 超碰在线播放91| 一区二区三区伦理| 久久久高清一区二区三区| 91在线视频一区| 国产精品suv一区二区三区| 波多野结衣在线观看一区二区三区| 欧美一区二区三区不卡| 久久久久久久久久久福利| 国产一区久久精品| 久久看人人爽人人| 99视频在线播放| 黄色网址中文字幕| 日韩视频一区二区三区在线播放免费观看| 在线视频精品一| 久久久久久久穴| 国产成人免费av一区二区午夜 | 国产肥臀一区二区福利视频| 蜜桃视频网站在线| 久久久国产精品午夜一区ai换脸| 成人av免费电影| 国产精品视频一二区| 天堂va蜜桃一区二区三区| 欧美黑人一区二区三区| 26uuu成人网| 欧美限制电影| 亚洲天堂免费在线| 亚洲精品乱码久久久久久蜜桃图片| 亚洲欧美在线人成swag| 欧美性猛交xxxxxx富婆| 国模吧无码一区二区三区| 菠萝蜜视频在线观看www入口| 国产精品青草综合久久久久99| 免费精品视频一区| 亚洲 美腿 欧美 偷拍| 成人激情小说网站| 国产高清一区二区三区| av网站在线免费看| 国产一区二区三区香蕉| 91精品国产自产在线| 91视频在线视频| 日韩精品一二区| 奇米成人av国产一区二区三区| 精品无码人妻一区二区三| 欧美理论在线| 九九九久久国产免费| 欧美一区免费观看| 欧美国产精品| 久久91精品国产| 欧美黄色免费观看| 激情一区二区| 久久久在线观看| 日韩欧美高清在线观看| 999亚洲国产精| 全亚洲最色的网站在线观看| 男人天堂2024| 免费高清在线视频一区·| 国产日韩视频在线观看| 国产精品午夜福利| 成人综合在线网站| 久久国产精品免费一区| 男人天堂亚洲二区| 国产精品欧美一级免费| 2021狠狠干| 懂色av一区| 欧美午夜精品久久久久久浪潮| 青青在线视频观看| 欧美天堂在线| 日韩视频免费观看高清在线视频| 欧美一级片在线免费观看| 懂色av一区二区| 亚洲天堂视频在线观看| 午夜剧场免费在线观看| 激情综合在线| 国产成人亚洲综合青青| 亚洲综合视频在线播放| 国产成人精品亚洲777人妖| 亚洲a级在线播放观看| 天堂在线视频免费| 国产欧美一区二区精品性色| 中文字幕一区二区三区四区五区| 欧美黑人猛交的在线视频| 色综合久久久久综合99| 污污的视频免费观看| 成人盗摄视频| 亚洲男人第一网站| 91插插插插插插| 午夜综合激情| 成人亚洲激情网| 你懂的视频在线免费| 亚洲天堂精品在线观看| 日本中文字幕片| 国产精品久久久久久av公交车| 日韩精品免费在线视频观看| 青青青视频在线免费观看| 在线精品一区二区| 国产精品男女猛烈高潮激情| 黑人精品一区二区三区| 国产精品嫩草99a| 欧美亚洲日本一区二区三区 | 成人自拍视频网站| а√天堂中文在线资源bt在线| 怡红院av一区二区三区| 丰满少妇在线观看| 五月天亚洲一区| 欧美成人手机在线| 亚洲性生活大片| 久久综合资源网| a级免费在线观看| 久久亚洲精品中文字幕| 亚洲男人av电影| 久久久久久久久久久久久久久久久 | 人妻少妇被粗大爽9797pw| 日韩在线成人| 久久久精品国产| 波多野结衣一区二区三区四区| 成人污污视频在线观看| 最近中文字幕免费mv| 日韩一区二区三区免费| 亚洲精品第一国产综合精品| 黑鬼狂亚洲人videos| 奇米精品一区二区三区在线观看一| 国产麻豆日韩| 欧美1234区| 日韩一区二区免费视频| 小泽玛利亚一区| 免费成人美女在线观看| 日韩精品一区二区三区色偷偷| 日韩不卡高清视频| 福利一区二区在线| 欧美日韩dvd| 国产精品xnxxcom| 日韩一区二区三区在线播放| chinese国产精品| www亚洲一区| 国产av国片精品| 国产一区二区三区不卡av| 欧美日韩国产二区| 亚洲国产精品国自产拍久久| 亚洲免费av高清| 日本黄色福利视频| 亚洲国产老妈| 91在线免费看片| 爱看av在线| 亚洲福利视频网站| 九一国产在线观看| 久久久久久黄色| 亚洲污视频在线观看| 久久要要av| 91日本视频在线| 污网站在线免费看| 欧美tk丨vk视频| 日本一区二区网站| 91丨九色丨蝌蚪丨老版| 成年人在线看片| 欧美色就是色| 国产日韩欧美日韩大片| 超碰在线免费播放| 精品福利一二区| av大全在线观看| 中文字幕第一页久久| 午夜激情影院在线观看| 欧美日本亚洲韩国国产| 精品日韩电影| 2019年精品视频自拍| 久久精品视频网站| 免费观看毛片网站| 色88888久久久久久影院野外| 日本伦理一区二区三区| 国产精品一二三四| 每日在线更新av| 成人在线免费观看网站| 97人人做人人人难人人做| а√天堂中文在线资源8| 一个色综合导航| 国产黄色片av| 91久久一区二区| 一区二区国产精品精华液| 99在线热播精品免费| 午夜免费精品视频| 午夜亚洲福利| 欧美精品成人一区二区在线观看| 涩涩涩久久久成人精品| 97国产精品视频| 91看片在线观看| 精品欧美一区二区在线观看| 看黄色一级大片| 亚洲午夜三级在线| 超碰97av在线| 不卡视频在线观看| 日韩av自拍偷拍| 午夜在线精品偷拍| avav在线播放| 日韩av自拍| 裸模一区二区三区免费| 成人网av.com/| 热久久免费国产视频| 亚洲淫性视频| 中文字幕日韩高清| 天天操天天干天天舔| 欧美日本一区二区在线观看| 中文字幕亚洲精品在线| 亚洲精品中文在线影院| 成人性生交大片免费看无遮挡aⅴ| 成人听书哪个软件好| 手机av在线免费| 久热综合在线亚洲精品| 国产一区二区三区小说| 性xxxx欧美老肥妇牲乱| 欧美一区三区二区在线观看| 高潮久久久久久久久久久久久久 | 日韩影片在线观看| 国产欧美精品一区二区三区介绍| 成人国产二区| 午夜免费久久久久| 人交獸av完整版在线观看| 日韩中文在线不卡| 成人动漫在线免费观看| 亚洲激情在线视频| 黄色片一区二区| 日韩欧美激情一区| 99精品视频在线播放免费| 欧美日韩一区久久| 波多野结衣爱爱| 色综合久久久久综合| 天天综合天天干| 五月婷婷久久综合| 国产一级淫片a| 亚洲一区二区视频| 国产精品自拍视频一区| 亚洲图片有声小说| 免费中文字幕视频| 亚洲制服欧美中文字幕中文字幕| 小泽玛利亚一区| 亚洲精品视频免费观看| 中文字幕电影av| 亚洲蜜臀av乱码久久精品| 日韩影院一区二区| 亚洲欧美一区二区三区国产精品 | av在线播放av| 中文字幕日韩在线观看| 高清日韩av电影| 色爱av美腿丝袜综合粉嫩av| 在线观看美女网站大全免费| 色偷偷88888欧美精品久久久 | 蜜桃一区二区三区| 日本一区二区久久精品| 日本电影一区二区| 亚洲永久一区二区三区在线| 97精品国产福利一区二区三区| 在线看视频不卡| 欧美激情91| 黄页免费在线观看视频| 蜜桃av综合| 不卡av免费在线| 精品一区二区影视| 色哟哟网站在线观看| www.欧美色图| 成人黄色免费网址| 中文字幕在线观看一区二区| 国产成人无码aa精品一区| 午夜激情综合网| 无码人妻丰满熟妇奶水区码| 欧美久久久久中文字幕| 亚洲第一天堂影院| 精品无人区太爽高潮在线播放| av网站在线播放| 欧美日韩高清在线观看| 中文字幕这里只有精品| 成人激情电影一区二区| 久久夜色电影| 新呦u视频一区二区| 国产精品大片| 五十路熟女丰满大屁股| 久久精品99久久久| 人妻激情偷乱频一区二区三区| 久久精品一二三| 亚洲色婷婷一区二区三区| 色综合一区二区三区| 国产免费高清av| 亚洲欧美国产精品| 在线看福利影| 国产精品1区2区在线观看| 日本亚洲视频| 日韩精品久久一区| 国产精品啊啊啊| 在线观看免费视频高清游戏推荐| 成人黄色777网| 青青青视频在线免费观看| 午夜精品123| 精品久久久无码中文字幕| 精品调教chinesegay| 在线观看小视频| 国产欧美最新羞羞视频在线观看| 国产精品白丝一区二区三区| 亚洲欧美久久234| 99精品热视频只有精品10| 亚洲欧美日韩网站| 欧美—级在线免费片| 99视频在线看| 日韩欧美一二三区| 欧美尤物美女在线| 日本在线精品视频| 成人资源在线| 日本丰满大乳奶| 日韩和欧美的一区| 欧美成人午夜精品免费| 亚洲一区二区三区视频在线| 91九色蝌蚪91por成人| 亚洲天堂男人天堂女人天堂| 国产在线精彩视频| wwwxx欧美| 欧美va天堂在线| 国产一伦一伦一伦| 国产日韩欧美精品在线| 久久久久久久久影院| 亚洲成人久久一区| 日本性爱视频在线观看| 91精品国产高清久久久久久91裸体| 久久理论电影| 黄色一级免费大片| 国产日产欧美一区二区视频| 亚洲欧美偷拍视频| 亚洲精品不卡在线| 麻豆免费在线| 精品在线一区| 一本久道久久久| avtt香蕉久久| 精品久久久国产| 午夜小视频免费| 欧美中文字幕视频在线观看| 日韩中文av| 黑森林福利视频导航| 久久日韩粉嫩一区二区三区| 好吊妞视频一区二区三区| 日韩成人av在线| 一二三四视频在线中文| 欧美日韩一区二| 日韩av一区二区在线影视| 国产午夜福利一区| 欧美久久久一区| 18网站在线观看| 国产精品福利视频| 国产模特精品视频久久久久| 国产男男chinese网站| 色视频欧美一区二区三区| av在线资源网| 91精品久久久久久久久久| 亚洲综合色网| 日本不卡视频一区| 欧美性xxxx极品hd满灌| 国产大片在线免费观看| 国产免费一区二区三区香蕉精| 久久精品久久久| 蜜臀av粉嫩av懂色av| 懂色aⅴ精品一区二区三区蜜月| 欧美孕妇孕交xxⅹ孕妇交| 国产精品丝袜视频| 一精品久久久| 亚洲中文字幕无码av| 欧美在线免费观看亚洲| 嫩草在线视频| 成人一区二区在线| 蜜乳av另类精品一区二区| 正在播放国产对白害羞| 日韩欧美国产综合| 蜜桃视频在线观看免费视频| 日韩欧美视频第二区| 极品少妇xxxx偷拍精品少妇| 国产午夜小视频| 亚洲天堂影视av| 日韩区一区二| 日批视频在线免费看| 亚洲免费大片在线观看| 天堂91在线| 91在线精品播放| 香蕉久久夜色精品| 欧美三级日本三级| 亚洲欧美日韩另类| 国产精品亚洲欧美日韩一区在线| 欧美视频在线观看网站| 欧美激情一区二区| 好吊视频一区二区三区| 国产精品私拍pans大尺度在线 | 人人妻人人澡人人爽久久av| 国产精品va在线播放| 欧美精品导航| 欧美激情 一区| 亚洲第一精品夜夜躁人人躁| 狠狠久久综合| 欧美色图另类小说| 亚洲精品视频免费看| 91九色在线porn| 麻豆久久久av免费| 国产91精品久久久久久久网曝门 | 日韩成人av电影| 福利在线一区二区| 日韩美女视频一区|