網路程式設計(40)—— 使用訊號量semaphore進行多程序間的同步
#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的後面,但要