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

Linux 多線程的信號處理機制

系統 Linux
操作系統會根據情況選擇一個線程并喚醒它,然后在該線程的執行上下文處理信號時,會先判斷有沒有收到線程級的信號,如果沒有的話再判斷是否有進程級的信號,然后進行處理。

信號機制是操作系統中非常重要的部分,它可以用于進程/線程間通信、控制進程線程的行為和處理高優的任務,GDB、CPU Profile 采集、Go 搶占式調度都依賴信號機制。信號的大致原理不是很復雜,在單進程單線程環境中,信號機制類似一個訂閱發布模式,用戶注冊信號處理函數,收到信號時,操作系統執行對應的函數,但是在多線程環境下,里面的邏輯就變得復雜了,比如說我們是否可以單獨給線程發送信號,應該在哪個線程中注冊信號處理函數,給進程發送信號時哪個線程會處理等等。本文從 Linux 源碼角度分析信號的實現原理。

進程的信號原理

首先從早期的內核代碼(1.2.13)看一下信號的實現,因為早期的代碼易于我們理解原理。我們知道 Linux 不區分進程線程,統一使用 task_struct 來表示,task_struct 中有幾個字段和信號機制相關。

unsigned long signal;  // 當前收到的信號,每一 bit 對應一個信號,比如 0b10 對應信號 2
unsigned long blocked; // 屏蔽這些信號,即不處理這些信號
/*
struct sigaction {
 __sighandler_t sa_handler; // 處理函數
 sigset_t sa_mask;
 unsigned long sa_flags;
 void (*sa_restorer)(void);
};
*/
struct sigaction sigaction[32]; // 信號對應的處理函數,和信號的值一一對應,比如信號 1 對應數組 第一個元素

了解了和信號相關的數據結構后,接著從注冊信號、發送信號、處理信號幾個方面分析信號的實現。

注冊信號

asmlinkage int sys_sigaction(int signum, const struct sigaction * action,
 struct sigaction * oldaction)
{
struct sigaction new_sa, *p;
// current 表示當前調用進程,p 指向 signum 對應的處理函數
 p = signum - 1 + current->sigaction;
if (action) {
    // 復制內存
  memcpy_fromfs(&new_sa, action, sizeof(struct sigaction));
 }
// 返回舊的處理函數
if (oldaction) {
int err = verify_area(VERIFY_WRITE, oldaction, sizeof(*oldaction));
if (err)
   return err;
  memcpy_tofs(oldaction, p, sizeof(struct sigaction));
 }
// 設置新的處理函數
if (action) {
  *p = new_sa;
  check_pending(signum);
 }
return0;
}

注冊信號就是在 task_struct 的 sigaction 中記錄信號對應的處理函數。

發送信號

我們應該都試過用 kill 命令給某個進程發送信息,我們也可以通過操作系統底層提供的 kill 系統調用給進程發送信息。

asmlinkage int sys_kill(int pid,int sig)
{
int err, retval = 0, count = 0;
// 如果沒有傳pid,則給該進程所在組所有進程發該信號
if (!pid)
return(kill_pg(current->pgrp,sig,0));
// 如果pid等于-1,則給除了自己和0進程外的所有進程發該信號
if (pid == -1) {
struct task_struct * p;
  for_each_task(p) {
   if (p->pid > 1 && p != current) {
    ++count;
    if ((err = send_sig(sig,p,0)) != -EPERM)
     retval = err;
   }
  }
return(count ? retval : -ESRCH);
 }
// 如果pid等于除-1外的負數,則取絕對值后,給該進程組發該信號
if (pid < 0) 
return(kill_pg(-pid,sig,0));
/* Normal kill */
// 否則給某個進程發該信號
return(kill_proc(pid,sig,0));
}

sys_kill 支持多種場景,這里我們只關注給指定進程發送的部分。

int kill_proc(int pid, int sig, int priv)
{
struct task_struct *p;
// 遍歷進程列表,找到對應的進程,然后發送信號
 for_each_task(p) {
if (p && p->pid == pid)
   return send_sig(sig,p,priv);
 }
return(-ESRCH);
}

int send_sig(unsigned long sig,struct task_struct * p,int priv)
{
// 設置對應的位為 1
 p->signal |= 1 << (sig-1);
return0;
}

發送信號的實現很簡單,就是在 task_struct 的 signal 字段置對應的為 1,表示收到了該信息。從這里可以看到重復發生信號可能會發生覆蓋,最終只會處理一次,不過現在的內核版本已經支持記錄每一個收到的信號了。

處理信號

剛才看到,發送信號時只是打了個標記,并沒有處理該信號,也就是執行用戶注冊的函數。那么什么時候才會處理呢?處理的時機有幾個,比如中斷處理后、系統調用結束后。下面是系統調用后處理信號的邏輯。

_system_call:
 pushl %eax   # save orig_eax
 SAVE_ALL
 // 執行系統調用
 movl _sys_call_table(,%eax,4),%eax
 call *%eax
 movl %eax,EAX(%esp)  # save the return value
 movl errno(%ebx),%edx
 negl %edx
 je ret_from_sys_call

ret_from_sys_call:
// 當天進程結構體賦值到 eax
  movl _current,%eax
// 判斷是否有信號并且沒有被屏蔽
 movl blocked(%eax),%ecx
 movl %ecx,%ebx   
 notl %ecx
 andl signal(%eax),%ecx
// 非 0 說明有信號需要處理
 jne signal_return

signal_return:
 movl %esp,%ecx
 pushl %ecx
 pushl %ebx
// 處理信號
 call _do_signal
 popl %ebx
 popl %ebx

接著看一些 do_signal 的實現。

asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
{
// mask 等于 blocked 取反,表示沒有被屏蔽的信號
unsignedlong mask = ~current->blocked;
unsignedlong handler_signal = 0;
unsignedlong *frame = NULL;
unsignedlong eip = 0;
unsignedlong signr;
struct sigaction * sa;
// 收集需要處理的信號
while ((signr = current->signal & mask)) {
    // 獲取
  __asm__("bsf %3,%1\n\t"
   "btrl %1,%0"
   :"=m" (current->signal),"=r" (signr)
   :"0" (current->signal), "1" (signr));
  signr++;
    // 哪些信息需要處理
  handler_signal |= 1 << (signr-1);
    // 執行當前信號時需要屏蔽的信息,取反再與得到最終需要處理的信息
  mask &= ~sa->sa_mask;
 }

// 當前的指令地址
 eip = regs->eip;
 frame = (unsignedlong *) regs->esp;
 signr = 1;
 sa = current->sigaction;
// 逐個信號處理
for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
    // 構造棧內存布局
  setup_frame(sa,&frame,eip,regs,signr,oldmask);
    // do_signal 執行完畢后執行的執行
  eip = (unsignedlong) sa->sa_handler;
  current->blocked |= sa->sa_mask;
  oldmask |= sa->sa_mask;
 }
// 設置信息的棧地址和指令
 regs->esp = (unsignedlong) frame;
 regs->eip = eip;  /* "return" to the first handler */
return1;
}

void setup_frame(struct sigaction * sa, 
                     unsigned long ** fp, // 當前的棧地址
                     unsigned long eip,
                     struct pt_regs * regs,
                     int signr, 
                     unsigned long oldmask)
{
      unsignedlong * frame;
      frame = *fp;
      // ...
      put_fs_long(signr, frame+1);
      put_fs_long(eip, frame+16);// 執行完處理函數后的回跳地址
      put_fs_long(regs->cs, frame+17);
      put_fs_long(regs->eflags, frame+18);
      put_fs_long(regs->esp, frame+19);
      put_fs_long(regs->ss, frame+20);
      put_fs_long(0x0000b858, CODE(0)); /* popl %eax ; movl $,%eax */
      put_fs_long(0x80cd0000, CODE(4)); /* int $0x80 */
      // 恢復現場,回到正常流程。
      put_fs_long(__NR_sigreturn, CODE(2));
}

信號處理的過程涉及到的東西比較復雜,如果大家對函數調用時棧的布局了解的話應該會比較好理解,大概就是在當前的棧上進行內存布局,讓所有的處理函數連成一條執行鏈,然后 do_signal 執行完后會從第一個執行函數開始執行,一直執行到最后一個,最終恢復現場回到正常執行流程。Go 的搶占式調度同樣是用了類似的原理,就是在信號處理函數里修改棧內存注入自定義的函數,信號處理完成后,執行自定義的函數實現搶占。

多線程的信號原理

通過剛才的介紹,大概了解了信號處理的過程和原理。但是多線程中的情況有一點不一樣,在 Linux 中,可以給進程或線程發送信號,線程有自己的接收信號和信號屏蔽字,但是進程內的所有線程共享信號處理函數,另外線程還會共享進程收到的信號。

圖片圖片

下面通過內核源碼看一下實現(2.6.11.1),該版本代碼中和信號相關的字段如下。

/*
  struct signal_struct {
    // 進程級的信號,多個線程共享
    struct sigpending shared_pending;
  }
*/
struct signal_struct *signal;   // 進程級信號
struct sighand_struct *sighand; // 進程級信號處理函數
sigset_t blocked, real_blocked; // 線程級信號屏蔽字
/*
  // 同一個信號可能收到多次,在 list 中排隊,通過 signal 表示收到了什么信號
  struct sigpending {
    struct list_head list; // 收到的所有信號
    sigset_t signal; // 收到了哪些信號
  };
*/
struct sigpending pending; // 線程級收到的信號

unsigned long sas_ss_sp; // 信號處理函數的棧
size_t sas_ss_size;

創建線程

當通過 clone 創建線程時,會對上面的字段進行處理。

asmlinkage int sys_clone(struct pt_regs regs)
{
return do_fork(...);
}

long do_fork(...)
{
struct task_struct *p;
// 復制內容
 p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);
return pid;
}

static task_t *copy_process(...)
{
int retval;
struct task_struct *p = NULL;

// 創建線程必須設置 CLONE_SIGHAN,共享信號處理函數
if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
return ERR_PTR(-EINVAL);
    
// 獲取一個新的 task_struct 結構體,內容復制當前進程的
 p = dup_task_struct(current);  
// 初始化信號相關字段
 clear_tsk_thread_flag(p, TIF_SIGPENDING);
 init_sigpending(&p->pending);

/*
    // 引用計數加一
    if (clone_flags & (CLONE_SIGHAND | CLONE_THREAD)) {
      atomic_inc(¤t->sighand->count);
      return 0;
    }
  */
  copy_sighand(clone_flags, p));

/*
    // 引用計數加一
    if (clone_flags & CLONE_THREAD) {
      atomic_inc(¤t->signal->count);
      atomic_inc(¤t->signal->live);
      return 0;
    }
  */
  copy_signal(clone_flags, p));
}

可以每個線程都有自己接收的信號(p->pending 字段),但是進程級的數據結構只是引用計數加一,也就是說它們是多個線程共享的。

發送信號

接著看給線程發送信號時的過程。

// tgid 為線程組 id,即進程 id,pid 為線程 id,即 tid
asmlinkage long sys_tgkill(int tgid, int pid, int sig)
{
struct siginfo info;
int error;
struct task_struct *p;

 info.si_signo = sig;
 info.si_errno = 0;
 info.si_code = SI_TKILL;
 info.si_pid = current->tgid;
 info.si_uid = current->uid;
// 根據線程 id 找到對應的 task_struct
 p = find_task_by_pid(pid);
 error = -ESRCH;
// 只能給同進程的線程發
if (p && (p->tgid == tgid)) {
if (sig && p->sighand) {
   error = specific_send_sig_info(sig, &info, p);
  }
 }
return error;
}

static int specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
{
int ret = 0;
 ret = send_signal(sig, info, t, &t->pending);
return ret;
}

static int send_signal(int sig, struct siginfo *info, struct task_struct *t,
   struct sigpending *signals)
{
struct sigqueue * q = NULL;
int ret = 0;
// 分配一個 struct sigqueue,表示一個信號
 q = __sigqueue_alloc(t, GFP_ATOMIC);
if (q) {
    // 插入 task_struct 結構體 pending 字段的隊列,即線程級的信號
  list_add_tail(&q->list, &signals->list);
    // ...
 }
// 設置 bitmap,表示收到該信號
  sigaddset(&signals->signal, sig);
}

給線程發送信號,最后就是在 task_struct 的 pending 字段記錄置位并把信號信息加入隊列中。接下來,再看下通過 sys_kill 給進程發送信號的流程。

asmlinkage long sys_kill(int pid, int sig)
{
struct siginfo info;

 info.si_signo = sig;
 info.si_errno = 0;
 info.si_code = SI_USER;
 info.si_pid = current->tgid;
 info.si_uid = current->uid;
return kill_something_info(sig, &info, pid);
}

static int kill_something_info(int sig, struct siginfo *info, int pid)
{
return kill_proc_info(sig, info, pid);
}

int kill_proc_info(int sig, struct siginfo *info, pid_t pid)
{
int error;
struct task_struct *p;

 read_lock(&tasklist_lock);
// 通過 pid 找到進程結構體
 p = find_task_by_pid(pid);
 error = -ESRCH;
if (p)
  error = group_send_sig_info(sig, info, p);
 read_unlock(&tasklist_lock);
return error;
}

int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{
unsignedlong flags;
int ret;

if (!ret && sig && p->sighand) {
  ret = __group_send_sig_info(sig, info, p);
 }

return ret;
}

staticint __group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{
int ret = 0;
 ret = send_signal(sig, info, p, &p->signal->shared_pending);
 __group_complete_signal(sig, p);
return0;
}

給進程發送信號的流程和線程的類似,最終都是調 send_signal,但是有一些區別,給線程發送時 send_signal 的最后一個參數是 p->pending,給進程發送時參數是 p->signal->shared_pending,所以分別是在不同的字段記錄了接收到的信號,另外還有一個核心的邏輯在__group_complete_signal 中。

// p 沒有屏蔽該信號,p 不是退出狀態,p 是當前 task_struct 或沒有待處理的信號
#define wants_signal(sig, p, mask)    \
 (!sigismember(&(p)->blocked, sig)  \
  && !((p)->state & mask)   \
  && !((p)->flags & PF_EXITING)   \
  && (task_curr(p) || !signal_pending(p)))

staticvoid __group_complete_signal(int sig, struct task_struct *p)
{
unsignedint mask;
struct task_struct *t;
// p 適合處理該信號則給 p
if (wants_signal(sig, p, mask))
  t = p;
elseif (thread_group_empty(p))
/*
   * There is just one thread and it does not need to be woken.
   * It will dequeue unblocked signals before it runs again.
   */
return;
else {
/*
   * Otherwise try to find a suitable thread.
   */
    
  t = p->signal->curr_target;
if (t == NULL)
   /* restart balancing at this thread */
   t = p->signal->curr_target = p;
    
    // 遍歷進程下的線程看哪個適合處理
while (!wants_signal(sig, t, mask)) {
   t = next_thread(t);
   if (t == p->signal->curr_target)
    /*
     * No thread needs to be woken.
     * Any eligible threads will see
     * the signal in the queue soon.
     */
    return;
  }
  p->signal->curr_target = t;
 }
// 喚醒選擇的線程處理信號
  signal_wake_up(t, ....);
return;
}

給線程發送信號時,給哪個線程發就在哪個線程的上下文執行信號處理函數,但是給進程發信號時,Linux 會根據情況選擇一個適合處理該信號的線程,然后喚醒它處理。

處理信號

最后再看下信號處理的過程。

void do_notify_resume(struct pt_regs *regs, sigset_t *oldset,
        __u32 thread_info_flags)
{
if (thread_info_flags & _TIF_SIGPENDING)
  do_signal(regs,oldset);
}

int fastcall do_signal(struct pt_regs *regs, sigset_t *oldset)
{
siginfo_t info;
int signr;
struct k_sigaction ka;
// 獲取一個信號
 signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) {
    // 處理信號
  handle_signal(signr, &info, &ka, oldset, regs);
return1;
 }

return0;
}

處理信號的過程和前面的介紹類似,修改棧內存布局,注入信號處理函數,執行完 do_notify_resume 后開始執行信號處理函數,最終返回再返回正常流程。這里主要看一下獲取信號的流程。

int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
     struct pt_regs *regs, void *cookie)
{
sigset_t *mask = ¤t->blocked;
int signr = 0;

relock:
 spin_lock_irq(¤t->sighand->siglock);
for (;;) {
struct k_sigaction *ka;
    // 獲取一個信號
  signr = dequeue_signal(current, mask, info);
    // 設置信號處理函數
  ka = ¤t->sighand->action[signr-1];
  }
return signr;
}

int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
{
// 先從線程自己的信號字段獲取
int signr = __dequeue_signal(&tsk->pending, mask, info);
// 沒有再從進程的信號獲取
if (!signr)
  signr = __dequeue_signal(&tsk->signal->shared_pending,
      mask, info);
return signr;
}

staticint __dequeue_signal(struct sigpending *pending, sigset_t *mask,
   siginfo_t *info)
{
int sig = 0;
// 獲取下一個待處理的信息
 sig = next_signal(pending, mask);
if (sig) {
    // 處理了該信號,修改相關的數據結構
if (!collect_signal(sig, pending, info))
   sig = 0;
 }
 recalc_sigpending();

return sig;
}

static inline int collect_signal(int sig, struct sigpending *list, siginfo_t *info)
{
struct sigqueue *q, *first = NULL;
int still_pending = 0;

// 從信號隊列中獲取一個信號,并判斷是否還有該信號需要處理,因為一類信號可能會收到多個
 list_for_each_entry(q, &list->list, list) {
if (q->info.si_signo == sig) {
   if (first) {
    still_pending = 1;
    break;
   }
   first = q;
  }
 }
if (first) {
    // 移出隊列
  list_del_init(&first->list);
  copy_siginfo(info, &first->info);
  __sigqueue_free(first);
    // 如果沒有該類信號則把信號 bitmap 置 0
if (!still_pending)
   sigdelset(&list->signal, sig);
 }
return1;
}

總結

通過前面的分析可以看到,在多線程環境中,哪個線程設置處理函數并不重要,因為都是進程內共享的,重要的是給線程還是進程發送信號,當給線程發送信號時,會在線程獨有的信號字段記錄收到的信號,該線程會在自己的執行上下文調用信號處理函數,當給進程發送信號時,會在所有線程都共享的字段中記錄收到的信號,而這個信號給哪個線程處理是不確定的,操作系統會根據情況選擇一個線程并喚醒它,然后在該線程的執行上下文處理信號時,會先判斷有沒有收到線程級的信號,如果沒有的話再判斷是否有進程級的信號,然后進行處理。

責任編輯:武曉燕 來源: 編程雜技
相關推薦

2010-01-21 11:27:30

linux多線程機制線程同步

2011-03-17 09:20:05

異常處理機制

2009-12-08 12:14:43

2011-07-01 14:20:59

Qt 事件

2011-07-01 14:14:34

Qt 事件

2024-03-04 10:00:35

數據庫處理機制

2009-06-02 10:32:30

Oracle并發處理

2011-04-06 10:27:46

Java異常處理

2023-09-27 15:41:32

Linux系統

2020-11-10 15:25:26

SemaphoreLinux翻譯

2011-07-21 15:20:41

java異常處理機制

2009-09-02 18:34:28

C#鼠標事件

2009-06-19 16:20:14

ASP.NET錯誤處理

2024-07-05 08:32:36

2021-07-03 17:53:52

Java異常處理機制

2009-07-09 18:15:42

JDBC事務處理

2010-03-05 15:40:16

Python異常

2009-08-05 18:09:17

C#異常處理機制

2023-03-08 08:54:59

SpringMVCJava

2023-11-08 09:49:19

Java實踐
點贊
收藏

51CTO技術棧公眾號

亚洲激情国产| 波多野结衣一区二区三区免费视频| 久久久精品日韩欧美| 国产精品福利网站| 日韩影院一区二区| 日本欧美高清| 欧美日韩国产高清一区二区| 国产在线xxxx| 岛国最新视频免费在线观看| 国产精品 日产精品 欧美精品| 91高清在线免费观看| 天堂av网手机版| 哺乳一区二区三区中文视频 | 日韩色在线观看| 99热在线这里只有精品| 免费a在线看| 99精品欧美一区二区三区小说| 国产精品日韩久久久久| 国产午夜小视频| 91综合久久一区二区| 亚洲国产一区二区三区在线观看 | 日韩激情视频在线| 中文国产在线观看| 亚洲www啪成人一区二区| 亚洲午夜精品一区二区三区他趣| 亚洲成人自拍| 少妇人妻偷人精品一区二区| 国产在线精品免费av| 欧美最顶级丰满的aⅴ艳星| 丰满少妇高潮久久三区| 精品午夜久久| 日韩精品中文字幕在线播放| 乳色吐息在线观看| 国产精品白丝久久av网站| 色婷婷综合久久久久中文一区二区 | 久久资源免费视频| 无码少妇精品一区二区免费动态| 激情小说亚洲图片| 91精品国产色综合久久ai换脸| 成人一区二区三| 依依综合在线| 婷婷夜色潮精品综合在线| 欧洲金发美女大战黑人| 久操免费在线| 亚洲色图制服诱惑| 一区二区三区三区在线| 日本欧美在线视频免费观看| 欧美激情一区二区三区在线| 美媛馆国产精品一区二区| 天堂网在线资源| 不卡电影一区二区三区| 99理论电影网| 国产哺乳奶水91在线播放| 精品一区二区三区在线观看 | 91丨九色丨尤物| 国产一区在线免费观看| 人人妻人人澡人人爽人人欧美一区| 国产精品99久久久久| 91中文精品字幕在线视频| 91 中文字幕| 久久99热狠狠色一区二区| 国产精品自产拍在线观| 96日本xxxxxⅹxxx17| 国产真实乱偷精品视频免| 国产在线精品一区免费香蕉 | 奇米色777欧美一区二区| 国产福利视频一区| 这里只有久久精品视频| 日本不卡一区二区三区高清视频| 国产精品国产三级国产aⅴ浪潮| 国产一级片免费在线观看| 日日嗨av一区二区三区四区| 国产精品久久中文| 国产精品视频无码| 国产1区2区3区精品美女| 国外成人免费视频| 黄色片视频在线观看| 欧美激情一区在线观看| 欧美一级免费在线观看| 男女免费观看在线爽爽爽视频| 亚洲国产精品久久久男人的天堂 | 538在线观看| 黑人巨大精品欧美一区免费视频| 国产一区亚洲二区三区| 视频欧美精品| 亚洲成avwww人| 日本xxx在线播放| 日韩欧美视频| 久久久久久久久爱| 精品成人无码久久久久久| 九色综合国产一区二区三区| 国产v亚洲v天堂无码| 欧美男男同志| 亚洲精品乱码久久久久久日本蜜臀| 亚洲熟妇无码一区二区三区导航| 91看片一区| 日韩欧美的一区| 亚洲av成人无码久久精品| 欧美全黄视频| 国产精品扒开腿做爽爽爽男男| 99国产精品欲| 国产视频一区在线观看| h无码动漫在线观看| 亚洲国产尤物| 亚洲成人999| 羞羞在线观看视频| 男女av一区三区二区色多| 亚洲伊人成综合成人网| 国内av一区二区三区| 亚洲韩国一区二区三区| 久久久久久久久久久久91| 欧美精品中文字幕亚洲专区| 啊v视频在线一区二区三区| 特级做a爱片免费69| 国产成人综合在线播放| 亚洲精品成人久久久998| 在线天堂新版最新版在线8| 欧美一区午夜精品| 日本一区二区视频在线播放| 国产色综合网| 国产精品自拍首页| 在线免费观看的av| 欧美日韩1区2区| 非洲一级黄色片| 在线综合亚洲| 国产欧美综合精品一区二区| 青青青青在线| 欧美日韩精品是欧美日韩精品| 国产精品久久不卡| 一区福利视频| 超碰97在线人人| 超碰在线免费播放| 4438x亚洲最大成人网| 成人免费av片| 99成人精品| 国产日韩欧美综合精品| av片在线观看| 91精品国产一区二区三区蜜臀| 国产不卡在线观看视频| 视频一区二区中文字幕| 欧美日韩一区二区视频在线 | 欧美日韩在线中文字幕| 亚洲成人av免费| aaa黄色大片| 极品av少妇一区二区| av一区二区三区四区电影| 亚洲色图美国十次| 欧美一区二区二区| 国产va在线播放| 粉嫩蜜臀av国产精品网站| 国产精品视频一二三四区| 日韩一区二区三区精品视频第3页| 日韩在线观看高清| 国产又粗又长又大视频| 国产精品三级av| 亚洲国产成人va在线观看麻豆| 欧美高清在线| 亚洲精品日韩激情在线电影| 国产激情视频在线观看| 日韩三级高清在线| 国产无套粉嫩白浆内谢| 99国产精品久| 青青青在线播放| 成人在线丰满少妇av| 成人免费网视频| 天堂成人av| 亚洲福利精品在线| 日韩精品一区不卡| 欧美国产国产综合| 91亚洲一区二区| 影音先锋久久精品| 欧美日韩国产精品一卡| 国产伊人久久| 欧美日韩国产二区| 性xxxx视频播放免费| 色视频一区二区| www.com.av| 成人美女在线观看| mm1313亚洲国产精品无码试看| 先锋资源久久| 国产精品一区二区欧美| 亚洲高清黄色| 美女999久久久精品视频| 人妻丰满熟妇av无码区hd| 91久久精品国产91性色tv| 可以免费看av的网址| 高潮精品一区videoshd| 国产成人精品无码播放| 亚洲天堂一区二区三区四区| 精品麻豆av| 国产第一亚洲| 97在线视频免费看| 91.xxx.高清在线| 亚洲成年人在线播放| 夜夜躁狠狠躁日日躁av| 天天操天天色综合| 香蕉成人在线视频| 91在线视频免费观看| 色一情一区二区| 国产精品五区| 欧洲精品视频在线| 精品免费在线| 久久99九九| 日韩精品久久久久久久软件91| 国产精品18久久久久久首页狼| 性欧美ⅴideo另类hd| 一区二区亚洲精品国产| 天天综合网在线观看| 欧美老年两性高潮| 亚洲欧美一二三区| 亚洲一区二区三区四区在线| 一级在线观看视频| 91亚洲大成网污www| 91福利视频免费观看| 日韩在线一区二区三区| 狠狠97人人婷婷五月| 欧美在线1区| 视频在线99re| 婷婷国产精品| 国产高清在线精品一区二区三区| 久久婷婷五月综合色丁香| 欧洲成人免费aa| 波多野结衣在线观看| 欧美成人午夜免费视在线看片 | 国产96在线 | 亚洲| 一区二区三区国产精华| 亚洲欧美日韩不卡一区二区三区| 亚洲免费专区| 久久涩涩网站| 国语一区二区三区| 99久久精品免费看国产一区二区三区 | 日本免费色视频| 日本不卡在线视频| 国产九九在线视频| 久久一区精品| 国产精品69页| 亚洲综合欧美| 97在线播放视频| 国产日韩欧美在线播放不卡| 免费看日本毛片| 亚洲免费观看| 自拍日韩亚洲一区在线| 亚洲人成免费| 每日在线更新av| 在线一区免费观看| 人妻熟女一二三区夜夜爱| 亚洲欧美日本日韩| 国产精品秘入口18禁麻豆免会员| 亚洲激情成人| 国产性xxxx18免费观看视频| 久久亚洲不卡| 日韩欧美国产片| 激情文学综合丁香| 亚洲热在线视频| 国产成人综合视频| 国产精品一区二区人妻喷水| 99精品国产热久久91蜜凸| 欲求不满的岳中文字幕| 26uuu精品一区二区三区四区在线 26uuu精品一区二区在线观看 | 青草影院在线观看| 亚洲精品乱码久久久久久| 欧美国产精品一二三| 亚洲午夜av在线| 日韩成人免费在线视频| 色综合久久中文字幕综合网| 色老头在线视频| 制服丝袜亚洲网站| 亚洲精品成人电影| 亚洲精品v天堂中文字幕| 日韩a在线看| 中文字幕日韩电影| 色呦呦在线资源| 欧美在线一级va免费观看| 澳门av一区二区三区| 成人黄色短视频在线观看| 最新国产精品精品视频| 欧美另类一区| 99久久婷婷| 蜜臀av无码一区二区三区| 久热精品在线| 日本成人在线免费观看| 91性感美女视频| 91大神福利视频| 偷拍亚洲欧洲综合| 久久精品国产亚洲av麻豆蜜芽| 欧美精选午夜久久久乱码6080| 性色av蜜臀av| 亚洲人成亚洲人成在线观看| 麻豆传媒在线免费| 91精品国产91久久久久福利| 激情亚洲小说| 国内一区在线| 亚洲综合婷婷| 一本久道综合色婷婷五月| 国产激情一区二区三区| 最新中文字幕视频| 亚洲免费观看视频| 中文字幕精品视频在线观看| 日韩一区二区在线看片| 女人偷人在线视频| 美女av一区二区三区| 国产成人精品亚洲日本在线观看| 999国内精品视频在线| 国产区精品区| 免费高清一区二区三区| 美女尤物国产一区| 91精品国产自产| 亚洲激情在线激情| 做爰视频毛片视频| 国产视频精品xxxx| 青青青草视频在线| 91精品久久久久久久久久另类| 欧美18xxxx| 777久久精品一区二区三区无码| 日本视频中文字幕一区二区三区| 日韩无码精品一区二区| 亚洲人吸女人奶水| 一区二区自拍偷拍| 亚洲欧洲免费视频| 欧美男男tv网站在线播放| 成人在线视频电影| 911精品美国片911久久久| 国产三级日本三级在线播放| 91网上在线视频| 国产真实乱偷精品视频| 日韩三级视频在线观看| 乱人伦中文视频在线| 国产美女91呻吟求| 九九在线精品| 久久精品.com| 99re8在线精品视频免费播放| 青青操视频在线播放| 91精品综合久久久久久| 幼a在线观看| 国产乱人伦真实精品视频| 成人精品电影| 婷婷激情四射五月天| 国产三级一区二区| 一级黄色在线观看| 亚洲性生活视频| 欧美色网在线| 欧美重口乱码一区二区| 日韩精品福利网| 一区二区黄色片| 91久久国产最好的精华液| 麻豆国产在线播放| 国产精品91久久久| 欧美日韩一二三四| 亚洲a级黄色片| 亚洲欧美日韩国产手机在线| www天堂在线| 欧美激情aaaa| 理论片一区二区在线| 国产超级av在线| 久久精品亚洲国产奇米99| 樱花视频在线免费观看| 最新的欧美黄色| 少妇高潮一区二区三区99| 三级在线免费观看| 国产不卡一区视频| 国产午夜免费视频| 精品在线观看国产| 99欧美精品| 国产免费xxx| 91亚洲精品久久久蜜桃网站| 精品人妻一区二区三区免费看| 亚洲网址你懂得| 国产精品2区| 少妇av一区二区三区无码| 久久婷婷国产综合国色天香 | 日韩一区二区三区免费看| 黄色成人在线网| 日本在线视频不卡| 狠狠色狠狠色综合系列| 精品无码人妻一区二区三| 亚洲精品一二区| 视频91a欧美| 人妻久久久一区二区三区| 欧美极品另类videosde| 国产高清第一页| 日韩av日韩在线观看| 亚洲激情中文| 日本丰满少妇裸体自慰| 欧美日韩国产首页在线观看| 欧美videosex性极品hd| 欧美性色黄大片人与善| 国产中文字幕一区| 日本免费在线观看视频| xvideos亚洲人网站| 黄色免费大全亚洲| 黄色手机在线视频| 性久久久久久久久久久久| sese在线视频| 精品国产一区二区三区四区vr| 奇米影视一区二区三区小说| 国产中文字幕免费| 久久精品成人动漫| 蜜桃成人av| zjzjzjzjzj亚洲女人| 欧美日韩精品久久久|