1. 程式人生 > >網路程式設計(40)—— 使用訊號量semaphore進行多程序間的同步

網路程式設計(40)—— 使用訊號量semaphore進行多程序間的同步

        本文主要介紹下在多程序中使用訊號量semaphore的方法。在上一文中,我們已經知道semaphore和mutex對臨界區訪問控制的一個最主要區別就是semaphore可以跨程序使用,而mutex只能在一個程序中使用。我們再來看下sem_init的原型,熟悉決定程序共享或者執行緒共享的方法:
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);

        通過設定pshared的值來控制訊號量是屬於程序間共享還是執行緒間共享,若pshared為0表明是多執行緒共享,否則就是多程序間共享。接下來我們實驗思路是:建立兩個程序,一個程序負責讀取使用者在介面輸入的資料,然後存入本地的test.txt檔案;另一個程序負責讀取該檔案,然後在標準輸出上顯示讀取的內容。為此,我們需要建立兩個個支援兩個程序訪問的訊號量sem1和sem2,讀檔案時需要獲取sem1訊號,讀取結束後釋放sem2訊號;寫檔案需要獲取sem2訊號,寫檔案結束後方式sem1訊號。sem2的初始值為1,sem1的初始值為0,以保證先寫入再進行讀取,原始碼如下,稍後挑關鍵內容進行解釋:
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>
#include<string.h>
#include<sys/mman.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define BUF_SIZE 30

void readfile(sem_t* psem1,sem_t* psem2)
{
    FILE* fp;
    char buf[BUF_SIZE];
    int str_len,str_seek=0;
    while(1)
    {
        sem_wait(psem1);
        fp=fopen("data.txt","r+");
        if(fp==NULL)
        return ;
        memset(buf,0,sizeof(BUF_SIZE));
        fseek(fp,str_seek,SEEK_SET);
        str_len=fread(buf,sizeof(char),BUF_SIZE-1,fp);
        buf[str_len]=0;
        str_seek+=str_len;
        fputs("output:",stdout);
        puts(buf);
        fclose(fp);
        sem_post(psem2);
    }
}
void writefile(sem_t* psem1,sem_t* psem2)
{
        FILE* fp;
        char buf[BUF_SIZE];
        while(1)
        {
            sem_wait(psem2);
            fp=fopen("data.txt","a");
            if(fp==NULL)
            return;
            memset(buf,0,BUF_SIZE);
            fputs("Input:",stdout);
            fgets(buf,BUF_SIZE,stdin);
            fwrite(buf,sizeof(char),strlen(buf),fp);
            fclose(fp);
            sem_post(psem1);
        }
}

int main()
{
    int pid;
    int fd1,fd2;
    void* pv;
    sem_t* psem1;
    sem_t* psem2;
    fd1=open("data1",O_CREAT|O_RDWR|O_TRUNC,0666);
    fd2=open("data2",O_CREAT|O_RDWR|O_TRUNC,0666);\
    ftruncate(fd1,8192);
    ftruncate(fd2,8192);
    //lseek(fd,5000,SEEK_SET);
    psem1=(sem_t*)mmap(NULL,sizeof(sem_t),PROT_READ|PROT_WRITE,MAP_SHARED,fd1,0);
    psem2=(sem_t*)mmap(NULL,sizeof(sem_t),PROT_READ|PROT_WRITE,MAP_SHARED,fd2,0);
    sem_init(psem1,1,0);
    sem_init(psem2,1,1);
    pid=fork();
    if(pid==0)
    {
        puts("進入子程序");
        writefile(psem1,psem2);
    }
    else
    {
        puts("進入父程序");
        readfile(psem1,psem2);
    }
    sem_destroy(psem1);
    sem_destroy(psem2);
    munmap(psem1,sizeof(sem_t));
    munmap(psem2,sizeof(sem_t));
    close(fd1);
    close(fd2);
    return 0; 
}

        為了能夠跨程序使用semaphore,我們引入了跨程序的技術mmap,第61、第62行分別打開了兩個mmap需要對映的檔案,和我們平時用的open函式不同,這裡面為程式賦予了該檔案的666許可權。這點很重要,因為mmap需要對映的本地檔案必須明確賦予其可讀寫的許可權,否則無法通訊。
        第63行和第64行分別設定兩個本地對映檔案的大小,以保證有充分的空間在mmap中對映並容納我們定義的sem_t變數。這點也很重要,如果空間不夠會造成匯流排錯誤。
        第66行和第67行分別利用mmap在共享記憶體中映射了兩個sem_t型別的指標,這就是我們需要sem_init的訊號量。
        第68、69行開始初始化訊號量。
        70行fork了兩個程序,在子程序中我們進行寫操作,在主程序中我們進行讀操作。讀寫操作的程式碼比較簡單,在這裡不再多說。
        第81到86行在使用完訊號量後分別是銷燬訊號量、釋放共享記憶體、關閉檔案操作符。
       程式寫到這裡基本上完成了這個實驗,可以看下執行的結果:

[
[email protected]
semphare]$ ./a.out 進入父程序 進入子程序 Input:你好 Output:你好 Input:

        我們可以簡單總結下在多程序中使用訊號量的步驟:
(1)open()用於進行mmap對映的檔案,得到檔案操作符fd;
(2)把對映檔案用ftruncate或者fseek重新設定大小,以保證有足夠的空間容納我們需要傳遞的sem_t變數;
(3)利用mmap函式在共享記憶體中建立sen_t型別的指標。
(4)用sem_init()函式初始化第(3)步中建立的指標,也就得到了我們需要的訊號量。
(5)用sem_wait()和sem_post()函式進行訊號量的等待和釋放。
(6)用sem_destroy()銷燬訊號量。

(7)用munmap()釋放共享記憶體以及用close()函式關閉檔案操作符。

Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本專案:
git clone [email protected]:HymanLiuTS/NetDevelopment.git
獲取本文原始碼:
git checkout NL40

相關推薦

網路程式設計40—— 使用訊號semaphore進行程序同步

        本文主要介紹下在多程序中使用訊號量semaphore的方法。在上一文中,我們已經知道semaphore和mutex對臨界區訪問控制的一個最主要區別就是semaphore可以跨程序使用,而mutex只能在一個程序中使用。我們再來看下sem_init的原型,熟悉

Android使用訊號Semaphore進行執行緒任務排程

話不多說,先上程式碼 import android.os.Handler; import android.os.Looper; import android.os.Message; import java.util.LinkedList; import java.util

python學習-網路程式設計

udp的接收和傳送資料程式碼: udp的傳送資料程式碼如下: import socket def main():     #建立套接字     udp_socket = socket.socket(socket.AF_I

python------Socket網路程式設計粘包問題

一.socket網路程式設計  粘包:服務端兩次傳送指令在一起,它會把兩次傳送內容合在一起傳送,稱為粘包,從而出現錯誤。 解決方法:(比較low的方法) 有些需要實時更新的,用sleep有延遲,不能這樣解決問題。 解決方法之高階方法: 客戶端: 二.傳送檔案 ftp s

網路程式設計:埠那些事兒

TCP和UDP協議都存在一個叫做埠的東西,但埠卻不是IP協議的一部分。 埠被設計出來主要是為了給協議棧和應用對應: 協議棧用埠號將資料分配給不同的應用層程式 應用層程式用埠號去區分不同的連線,參見之前提到過的“四元組” TCP和UDP協議都使用了埠號(Port num

網路程式設計——伺服器和客戶端資訊的獲取

  目錄 1、字串IP地址和二進位制IP地址結構的轉換 2.套接字檔案描述符的判定 3、IP地址與域名之間的相互轉換 4、協議名稱處理函式 1、字串IP地址和二進位制IP地址結構的轉換 #include <sys/socket.h> #inclu

Linux基礎——訊號與PV操作

在計算機作業系統中,PV操作是程序管理中的難點。1、基本含義      什麼是訊號量?訊號量(semaphore)的資料結構為一個值和一個指標,指標指向等待該訊號量的下一個程序。訊號量的值與相應資源的使用情況有關。當它的值大於0時,表示當前可用資源的

Python Socket網路程式設計初識Socket和Socket初步使用

目錄 前言 網路程式設計 實質 IP地址和埠 資料傳輸協議 協議 Socket

Python Socket網路程式設計區域網內和區域網與廣域網的持續通訊

目錄 前言 IP地址 簡介 公有IP 私有IP 區域網之間網路通訊 前提 功能描述

python3 實現網路程式設計socket

使用方法:要先執行伺服器指令碼:server.py,再執行客戶端指令碼:client.py  server.py原始碼: #!/usr/bin/python3 import socket import sys #建立socket物件 server = socket.socket

Socket網路程式設計

此文使用的協議是 TCP       首先要寫入以下程式碼,不然很多函式都用不了 #include <WinSock2.h> #pragma comment(lib, "ws2_32.lib")   ●伺服器端    

Linux學習之網路程式設計select

言之者無罪,聞之者足以戒。 - “詩序” 1、阻塞式I/O 下面看一下實現的邏輯: 2、非阻塞式I/O 下面看一下實現的邏輯: 3、I/O複用(select/epoll) (1)  int  select (int maxfdp, fd_set

Python之路十五網路程式設計

python基礎之網路程式設計(上篇)   socket程式設計   本篇介紹socket是基於什麼來的,為什麼要知道網際網路底層實現通訊的原理 一、客戶端/服務端架構 即C/S架構,包括 1.硬體C/S架構(印表機) 2.軟體C/S架構(web

Windows網路程式設計:訊息選擇模型

概述 之前介紹過,系統提供了幾種網路模型用於非同步的網路互動,訊息選擇模型就是其中一種。 這種模型的使用需要在呼叫完socket()函式以後呼叫WSAAsyncSelect(),這個函式的宣告如下: int WSAAsyncSelect(SOCKET s,HWND h

Windows網路程式設計:非阻塞模式非同步模式

前面幾篇文章介紹的無論是TCP通訊還是UDP通訊都是阻塞式的,它們在執行recv或recvfrom時會線上程中等待,直到接收到資訊為止,所以在應用的時候一般都需要開闢子執行緒,在子執行緒裡專門做這類事情,不然它會影響主執行緒的執行。  系統提供三種網路模型

Windows網路程式設計:原始套接字開發

在呼叫socket()函式時,如果將第二個引數填為SOCK_RAW,代表建立的是原始套接字型別,第三個引數可以選擇IPPROTO_ICMP、IPPROTO_TCP、IPPROTO和IPPROTO_RAW。 #include <winsock2.h> #pragma co

Windows網路程式設計:IP Helper

IP Helper是Windows系統與IP配置和管理的重要介面,通過IP Helper 可以獲得很多跟網路配置相關的資訊。比如說本機IP、閘道器設定、網絡卡數量和連線資訊。 #include <windows.h> #include "iphlpapi.h" /* 全域

Windows網路程式設計執行緒訊息處理

對於服務端來說,呼叫accept()函式同意客戶端連線的請求後,需要處理完與這個客戶端的通訊後回到accept()繼續等待下一個客戶端的連線,如果一個客戶端請求連線時服務端並沒有在accept()處等待,客戶端是無法成功連上服務端的,因此併發客戶端連線的服務端必然是多執行緒的。 服務

Windows網路程式設計:建立UDP連線和收發訊息

UDP訊息的傳送和接收需要UDP連線,所以,上面的TCP連線已經不適用了,具體的區別主要有: 建立Socket時引數不同建立服務端時不需要listen和accept操作建立客戶端時不需要connect操作伺服器需要bind操作,客戶端不需要。 傳送和接收UDP訊息要用到sendt

Windows網路程式設計:建立TCP連線和收發訊息

先看服務端: // ConsoleApplication3.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #define _WINSOCK_DEPRECATED_NO_WARNINGS //這個宣告要在stdafx.h的後面,但要