進程間通訊 (IPC) 方法總結(三)

進程間通訊 (IPC) 方法總結(三)

信號量(SEMAPHORE)

信號量是一個計數器,用於多進程對共享數據的訪問,信號量的意圖在於進程間同步。
爲了得到共享資源,進程須要執行下列操做:html

  1. 建立一個信號量:這要求調用者指定初始值,對於二值信號量來講,它一般是1,也但是0。
  2. 等待一個信號量:該操做會測試這個信號量的值,若是小於0,就阻塞。也稱爲P操做。
  3. 掛出一個信號量:該操做將信號量的值加1,也稱爲V操做。

爲了正確地實現信號量,信號量值的測試及減1操做應當是原子操做。爲此,信號量一般是在內核中實現的。Linux環境中,有三種類型:Posix(可移植性操做系統接口)有名信號量(使用Posix IPC名字標識)、Posix基於內存的信號量(存放在共享內存區中)、System V信號量(在內核中維護)。這三種信號量均可用於進程間或線程間的同步。linux

Posix有名信號量服務器

Posix基於內存的信號量網絡

System V信號量多線程

信號量與普通整型變量的區別

  1. 信號量是非負整型變量,除了初始化以外,它只能經過兩個標準原子操做:wait(semap) , signal(semap) ; 來進行訪問;
  2. 操做也被成爲PV原語(P來源於荷蘭語proberen"測試",V來源於荷蘭語verhogen"增長",P表示經過的意思,V表示釋放的意思),而普通整型變量則能夠在任何語句塊中被訪問;socket

    信號量與互斥量之間的區別

  3. 互斥量用於線程的互斥,信號量用於線程的同步。這是互斥量和信號量的根本區別,也就是互斥和同步之間的區別。tcp

    互斥:是指某一資源同時只容許一個訪問者對其進行訪問,具備惟一性和排它性。但互斥沒法限制訪問者對資源的訪問順序,即訪問是無序的。函數

    同步:是指在互斥的基礎上(大多數狀況),經過其它機制實現訪問者對資源的有序訪問。測試

    在大多數狀況下,同步已經實現了互斥,特別是全部寫入資源的狀況一定是互斥的。少數狀況是指能夠容許多個訪問者同時訪問資源ui

  4. 互斥量值只能爲0/1,信號量值能夠爲非負整數。

    也就是說,一個互斥量只能用於一個資源的互斥訪問,它不能實現多個資源的多線程互斥問題。信號量能夠實現多個同類資源的多線程互斥和同步。當信號量爲單值信號量是,也能夠完成一個資源的互斥訪問。

  5. 互斥量的加鎖和解鎖必須由同一線程分別對應使用,信號量能夠由一個線程釋放,另外一個線程獲得。

套接字(SOCKET)

套接字是一種通訊機制,憑藉這種機制,客戶/服務器(即要進行通訊的進程)系統的開發工做既能夠在本地單機上進行,也能夠跨網絡進行。也就是說它可讓不在同一臺計算機但經過網絡鏈接計算機上的進程進行通訊。
套接字示意圖

套接字是支持TCP/IP的網絡通訊的基本操做單元,能夠看作是不一樣主機之間的進程進行雙向通訊的端點,簡單的說就是通訊的兩方的一種約定,用套接字中的相關函數來完成通訊過程。

套接字特性

套接字的特性由3個屬性肯定,它們分別是:域、端口號、協議類型。

套接字的域

它指定套接字通訊中使用的網絡介質,最多見的套接字域有兩種:

  1. AF_INET,它指的是Internet網絡。當客戶使用套接字進行跨網絡的鏈接時,它就須要用到服務器計算機的IP地址和端口來指定一臺聯網機器上的某個特定服務,因此在使用socket做爲通訊的終點,服務器應用程序必須在開始通訊以前綁定一個端口,服務器在指定的端口等待客戶的鏈接。
  2. AF_UNIX,表示UNIX文件系統,它就是文件輸入/輸出,而它的地址就是文件名。

    套接字的端口號

    每個基於TCP/IP網絡通信的程序(進程)都被賦予了惟一的端口和端口號,端口是一個信息緩衝區,用於保留Socket中的輸入/輸出信息,端口號是一個16位無符號整數,範圍是0-65535,以區別主機上的每個程序(端口號就像房屋中的房間號),低於256的端口號保留給標準應用程序,好比pop3的端口號就是110,每個套接字都組合進了IP地址、端口,這樣造成的總體就能夠區別每個套接字。

    套接字協議類型
  3. 流套接字
    流套接字在域中經過TCP/IP鏈接實現,同時也是AF_UNIX中經常使用的套接字類型。流套接字提供的是一個有序、可靠、雙向字節流的鏈接,所以發送的數據能夠確保不會丟失、重複或亂序到達,並且它還有必定的出錯後從新發送的機制。
  4. 數據報套接字
    它不須要創建鏈接和維持一個鏈接,它們在域中一般是經過UDP/IP協議實現的。它對能夠發送的數據的長度有限制,數據報做爲一個單獨的網絡消息被傳輸,它可能會丟失、複製或錯亂到達,UDP不是一個可靠的協議,可是它的速度比較高,由於它並一須要老是要創建和維持一個鏈接。
  5. 原始套接字
    原始套接字容許對較低層次的協議直接訪問,好比IP、 ICMP協議,它經常使用於檢驗新的協議實現,或者訪問現有服務中配置的新設備,由於RAW SOCKET能夠自如地控制Windows下的多種協議,可以對網絡底層的傳輸機制進行控制,因此能夠應用原始套接字來操縱網絡層和傳輸層應用。好比,咱們能夠經過RAW SOCKET來接收發向本機的ICMP、IGMP協議包,或者接收TCP/IP棧不可以處理的IP包,也能夠用來發送一些自定包頭或自定協議的IP包。網絡監聽技術很大程度上依賴於SOCKET_RAW。

原始套接字與標準套接字的區別

原始套接字能夠讀寫內核沒有處理的IP數據包,而流套接字只能讀取TCP協議的數據,數據報套接字只能讀取UDP協議的數據。所以,若是要訪問其餘協議發送數據必須使用原始套接字。

套接字通訊的創建

套接字通訊的創建

  • 服務端
    1. 首先服務器應用程序用系統調用socket來建立一個套接字,它是系統分配給該服務器進程的相似文件描述符的資源,它不能與其餘的進程共享。(socket)
    2. 服務器進程會給套接字起個名字,咱們使用系統調用bind來給套接字命名。而後服務器進程就開始等待客戶鏈接到這個套接字。(bind)
    3. 系統調用listen來建立一個隊列並將其用於存放來自客戶的進入鏈接。(listen)
    4. 服務器經過系統調用accept來接受客戶的鏈接。它會建立一個與原有的命名套接不一樣的新套接字,這個套接字只用於與這個特定客戶端進行通訊,而命名套接字(即原先的套接字)則被保留下來繼續處理來自其餘客戶的鏈接(創建客戶端和服務端的用於通訊的流,進行通訊)。(accept--read/write)
  • 客戶端
    1. 客戶應用程序首先調用socket來建立一個未命名的套接字。(socket)
    2. 將服務器的命名套接字做爲一個地址來調用connect與服務器創建鏈接。(connect)
    3. 一旦鏈接創建,咱們就能夠像使用底層的文件描述符那樣用套接字來實現雙向數據的通訊(經過流進行數據傳輸)(read/write)

eg.

服務端代碼

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> //socket listen bind
#include <sys/socket.h>//socket listen bind 
#include <unistd.h>//unlink
#include <sys/un.h>//struct sockaddr_un
  
int main()  
{  
  /* delete the socket file */  
  unlink("server_socket");  
    
  /* create a socket */  
  int server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);  
    
  struct sockaddr_un server_addr;  
  server_addr.sun_family = AF_UNIX;  
  strcpy(server_addr.sun_path, "server_socket");  
    
  /* bind with the local file */  
  bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));  
    
  /* listen */  
  listen(server_sockfd, 5);  
    
  char ch;  
  int client_sockfd;  
  struct sockaddr_un client_addr;  
  socklen_t len = sizeof(client_addr);  
  while(1)  
  {  
    printf("server waiting:\n");  
      
    /* accept a connection */  
    client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len);  
      
    /* exchange data */  
    read(client_sockfd, &ch, 1);  
    printf("get char from client: %c\n", ch);  
    ++ch;  
    write(client_sockfd, &ch, 1);  
      
    /* close the socket */  
    close(client_sockfd);  
  }  
    
  return 0;  
}

客戶端代碼

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> //socket listen bind
#include <sys/socket.h>//socket listen bind 
#include <unistd.h>//unlink
#include <sys/un.h>//struct sockaddr_un  
  
int main()  
{  
  /* create a socket */  
  int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);  
    
  struct sockaddr_un address;  
  address.sun_family = AF_UNIX;  
  strcpy(address.sun_path, "server_socket");  
    
  /* connect to the server */  
  int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));  
  if(result == -1)  
  {  
    perror("connect failed: ");  
    exit(1);  
  }  
    
  /* exchange data */  
  char ch = 'A';  
  write(sockfd, &ch, 1);  
  read(sockfd, &ch, 1);  
  printf("get char from server: %c\n", ch);  
    
  /* close the socket */  
  close(sockfd);  
    
  return 0;  
}

若是咱們首先運行tcp_client,會提示沒有這個文件:

由於咱們是以AF_UNIX方式進行通訊的,這種方式是經過文件來將服務器和客戶端鏈接起來的,所以咱們應該先運行tcp_server,建立這個文件,默認狀況下,這個文件會建立在當前目錄下,而且第一個s表示它是一個socket文件:

程序運行的結果以下圖:


參考文章:

  1. 進程間通訊IPC (InterProcess Communication)

  2. 進程間通訊--管道

  3. UNIX/Linux進程間通訊IPC系列(四)消息隊列

  4. Linux進程間通訊(四) - 共享內存

  5. 本地socket通信

相關文章
相關標籤/搜索