国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁 > news >正文

網(wǎng)站游戲制作開發(fā)神秘網(wǎng)站

網(wǎng)站游戲制作開發(fā),神秘網(wǎng)站,自動發(fā)貨網(wǎng)站怎么做,黃頁88推廣效果怎么樣文章目錄 進程間通信介紹進程間通信目的進程間通信發(fā)展 管道什么是管道 匿名管道用fork來共享管道原理站在文件描述符角度-深度理解管道站在內(nèi)核角度-管道本質(zhì)管道讀寫規(guī)則管道特點 命名管道創(chuàng)建一個命名管道匿名管道與命名管道的區(qū)別命名管道的打開規(guī)則 命名管道的刪除用命名管…

文章目錄

  • 進程間通信介紹
    • 進程間通信目的
    • 進程間通信發(fā)展
  • 管道
    • 什么是管道
  • 匿名管道
    • 用fork來共享管道原理
    • 站在文件描述符角度-深度理解管道
    • 站在內(nèi)核角度-管道本質(zhì)
    • 管道讀寫規(guī)則
    • 管道特點
  • 命名管道
    • 創(chuàng)建一個命名管道
    • 匿名管道與命名管道的區(qū)別
    • 命名管道的打開規(guī)則
  • 命名管道的刪除
  • 用命名管道實現(xiàn)文件拷貝
    • 用命名管道實現(xiàn)server&client通信
  • system V共享內(nèi)存
    • 共享內(nèi)存示意圖
    • 共享內(nèi)存數(shù)據(jù)結(jié)構(gòu)
    • 共享內(nèi)存函數(shù)
  • system V信號量 - 選學(xué)了解即可

在這里插入圖片描述

進程間通信介紹

進程間通信目的

  1. 數(shù)據(jù)傳輸:一個進程需要將它的數(shù)據(jù)發(fā)送給另一個進程
  2. 資源共享:多個進程之間共享同樣的資源。
  3. 通知事件:一個進程需要向另一個或一組進程發(fā)送消息,通知它(它們)發(fā)生了某種事件(如進程終止時要通知父進程)。
  4. 進程控制:有些進程希望完全控制另一個進程的執(zhí)行(如Debug進程),此時控制進程希望能夠攔截另一個進程的所有陷入和異常,并能夠及時知道它的狀態(tài)改變。

進程間通信發(fā)展

進程間通信(IPC)的發(fā)展經(jīng)歷了多個階段,主要包括:

  • 管道(Pipes):最早期的IPC機制之一,允許具有親緣關(guān)系的進程(如父子進程)進行通信。管道是半雙工(某一時刻只允許信號在管道上的單向通信)的,數(shù)據(jù)只能單向流動。

  • 命名管道(Named Pipes):與管道類似,但它們允許沒有親緣關(guān)系的進程通信,并且可以在文件系統(tǒng)中有一個名稱。

  • System V IPC:這是一組傳統(tǒng)的IPC機制,包括消息隊列、信號量和共享內(nèi)存。這些機制允許不同進程共享和傳遞復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。

  • POSIX IPC:基于System V IPC,提供了更加標(biāo)準(zhǔn)化和可移植的IPC機制,包括消息傳遞、同步以及共享內(nèi)存的新方法。

  • 套接字(Sockets):用于不同機器上的進程通信,也可以在同一臺機器上的進程之間通信,非常靈活。

  • 遠程過程調(diào)用(RPC):允許一個進程調(diào)用另一個進程的函數(shù)或方法,就像調(diào)用本地函數(shù)一樣。

  • 內(nèi)存映射文件(Memory-mapped files):通過映射文件到內(nèi)存,不同進程可以訪問同一塊內(nèi)存區(qū)域,實現(xiàn)通信。

  • 信號(Signals):用于進程間的簡單通信,一個進程可以向另一個進程發(fā)送信號,以通知某些事件的發(fā)生。

隨著技術(shù)的發(fā)展,IPC機制也在不斷進步,以滿足更高效、更安全的進程通信需求。全部的進程間通信雖然體系龐大,但是其基本思想都離不開操作系統(tǒng)的性質(zhì)–先描述,再組織。我們只需學(xué)習(xí)早期的通信方法管道和System V通信,觸類旁通。

管道

什么是管道

  1. 管道是Unix中最古老的進程間通信的形式。
  2. 我們把從一個進程連接到另一個進程的一個數(shù)據(jù)流稱為一個“管道”。

在這里插入圖片描述

匿名管道

匿名管道(Anonymous Pipe)是一種在進程間進行單向通信的機制,其中一個進程作為寫入端,另一個進程作為讀取端。它是一種簡單而有效的進程間通信方式,常用于父子進程或兄弟進程之間的通信。

在Linux系統(tǒng)中,可以使用pipe函數(shù)來創(chuàng)建匿名管道。pipe函數(shù)會創(chuàng)建一個管道,并返回兩個文件描述符,一個用于讀取數(shù)據(jù),另一個用于寫入數(shù)據(jù)。其中,文件描述符0表示標(biāo)準(zhǔn)輸入(stdin),文件描述符1表示標(biāo)準(zhǔn)輸(stdout)。

#include <unistd.h>
int pipe(int fd[2]);
//創(chuàng)建失敗返回-1,創(chuàng)建成功返回兩個文件描述符構(gòu)成的數(shù)組

以下是使用匿名管道進行進程間通信的基本步驟:

  1. 調(diào)用pipe函數(shù)創(chuàng)建管道,獲取讀取端和寫入端的文件描述符。
  2. 調(diào)用fork函數(shù)創(chuàng)建一個子進程。
  3. 在父進程中關(guān)閉讀取端的文件描述符,保留寫入端的文件描述符。
  4. 在子進程中關(guān)閉寫入端的文件描述符,保留讀取端的文件描述符。
  5. 父進程可以通過寫入端的文件描述符向管道寫入數(shù)據(jù)。
  6. 子進程可以通過讀取端的文件描述符從管道讀取數(shù)據(jù)。

下面是一個簡單的示例代碼,演示了如何使用匿名管道進行進程間通信:

#include <stdio.h>
#include <unistd.h>int main() {int pipefd[2];char buffer[20];pid_t pid;// 創(chuàng)建管道if (pipe(pipefd) == -1) {perror("pipe");return 1;}// 創(chuàng)建子進程pid = fork();if (pid == -1) {perror("fork");return 1;}if (pid == 0) {// 子進程close(pipefd[1]);  // 關(guān)閉寫入端// 從管道讀取數(shù)據(jù)read(pipefd[0], buffer, sizeof(buffer));printf("子進程接收到的數(shù)據(jù):%s\n", buffer);close(pipefd[0]);  // 關(guān)閉讀取端} else {// 父進程close(pipefd[0]);  // 關(guān)閉讀取端// 向管道寫入數(shù)據(jù)write(pipefd[1], "Hello, child process!", 21);close(pipefd[1]);  // 關(guān)閉寫入端}return 0;
}

在上述示例中,父進程向管道寫入了一段字符串,子進程從管道中讀取該字符串并輸出。通過匿名管道,父子進程之間實現(xiàn)了簡單的通信。
需要注意的是,匿名管道只能實現(xiàn)單向通信,如果需要雙向通信,可以考慮使用命名管道(Named Pipe)或其他進程間通信機制,如共享內(nèi)存或消息隊列。

用fork來共享管道原理

在這里插入圖片描述

站在文件描述符角度-深度理解管道

在這里插入圖片描述

站在內(nèi)核角度-管道本質(zhì)

在這里插入圖片描述
所以,看待管道,就如同看待文件一樣!管道的使用和文件一致,迎合了“Linux一切皆文件思想”。

管道讀寫規(guī)則

當(dāng)沒有數(shù)據(jù)可讀時

  1. O_NONBLOCK disable:read調(diào)用阻塞,即進程暫停執(zhí)行,一直等到有數(shù)據(jù)來到為止。
  2. O_NONBLOCK enable:read調(diào)用返回-1,errno值為EAGAIN。
    當(dāng)管道滿的時候
  3. O_NONBLOCK disable: write調(diào)用阻塞,直到有進程讀走數(shù)據(jù)
  4. O_NONBLOCK enable:調(diào)用返回-1,errno值為EAGAIN
如果所有管道寫端對應(yīng)的文件描述符被關(guān)閉,則read返回0
如果所有管道讀端對應(yīng)的文件描述符被關(guān)閉,則write操作會產(chǎn)生信號SIGPIPE,進而可能導(dǎo)致write進程
退出
當(dāng)要寫入的數(shù)據(jù)量不大于PIPE_BUF時,linux將保證寫入的原子性。
當(dāng)要寫入的數(shù)據(jù)量大于PIPE_BUF時,linux將不再保證寫入的原子性。

管道特點

  1. 只能用于具有共同祖先的進程(具有親緣關(guān)系的進程)之間進行通信;通常,一個管道由一個進程創(chuàng)建,然后該進程調(diào)用fork,此后父、子進程之間就可應(yīng)用該管道。
  2. 管道提供流式服務(wù)。
  3. 一般而言,進程退出,管道釋放,所以管道的生命周期隨進程。
  4. 一般而言,內(nèi)核會對管道操作進行同步與互斥。
  5. 管道是半雙工的,數(shù)據(jù)只能向一個方向流動;需要雙方通信時,需要建立起兩個管道。如下圖:
    在這里插入圖片描述

命名管道

  1. 管道應(yīng)用的一個限制就是只能在具有共同祖先(具有親緣關(guān)系)的進程間通信。
  2. 如果我們想在不相關(guān)的進程之間交換數(shù)據(jù),可以使用FIFO文件來做這項工作,它經(jīng)常被稱為命名管道。
  3. 命名管道是一種特殊類型的文件。

創(chuàng)建一個命名管道

  1. 命名管道可以從命令行上創(chuàng)建,命令行方法是使用下面這個命令:
mkfifo filename

其中,filename 是要創(chuàng)建的命名管道的路徑和名稱。
例如,要在命令行上創(chuàng)建一個名為 “myfifo” 的命名管道,可以執(zhí)行以下命令:

mkfifo myfifo

執(zhí)行該命令后,系統(tǒng)將在當(dāng)前目錄下創(chuàng)建一個名為 “myfifo” 的命名管道文件。

需要注意的是,命名管道是一種特殊的文件可以像普通文件一樣進行讀寫操作。在命令行中創(chuàng)建的命名管道可以被其他進程打開并進行讀寫操作。

  1. 命名管道也可以從程序里創(chuàng)建,相關(guān)函數(shù)有:
    mkfifo 是一個用于創(chuàng)建命名管道(Named Pipe)的系統(tǒng)調(diào)用。它可以在文件系統(tǒng)中創(chuàng)建一個特殊的文件,用于進程間的通信。

具體使用方法如下:

#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);
  • pathname:要創(chuàng)建的命名管道的路徑和名稱。
  • mode:創(chuàng)建的命名管道的權(quán)限模式。

返回值:

  • 成功:返回0。
  • 失敗:返回-1,并設(shè)置相應(yīng)的錯誤碼。

使用示例:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>int main() {const char *pathname = "myfifo";mode_t mode = 0666; // 設(shè)置權(quán)限為讀寫所有者、組和其他用戶if (mkfifo(pathname, mode) == -1) {perror("mkfifo");return 1;}printf("命名管道創(chuàng)建成功:%s\n", pathname);return 0;
}

在上述示例中,我們調(diào)用了 mkfifo 函數(shù)創(chuàng)建了一個命名管道,并指定了路徑和名稱為 “myfifo”,權(quán)限模式為 0666,表示讀寫所有者、組和其他用戶都有權(quán)限。

需要注意的是,命名管道是一種特殊的文件,可以像普通文件一樣進行讀寫操作。在進程間通信時,一個進程可以將數(shù)據(jù)寫入命名管道,而另一個進程可以從該管道讀取數(shù)據(jù)。但是,需要注意管道的讀取和寫入操作應(yīng)該在不同的進程中進行,否則可能會造成阻塞。

匿名管道與命名管道的區(qū)別

  1. 匿名管道由pipe函數(shù)創(chuàng)建并打開。
  2. 命名管道由mkfifo函數(shù)創(chuàng)建,打開用open
  3. FIFO(命名管道)與pipe(匿名管道)之間唯一的區(qū)別在它們創(chuàng)建與打開的方式不同,一但這些工作完成之后,它們具有相同的語義。

命名管道的打開規(guī)則

對于命名管道(FIFO)的打開規(guī)則,取決于打開操作是為讀還是為寫。

  1. 當(dāng)打開操作為讀時:

    • 如果打開操作沒有設(shè)置 O_NONBLOCK 標(biāo)志(即阻塞模式),則打開操作會阻塞,直到有其他進程以寫模式打開該命名管道。
    • 如果打開操作設(shè)置了 O_NONBLOCK 標(biāo)志(即非阻塞模式),則打開操作會立即返回成功,不會阻塞等待。
  2. 當(dāng)打開操作為寫時:

    • 如果打開操作沒有設(shè)置 O_NONBLOCK 標(biāo)志(即阻塞模式),則打開操作會阻塞,直到有其他進程以讀模式打開該命名管道。
    • 如果打開操作設(shè)置了 O_NONBLOCK 標(biāo)志(即非阻塞模式),則打開操作會立即返回失敗,并返回錯誤碼 ENXIO(表示設(shè)備或地址不存在)。

這些規(guī)則確保了在讀寫操作之間建立正確的通信連接。如果讀操作和寫操作同時進行,并且滿足打開規(guī)則,那么數(shù)據(jù)可以在進程之間通過命名管道進行傳輸。

需要注意的是,打開規(guī)則只適用于命名管道的打開操作,而不是創(chuàng)建操作。創(chuàng)建命名管道時,并不需要等待其他進程的打開操作。只有在打開操作時,才會根據(jù)規(guī)則進行阻塞或立即返回。

命名管道的刪除

用命名管道實現(xiàn)文件拷貝

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<stdlib.h>
#define BUFFER_SIZE 50
int main()
{const char *pathname = "myfifo";mode_t mode = 0666; // 設(shè)置權(quán)限為讀寫所有者、組和其他用戶if (mkfifo(pathname, mode) == -1){perror("mkfifo");return 1;}printf("命名管道創(chuàng)建成功:%s\n", pathname);// 打開源文件和目標(biāo)文件    int source_fd = open("./text.txt", O_RDONLY);if (source_fd == -1) {perror("open source file");return 1;}int destination_fd = open("./popytext.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (destination_fd == -1) {perror("open destination file");return 1;}// 打開命名管道int fifo_fd = open("myfifo", O_RDWR);if (fifo_fd == -1) {perror("open fifo");return 1;}// 創(chuàng)建子進程進行數(shù)據(jù)傳輸pid_t pid = fork();if (pid == -1) {perror("fork");return 1;}char buffer[BUFFER_SIZE];ssize_t bytes_read;if (pid == 0) {// 子進程從命名管道中讀取數(shù)據(jù)并寫入目標(biāo)文件if((bytes_read = read(fifo_fd, buffer, BUFFER_SIZE)) > 0) {write(destination_fd, buffer, bytes_read);}exit(EXIT_SUCCESS);}// 父進程從源文件中讀取數(shù)據(jù)并寫入命名管道if((bytes_read = read(source_fd, buffer, BUFFER_SIZE)) > 0) {write(fifo_fd, buffer, bytes_read);}close(source_fd);close(fifo_fd);close(destination_fd);// 等待子進程結(jié)束wait(NULL);// 刪除命名管道unlink("myfifo");return 0;
}

用命名管道實現(xiàn)server&client通信

comm.hpp

#pragma once 
#include<iostream>
#include<cerrno>
#include<cstring>
#include<cstdlib>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>#define FIFO_FILE "./myfifo"
#define MODE 0664
enum{FIFO_CREATE_ERR = 1,FIFO_DELETE_ERR, FILE_OPEN_ERR
};
class Init
{
public:Init(){int n = mkfifo(FIFO_FILE,MODE);if(n == -1){//printf("%d:%s\n",errno,strerror(errno));perror("mkfifo");exit(FIFO_CREATE_ERR);}}~Init(){int m = unlink(FIFO_FILE);if(m== -1){perror("unlink");exit(FIFO_DELETE_ERR);}}
};

log.hpp

#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch (level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}void printLog(int level, const std::string &logtxt){switch (printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"if (fd < 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string &logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"printOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默認部分+自定義部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);// printf("%s", logtxt); // 暫時打印printLog(level, logtxt);}private:int printMethod;std::string path;
};

server.cc

#include"comm.hpp"
#include"log.hpp"using namespace std;//管理管道文件
int main()
{/*logmessage(Info,"hello linux");*///創(chuàng)建信道Init init;//打開管道int fd = open(FIFO_FILE,O_RDONLY);if(fd<0){//perror("open");logmessage(Fatal,"error string:%s,error code:%d",strerror(errno),errno);exit(FILE_OPEN_ERR);}logmessage(Info,"error string:%s,error code:%d",strerror(errno),errno);//開始通信while(true){char buffer[1024]={0};int x = read(fd,buffer,sizeof(buffer));if(x>0){buffer[x]=0;cout<<"client say#"<<buffer<<endl;}else if(x==0){logmessage(Debug,"client quit,me too!:%s,error code:%d",strerror(errno),errno);break;}elsebreak;}//關(guān)閉管道 close(fd);return 0;
}

client.cc

#include"comm.hpp"
using namespace std;int main()
{int fd = open(FIFO_FILE,O_WRONLY);if(fd<0){perror("open");exit(FILE_OPEN_ERR);}string line;while(true){cout<<"Please Enter@";getline(cin,line);write(fd,line.c_str(),line.size());}close(fd);return 0;
}

system V共享內(nèi)存

共享內(nèi)存區(qū)是最快的IPC形式。一旦這樣的內(nèi)存映射到共享它的進程的地址空間,這些進程間數(shù)據(jù)傳遞不再涉及到內(nèi)核,換句話說是進程不再通過執(zhí)行進入內(nèi)核的系統(tǒng)調(diào)用來傳遞彼此的數(shù)據(jù)。

共享內(nèi)存示意圖

在這里插入圖片描述

共享內(nèi)存數(shù)據(jù)結(jié)構(gòu)

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_ds 結(jié)構(gòu)體,用于描述 System V 共享內(nèi)存段的屬性和狀態(tài)。讓我們逐個解釋這些字段的含義:

  • shm_perm:一個 struct ipc_perm 類型的結(jié)構(gòu)體,用于描述共享內(nèi)存段的權(quán)限信息,包括所有者、所屬組和訪問權(quán)限等。

  • shm_segsz:表示共享內(nèi)存段的大小,以字節(jié)為單位。

  • shm_atime:表示最后一次連接(attach)共享內(nèi)存段的時間,以 UNIX 時間戳(__kernel_time_t 類型)表示。

  • shm_dtime:表示最后一次分離(detach)共享內(nèi)存段的時間,以 UNIX 時間戳表示。

  • shm_ctime:表示最后一次修改共享內(nèi)存段屬性的時間,以 UNIX 時間戳表示。

  • shm_cpid:表示創(chuàng)建共享內(nèi)存段的進程的進程 ID(__kernel_ipc_pid_t 類型)。

  • shm_lpid:表示最后一次操作共享內(nèi)存段的進程的進程 ID。

  • shm_nattch:表示當(dāng)前連接(attach)到共享內(nèi)存段的進程數(shù)量。

  • shm_unused:一個未使用的字段,用于兼容性。

  • shm_unused2shm_unused3:兩個未使用的字段,可能由某些特定的實現(xiàn)或庫使用。

通過使用 struct shmid_ds 結(jié)構(gòu)體,可以獲取共享內(nèi)存段的屬性信息,例如大小、創(chuàng)建時間、連接數(shù)等。這些信息對于管理和監(jiān)控共享內(nèi)存的使用非常有用。

請注意,struct shmid_ds 結(jié)構(gòu)體中的字段類型可能會因操作系統(tǒng)和編譯器而異,例如 __kernel_time_t__kernel_ipc_pid_t。在實際使用時,你需要查閱相關(guān)的系統(tǒng)文檔或頭文件來了解具體的字段類型和定義。

共享內(nèi)存函數(shù)

shmget函數(shù)

功能:用來創(chuàng)建共享內(nèi)存
原型
int shmget(key_t key, size_t size, int shmflg);
參數(shù)
- key:用于標(biāo)識共享內(nèi)存段的鍵值??梢允褂?`ftok` 函數(shù)生成一個唯一的鍵值,
- 也可以使用已知的鍵值來獲取現(xiàn)有的共享內(nèi)存段。不同的共享內(nèi)存段應(yīng)該使用不同的鍵值。
- size:指定共享內(nèi)存段的大小,以字節(jié)為單位。共享內(nèi)存段的大小應(yīng)根據(jù)實際需求進行設(shè)置。
- shmflg:用于指定創(chuàng)建共享內(nèi)存段的標(biāo)志和訪問權(quán)限??梢酝ㄟ^按位或運算符 `|` 組合多
- 個標(biāo)志。常用的標(biāo)志包括:
- IPC_CREAT:如果共享內(nèi)存段不存在,則創(chuàng)建一個新的共享內(nèi)存段。  
- IPC_EXCL:不單獨使用,與 IPC_CREAT 一起使用,如果共享內(nèi)存段已存在,則返回錯誤。
- 0666:指定共享內(nèi)存段的訪問權(quán)限。這里的權(quán)限是八進制表示,表示所有者、
- 所屬組和其他用戶的讀寫權(quán)限。
返回值:成功返回一個非負整數(shù),即該共享內(nèi)存段的標(biāo)識碼;失敗返回-1

上面的Key有講究的:

  1. key是一個數(shù)字,這個數(shù)字是幾,并不重要。關(guān)鍵在于它必須在內(nèi)核中具有唯一性,能夠讓不同的進程進行唯一性標(biāo)識。
  2. 第一個進程可以通過key創(chuàng)建共享內(nèi)存,第二個之后的進程,只要拿著同一個key就可以和第一個進程看到同一個共享內(nèi)存了!
  3. 對于一個已經(jīng)創(chuàng)建好的共享內(nèi)存,key在哪? key在共享內(nèi)存的描述對象中!
  4. 第一次創(chuàng)建的時候,必須有一個key了。怎么有?可以使用 ftok 函數(shù)生成一個唯一的鍵值,也可以使用已知的鍵值來獲取現(xiàn)有的共享內(nèi)存段。不同的共享內(nèi)存段應(yīng)該使用不同的鍵值。
    ftok 函數(shù)用于將一個路徑名和一個整數(shù)標(biāo)識符轉(zhuǎn)換為一個唯一的鍵值,用于標(biāo)識 System V 共享內(nèi)存段、消息隊列或信號量集。下面是 ftok 函數(shù)的使用方法:
#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
  • pathname:一個指向路徑名的字符串,可以是任意有效的文件路徑。通常選擇一個與共享內(nèi)存相關(guān)的文件作為路徑名,確保不同的共享內(nèi)存段使用不同的路徑名。

  • proj_id:一個整數(shù)標(biāo)識符,用于區(qū)分不同的共享內(nèi)存段。通常選擇一個非零的整數(shù)作為標(biāo)識符。

ftok 函數(shù)將 pathnameproj_id 組合起來,生成一個唯一的鍵值。該鍵值可以用于創(chuàng)建共享內(nèi)存段、獲取現(xiàn)有的共享內(nèi)存段或操作其他 System V IPC 對象。

需要注意的是,ftok 函數(shù)的結(jié)果并不是絕對唯一的,因為它使用了路徑名和整數(shù)標(biāo)識符的組合。因此,在使用 ftok 生成鍵值時,應(yīng)確保路徑名和標(biāo)識符的組合在整個系統(tǒng)中是唯一的,以避免沖突。

以下是一個示例,展示了如何使用 ftok 函數(shù)生成一個鍵值:

#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>int main() {const char *pathname = "/path/to/file"; // 路徑名int proj_id = 1; // 標(biāo)識符key_t key = ftok(pathname, proj_id);if (key == -1) {perror("ftok");return 1;}printf("Generated key: %d\n", key);return 0;
}

在上述示例中,我們使用 /path/to/file 作為路徑名,1 作為標(biāo)識符,然后調(diào)用 ftok 函數(shù)生成一個鍵值。如果成功,將打印生成的鍵值。如果發(fā)生錯誤,將打印相應(yīng)的錯誤信息。

請注意,生成的鍵值應(yīng)與其他進程共享,以便它們可以使用相同的鍵值訪問相同的共享內(nèi)存段。

shmat函數(shù)

功能:將共享內(nèi)存段連接到進程地址空間
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
參數(shù)
shmid: 共享內(nèi)存標(biāo)識
shmaddr:指定連接的地址
shmflg:它的兩個可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一個指針,指向共享內(nèi)存第一個節(jié);失敗返回-1

說明:

  1. shmaddr為NULL,核心自動選擇一個地址
  2. shmaddr不為NULL且shmflg無SHM_RND標(biāo)記,則以shmaddr為連接地址。
  3. shmaddr不為NULL且shmflg設(shè)置了SHM_RND標(biāo)記,則連接的地址會自動向下調(diào)整為SHMLBA的整數(shù)倍。公式:shmaddr -(shmaddr % SHMLBA)
  4. shmflg=SHM_RDONLY,表示連接操作用來只讀共享內(nèi)存
    shmdt函數(shù)
功能:將共享內(nèi)存段與當(dāng)前進程脫離
原型
int shmdt(const void *shmaddr);
參數(shù)
shmaddr: 由shmat所返回的指針
返回值:成功返回0;失敗返回-1
注意:將共享內(nèi)存段與當(dāng)前進程脫離不等于刪除共享內(nèi)存段

shmctl函數(shù)

功能:用于控制共享內(nèi)存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
參數(shù)
shmid:由shmget返回的共享內(nèi)存標(biāo)識碼
cmd:將要采取的動作(有三個可取值)
buf:指向一個保存著共享內(nèi)存的模式狀態(tài)和訪問權(quán)限的數(shù)據(jù)結(jié)構(gòu)
返回值:成功返回0;失敗返回-1

在這里插入圖片描述

System V 共享內(nèi)存的使用涉及以下幾個步驟:

  1. 創(chuàng)建共享內(nèi)存段:使用 shmget 函數(shù)創(chuàng)建一個共享內(nèi)存段。該函數(shù)接受三個參數(shù):鍵值(用于標(biāo)識共享內(nèi)存段),大小(指定共享內(nèi)存的大小),和標(biāo)志(用于指定權(quán)限和創(chuàng)建方式)。成功創(chuàng)建共享內(nèi)存段后,shmget 函數(shù)返回一個唯一的標(biāo)識符(共享內(nèi)存標(biāo)識符)。

  2. 連接共享內(nèi)存:使用 shmat 函數(shù)將共享內(nèi)存段連接到進程的地址空間。shmat 函數(shù)接受兩個參數(shù):共享內(nèi)存標(biāo)識符和地址(如果地址為 NULL,則系統(tǒng)會自動選擇一個可用的地址)。成功連接共享內(nèi)存后,shmat 函數(shù)返回共享內(nèi)存的起始地址。

  3. 使用共享內(nèi)存:一旦共享內(nèi)存連接到進程的地址空間,進程可以像使用普通內(nèi)存一樣使用它??梢酝ㄟ^指針訪問共享內(nèi)存中的數(shù)據(jù)。

  4. 分離共享內(nèi)存:使用 shmdt 函數(shù)將共享內(nèi)存從進程的地址空間中分離。shmdt 函數(shù)接受一個參數(shù),即共享內(nèi)存的起始地址。成功分離共享內(nèi)存后,進程將無法再訪問共享內(nèi)存中的數(shù)據(jù)。

  5. 刪除共享內(nèi)存段:使用 shmctl 函數(shù)刪除共享內(nèi)存段。shmctl 函數(shù)接受三個參數(shù):共享內(nèi)存標(biāo)識符、命令(用于指定要執(zhí)行的操作),和一個結(jié)構(gòu)體指針(用于傳遞額外的參數(shù))。通過指定命令為 IPC_RMID,可以刪除共享內(nèi)存段。

下面是一個簡單的 C 語言示例,展示了如何使用 System V 共享內(nèi)存:

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main() {key_t key = ftok("shared_memory_key", 1234);  // 創(chuàng)建一個唯一的鍵值int shm_id = shmget(key, sizeof(int), IPC_CREAT | 0666);  // 創(chuàng)建共享內(nèi)存段if (shm_id == -1) {perror("shmget");return 1;}int *shared_data = (int *)shmat(shm_id, NULL, 0);  // 連接共享內(nèi)存if (shared_data == (int *)-1) {perror("shmat");return 1;}*shared_data = 42;  // 在共享內(nèi)存中寫入數(shù)據(jù)printf("Shared data: %d\n", *shared_data);shmdt(shared_data);  // 分離共享內(nèi)存shmctl(shm_id, IPC_RMID, NULL);  // 刪除共享內(nèi)存段return 0;
}

在上面的示例中,我們使用 ftok 函數(shù)創(chuàng)建一個唯一的鍵值,用于標(biāo)識共享內(nèi)存段。然后,我們使用 shmget 函數(shù)創(chuàng)建一個大小為 sizeof(int) 的共享內(nèi)存段,并指定了創(chuàng)建標(biāo)志 IPC_CREAT | 0666。接下來,我們使用 shmat 函數(shù)將共享內(nèi)存連接到進程的地址空間,并將其強制轉(zhuǎn)換為 int 類型的指針。然后,我們可以通過指針訪問共享內(nèi)存中的數(shù)據(jù)。最后,我們使用 shmdt 函數(shù)分離共享內(nèi)存,并使用 shmctl 函數(shù)刪除共享內(nèi)存段。

請注意,System V 共享內(nèi)存是一種底層的機制,需要謹慎使用,以確保正確的同步和互斥。在實際應(yīng)用中,你可能需要使用其他同步機制(如信號量或互斥鎖)來保護共享內(nèi)存的訪問。

system V信號量 - 選學(xué)了解即可

信號量主要用于同步和互斥的,下面先來看看什么是同步和互斥。
在多進程或多線程的環(huán)境中,進程互斥(Process Mutual Exclusion)是指一種機制,用于確保同時只有一個進程(或線程)可以訪問共享資源,以避免數(shù)據(jù)競爭和不一致的結(jié)果。

當(dāng)多個進程或線程同時訪問共享資源時,可能會出現(xiàn)以下問題:

  1. 競態(tài)條件(Race Condition):多個進程或線程以不可預(yù)測的方式相互干擾,導(dǎo)致結(jié)果的正確性無法保證。

  2. 數(shù)據(jù)不一致:多個進程或線程對共享資源進行讀寫操作時,可能會導(dǎo)致數(shù)據(jù)的不一致性,破壞程序的正確性。

為了解決這些問題,需要使用進程互斥機制來確保共享資源的互斥訪問。常見的進程互斥機制包括使用互斥鎖(Mutex)、信號量(Semaphore)和臨界區(qū)(Critical Section)等。

互斥鎖是一種最常見的進程互斥機制,它提供了兩個基本操作:上鎖(Lock)和解鎖(Unlock)。當(dāng)一個進程或線程需要訪問共享資源時,它會嘗試上鎖,如果成功獲取到鎖,則可以訪問共享資源;如果鎖已經(jīng)被其他進程或線程持有,則該進程或線程會被阻塞,直到鎖被釋放。

通過使用互斥鎖,可以確保同一時間只有一個進程或線程可以訪問共享資源,從而避免了競態(tài)條件和數(shù)據(jù)不一致的問題。

需要注意的是,進程互斥只是一種機制,它并不能解決所有的并發(fā)問題。在設(shè)計并發(fā)程序時,還需要考慮其他因素,如死鎖(Deadlock)和活鎖(Livelock)等。

進程同步(Process Synchronization)是指在多個進程或線程之間協(xié)調(diào)和控制它們的執(zhí)行順序,以確保它們按照一定的順序和時間間隔執(zhí)行。

在并發(fā)環(huán)境中,多個進程或線程可能會同時執(zhí)行,并且彼此之間的執(zhí)行速度和順序是不確定的。這可能導(dǎo)致一些問題,如競態(tài)條件、數(shù)據(jù)不一致和死鎖等。

進程同步的目的是通過使用同步機制來協(xié)調(diào)和控制進程或線程的執(zhí)行,以避免這些問題。常見的進程同步機制包括互斥鎖、信號量、條件變量和屏障等。

以下是一些常見的進程同步場景:

  1. 互斥訪問共享資源:多個進程或線程需要訪問共享資源時,通過使用互斥鎖來確保同一時間只有一個進程或線程可以訪問共享資源,避免數(shù)據(jù)競爭和不一致的結(jié)果。

  2. 同步執(zhí)行順序:在某些情況下,需要確保多個進程或線程按照特定的順序執(zhí)行。例如,一個進程可能需要等待另一個進程完成某個任務(wù)后才能繼續(xù)執(zhí)行。這可以通過使用信號量或條件變量來實現(xiàn)。

  3. 生產(chǎn)者-消費者問題:在生產(chǎn)者-消費者問題中,多個生產(chǎn)者進程和消費者進程共享一個有限的緩沖區(qū)。生產(chǎn)者進程將數(shù)據(jù)放入緩沖區(qū),而消費者進程從緩沖區(qū)中取出數(shù)據(jù)。需要確保生產(chǎn)者和消費者之間的操作同步,以避免緩沖區(qū)溢出或下溢。這可以通過使用信號量來實現(xiàn)。

進程同步是并發(fā)編程中的重要概念,它可以幫助解決并發(fā)執(zhí)行時可能出現(xiàn)的問題,確保程序的正確性和一致性。

總之以上是進程間通信的基本概念,在以后的學(xué)習(xí)中會慢慢深入進程更加詳細的知識。

http://aloenet.com.cn/news/32675.html

相關(guān)文章:

  • 建什么網(wǎng)站能百度收錄國際國內(nèi)新聞最新消息今天
  • 免費php企業(yè)網(wǎng)站源碼關(guān)鍵詞優(yōu)化怎么做
  • 理財平臺網(wǎng)站建設(shè)交換鏈接營銷成功案例
  • 互聯(lián)網(wǎng)行業(yè)信息網(wǎng)站免費b2b網(wǎng)站推廣渠道
  • wordpress圖片燈箱效果修改百度seo營銷推廣
  • 廣州做網(wǎng)站厲害的公司互聯(lián)網(wǎng)營銷師證書騙局
  • 簡單建設(shè)一個網(wǎng)站的過程長春網(wǎng)站seo公司
  • 免費信息網(wǎng)站建設(shè)7個湖北seo網(wǎng)站推廣策略
  • 陜西網(wǎng)站建設(shè)通報網(wǎng)址搜索
  • 做購物網(wǎng)站 需要手續(xù)百度搜索廣告怎么投放
  • 020網(wǎng)站建設(shè)和維護費用數(shù)據(jù)分析培訓(xùn)班
  • 做網(wǎng)站軟件html css百度灰色關(guān)鍵詞代做
  • 東莞做網(wǎng)站價格360網(wǎng)站推廣怎么做
  • 企業(yè)局域網(wǎng)游戲網(wǎng)站如何做限制自動點擊器安卓
  • 哪里有學(xué)編程的培訓(xùn)班神馬seo教程
  • 寧晉網(wǎng)站建設(shè)代理價格深圳百度推廣優(yōu)化
  • 攝影網(wǎng)站建設(shè)廣東廣州疫情最新情況
  • 石家莊網(wǎng)站建設(shè)公司哪家好如何制作網(wǎng)頁鏈接
  • 小兔自助建站百度一下1688
  • 泉州網(wǎng)站建設(shè)網(wǎng)絡(luò)推廣要求
  • 裝修網(wǎng)站怎么做seo 工具
  • 正保建設(shè)工程教育網(wǎng)站線上推廣方式有哪些
  • 家電維修做網(wǎng)站生意怎么樣合肥網(wǎng)站維護公司
  • 徐州網(wǎng)站建設(shè)哪家好企業(yè)管理培訓(xùn)機構(gòu)
  • 日照網(wǎng)站建設(shè)價格蘇貨運公司回收百度賬戶推廣登陸
  • 百度快照入口seo站長工具 論壇
  • 與做網(wǎng)站有關(guān)的參考文獻網(wǎng)絡(luò)營銷的四種形式
  • 做網(wǎng)站要霸屏嗎推廣營銷
  • 溫江做網(wǎng)站seo在線短視頻發(fā)布頁
  • 常州專業(yè)網(wǎng)站建設(shè)推廣seo基礎(chǔ)培訓(xùn)機構(gòu)