Preface
Interprocess Communication (IPC) refers to the dissemination or exchange of information between different processes.
The main ways are pipes (including nameless pipes, advanced pipes and named pipes), message queues, semaphores, shared memory, sockets, etc. Socket can be used for inter-process communication on different hosts.
The main purposes of process communication are as follows:
Data transmission: A process needs to send its data to another process, and the amount of data sent is between one byte and several M bytes.
Shared data: Multiple processes want to operate on shared data, one process changes the shared data, and other processes should see it immediately.
Notification events: A process needs to send a message to another or a group of processes to inform them that something has happened (e.g., notify the parent process when the process terminates).
Resource sharing: Multiple processes share the same resource. To do this, the kernel needs to provide lock and synchronization mechanisms.
Process control: Some processes want complete control over the execution of another process (e.g. Debug process), in which case the control process wants to intercept all traps and exceptions of another process and be able to know its state changes in time.
IPC mode
Pipeline: Pipeline is a half-duplex communication mode, data can only flow in one direction, and can only be used between processes with affinity. The relationship of process usually refers to the relationship between father and son.
Advanced Pipeline: If another program is started as a new process in the current process, then it is a sub-process of the current program. This way we become the advanced pipeline mode.
Named pipe: Well-known pipe is also a half-duplex communication mode, but it allows communication between unrelated processes.
Message queue: A message queue is a linked list of messages stored in the kernel and identified by the message queue identifier. Message queue overcomes the shortcomings of less signal transmission information, pipeline can only carry unformatted byte stream and buffer size limitation.
Semophore: A semophore is a counter that can be used to control access to shared resources by multiple processes. It is often used as a lock mechanism to prevent a process from accessing a shared resource while other processes are accessing the resource. Therefore, it is mainly used as a means of synchronization between processes and between different threads in the same process.
Signal (sinal): Signal is a complex way of communication to inform the receiving process that an event has occurred.
Shared memory: Shared memory is the mapping of a piece of memory that can be accessed by other processes. This shared memory is created by one process, but can be accessed by multiple processes. Shared memory is the fastest IPC mode. It is specially designed for the low efficiency of other inter-process communication modes. It is often used in conjunction with other communication mechanisms, such as signal two, to achieve synchronization and communication between processes.
Socket: Socket is also an inter-process communication mechanism. Unlike other communication mechanisms, socket can be used for process communication between different machines.
name pipes
This article mainly explains the command pipeline.
Named pipes exist as a special device file in the file system. Data can be shared between processes of different ancestors through pipes. When all processes accessing pipes are finished, named pipes will continue to exist for future use.
Creation of Named Pipeline
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode);
mkfifo is used to create a FIFO file, and the parameter mode l determines the file permissions.
The file creation mask of the process (see umask()) modifies the file permission bits of mode. The owner ID of the FIFO is set to the effective user ID of the process. The FIFO group ID is set to the effective group ID of the process.
If successful, mkfifo() returns 0; if unsuccessful, it returns - 1, and errno is assigned the following value:
EACCES: A level path has no access rights, or a directory pointed to by pathname has no write rights
EEXIST: The file with the same name already exists
EINVAL: Describing paths on NuTCRCKER platform is not recorded in fifos
ENAMETOOLONG: pathname name is too long
ENOENT: A level directory does not exist in the path
ENOSPC: Disk full
ENOTDIR: A level in the path is not a directory
EROFS: File System Read-Only
Details can be viewed: [here]( http://man7.org/linux/man-pag...)
The file mode is as follows:
S_IRWXU: 00700 read, write, execute/search by owner
S_IRUSR: 00400 read permission, owner
S_IWUSR: 00200 write permission, owner
S_IXUSR: 00100 execute/search permission, owner
S_IRWXG: 00070 read, write, execute/search by group
S_IRGRP: 00040 read permission, group
S_IWGRP: 00020 write permission, group
S_IXGRP: 00010 execute/search permission, group
S_IRWXO: 00007 read, write, execute/search by others
S_IROTH: 00004 read permission, others
S_IWOTH: 00002 write permission, others
S_IXOTH: 00001 execute/search permission, others
S_ISUID: 0004000 set-user-ID on execution
S_ISGID: 0002000 set-group-ID on execution
S_ISVTX: 0001000 on directories, restricted deletion flag
System I/O functions such as open, close, read, write can operate FIFO files.
Read/write, blocking/non-blocking
block
Blocking mode when O_NONBLOCK is not specified by default
When open opens FIFO in read-only mode, it blocks a process to open FIFO for writing
When open opens FIFO in write-only mode, it blocks a process to open the FIFO for reading.
When open opens FIFO in read-only + write-only mode, read blocks when calling read function
When the write function is called to write data to FIFO, the write will block when the buffer is full
In the process of communication, if the write process exits first, it will not block when calling read function to read data from FIFO; if the write process runs again, it will resume blocking when calling read function to read data from FIFO.
If the reading process exits first in the communication process, the writing process writes data to FIFO, and receives SIGPIPE signal and exits.
open does not block when FIFO is opened in read-write mode
Non blocking
open opens FIFO in a read-only manner. If no process opens FIFO for writing, read-only opens successfully and opens without blocking.
Open opens FIFO in a write-only manner, and if no process opens FIFO for reading, writing open only returns an error to - 1 (ENXIO: No Such device or address)
read, write read and write FIFO data without blocking
In the communication process, when the read process exits and the write process writes data to the named pipeline, the write process will receive SIGPIPE signal and exit.
Examples
Non-Blocking
reader
// // Created by : Harris Zhu // Filename : fifo_read.cpp // Author : Harris Zhu // Created On : 2017-08-17 16:46 // Last Modified : // Update Count : 2017-08-17 16:46 // Tags : // Description : // Conclusion : // //======================================================================= #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define FIFO_SERVER "myfifo" #define OPEN_MODE O_RDONLY | O_NONBLOCK #define FIFO_MODE O_CREAT|O_RDWR|O_NONBLOCK int main(int argc, char** argv) { char buf_r[100]; int fd; int nread; int res; if (((res=mkfifo(FIFO_SERVER, FIFO_MODE)) < 0) && (errno != EEXIST)) { printf("can not creat fifoserver %d :\n", res, errno); exit(1); } printf("preparing for reading bytes...\n"); char cmd[100]; sprintf(cmd, "chmod 704 %s", FIFO_SERVER); system(cmd); fd = open(FIFO_SERVER, OPEN_MODE); if (fd == -1) { perror("error in openning fifo server"); exit(1); } int i=0; int len; while (i++<21) { memset(buf_r, 0, sizeof(buf_r)); if ((nread = read(fd, buf_r, sizeof(buf_r))) < 0) { if (errno == EAGAIN) { printf("no data yet\n"); sleep(1); } } else { if(nread > 0) { printf("read %s from FIFO %d \n", buf_r, i); } sleep(1); } } // pause(); close(fd); unlink(FIFO_SERVER); return 0; }
writer
// // Created by : Harris Zhu // Filename : fifo_write.c // Author : Harris Zhu // Created On : 2017-08-17 18:04 // Last Modified : // Update Count : 2017-08-17 18:04 // Tags : // Description : // Conclusion : // //======================================================================= #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <time.h> #define FIFO_SERVER "myfifo" #define FIFO_MODE O_CREAT|O_NONBLOCK|O_RDWR #define FILE_MODE O_WRONLY | O_NONBLOCK int main(int argc, char** argv) { int fd; char w_buf[100]; char w_t_buf[50]; const char *hstr = "hello world"; if(mkfifo(FIFO_SERVER, FIFO_MODE<0)&& (errno != EEXIST)) { perror("failed to create fifo server"); exit(1); } char cmd[100]; sprintf(cmd, "chmod 704 %s", FIFO_SERVER); system(cmd); int nwrite; fd = open(FIFO_SERVER, FILE_MODE); if (fd == -1) { if (errno == ENXIO) { printf("open errop;no reading process\n"); } else { perror("open error"); exit(EXIT_FAILURE); } } if (argc >= 2) { strcpy(w_t_buf, argv[1]); } else { strcpy(w_t_buf, hstr); } int i=0; int n; time_t tp; while(i++<20) { time(&tp); n=sprintf(w_buf, "Process %d is sending %s at %s", getpid(), w_t_buf, ctime(&tp)); if ((nwrite = write(fd, w_buf, n)) < 0) { if (errno == EAGAIN) { printf("the fifo has not been read yet.Please try later\n"); } else { exit(1); } } printf("Send Message to FIFO: %s \n", w_buf); sleep(1); } close(fd); return 0; }
Run reader first, then writer
Makefile reads as follows
build: gcc -g read.c -o read gcc -g write.c -o write run: xterm -e ./read & sleep 1 xterm -e ./write & clean: rm -rf read write
Because the reader starts reading first, when the writer starts, the reader has already passed through the while and the individual loop, so the reader will end first, when the writer also wants to write data to FIFO, it will encounter the "Broken pipe" problem.
If we reduce the waiting time of writer s, the receivers will be slower, so there will be more data in fifo, and the reader will not receive one line at a time.
harriszh Fri 17:30@ ~/trunk/cpp/fifo1$ ./fifo_read preparing for reading bytes... read Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello worl from FIFO read d at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Proce from FIFO read ss 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at from FIFO read Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11 from FIFO read 863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri A from FIFO read ug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 i from FIFO read s sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 from FIFO read 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sen from FIFO read ding hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:3 from FIFO read 1:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending from FIFO read hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 from FIFO read 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello from FIFO read world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 from FIFO
Blocking
It's simpler to look at the blockage after seeing the non-blockage
The code is as follows
Reader
// // Created by : Harris Zhu // Filename : write.c // Author : Harris Zhu // Created On : 2017-08-17 22:05 // Last Modified : // Update Count : 2017-08-17 22:05 // Tags : // Description : // Conclusion : // //======================================================================= #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<signal.h> #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(int argc, char *argv[]) { int outfd = open("Makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (outfd == -1) ERR_EXIT("open Makefile2 error"); int infd; infd = open("tp", O_RDONLY ); if (infd == -1) ERR_EXIT("open tp error"); char buf[1024]; int n; while ((n = read(infd, buf, 1024)) > 0) write(outfd, buf, n); close(infd); close(outfd); unlink("tp"); // delete a name and possibly the file it refers to return 0; }
Writer
// // Created by : Harris Zhu // Filename : read.c // Author : Harris Zhu // Created On : 2017-08-17 22:05 // Last Modified : // Update Count : 2017-08-17 22:05 // Tags : // Description : // Conclusion : // //======================================================================= #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<signal.h> #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(int argc, char *argv[]) { mkfifo("tp", 0644); int infd = open("Makefile", O_RDONLY ); if (infd == -1) ERR_EXIT("open Makefile error"); int outfd; outfd = open("tp", O_WRONLY ); if (outfd == -1) ERR_EXIT("open tp error"); char buf[1024]; int n; while ((n = read(infd, buf, 1024)) > 0) write(outfd, buf, n); printf("Makefile2 is created successfully!\n") close(infd); close(outfd); return 0; }
Run writer first, then reader, because FIFO was created by writer
Makefile
build: gcc -g read.c -o read gcc -g write.c -o write clean: rm -rf Makefile2 rm -rf ./read ./write run: xterm -e ./write & sleep 0.1 xterm -e ./read & .PHONY: clean
After running, the reader saves the content written from the writer to Makefile 2