The purpose of interprocess communication: to realize communication between two different processes, the premise is to let the two processes see the same (memory space) resource
This chapter describes the inter process communication of the System V standard
Purpose of interprocess communication:
1. Data transmission: one process needs to send its data to another process.
2. Resource sharing: multiple processes share the same resources.
3. Notification event: a process needs to send a message to another process or another group of processes to notify it (them) of an event (such as notifying the parent process when the child process terminates)
4. Process control: some processes want to fully control the execution of another process (such as the Debug process). At this time, the control process wants to be able to intercept all traps and exceptions of another process and know its state changes in time.
The Conduit
Pipeline is a kind of half duplex communication, which can select the direction of one-way communication.
Essence: it is to open up a buffer (a piece of memory in kernel space) in the kernel
Principle: multiple processes communicate by accessing the buffer in the same memory
Classification:
1) Anonymous pipeline: the buffer has no identifier and can only be used for inter process communication with kinship.
2) Named pipes: buffers have identifiers that can be used for any interprocess communication on the same host.
Access to the pipeline is completed through IO operations.
Anonymous Pipe
Create: int pipefd[2]={0};
Header file:#include<unistd.h> int pipe(int pipefd[2]) ---->>> pipe(pipefd[2]) Return value: success 0; fail-1. pipefd[0]:read; pipefd[1]:Write.
Characteristics of anonymous pipeline: because the anonymous pipeline has no identifier, it cannot be found by other processes. It can only obtain the operation handle by copying the parent process by the child process to realize communication. Therefore, anonymous pipes can only be used for inter process communication with kinship
#include<iostream> #include<unistd.h> #include<stdlib.h> #include<string.h> #include<sys/wait.h> #include<sys/types.h> using namespace std; int main() { int pipefd[2]={0}; pipe(pipefd); pid_t id = fork(); if(0 == id) { //child write close(pipefd[0]);//Turn off the reader, mutual exclusion const char* buf ="hello my dream!"; int count = 0; while(count <= 3) { write(pipefd[1],buf,strlen(buf)); sleep(1); count++; } } else if(id > 0) { //parent read close(pipefd[1]);Turn off write side, mutual exclusion char buf[64]; while(1) { ssize_t s = read(pipefd[0],buf,sizeof(buf)-1); if(s > 0) { buf[s] = '\0'; cout<<s<<endl; cout<<"father get a msg :"<<buf<<endl; sleep(1); } if(0 == s) break; } } return 0; }
In depth understanding of pipeline:
Case 1: (synchronous)
If the read conditions are not met (the write end is not closed and no longer write), the read end will be blocked
#include<iostream> #include<unistd.h> #include<stdlib.h> #include<string.h> #include<sys/wait.h> #include<sys/types.h> using namespace std; int main() { int pipefd[2]={0}; pipe(pipefd); pid_t id = fork(); if(0 == id) { //child write close(pipefd[0]); const char* buf ="hello my dream!"; int count = 0; while(count <= 3) { write(pipefd[1],buf,strlen(buf)); sleep(1); count++; } } else if(id > 0) { //parent read close(pipefd[1]); char buf[64]; while(1) { ssize_t s = read(pipefd[0],buf,sizeof(buf)-1); if(s > 0) { buf[s] = '\0'; cout<<s<<endl; cout<<"father get a msg :"<<buf<<endl; sleep(1); } } //If the parent process exits, there will be no blocking } return 0; }
Case 2: (synchronous)
If the write conditions are not met (when the reader does not read data and does not close the file descriptor), the writer will be blocked.
#include<iostream> #include<unistd.h> #include<stdlib.h> #include<string.h> #include<sys/wait.h> #include<sys/types.h> using namespace std; int main() { int pipefd[2]={0}; pipe(pipefd); pid_t id = fork(); if(0 == id) { //child write close(pipefd[0]); const char* buf ="hello my dream!"; while(1) { write(pipefd[1],buf,strlen(buf)); sleep(1); } } else if(id > 0) { //parent read close(pipefd[1]); char buf[64]; int count = 0; while(1) { while(count <= 5) { ssize_t s = read(pipefd[0],buf,sizeof(buf)-1); if(s > 0) { buf[s] = '\0'; cout<<s<<endl; cout<<"father get a msg :"<<buf<<endl; sleep(1); } if(0 == s) break; count++; } } } return 0; }
Test results:
Case 3: the writer closes the file descriptor. When the reader reads the pipeline data, it will read to the end of the file. Read returns 0, and the reader is not blocked
#include<iostream> #include<unistd.h> #include<stdlib.h> #include<string.h> #include<sys/wait.h> #include<sys/types.h> using namespace std; int main() { int pipefd[2]={0}; pipe(pipefd); pid_t id = fork(); if(0 == id) { //child write close(pipefd[0]); const char* buf ="hello my dream!"; int count = 0; while(count <= 20) { write(pipefd[1],buf,strlen(buf)); count++; } close(pipefd[1]); } else if(id > 0) { //parent read close(pipefd[1]); char buf[64]; while(1) { ssize_t s = read(pipefd[0],buf,sizeof(buf)-1); cout<<s<<endl; if(s > 0) { buf[s] = '\0'; cout<<"father get a msg :"<<buf<<endl; sleep(1); } } } int status = 0; waitpid(id,&status,0); cout<<"child exit num is: "<<(status & 0x7F); return 0; }
Test results:
Case 4:
If the reader is closed, the writer process may be killed by subsequent processes (SIGPIPE)
#include<iostream> #include<unistd.h> #include<stdlib.h> #include<string.h> #include<sys/wait.h> #include<sys/types.h> using namespace std; int main() { int pipefd[2]={0}; pipe(pipefd); pid_t id = fork(); if(0 == id) { //child write close(pipefd[0]); const char* buf ="hello my dream!"; while(1) { write(pipefd[1],buf,strlen(buf)); sleep(1); } } else if(id > 0) { //parent read close(pipefd[1]); char buf[64]; int count = 0; while(1) { ssize_t s = read(pipefd[0],buf,sizeof(buf)-1); if(s > 0) { buf[s] = '\0'; cout<<s<<endl; cout<<"father get a msg :"<<buf<<endl; sleep(1); } if(count == 5) { close(pipefd[0]); break; } count++; cout<<"father exit renturn:"<<s<<endl; } int status = 0; waitpid(id,&status,0); cout<<"child exit num is: "<<(status & 0x7F)<<endl; } return 0; }
Test results:
name pipes
In essence, it is a buffer in the kernel with identifier, which can be found by other processes, so it can be used for any inter process communication of the same host.
The identifier of a named pipe is a pipe type file that can be seen in the file system.
Multiple processes communicate by opening the same pipe file and accessing the buffer in the same kernel.
To create a named pipe:
Header file: #include < sys. Types. H > #include < sys / stat.h >
int mkfifo(const char *pathname,mode_t mode);
pathname: file path, mdoe: file permissions
Return value: 0 for success and - 1 for failure
mkfifo () is also a system call interface. You can enter directly at the command line to create named pipes.
//server.cc file #include<iostream> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #define FIFO_FILE "./fifo" using namespace std; int main() { umask(0); if(-1 == mkfifo(FIFO_FILE,0666))//Create named pipe { cout<<"mkfifo ,File exist"<<endl; } int fd = open(FIFO_FILE,O_RDONLY);//Open the pipeline file to get the identifier int fd1 = open("./log.txt",O_WRONLY);establish log.txt file if(fd1 >= 0) { dup2(fd1,1);//Output redirection, the data read from the buffer should have been printed on the display and redirected to the log.txt file } if(fd >= 0) { char buf[64]; while(1) { ssize_t s = read(fd,buf,sizeof(buf)-1); if(s>0) { buf[s]=0; cout<<"client#"<<buf; } else if(s == 0) { //client exit cout<<"client exist,me too!"<<endl; break; } else{ cout<<"read"<<endl; break; } } } return 0; } //client.cc file #include<iostream> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include<stdio.h> #define FIFO_FILE "./fifo" using namespace std; int main() { int fd = open(FIFO_FILE,O_WRONLY);Open pipeline file in write only mode if(fd >= 0) { char buf[64]; while(1) { cout<<"please input message#"; fflush(stdout);//Refresh C + + standard library buffer data ssize_t s = read(0,buf,sizeof(buf)-1);//Considering that if it is a string, there is' \ 0 ' if(s>0) { buf[s]=0; write(fd,buf,s); } } } return 0; }
These two program files realize the process that the client sends data as the client, the server receives data as the server, and finally prints the received data to log.txt.
reflection:
Due to the IO operation between the client and the server, if the named pipe file exists in the hard disk, the IO efficiency will be reduced. Therefore, the pipe file fifo is only a symbolic pipe file, exists in memory, and the data will not be refreshed to the disk.
Shared memory (for data sharing between processes)
Essence: a piece of physical memory (unlike message queues and pipes)
Principle: open up a physical memory space, multiple processes map the same memory to their own virtual space, and access it directly through the virtual address, so as to realize data sharing.
Operation process:
1. Create or open shared memory
int shmget(ket_t key ,size_t size ,int shmflg); Header file:#include <sys/ipc.h>#include <sys/shm.h> key:It reflects the uniqueness of shared memory at the system level key Value enables multiple processes to see the same memory resource. size: The amount of space created (in memory pages: 4096) shmflg:Open mode+create a privilege IPC_CREAT |IPC_EXCL|0664 Return value: successfully returned non negative integer - operation handle; Failure Return-1
2. Map the shared memory to the virtual address space of the process
void* shmat(int shmid,const void* shmaddr,int shmflg); Header file:#include <sys/types.h> #include <sys/shm.h> shmid: shmget Returned operation handle (user level) shmaddr: Mapping address - usually set NULL shmflg: Access method after successful mapping; SHM_RDONLY—Read only; 0 - write only Return value: the mapped first address (virtual address) is returned successfully, and the failure is returned( void*)-1
3. Perform various memory operations through the mapped virtual address
4. Unmapping
int shmdt(const void *shmaddr);//Unmapping relationship Header file:#include <sys/types.h> #include <sys/shm.h> shmaddr: First address after mapping Return value: 0 for success; 0 for failure-1
5. Delete shared memory
int shmctl(int shmid,int cmd,struct shmid_ds* buf)//Delete shared memory shmid: shmget Returned operation handle cmd: Operation type - IPC_RMID Mark the shared memory as destroyed. buf: It is used to set or obtain shared memory information. It can be left blank without setting. Return value: for IPC_RMID For example, 0 is returned for success and 0 is returned for failure-1.
Essential principle
Open up a memory space, multiple processes map the same memory to the virtual address space, access through their own virtual address space address, and then realize data sharing.
characteristic
The fastest way of interprocess communication. The life cycle of shared memory resources varies with the kernel.
The shared memory directly accesses the physical memory through the virtual address to realize data sharing. Compared with other methods (such as pipeline and message queue), it needs to copy the data to the kernel and to the user state when in use, which reduces two data copy operations. It is the fastest form of IPC.