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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux进程通信的四种方式——共享内存、信号量、无名管道、消息队列|实验、代码、分析、总结

發布時間:2024/7/19 linux 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux进程通信的四种方式——共享内存、信号量、无名管道、消息队列|实验、代码、分析、总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Linux進程通信的四種方式——共享內存、信號量、無名管道、消息隊列|實驗、代碼、分析、總結


每個進程各自有不同的用戶地址空間,任何一個進程的全局變量在另一個進程中都看不到,所以進程之間要交換數據必須通過內核,在內核中開辟一塊緩沖區,生產者進程把數據從用戶空間拷到內核緩沖區,消費者進程再從內核緩沖區把數據讀走,內核提供的這種機制稱為進程間通信(IPC,InterProcess Communication).

轉載需注明出處:?? Sylvan Ding ??


文章目錄

  • Linux進程通信的四種方式——共享內存、信號量、無名管道、消息隊列|實驗、代碼、分析、總結
    • 實驗目標
      • 問題描述
      • 需求分析
      • 實驗環境
    • 共享內存
      • 實驗設計
        • 共享內存原理
        • 使用函數介紹
          • sprintf()
          • ftoc()
          • 獲取共享內存
          • 關聯共享內存
          • 取消關聯共享內存
        • 實驗設計與編程
          • 生產者進程A
          • 消費者進程B
      • 實驗結果與分析
    • 信號量
      • 實驗設計
        • 信號量的工作原理
        • 使用函數介紹
          • 頭文件
          • 信號量創建
          • 信號量初始化和刪除
          • 信號量值修改
        • 實驗設計與編程
          • 進程A
          • 進程B
          • uni_clock.h
      • 實驗結果與分析
    • 管道
      • 實驗設計
        • 管道的實現機制
          • 環形緩沖區
          • 管道對象
        • 無名管道的實驗設計與編程
      • 實驗結果與分析
    • 消息隊列
      • 實驗設計
        • 消息隊列的實現原理
          • 用戶消息緩沖區
        • 使用函數介紹
          • 消息隊列的創建
          • 向消息隊列中添加信息
          • 從消息隊列中讀取信息
          • 消息隊列的控制函數
        • ipcs 命令
        • 實驗設計與編程
          • 消費者進程 A
          • 生產者進程 B
      • 實驗結果與分析
    • 總結和實驗心得
    • 參考文獻
    • 轉載注意事項


實驗目標

本實驗主要實現單機上不同進程間的通信,故在網絡環境中廣泛應用的客戶機-服務器系統通信的三種實現方法(套接字、遠程過程調用和遠程方法調用)不予實現。

問題描述

生產者一次生成一個元素放入緩沖池中,消費者一次可以從緩沖池中取出一個元素。生產者放入的元素個數要與消費者取出的元素個數一致。實驗的輸出要能跟蹤生產者的每次“生產”行為,以及消費者的每次“消費”行為。

需求分析

利用OS提供的高級通信工具,在單機環境下,設計和編程實現進程間數據的高效傳送,并對比分析各方法的優缺點。編寫生產者和消費者的相關程序,在Linux系統下實現信號量、共享內存、管道(無名管道和有名管道)、消息隊列等四種進程間通信方式。實驗的輸出要能跟蹤生產者的每次“生產”行為,以及消費者的每次“消費”行為。

實驗環境

  • 操作系統:Ubuntu
  • 編程語言:C
  • 編譯器:GCC 7.5.0

共享內存

共享存儲區(Share Memory)是Linux系統中通信速度最高的通信機制,因為數據不需要在客戶機和服務器端之間復制,數據直接寫到內存,不用若干次數據拷貝,所以這是最快的一種IPC。該機制中共享內存空間和進程的虛地址空間滿足多對多的關系。即一個共享內存空間可以映射多個進程的虛地址空間,一個進程的虛地址空間又可以連接多個共享存儲區。當進程間預利用共享存儲區通信時,先要在主存中建立一個共享存儲區,然后將它附接到自己的虛地址空間。該機制只為進程提供了用于實現通信的共享存儲區和對共享存儲區進行操作的手段,然而并未提供對該區進行互斥訪問及進程同步的措施,所以要使用信號量來實現對共享內存的存取的同步。

實驗設計

共享內存原理

通過上圖可知,共享內存是通過將不同進程的虛擬內存地址映射到相同的物理內存地址來實現的。

在Linux內核中,每個共享內存都由一個名為 struct shmid_kernel 的結構體來管理,而且Linux限制了系統最大能創建的共享內存為128個。通過類型為 struct shmid_kernel 結構的數組來管理,如下:

struct shmid_ds {struct ipc_perm shm_perm; /* operation perms */int shm_segsz; /* size of segment (bytes) */__kernel_time_t shm_atime; /* last attach time */__kernel_time_t shm_dtime; /* last detach time */__kernel_time_t shm_ctime; /* last change time */__kernel_ipc_pid_t shm_cpid; /* pid of creator */__kernel_ipc_pid_t shm_lpid; /* pid of last operator */unsigned short shm_nattch; /* no. of current attaches */unsigned short shm_unused; /* compatibility */void *shm_unused2; /* ditto - used by DIPC */void *shm_unused3; /* unused */ };struct shmid_kernel { struct shmid_ds u;/* the following are private */unsigned long shm_npages; /* size of segment (pages) */pte_t *shm_pages; /* array of ptrs to frames -> SHMMAX */ struct vm_area_struct *attaches; /* descriptors for attaches */ };static struct shmid_kernel *shm_segs[SHMMNI]; // SHMMNI等于128

struct shmid_kernel 結構體中,shm_npages 字段表示共享內存使用了多少個內存頁,而 shm_pages 字段指向了共享內存映射的虛擬內存頁表項數組。

使用函數介紹

sprintf()

C 庫函數 int sprintf(char *str, const char *format, ...) 發送格式化輸出到 str 所指向的字符串。下面是 sprintf() 函數的聲明:

int sprintf(char *str, const char *format, ...)
  • str – 這是指向一個字符數組的指針,該數組存儲了 C 字符串。
  • format – 這是字符串,包含了要被寫入到字符串 str 的文本。它可以包含嵌入的 format 標簽,format 標簽可被隨后的附加參數中指定的值替換,并按需求進行格式化。
ftoc()

進程間的通訊,必須有個公共的標識來確保使用同一個通訊通道。任何一個進程如果使用同一個通訊標識,則內核就可以通過該標識找到對應的那個信道,這個標識就是IPC鍵值。

函數 ftok 把一個已存在的路徑名和一個整數標識符轉換成一個key_t值,稱為IPC鍵值。函數原型如下:

key_t ftok(const char *pathname, int proj_id)
  • pathname:指定的文件,此文件必須存在且可存取
  • proj_id:計劃代號(project ID)

函數 ftok 把從pathname導出的信息與id的低序8位組合成一個整數IPC鍵,從而避免用戶使用key值的沖突。所需頭文件 #include <sys/types.h> 和 #include <sys/ipc.h>,成功:返回key_t值(即IPC 鍵值),出錯返回-1,錯誤原因存于error中。

給semget、msgget、shmget傳入key值,它們返回的都是相應的IPC對象標識符。IPC鍵值和IPC標識符是兩個概念,后者是建立在前者之上。

上圖畫出了從 IPC 鍵值生成 IPC 標識符圖,其中key為 IPC 鍵值,由ftok函數生成,ipc_id 為IPC標識符,由 semget、msgget、shmget 函數生成。ipc_id 在信號量函數中稱為 semid,在消息隊列函數中稱為 msgid,在共享內存函數中稱為 shmid,它們表示的是各自 IPC 對象標識符。

struct ipc_perm {key_t key ; /* 此IPC對象的key鍵 */uid_t uid ; /* 此IPC對象用戶ID */gid_t gid ; /* 此IPC對象組ID */uid_t cuid ; /* IPC對象創建進程的有效用戶ID */gid_t cgid ; /* IPC對象創建進程的有效組ID */mode_t mode ; /* 此IPC的讀寫權限 */ulong_t seq ; /* IPC對象的序列號 */ };

系統為每一個IPC對象保存一個ipc_perm結構體,該結構說明了IPC對象的權限和所有者,并確定了一個IPC操作是否可以訪問該IPC對象。

msgget、semget、shmget 函數最右邊的形參flag(msgget中為msgflg、semget中為semflg、shmget中shmflg)為IPC對象創建權限。IPC對象創建權限(即flag)格式為0xxxxx,其中0表示8位制,低三位為用戶、屬組、其他的讀、寫、執行權限(執行位不使用)。IPC對象存取權限常與下面IPC_CREAT、IPC_EXCL兩種標志進行或(|)運算完成對IPC對象創建的管理,下面是兩種創建模式標志在<sys/ipc.h>頭文件中的宏定義。

#define IPC_CREAT 01000 /* Create key if key does not exist. */ #define IPC_EXCL 02000 /* Fail if key exists. */
獲取共享內存

使用 shmget() 函數獲取共享內存,shmget() 函數的原型如下:

int shmget(key_t key, size_t size, int shmflg);
  • 參數 key 一般由 ftok() 函數生成,用于標識系統的唯一IPC資源。
  • 參數 size 指定創建的共享內存大小。
  • 參數 shmflg 指定 shmget() 函數的動作,比如傳入 IPC_CREAT 表示要創建新的共享內存。

函數調用成功時返回一個新建或已經存在的的共享內存標識符,取決于shmflg的參數。失敗返回-1,并設置錯誤碼。需要頭文件 #include <sys/shm.h>。

shmget() 函數的實現比較簡單,首先調用 findkey() 函數查找值為key的共享內存是否已經被創建,findkey() 函數返回共享內存在 shm_segs數組 的索引。如果找到,那么直接返回共享內存的標識符即可。否則就調用 newseg() 函數創建新的共享內存。newseg() 函數的實現也比較簡單,就是創建一個新的 struct shmid_kernel 結構體,然后設置其各個字段的值,并且保存到 shm_segs數組 中。

關聯共享內存

shmget() 函數返回的是一個標識符,而不是可用的內存地址,所以還需要調用 shmat() 函數把共享內存關聯到某個虛擬內存地址上。shmat() 函數的原型如下:

void *shmat(int shmid, const void *shmaddr, int shmflg);
  • 參數 shmid 是 shmget() 函數返回的標識符。
  • 參數 shmaddr 是要關聯的虛擬內存地址,如果傳入0,表示由系統自動選擇合適的虛擬內存地址。
  • 參數 shmflg 若指定了 SHM_RDONLY 位,則以只讀方式連接此段,否則以讀寫方式連接此段。

函數調用成功返回一個可用的指針(虛擬內存地址),出錯返回-1。

shmget() 主要通過 shmid 標識符來找到共享內存描述符,系統中所有的共享內存到保存在 shm_segs 數組中。接著,找到一個可用的虛擬內存地址,如果在調用 shmat() 函數時沒有指定了虛擬內存地址,那么就通過 get_unmapped_area() 函數來獲取一個可用的虛擬內存地址。通過調用 kmem_cache_alloc() 函數創建一個 vm_area_struct 結構,vm_area_struct 結構用于管理進程的虛擬內存空間。shmat() 函數只是申請了進程的虛擬內存空間,而共享內存的物理空間并沒有申請,當進程發生缺頁異常的時候會調用 shm_nopage() 函數來恢復進程的虛擬內存地址到物理內存地址的映射。shm_nopage() 函數的主要功能是當發生內存缺頁時,申請新的物理內存頁,并映射到共享內存中。由于使用共享內存時會映射到相同的物理內存頁上,從而不同進程可以共用此塊內存。

取消關聯共享內存

當一個進程不需要共享內存的時候,就需要取消共享內存與虛擬內存地址的關聯。取消關聯共享內存通過 shmdt() 函數實現,原型如下:

int shmdt(const void *shmaddr);
  • 參數 shmaddr 是要取消關聯的虛擬內存地址,也就是 shmat() 函數返回的值。

函數調用成功返回0,出錯返回-1。

實驗設計與編程

設置兩個進程,分別為生產者 進程A和消費者 進程B,進程A 創建一塊共享內存,然后寫入數據(字符串:Process A generated!),進程B 獲取這塊共享內存并且讀取其字符串內容并輸出。

生產者進程A
/* Process A - Producer */#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>#define SHM_PATH "~/shm" // 指定文件,以此生成 IPC key #define SHM_SIZE 128 // 設置共享內存區大小:128字節int main(int argc, char *argv[]) {int shmid; // 共享內存標識符char *addr; // 虛存中的字符串地址key_t key = ftok(SHM_PATH, 0x0066); // 生成 IPC key// 在進程A的虛存中開辟對應key的共享存儲空間// 共享存儲區大小為 128 字節// 指定 flag 為 IPC_CREAT|IPC_EXCL|0666// 因為有 IPC_EXCL 存在,所以第二次運行時,對應 key 已經存在,所以會失敗shmid = shmget(key, SHM_SIZE, IPC_CREAT|IPC_EXCL|0666);if (shmid < 0) {printf("failed to create share memory\n");return -1;}// 將虛擬內存的共享空間和物理內存的共享空間相關聯// 返回對應的虛存地址addr = shmat(shmid, NULL, 0);if (addr <= 0) {printf("failed to map share memory\n");return -1;}// 向進程A的虛存地址 addr 中寫入字符串// 實際寫入了在A和B的公共物理內存上sprintf(addr, "%s", "Process A created!\n");return 0; }
消費者進程B
/* Process B - Consumer */#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>#define SHM_PATH "~/shm" // 指定文件,以此生成 IPC key #define SHM_SIZE 128 // 設置共享內存區大小:128字節int main(int argc, char *argv[]) {int shmid; // 共享內存標識符char *addr; // 進程B虛存中的字符串地址key_t key = ftok(SHM_PATH, 0x0066); // 生成 IPC keychar buf[128]; // 接受進程B虛存對應的共享物理內存中的內容// 在進程B的虛存中開辟對應key的共享存儲空間// 對應 key 已經存在,所以 flag 參數不加 IPC_EXCLshmid = shmget(key, SHM_SIZE, IPC_CREAT);if (shmid < 0) {printf("failed to get share memory\n");return -1;}// 將虛擬內存的共享空間和物理內存的共享空間相關聯addr = shmat(shmid, NULL, 0);if (addr <= 0) {printf("failed to map share memory\n");return -1;}// 拷貝出共享物理內存中對應的 128 字節數據// 輸出:Process A created!strcpy(buf, addr);printf("%s", buf);return 0; }

實驗結果與分析

共享內存是進程間通信中最簡單的方式之一。共享內存允許兩個或更多進程訪問同一塊內存,當一個進程改變了這塊地址中的內容的時候,其它進程都會察覺到這個更改。因為所有進程共享同一塊內存,共享內存在各種進程間通信方式中具有最高的效率。訪問共享內存區域和訪問進程獨有的內存區域一樣快,并不需要通過系統調用或者其它需要切入內核的過程來完成。同時它也避免了對數據的各種不必要的復制。

但是,系統內核沒有對訪問共享內存進行同步,解決這些問題的常用方法是通過使用信號量進行同步。雖然每個使用者都可以讀取寫入數據,但是所有程序之間必須達成并遵守一定的協議,以防止諸如在讀取信息之前覆寫內存空間等競爭狀態的出現。不幸的是,Linux無法嚴格保證提供對共享內存塊的獨占訪問,甚至是在通過使用IPC_PRIVATE創建新的共享內存塊的時候也不能保證訪問的獨占性。 同時,多個使用共享內存塊的進程之間必須協調使用同一個鍵值。

信號量

信號量是一個計數器,可以用來控制多個線程對共享資源的訪問。它不是用于交換大批數據,而用于多線程之間的同步。它常作為一種鎖機制,防止某進程在訪問資源時其它進程也訪問該資源。因此,主要作為進程間以及同一個進程內不同線程之間的同步手段。

實驗設計

信號量的工作原理

信號量本質上是一個計數器,它不以傳送數據為主要目的,它主要是用來保護共享資源(信號量也屬于臨界資源),使得資源在一個時刻只有一個進程獨享。

信號量有初值(>0),每當有進程申請使用信號量,通過一個P操作來對信號量進行-1操作,當計數器減到0的時候就說明沒有資源了,其他進程要想訪問就必須等待。當該進程執行完這段工作之后,就會執行V操作,對信號量進行+1操作。

當有進程要求使用共享資源時,需要執行以下操作:

  • 系統首先要檢測該資源的信號量
  • 若該資源的信號量值大于0,則進程可以使用該資源,此時,進程將該資源的信號量值減-1
  • 若該資源的信號量值為0,則進程進入休眠狀態,直到信號量值大于0時進程被喚醒,訪問該資源
  • 當進程不再使用由一個信號量控制的共享資源時,該信號量值增+1,如果此時有進程處于休眠狀態等待此信號量,則該進程會被喚醒

在信號量進行PV操作時都為原子操作(因為它需要保護臨界資源)。

二元信號量(Binary Semaphore)是最簡單的一種鎖(互斥鎖),它只用兩種狀態:占用與非占用,通常用來替代互斥鎖實現線程同步。所以它的引用計數為1。

使用函數介紹

Linux提供了一組精心設計的信號量接口來對信號進行操作。

頭文件
#include <sys/sem.h> // 有名信號量

信號量在進程是以有名信號量進行通信的,在線程中則是以無名信號進行通信的。本實驗主要實現進程間信號量的通信,所以使用 <sys/sem.h> .

信號量創建

作用是創建一個新信號量或取得一個已有信號量,原型為:

int semget(key_t key, int num_sems, int sem_flags);
  • num_sems指定需要的信號量數目,它的值幾乎總是1
  • sem_flags是一組標志,設置了IPC_CREAT標志后,即使給出的鍵是一個已有信號量的鍵,也不會產生錯誤。而IPC_CREAT | IPC_EXCL則可以創建一個新的,唯一的信號量,如果信號量已存在,返回一個錯誤。
信號量初始化和刪除

semctl() 函數用來直接控制信號量信息,它的原型為:

int semctl(int sem_id, int sem_num, int command, ...);
  • command 通常是下面兩個值中的其中一個:SETVAL 用來把信號量初始化為一個已知的值,p 值通過union semun中的val成員設置,其作用是在信號量第一次使用前對它進行設。IPC_RMID 用于刪除一個已經無需繼續使用的信號量標識符。

如果有第四個參數,它通常是一個union semum結構,定義如下:

union semun {int val;struct semid_ds *buf;unsigned short *arry; };
信號量值修改

作用是改變信號量的值,原型為:

int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
  • sem_id是由semget()返回的信號量標識符

sembuf 結構的定義如下:

struct sembuf{short sem_num; // 除非使用一組信號量,否則它為0short sem_op; // 信號量在一次操作中需要改變的數據,通常是兩個數,一個是-1,即P(等待)操作,// 一個是+1,即V(發送信號)操作。short sem_flg; // 通常為SEM_UNDO,使操作系統跟蹤信號,// 并在進程沒有釋放該信號量而終止時,操作系統釋放信號量 };

實驗設計與編程

使用二元信號量對“共享內存”方式進行改進。原本進程A和B能同時訪問并修改共享內存區,進程A寫入字符串,進程B讀出字符串。現在,假定進程A寫入時間需要5s,先啟動進程A,A在執行寫入前進行P操作,此時啟動B,B執行讀取操作,但因為有信號量對臨界區的限制,所以進程B此時應當被掛起。5s后進程A寫入成功,執行V操作,脫離臨界區,進程B從休眠態被喚醒,從共享內存區中取進程A存入的字符串數據。最后,B釋放共享內存區并刪除信號量。

進程A
// // Created by Sylvan Ding on 2022/3/5. ///* Process A - Producer */#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include "uni_clock.h"#define SHM_PATH "~/shm" #define SHM_SIZE 128#define SEM_PATH "~/sem"union semun {// 信息量的控制單元int val;struct semid_ds *buf;unsigned short *arry; };static int set_semvalue(int semid); static int semaphore_p(int semid); static int semaphore_v(int semid);int main(int argc, char *argv[]) {int shmid;char *addr;key_t key = ftok(SHM_PATH, 0x0066);// 生成信號量標識符int semid;key_t key_sem = ftok(SEM_PATH, 0x0066);semid = semget(key_sem, 1, IPC_CREAT|IPC_EXCL|0666);// 信號量初始化if(set_semvalue(semid)<0) {printf("failed to initialize semaphore\n");return -1;}// 開辟共享存儲空間shmid = shmget(key, SHM_SIZE, IPC_CREAT|IPC_EXCL|0666);if (shmid < 0) {printf("failed to create share memory\n");return -1;}// 將虛擬內存的共享空間和物理內存的共享空間相關聯addr = shmat(shmid, NULL, 0);if (addr <= 0) {printf("failed to map share memory\n");return -1;}// 進入臨界區if(semaphore_p(semid)<0) {printf("A semaphore_p failed\n");return -1;};// 進程A開始向共享內存中寫入字符串printf("%s: Process A is on writing...\n", getTime());sleep(5); // 模擬寫入過程5ssprintf(addr, "%s", "Hello, world!\n");// 進程A提示寫入完畢printf("%s: Process A has finished writing!\n", getTime());// 進程A寫入完畢,退出臨界區if(semaphore_v(semid)<0) {printf("A semaphore_v failed\n");return -1;}// 斷開進程A與共享內存區的連接shmdt(addr);return 0; }static int set_semvalue(int semid) {// 信息量初始化union semun sem_union;sem_union.val = 1; // 二元信息量if(semctl(semid, 0, SETVAL, sem_union) == -1) {return -1;}return 0; }static int semaphore_p(int semid) {// 等待P(sv)struct sembuf sem_b;sem_b.sem_num = 0;sem_b.sem_op = -1; // P()sem_b.sem_flg = SEM_UNDO;if(semop(semid, &sem_b, 1) == -1) {return -1;}return 0; }static int semaphore_v(int semid) {// V(sv)struct sembuf sem_b;sem_b.sem_num = 0;sem_b.sem_op = 1; // V()sem_b.sem_flg = SEM_UNDO;if(semop(semid, &sem_b, 1) == -1) {return -1;}return 0; }
進程B
// // Created by Sylvan Ding on 2022/3/5. ///* Process B - Consumer */#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include "uni_clock.h"#define SHM_PATH "~/shm" #define SHM_SIZE 128#define SEM_PATH "~/sem"union semun {// 信息量的控制單元int val;struct semid_ds *buf;unsigned short *arry; };static int semaphore_p(int semid); static int semaphore_v(int semid); static int del_semvalue(int semid);int main(int argc, char *argv[]) {int shmid;char *addr;key_t key = ftok(SHM_PATH, 0x0066);// 生成信號量標識符int semid;key_t key_sem = ftok(SEM_PATH, 0x0066);semid = semget(key_sem, 1, IPC_CREAT);char buf[128];// 進程B創建共享存儲空間shmid = shmget(key, SHM_SIZE, IPC_CREAT);if (shmid < 0) {printf("failed to get share memory\n");return -1;}// 將虛擬內存的共享空間和物理內存的共享空間相關聯addr = shmat(shmid, NULL, 0);if (addr <= 0) {printf("failed to map share memory\n");return -1;}// 進程B進入臨界區printf("%s: Process B started reading!\n", getTime());if(semaphore_p(semid)<0) {printf("B semaphore_p failed\n");return -1;};// 進程B讀共享內存中數據sleep(2); // B讀出數據需2sstrcpy(buf, addr);printf("%s: %s", getTime(), buf);// 退出臨界區if(semaphore_v(semid)<0) {printf("B semaphore_v failed\n");return -1;}// 銷毀信號量if(del_semvalue(semid)<0) {printf("failed to delete semaphore\n");return -1;}// 斷開進程與共享內存區的連接shmdt(addr);// 刪除共享內存區shmctl(shmid, IPC_RMID, NULL);return 0; }static int semaphore_p(int semid) {// 等待P(sv)struct sembuf sem_b;sem_b.sem_num = 0;sem_b.sem_op = -1; // P()sem_b.sem_flg = SEM_UNDO;if(semop(semid, &sem_b, 1) == -1) {return -1;}return 0; }static int semaphore_v(int semid) {// V(sv)struct sembuf sem_b;sem_b.sem_num = 0;sem_b.sem_op = 1; // V()sem_b.sem_flg = SEM_UNDO;if(semop(semid, &sem_b, 1) == -1) {return -1;}return 0; }static int del_semvalue(int semid) {// 刪除信號量union semun sem_union;if(semctl(semid, 0, IPC_RMID, sem_union) == -1) {return -1;}return 0; }
uni_clock.h
// // Created by Sylvan Ding on 2022/3/5. //#ifndef UNTITLED_UNI_CLOCK_H #define UNTITLED_UNI_CLOCK_H#include <time.h>char *getTime() {time_t t;time(&t);struct tm *tn;tn = localtime(&t);return asctime(tn); }#endif //UNTITLED_UNI_CLOCK_H

實驗結果與分析

由于競爭信號量的時候,未能拿到信號的進程會進入睡眠,所以信號量可以適用于長時間持有。而且信號量不適合短時間的持有,因為會導致睡眠的原因,維護隊列、喚醒等各種開銷,在短時鎖定,效率較低。由于睡眠的特性,只能在進程上下文進行調用,無法再中斷上下文中使用信號量。一個進程可以在持有信號量的情況下去睡眠,另外的進程嘗試獲取該信號量時候,不會死鎖。

管道

子進程從父進程繼承文件描述符。來源于早起Unix命令行輸入時的想法:能不能讓上一個進程的輸出重定向為下一個進程的輸入。流水線方式,稱為管道機制,即子進程共享父進程的一些資源。

管道的特點:

  • 管道只允許具有血緣關系的進程間通信,如父子進程間的通信
  • 管道只允許單向通信
  • 管道內部保證同步機制,從而保證訪問數據的一致性
  • 面向字節流
  • 管道隨進程,進程在管道在,進程消失管道對應的端口也關閉,兩個進程都消失管道也消失
  • 實驗設計

    管道的實現機制

    在Linux中,管道是一種使用非常頻繁的通信機制。從本質上說,管道也是一種文件,但它又和一般的文件有所不同,管道可以克服使用文件進行通信的兩個問題,具體表現為:

  • 限制管道的大小。實際上,管道是一個固定大小的緩沖區。在Linux中,該緩沖區的大小為1頁,即4K字節,使得它的大小不象文件那樣不加檢驗地增長。使用單個固定緩沖區在寫管道時可能變滿,當這種情況發生時,隨后對管道的write()調用將默認地被阻塞,等待某些數據被讀取,以便騰出足夠的空間供write()調用寫;
  • 讀取進程也可能工作得比寫進程快。當所有當前進程數據已被讀取時,管道變空。當這種情況發生時,一個隨后的read()調用將默認地被阻塞,等待某些數據被寫入,這解決了read()調用返回文件結束的問題。
  • 注意:從管道讀數據是一次性操作,數據一旦被讀,它就從管道中被拋棄,釋放空間以便寫更多的數據。
  • 在 Linux 中,管道的實現并沒有使用專門的數據結構,而是借助了文件系統的file結構和VFS的索引節點inode。通過將兩個 file 結構指向同一個臨時的 VFS 索引節點,而這個 VFS 索引節點又指向一個物理頁面而實現的。

    環形緩沖區

    在內核中,管道 使用了環形緩沖區來存儲數據。環形緩沖區的原理是:把一個緩沖區當成是首尾相連的環,其中通過讀指針和寫指針來記錄讀操作和寫操作位置。

    管道對象

    在 Linux 內核中,管道使用 pipe_inode_info 對象來進行管理,pipe_inode_info 對象的定義,如下所示:

    struct pipe_inode_info {wait_queue_head_t wait;unsigned int nrbufs,unsigned int curbuf;...unsigned int readers;unsigned int writers;unsigned int waiting_writers;...struct inode *inode;struct pipe_buffer bufs[16]; };
    • wait:等待隊列,用于存儲正在等待管道可讀或者可寫的進程。
    • bufs:環形緩沖區,由 16 個 pipe_buffer 對象組成,每個 pipe_buffer 對象擁有一個內存頁 。
    • nrbufs:表示未讀數據已經占用了環形緩沖區的多少個內存頁。
    • curbuf:表示當前正在讀取環形緩沖區的哪個內存頁中的數據。
    • readers:表示正在讀取管道的進程數。
    • writers:表示正在寫入管道的進程數。
    • waiting_writers:表示等待管道可寫的進程數。
    • inode:與管道關聯的 inode 對象。

    環形緩沖區是由 16 個 pipe_buffer 對象組成,定義如下:

    struct pipe_buffer {struct page *page;unsigned int offset;unsigned int len;... };
    • page:指向 pipe_buffer 對象占用的內存頁。
    • offset:如果進程正在讀取當前內存頁的數據,那么 offset 指向正在讀取當前內存頁的偏移量。
    • len:表示當前內存頁擁有未讀數據的長度。

    無名管道的實驗設計與編程

    無名管道一般用于父子進程之間相互通信,具體流程如下:

    • 父進程使用 pipe 系統調用創建一個管道
    • 父進程使用 fork 系統調用創建一個子進程
    • 由于子進程會繼承父進程打開的文件句柄,所以父子進程可以通過新創建的管道進行通信

    管道是單向的,所以要將管道分為讀端和寫端,需要兩個文件描述符來管理管道:fd[0] 為讀端,fd[1] 為寫端。

    #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <stdlib.h> #include <string.h>int main() {int fd[2]; // 用于管理管道的文件描述符pid_t pid; // 子進程pidchar buf[128] = {0};char *msg = "Hello world!!!";// 父進程創建管道// pipe返回值:成功,返回0,否則返回-1.// 參數數組包含pipe使用的兩個文件的描述符.// fd[0]:讀管道,fd[1]:寫管道.if (-1 == pipe(fd)) {printf("failed to create pipe\n");return -1;}// 創建子進程// fork函數將運行著的程序分成2個(幾乎)完全一樣的進程,// 每個進程都啟動一個從代碼的同一位置開始執行的線程。// 返回負值:創建子進程失敗,零返回到新創建的子進程,// 正值返回父進程或調用者。pid = fork();if (pid<0) {printf("failed to fork\n");return -1;}// 子進程-生產者if (0 == pid) {// 關閉管道的讀端close(fd[0]); // 向管道寫端寫入數據if(write(fd[1], msg, strlen(msg))<0) {printf("failed to write data\n");exit(1); }; exit(0); } else { // 父進程-消費者// 關閉管道的寫端close(fd[1]); // 從管道的讀端讀取數據if(read(fd[0], buf, sizeof(buf))<0) {printf("failed to read data\n");return -1;}; printf("Parent read data: %s\n", buf);}return 0; }

    實驗結果與分析

    父子進程通過 pipe 系統調用打開的管道,在內核空間中指向同一個管道對象(pipe_inode_info)。所以父子進程共享著同一個管道對象,那么就可以通過這個共享的管道對象進行通信。

    使用 pipe 系統調用打開管道時,并沒有立刻申請內存頁,而是當有進程向管道寫入數據時,才會按需申請內存頁。當內存頁的數據被讀取完后,內核會將此內存頁回收,來減少管道對內存的使用。

    消息隊列

    消息隊列是消息的鏈表,存放在內核中并由消息隊列標識符標識。消息隊列克服了信號傳遞信息少,管道只能承載無格式字節流以及緩沖區大小受限等特點。消息隊列是UNIX下不同進程之間可實現共享資源的一種機制,UNIX允許不同進程將格式化的數據流以消息隊列形式發送給任意進程。對消息隊列具有操作權限的進程都可以使用msget完成對消息隊列的操作控制。通過使用消息類型,進程可以按任何順序讀信息,或為消息安排優先級順序。

    實驗設計

    消息隊列的實現原理

    • 消息隊列的本質其實是一個內核提供的鏈表,內核基于這個鏈表,實現了一個數據結構;
    • 向消息隊列中寫數據,實際上是向這個數據結構中插入一個新結點;從消息隊列匯總讀數據,實際上是從這個數據結構中刪除一個結點;
    • 消息隊列提供了一個從一個進程向另外一個進程發送一塊數據的方法;
    • 消息隊列也有管道一樣的不足,就是每個數據塊的最大長度是有上限的,系統上全體隊列的最大總長度也有一個上限。Linux用宏MSGMAX和MSGMNB來限制一條消息的最大長度和一個隊列的最大長度.
    用戶消息緩沖區

    無論發送進程還是接收進程,都需要在進程空間中用消息緩沖區來暫存消息。該消息緩沖區的結構定義如下:

    struct msgbuf {long mtype; /* 消息的類型 */char mtext[]; /* 消息正文 */ };
    • 可通過 mtype 區分數據類型,同過判斷mtype,是否為需要接收的數據
    • mtext[] 為存放消息正文的數組,可以根據消息的大小定義該數組的長度

    使用函數介紹

    消息隊列的創建

    通過msgget創建消息隊列,函數原型如下:

    #include <sys/msg.h> int msgget(key_t key, int msgflg);
    • 成功 msgget 將返回一個非負整數,即該消息隊列的標識碼;
    • 失敗 則返回“-1”
    向消息隊列中添加信息

    向消息隊列中添加數據,使用到的是msgsnd()函數,函數原型如下:

    int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
    • msgid: 由msgget函數返回的消息隊列標識碼
    • msg_ptr: 是一個指針,指針指向準備發送的消息,
    • msg_sz: 是msg_ptr指向的消息長度,消息緩沖區結構體中mtext的大小,不包括數據的類型
    • msgflg: 控制著當前消息隊列滿或到達系統上限時將要發生的事情。比如,msgflg = IPC_NOWAIT 表示隊列滿不等待,返回EAGAIN錯誤

    注意,消息的數據結構卻有一定的要求,指針msg_ptr所指向的消息結構一定要是以一個長整型成員變量開始的結構體,接收函數將用這個成員來確定消息的類型。所以消息結構要定義成這樣:

    struct my_message {long int message_type;/* The data you wish to transfer */ };

    注意:msg_sz 是msg_ptr指向的消息的長度,注意是消息的長度,而不是整個結構體的長度,也就是說msg_sz是不包括長整型消息類型成員變量的長度。

    從消息隊列中讀取信息

    msgrcv() 用來從一個消息隊列獲取消息,它的原型為:

    int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);
    • msgid: 由msgget函數返回的消息隊列標識碼
    • msg_ptr: 是一個指針,指針指向準備接收的消息
    • msgsz: 是msg_ptr指向的消息長度
    • msgtype: 可以實現接收優先級的簡單形式
      • msgtype=0返回隊列第一條信息
      • msgtype>0返回隊列第一條類型等于msgtype的消息
      • msgtype<0返回隊列第一條類型小于等于msgtype絕對值的消息
    • msgflg: 控制著隊列中沒有相應類型的消息可供接收時將要發生的事。比如,msgflg=IPC_NOWAIT,隊列沒有可讀消息不等待,返回ENOMSG錯誤。msgflg=MSG_NOERROR,消息大小超過msgsz時被截斷

    調用成功時,該函數返回放到接收緩存區中的字節數,消息被復制到由msg_ptr指向的用戶分配的緩存區中,然后刪除消息隊列中的對應消息。失敗時返回-1。

    消息隊列的控制函數

    控制函數原型如下:

    int msgctl(int msqid, int command, strcut msqid_ds *buf);
    • command: 是將要采取的動作,它可以取3個值
      • IPC_STAT:把msgid_ds結構中的數據設置為消息隊列的當前關聯值,即用消息隊列的當前關聯值覆蓋msgid_ds的值
      • IPC_SET:如果進程有足夠的權限,就把消息列隊的當前關聯值設置為msgid_ds結構中給出的值
      • IPC_RMID:刪除消息隊列. 注意:若選擇刪除隊列,第三個參數傳NULL
    • buf是指向msgid_ds結構的指針,它指向消息隊列模式和訪問權限的結構
    • 如果操作成功,返回“0”;如果失敗,則返回“-1”

    msgid_ds結構至少包括以下成員:

    struct msgid_ds {uid_t shm_perm.uid;uid_t shm_perm.gid;mode_t shm_perm.mode; };

    ipcs 命令

    Linux系統下自帶的ipcs命令,可以查看當前系統下共享內存、消息隊列、信號量等等的使用情況,從而利于定位多進程通信中出現的通信問題。

    上圖分別查看了當前unix系統中信號量、共享內存和消息隊列的使用情況。

    命令格式如下:

    ipcs [resource-option] [output-format]

    resource選項:

    • ipcs -a 顯示系統內所有的IPC信息

    輸出格式控制:

    • ipcs -p 查看IPC資源的創建者和使用的進程ID
    • ipcs -u 查看IPC資源狀態匯總信息

    ipcrm 通過指定ID刪除IPC資源,同時將與IPC對象關聯的數據一并刪除,只有超級用戶或IPC資源創建者能夠刪除。

    實驗設計與編程

    • 編寫消費者進程 A,A 生成 IPC key,并創建消息隊列,使 A 保持對消息隊列的監聽,A 接受類型為 1 的消息;
    • 編寫生產者進程 B,B 和 A 使用相同的消息隊列。在另一個終端上運行進程 B,B 向消息隊列中發送自定義類型和內容的消息,觀察 A 所在終端的輸出。
    消費者進程 A
    // // Created by Sylvan Ding on 2022/3/5. ///* Process A - Consumer */#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h>#define IPCKEY 666 // ipc key #define MSGSIZE 128 // message sizestruct MyMsg {long msgtype; // msg類型char msg_cont[MSGSIZE]; // msg內容 };int msgid; int ret_val; long msgtype = 1; // msg type to receive struct MyMsg mymsg = {0}; const char *quit_msg = "quit"; // message to quitint main(int argc, char *argv[]) {// 創建消息隊列msgid = msgget((key_t)IPCKEY, IPC_CREAT|IPC_EXCL|0666);if (msgid < 0) {printf("failed to create message queue\n");return -1;}// 從消息隊列中獲取類型為1的消息mymsg.msgtype = msgtype;while (1) {ret_val = msgrcv(msgid, &mymsg, sizeof(mymsg.msg_cont), msgtype, IPC_NOWAIT);if (ret_val > 0) {// 退出監聽if (!strcmp(mymsg.msg_cont, quit_msg)) {printf("Process A received a command to QUIT!\n");break;}// 打印接收信息printf("A received a message from msq: %s\n", mymsg.msg_cont);fflush(stdout);}}// 消息隊列釋放msgctl(msgid, IPC_RMID, NULL);return 0; }
    生產者進程 B
    // // Created by Sylvan Ding on 2022/3/5. ///* Process B - Producer */#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h>#define IPCKEY 666 // ipc key #define MSGSIZE 128 // message sizestruct MyMsg {long msgtype; // msg類型char msg_cont[MSGSIZE]; // msg內容 };int msgid; int ret_val; struct MyMsg mymsg = {0};int main(int argc, char *argv[]) {// 創建消息隊列msgid = msgget((key_t)IPCKEY, IPC_CREAT);if (msgid < 0) {printf("failed to create message queue\n");return -1;}// 輸入消息類型和內容printf("Here is the procedure B: \n");printf("mgs_type = ");scanf("%ld", &(mymsg.msgtype));printf("input your msg:\n");scanf("%s", mymsg.msg_cont);// 向消息隊列發送信息ret_val = msgsnd(msgid, &mymsg, sizeof(mymsg.msg_cont), IPC_NOWAIT);if (ret_val < 0) {printf("failed to send message\n");return -1;}return 0; }

    實驗結果與分析

    總結和實驗心得

    本次實驗基于Linux系統實現了進程的共享內存、管道和消息隊列等三種IPC方式。其中,共享內存使各個進程共享一段物理內存空間,管道為父子進程間信息傳遞帶來便捷,消息隊列提供了消息類型選擇機制。實驗還通過信號量改進了共享內存,實現了進程對存儲空間的互斥訪問。通過實驗,初步掌握了Linux系統的IPC操作,了解了他們各自的優缺點,現做總結如下:

    如果用戶傳遞的信息較少,或是需要通過信號來觸發某些行為,軟中斷信號機制不失為一種簡捷有效的進程間通信方式。但若是進程間要求傳遞的信息量比較大或者進程間存在交換數據的要求,那就需要考慮別的通信方式了。

    無名管道簡單方便。但局限于單向通信的工作方式。并且只能在創建它的進程及其子孫進程之間實現管道的共享:有名管道雖然可以提供給任意關系的進程使用。但是由于其長期存在于系統之中,使用不當容易出錯,所以普通用戶一般不建議使用。

    消息緩沖可以不再局限于父子進程,而允許任意進程通過共享消息隊列來實現進程間通信。并由系統調用函數來實現消息發送和接收之間的同步。從而使得用戶在使用消息緩沖進行通信時不再需要考慮同步問題。使用方便,但是信息的復制需要額外消耗CPU的時間。不適宜于信息量大或操作頻繁的場合。

    共享內存針對消息緩沖的缺點改而利用內存緩沖區直接交換信息,無須復制,快捷、信息量大是其優點。但是共享內存的通信方式是通過將共享的內存緩沖區直接附加到進程的虛擬地址空間中來實現的。因此,這些進程之間的讀寫操作的同步問題操作系統無法實現。必須由各進程利用其他同步工具解決。另外,由于內存實體存在于計算機系統中。所以只能由處于同一個計算機系統中的諸進程共享。不方便網絡通信。

    不同的進程通信方式有不同的優點和缺點。因此。對于不同的應用問題,要根據問題本身的情況來選擇進程間的通信方式。

    參考文獻

  • Linux下進程間通信方式——共享內存
  • 6種Linux進程間的通信方式
  • 進程通信——百度百科
  • Linux共享存儲通信
  • 一文搞定:Linux共享內存原理
  • Linux信號量詳解
  • Linux下進程間通信方式——信號量(Semaphore)
  • 進程間通信的方式(四):信號量
  • linux管道詳解
  • 圖解 | Linux進程通信 - 管道實現
  • Linux進程間通信——消息隊列
  • Linux進程間通信(七):消息隊列 msgget()、msgsend()、msgrcv()、msgctl()
  • ipcs命令詳解
  • 轉載注意事項

    本文為原創文章,轉載需要在文章開頭或結尾注明出處!

    ?? ?? Sylvan Ding’s Blog ??

    總結

    以上是生活随笔為你收集整理的Linux进程通信的四种方式——共享内存、信号量、无名管道、消息队列|实验、代码、分析、总结的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    www.久久视频 | 麻豆综合网 | 91中文字幕永久在线 | 草在线视频 | 正在播放国产一区 | 久久久久一区二区三区 | 国产在线91在线电影 | 久久久久久久av麻豆果冻 | 欧洲高潮三级做爰 | 久久国产精品久久精品 | 婷婷色亚洲 | 超碰在线最新地址 | 黄色片网站大全 | 久久成年人视频 | 在线观看激情av | 在线观看视频一区二区 | 97精品久久人人爽人人爽 | 久久久国产一区二区三区四区小说 | 韩日精品在线 | 99热精品久久 | 免费国产在线精品 | 国产精品免费在线视频 | 婷婷四房综合激情五月 | 国产精品第7页 | 欧美精选一区二区三区 | www成人精品 | 久草www| 亚洲成免费 | 九九九在线观看 | 久久久久久久久久久久av | 九色自拍视频 | 国产精品久久久久一区二区国产 | 国产视频导航 | 美女在线观看网站 | 欧美日韩精品二区第二页 | 国产免费观看久久黄 | 日韩性xxxx | 中文av字幕在线观看 | 国产精品一级在线 | 国产精品99免视看9 国产精品毛片一区视频 | 亚洲a色| 麻豆久久 | 国产精品门事件 | 国产精品久久久久久电影 | a电影免费看 | 欧美日韩精品在线观看 | 国内久久久久 | 免费网站观看www在线观看 | 9999国产| 国产一区二区中文字幕 | 欧美久久久久久久久久久久久 | 99久久婷婷国产综合亚洲 | 九九综合在线 | 黄色成人在线网站 | 欧美一级免费高清 | 99精品视频免费在线观看 | 久久久免费精品视频 | 91成人在线看| 欧美色精品天天在线观看视频 | 日本中文在线 | 久久久五月天 | 日韩久久午夜一级啪啪 | 国产亚洲情侣一区二区无 | 久久精品国产精品亚洲 | 免费视频一区二区 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 中文字幕欲求不满 | 色av婷婷 | 超碰日韩| 久草在线观 | 国产精品女视频 | 精品uu| 日韩在线观看网站 | 亚洲伦理一区 | 黄色大全在线观看 | 亚洲国产成人精品久久 | 最新色站 | 国产一区免费观看 | 美女视频网站久久 | 在线免费av播放 | 五月天婷婷丁香花 | a级片久久| 一区中文字幕 | av成人免费 | 亚洲视频资源在线 | 国产女人18毛片水真多18精品 | 91精品国产欧美一区二区 | 9999毛片 | 国产一区在线精品 | 色久网 | 久久不卡av| а中文在线天堂 | 婷婷久久精品 | 国产福利91精品一区二区三区 | 中文字幕a∨在线乱码免费看 | 久久综合电影 | 亚洲六月丁香色婷婷综合久久 | 亚洲三区在线 | 在线观看中文字幕一区二区 | 伊人狠狠干 | 黄污网 | 国产老熟| 国产探花视频在线播放 | 国产精品毛片久久久久久久久久99999999 | 麻豆国产网站 | 免费国产在线视频 | 国产一区播放 | 日日干天天爽 | 久热爱| 9999免费视频 | 国产一区二区高清不卡 | 在线精品观看 | 国产黄色观看 | 亚洲第一av在线播放 | 国产视频在线播放 | 国产精品视频免费 | 亚洲国产精品电影 | 国产在线理论片 | 成人黄色大片在线观看 | 午夜精品久久久久久99热明星 | 97精品国产97久久久久久免费 | 在线视频18在线视频4k | 免费中文字幕 | 999在线观看视频 | 国产精品一区二区果冻传媒 | 色综合天天综合网国产成人网 | 久久露脸国产精品 | 久久夜夜操 | 婷婷六月天综合 | 超碰在线人人草 | 九九有精品 | 日本在线观看中文字幕无线观看 | 99国产精品| avcom在线| 成人黄色免费观看 | 亚洲理论在线观看 | 午夜精品久久久久久久99 | 亚洲最大av网 | 粉嫩av一区二区三区四区 | 精品免费一区 | 成人久久免费视频 | 午夜久草 | 久久久国产精品电影 | 91av色| 97超视频在线观看 | 色网免费观看 | 在线视频 一区二区 | 婷婷九九| 欧美性春潮| 国产精品第三页 | 久久深夜福利免费观看 | 日本中文字幕一二区观 | 亚洲激情在线视频 | 欧美亚洲xxx| 久久国内精品视频 | 黄av在线 | 日本黄色大片免费看 | 久久久久久久久久久久久久电影 | 国产最新在线视频 | 亚洲黄色一级视频 | 欧美日韩天堂 | 国产精品18久久久久vr手机版特色 | 三级黄色a | 日本精品视频在线 | 午夜精品一区二区国产 | 麻豆成人精品视频 | 国模视频一区二区 | 亚洲va天堂va欧美ⅴa在线 | 久久夜色精品国产欧美乱 | 日韩免费观看高清 | 国产精品久久久av久久久 | 国产裸体视频bbbbb | 欧美视频18 | 在线三级播放 | 久久精品人人做人人综合老师 | 午夜av日韩 | 中文字幕在线久一本久 | 深爱婷婷 | 91中文视频 | 国产一卡二卡在线 | 91精品啪| av一区在线播放 | 欧美成a人片在线观看久 | 久久综合久久综合久久 | 久久a v视频 | 久久超碰在线 | 高清精品在线 | 亚洲高清不卡av | 亚洲国产日本 | 欧洲亚洲精品 | 中文字幕视频一区 | 婷婷丁香在线视频 | 亚洲精品国产综合99久久夜夜嗨 | 97电影手机 | 午夜精品视频免费在线观看 | 亚洲精品自在在线观看 | 色视频在线免费观看 | 亚洲天堂网视频 | 日韩欧美视频在线免费观看 | 91视频久久久 | 97超碰国产精品 | 中文字幕免费国产精品 | 18国产精品白浆在线观看免费 | 亚洲最新毛片 | 国产精品久久艹 | 亚洲成色| 日韩av综合网站 | 日韩在线观看小视频 | 免费在线观看成年人视频 | 日批视频国产 | 97电影网手机版 | 久久精品99北条麻妃 | 亚洲伊人av | 四虎成人在线 | 91精品在线视频 | 黄色av电影网 | 天天草天天色 | 欧美日性视频 | 婷婷久久久久 | avove黑丝 | 2021国产在线| 亚洲黄色成人网 | 国产成人精品在线观看 | 中文字幕在线观看完整版电影 | 国产欧美精品一区二区三区四区 | 91麻豆精品国产91久久久使用方法 | 五月天婷婷在线播放 | 在线免费黄网站 | 狠狠色免费 | 精品uu | 欧美日韩国产一区二区三区在线观看 | 一区二区三区高清在线观看 | 在线精品观看 | 日韩欧美精品在线视频 | 又爽又黄又刺激的视频 | 国产高清日韩欧美 | 一级做a爱片性色毛片www | 91久久精品日日躁夜夜躁国产 | 日韩色在线观看 | 亚洲在线免费视频 | 在线观看色视频 | 亚洲视频2 | 九九交易行官网 | 日韩特级片 | 国产精品久久精品 | 国产极品尤物在线 | 亚洲在线视频网站 | 午夜精品一区二区国产 | 超碰在线观看99 | 在线观看免费av网 | 精品国偷自产国产一区 | 欧美国产日韩一区二区 | 久久免费国产精品1 | 欧美日韩不卡一区二区三区 | 成全免费观看视频 | 日韩在线免费播放 | 日本最新一区二区三区 | 久久国产高清 | 天天精品视频 | 日日干精品 | 懂色av一区二区三区蜜臀 | 亚洲精品乱码久久久一二三 | 精品视频在线免费 | 欧美一区日韩一区 | 亚洲在线视频播放 | 久久综合操 | 国产黄色视 | 91视频91蝌蚪 | 99久久精品一区二区成人 | 久久高清片 | 91最新中文字幕 | 亚洲视频六区 | 国产日韩在线一区 | 久久av影院 | 最近中文字幕免费观看 | 在线视频久 | 国产精品大片免费观看 | 久久精品国产精品亚洲精品 | 日日草视频| 久久久99精品免费观看乱色 | 九色一区二区 | 久草在线免费资源 | 九九热只有精品 | 免费毛片一区二区三区久久久 | 亚洲精品美女久久久 | 97中文字幕 | 91九色在线视频观看 | 国产高清在线看 | 成人9ⅰ免费影视网站 | 一区二区三区三区在线 | 深夜免费福利在线 | 国产精品午夜在线 | 久久久综合九色合综国产精品 | 国产一区二区在线免费播放 | 国产精品免费看久久久8精臀av | 不卡av免费在线观看 | 又黄又爽又刺激视频 | 国产分类视频 | 99九九99九九九视频精品 | 99久久精品网 | 亚洲另类视频 | 天天综合网在线观看 | 亚洲精品人人 | 99热只有精品在线观看 | 日韩欧美精品一区二区 | 天天干天天插伊人网 | 日韩免费视频观看 | 国产精品欧美久久久久无广告 | 天堂成人在线 | 国内精品久久久久影院一蜜桃 | 日本不卡一区二区 | 日韩av在线资源 | www成人精品 | 欧美精品天堂 | 天天色天天射天天干 | 国产小视频在线看 | 国产精品久一 | 久草com| 亚洲第五色综合网 | 毛片久久久| 久久一区二区三区超碰国产精品 | 免费色黄 | 亚洲网久久 | 激情av在线播放 | 最近免费中文字幕大全高清10 | 黄色亚洲免费 | 爱爱一区 | 色视频网站在线观看一=区 a视频免费在线观看 | 日韩欧美视频在线免费观看 | 日日干干夜夜 | 国产韩国精品一区二区三区 | a在线视频v视频 | 97av在线视频| 亚洲人成网站精品片在线观看 | 国产美女视频黄a视频免费 久久综合九色欧美综合狠狠 | 国产精品久久久久久久久久久免费看 | 视频 国产区 | 99免费在线视频 | 久久久麻豆 | 日本中文一区二区 | 狠狠干成人综合网 | 91麻豆精品国产91久久久久久 | 日本性高潮视频 | 精品免费99久久 | 国产97在线看 | 久久视频中文字幕 | 欧美日韩久久 | av成人免费在线观看 | 狠狠操夜夜操 | 日韩在线一级 | 欧美性生活久久 | 91网免费看| 日日爽| 五月婷久久| 国内揄拍国内精品 | 国产精品乱看 | 99热精品国产一区二区在线观看 | 菠萝菠萝蜜在线播放 | 93久久精品日日躁夜夜躁欧美 | 久久夜色精品国产欧美一区麻豆 | 国际精品网 | 天天射天天操天天色 | 国产精品99久久久久久宅男 | 亚洲高清国产视频 | 亚洲午夜精品一区二区三区电影院 | 最近最新mv字幕免费观看 | 亚洲老妇xxxxxx | 国产在线日本 | 一区二区三区 中文字幕 | 国产在线观看国语版免费 | a久久久久 | 在线免费观看视频a | 国产一区免费视频 | 黄色a在线 | www.com在线观看 | 国产精品麻豆果冻传媒在线播放 | av免费电影在线观看 | 久久久久国产成人免费精品免费 | www夜夜| 久久综合日 | 成人免费看片网址 | 丁香婷婷综合五月 | 免费看片网址 | 午夜视频导航 | 色综合天天天天做夜夜夜夜做 | 午夜av在线 | 激情综合电影网 | 中文字幕一区在线 | 成年人毛片在线观看 | 国产免费一区二区三区最新6 | 精品视频资源站 | 一级免费片 | 亚州人成在线播放 | 欧美aa一级| 国产高清免费av | 激情久久久| 91精品人成在线观看 | 91网站免费观看 | 成人黄色在线播放 | 国产精品女教师 | 午夜在线看 | 成人av在线直播 | 中文字幕中文中文字幕 | 日韩欧美xxxx | 精品国产电影 | 日韩欧美视频免费在线观看 | 午夜资源站 | 久久情网| 久久九九影视网 | 亚洲国产精品成人av | 久久乱码卡一卡2卡三卡四 五月婷婷久 | 狠狠色丁香久久综合网 | 欧美日韩国产亚洲乱码字幕 | 国产精品手机在线播放 | 久久超 | 久久久人人人 | 97视频免费观看 | 久久一区二区三区日韩 | av中文在线影视 | 91你懂的 | 日日操夜 | 99在线视频免费观看 | 亚洲一区欧美精品 | 久久精品亚洲 | 亚洲精品动漫久久久久 | 一区二区不卡 | 一级全黄毛片 | 久久99精品久久久久久清纯直播 | 欧美另类重口 | 精品 激情 | 色老板在线视频 | 国产91亚洲精品 | 久久久久久久久网站 | 国产精品综合久久久 | 这里只有精品视频在线 | 在线观看视频中文字幕 | 国产在线最新 | 成人国产精品电影 | 色综合久久综合中文综合网 | 粉嫩av一区二区三区四区 | 色九九影院 | 成年人视频在线免费观看 | 欧美在线观看视频一区二区 | 久久国产视频网 | 亚洲免费观看视频 | 激情久久伊人 | 国产99在线播放 | 国产精品99久久久久久大便 | 久久99久久99精品中文字幕 | 特级黄色片免费看 | 欧美日韩在线免费视频 | 欧美久草在线 | 欧美成a人片在线观看久 | 最新影院| 天天躁天天操 | 亚洲成人第一区 | 午夜精品剧场 | 亚洲精品在线免费观看视频 | 久久黄色免费观看 | 国产精品 欧美 日韩 | 97超碰人人 | 在线看黄色的网站 | 天天操天天透 | av东方在线| 91私密保健 | 麻豆成人精品视频 | 日本aa在线 | 久久精品一区二区三区国产主播 | www.色五月.com| 亚洲乱码精品久久久久 | 免费国产一区二区视频 | 这里只有精彩视频 | 欧美成人一二区 | 人人干天天干 | 国产精品一区二区三区在线看 | 亚洲精品午夜视频 | 免费的黄色av | 国产精品毛片久久 | 欧美亚洲精品在线观看 | 久久9视频 | 免费看的黄色的网站 | 国产专区一 | 天堂av免费 | av资源网在线播放 | 欧美日韩色婷婷 | 国产在线不卡一区 | 亚洲精品国产精品国自 | 久久视影 | 深爱婷婷网| 免费一区在线 | 久久久国际精品 | 国产又黄又爽又猛视频日本 | 天堂网一区二区三区 | 99看视频在线观看 | 国产精品久久久久久久久久久杏吧 | 久久久毛片 | 国产精品久免费的黄网站 | 天天爱天天爽 | 久久久久网址 | 成人黄色电影视频 | 二区三区精品 | 欧美伦理一区 | 伊人色**天天综合婷婷 | 最近日本韩国中文字幕 | 欧美精品久久久久久久久老牛影院 | 天堂网一区二区 | 最近最新中文字幕视频 | 在线免费视频一区 | 国产精品99久久久精品免费观看 | 黄色av网站在线观看免费 | 国产精品1024 | 婷婷综合国产 | 国产一区在线免费观看视频 | 在线导航av | 色婷婷国产 | 在线精品视频免费播放 | 亚洲电影在线看 | 国产精品视频免费看 | 国产一区二区三区四区大秀 | 91视频在线网址 | 久久久高清免费视频 | 久草在线免费看视频 | 中文字幕一区二区三区在线播放 | 久久99久久99精品中文字幕 | 亚洲一区二区三区miaa149 | 极品久久久 | 五月花丁香婷婷 | 日本激情动作片免费看 | 日韩电影中文字幕 | 日本精品中文字幕 | 午夜精品av在线 | 狠狠色丁香婷婷综合 | 一区二区三区不卡在线 | 手机av在线不卡 | 亚洲第一区在线播放 | 少妇bbbb搡bbbb搡bbbb | 色av网站| 在线观看中文字幕av | 在线播放视频一区 | 亚洲成色777777在线观看影院 | 天天躁日日躁狠狠躁av麻豆 | 精品国产片| 在线视频1卡二卡三卡 | 五月婷社区 | 激情开心网站 | 久久99在线 | 亚洲一级在线观看 | 日韩伦理片一区二区三区 | 日韩黄色一区 | 国产小视频福利在线 | 欧美成人h版电影 | 国产成人精品在线 | 521色香蕉网站在线观看 | 久草成人在线 | 99精品在线播放 | 欧美另类sm图片 | 久久69精品 | 免费国产在线视频 | 97超碰人人干 | 狠狠干夜夜操 | 天天草天天干天天 | 日韩有码在线播放 | 中文字幕视频观看 | 激情欧美日韩一区二区 | 久久国产精品免费看 | 五月婷婷激情 | 国产亚洲精品美女久久 | 国产精品久久久久影视 | 99热九九这里只有精品10 | 欧美日韩国产欧美 | 91精品久久久久久综合乱菊 | 国产大片黄色 | 色婷婷激情四射 | 国产视频在线免费 | 日韩三级免费观看 | 成人a免费| 九九视频免费观看视频精品 | 九色精品免费永久在线 | 在线91精品 | 日韩av一区二区在线影视 | 婷婷在线资源 | 国产精品免费小视频 | 特黄色大片 | 国产婷婷一区二区 | 欧美一区二区三区特黄 | 人人爱爱 | 久久精品永久免费 | 天天色.com | 欧美精品网站 | av高清在线观看 | 又爽又黄又刺激的视频 | 成人动图 | 久久好看免费视频 | 久久久性| 天天综合网在线 | 日韩免费电影一区二区 | 亚洲精品午夜国产va久久成人 | 麻豆影视网站 | 久久久久久毛片 | 中文国产字幕 | 有码中文字幕在线观看 | 天天激情站 | 高清国产在线一区 | 国产美女网站视频 | 国产99久久| 四虎在线免费观看 | 色视频网页 | 香蕉视频网站在线观看 | 亚洲 成人 欧美 | 国产高清精品在线观看 | 日韩欧美在线一区 | 亚洲一级片在线观看 | 国产999精品视频 | 91丨九色丨蝌蚪丨对白 | 一色屋精品视频在线观看 | 在线免费高清一区二区三区 | 色视频成人在线观看免 | 亚洲不卡av一区二区三区 | 天天综合网天天综合色 | 伊人网站| 天天干天天爽 | 99久久99久久免费精品蜜臀 | 精品国产一区二区三区久久 | 婷婷丁香av | 欧美在线资源 | 激情综合网在线观看 | 天天色天天干天天 | 久久综合色影院 | 国产一区二区手机在线观看 | 久久精品123 | 久热超碰 | 成人免费视频网站在线观看 | 91九色在线 | 国产一区二区三区免费在线观看 | 色资源在线 | av成人在线网站 | 91久久精品一区二区三区 | 999久久a精品合区久久久 | 国产精品九色 | 人人射网站 | 天天操天天摸天天射 | 国产福利a| 国产免费久久精品 | 久色婷婷 | 久久看视频| 激情视频在线高清看 | 国产很黄很色的视频 | 激情婷婷六月 | 美女在线免费视频 | 欧美最猛性xxxxx免费 | 在线看免费| 91在线精品一区二区 | 国产分类视频 | 在线观看视频你懂 | 国产成人亚洲精品自产在线 | 国产在线精品福利 | 国产精品成久久久久三级 | 麻豆视频观看 | 99久久婷婷国产综合亚洲 | 亚洲高清视频在线播放 | 免费a视频在线 | 成人资源网 | 久艹视频在线免费观看 | 亚洲伦理中文字幕 | 九九精品毛片 | 国产精品毛片一区二区在线看 | 不卡电影免费在线播放一区 | 国产精品一区二区无线 | 国产偷国产偷亚洲清高 | 国产小视频在线观看 | 美女精品在线 | 国产一区二区三区在线免费观看 | 亚洲一区天堂 | 久久99精品久久久久久秒播蜜臀 | 久久视影 | 亚洲高清av| 青青啪| 久久久久9999亚洲精品 | 国产色在线视频 | 国产短视频在线播放 | 97色国产 | 天天曰视频 | 激情五月综合网 | 中国一级片视频 | 一色av| 国产成人高清 | avwww在线 | 国产视频日韩 | 最新色站| 免费网站看v片在线a | 国产精品久久电影观看 | 亚洲人成综合 | 日韩精品一区二区三区在线播放 | 国产亚洲精品福利 | 亚洲mv大片欧洲mv大片免费 | 国产91勾搭技师精品 | a视频在线观看免费 | 欧美精品一区二区蜜臀亚洲 | 日本久久久亚洲精品 | 亚洲黄色av网址 | 91欧美视频网站 | 日韩视频在线播放 | 亚洲三级在线免费观看 | 国产精品第一视频 | 五月开心婷婷网 | 色综合综合 | 99视频精品| 五月天亚洲婷婷 | 久久亚洲私人国产精品 | 久久成人一区二区 | 精品视频www | 国产一区视频在线 | 在线不卡a | 青青河边草观看完整版高清 | 国产在线观看国语版免费 | 日韩一级电影在线 | 欧美污污网站 | 91综合视频在线观看 | 久久精品96 | 欧美另类xxxx | 国产一区高清在线观看 | 国产精品久久久久一区二区 | 亚洲精品乱码久久久久久按摩 | 欧美性成人 | 在线电影 一区 | www.玖玖玖 | 久久精品99视频 | 深爱激情五月综合 | 综合久久一本 | 国产一区二区三区免费在线观看 | 超碰在线中文字幕 | 啪啪资源 | 免费久久精品视频 | 国产精品一区二区无线 | 亚洲电影在线看 | 欧美视频国产视频 | 亚洲aⅴ乱码精品成人区 | 中文字幕在线观看完整版电影 | 国产一级免费片 | 夜夜澡人模人人添人人看 | 国产高清精 | 四虎影视精品成人 | 国产在线视频一区 | 伊人成人久久 | 国产成人精品日本亚洲999 | 国产91小视频 | 日韩不卡高清视频 | 国产一区免费看 | 婷婷综合久久 | www.狠狠操.com | 久久久精品欧美一区二区免费 | 午夜av网站 | 日韩免费看片 | www..com毛片| 免费av一级电影 | 日日躁天天躁 | 亚洲综合成人婷婷小说 | 久久激情综合网 | 亚洲最大成人网4388xx | 91最新地址永久入口 | 六月丁香激情综合色啪小说 | 在线免费成人 | 在线播放 日韩专区 | 男女激情网址 | 亚洲网站在线看 | 午夜婷婷在线播放 | 国模一二三区 | 中文字幕色婷婷在线视频 | 在线观看黄色国产 | 久久丝袜视频 | 超碰大片 | 色av婷婷| 色天天 | 国产精品中文字幕在线 | 国产精品久久二区 | 久久夜av| 天天干天天摸天天操 | 亚洲精品人人 | 玖玖在线精品 | 97免费公开视频 | 亚洲欧洲av在线 | 久久久av电影 | 天天干天天操天天拍 | 蜜臀av在线一区二区三区 | 中文字幕av免费在线观看 | 五月婷丁香 | 日韩理论电影在线 | 日韩三级av | 日本中文字幕电影在线免费观看 | 日韩美在线 | 精品欧美小视频在线观看 | 97人人看| 黄色一区二区在线观看 | 亚洲 精品在线视频 | 超碰在97| 欧美激情精品久久久久久免费印度 | 在线观看视频你懂得 | 亚洲人成人99网站 | 国产视频观看 | 国产小视频在线免费观看 | 日操干| 国产精品v a免费视频 | 91av影视| 日韩av成人在线观看 | 国产又黄又爽又猛视频日本 | 人人添人人澡人人澡人人人爽 | 亚洲视频 视频在线 | 久久麻豆精品 | 成人午夜影院在线观看 | 国产一级二级三级在线观看 | 成人理论在线观看 | 国产色视频网站2 | 久久久久久久久久网站 | 国产激情久久久 | www.五月婷| 欧美精品久久久久 | 香蕉日日 | 欧美亚洲精品在线观看 | 中文字幕日本电影 | 国产精品久久av | 久久久久亚洲最大xxxx | 天海冀一区二区三区 | 午夜精品中文字幕 | 欧美精彩视频在线观看 | 色婷婷亚洲 | 中文av在线播放 | 香蕉久久久久 | 亚洲综合色网站 | 日韩在线中文字幕视频 | 四虎影院在线观看av | 欧美在线观看禁18 | 蜜臀久久99精品久久久久久网站 | 国产无套精品久久久久久 | 国产精品一区二区三区免费看 | 亚洲婷婷网 | 九热在线| 亚洲精品66 | 国产伦精品一区二区三区免费 | 黄污视频网站大全 | 中文字幕久久精品 | 久久免费视频3 | 久久久精品午夜 | 九九热免费观看 | 欧美黑人巨大xxxxx | 精品日本视频 | 日日躁夜夜躁xxxxaaaa | 蜜桃传媒一区二区 | 久草在线这里只有精品 | 久久久久在线 | 青春草视频在线播放 | 久久久久福利视频 | 亚洲深夜影院 | 99精品国自产在线 | 五月天综合在线 | 国产青春久久久国产毛片 | 日免费视频 | 免费看黄视频 | 婷婷www| 国产精品 日韩 欧美 | 欧美一级专区免费大片 | 色综合a | 国产日产av | 五月天丁香亚洲 | 丁香婷婷综合色啪 | 久久精品久久精品 | 日本女人的性生活视频 | 国产视频一二三 | 亚洲经典视频 | 色激情五月 | 最近中文字幕高清字幕免费mv | 天躁狠狠躁| 日韩三级免费观看 | 国产在线视频一区二区三区 | 久久综合色天天久久综合图片 | 在线观看国产v片 | 国产精品系列在线播放 | 正在播放一区二区 | 久久精品一区二区三区中文字幕 | 国产一级片网站 | 国产午夜精品一区二区三区 | 亚洲美女免费精品视频在线观看 | 成人免费观看视频大全 | 青春草国产视频 | 免费瑟瑟网站 | 色网站在线免费 | 日韩欧美一区二区三区免费观看 | 999热线在线观看 | 国产精品99爱 | 夜夜夜| 涩涩网站在线播放 | 精品国产1区二区 | 亚洲成人av一区 | 美女视频a美女大全免费下载蜜臀 | 一区二区三区精品在线 | 亚洲成人第一区 | 色插综合| 激情网五月天 | 亚洲 欧美 精品 | 成人中文字幕在线 | 日本中文字幕观看 | 久久66热这里只有精品 | 日韩欧美高清视频在线观看 | 中文字幕乱码电影 | 天天操天天草 | 97热久久免费频精品99 | 色综合久久五月 | 日韩网| av成人在线看| 欧美激情在线网站 | 91av原创| 免费高清无人区完整版 | 精品国产免费人成在线观看 | 国产精品久久久精品 | 亚洲精品伦理在线 | 美女一区网站 | 色无五月| 亚洲日日夜夜 | free,性欧美| 97超碰免费在线 | 国产在线观 | 欧美 激情 国产 91 在线 | 欧美地下肉体性派对 | 色视频一区| 天堂av中文字幕 | 97视频一区| 国产成人久久精品一区二区三区 | 国产不卡片 | 日韩免费小视频 | 成人九九视频 | 奇米影视999| 九九免费在线观看视频 | 探花视频免费观看 | 9免费视频| 国产又粗又猛又黄又爽的视频 | 丝袜美腿在线 | 992tv人人网tv亚洲精品 | 蜜臀av在线一区二区三区 | 狠狠操夜夜操 | 久久精品一二区 | 91av免费在线观看 | 日韩 国产| 一区在线免费观看 | 国产一级在线 | 麻豆视频免费网站 | 一区二区中文字幕在线 | 国产精品网址在线观看 | 91av蜜桃| 久久久久免费视频 | 国产精品久久久久久久久久久免费看 | 免费看日韩片 | 国产人成看黄久久久久久久久 | 中文字幕一区二区三区视频 | 欧美一区中文字幕 | 欧美激情视频在线免费观看 | 日韩av一区二区三区四区 | 日韩试看 | 国产精品久久久久久婷婷天堂 | 久二影院 | 午夜久久成人 | 亚洲高清视频一区二区三区 | 91视频黄色 | 狠狠躁天天躁综合网 | 在线看一区 | www.com黄色| 首页av在线 | 国产国语在线 | 天天婷婷 | 欧美一级欧美一级 | 精品美女在线视频 | 色瓜 | 日韩午夜在线观看 | 97超级碰碰 | 国产一级黄色av | 国产成人精品一区二区三区在线观看 | 日本久久不卡视频 | 四虎海外影库www4hu | 91.麻豆视频 | 亚洲国产精品va在线 | 日韩三级久久 | 国产福利在线免费观看 | 中日韩男男gay无套 日韩精品一区二区三区高清免费 | 久久五月激情 | 丁香六月中文字幕 | 日本黄色免费在线观看 | 亚洲综合小说 | 视频二区在线 | www.大网伊人 | 久久久久久国产一区二区三区 | 亚洲少妇久久 | 一区二区三区免费播放 | 色在线中文字幕 | 久亚洲| 欧美一二三视频 | 香蕉久久久久久久 | 超碰免费在线公开 | 色婷婷骚婷婷 | 欧美一区在线观看视频 | 开心激情久久 |