Interprocess communication (IPC),
Interprocess communication (IPC) methods, in general, mainly include the following:
1. Nameless pipes (PIPES) and named pipes (FIFO).
2.
3, System V-IPC shared memory.
4. System V-IPC message queue.
5, System V-IPC semaphore.
6. Sockets.
1, pipes,
1.1. Anonymous Pipe
1.2. Characteristics of unnamed pipes
Characteristics of PIPE:
1, there is no name, so you cannot use open(). 2, can only be used between related processes (such as parent-child, sibling, grandparent...) Communication. 3. Half-duplex working mode: separate the reading and writing ends. 4. Write operations are not atomic and therefore can only be used in simple one-to-one communication situations. 5, cannot use lseek() to locate.Copy the code
1.3. Note that
The read-write end of an unnamed pipe is fixed. The read end (fd[0]) can only read, and the write end (fd[1]) can only writeCopy the code
1.4. When reading and writing data
(1) the process of writing data is dead, the pipe will still store written data and (2) read operation data, if there is no data found inside the pipeline, will produce block, has been waiting for the pipeline data (3) after reading the data of pipeline, pipeline is empty, not without a second before write data, read many times in a row, If the data in the pipe is not completely read, it can be directly read a second time.Copy the code
Example Code 1
#include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <error.h> #include <stdlib.h> #include <sys/wait.h> #include <string.h> int main(int argc, char const *argv[]) {// create a pipe int fd[2]; int ret = pipe(fd); if (ret == -1) { perror("pipe"); return -1; } printf(" Nameless pipe created successfully \n"); //fd[0]: read //fd[1]: write int s; Pid_t x = fork(); char buf[100] = {0}; char w_buf[100] = {0}; If (x == 0) {printf(" child starts running \n"); read(fd[0], buf, sizeof(buf)); printf("buf = %s\n", buf); Printf (" child process ends running \n"); } else if (x > 0) {printf(" parent starts running \n"); scanf("%s", w_buf); write(fd[1], w_buf, strlen(w_buf)); wait(&s); Printf (" parent finishes running \n"); } else { perror("fork"); return -1; } return 0; }Copy the code
Example code 2:
The parent process can communicate with the child process through an anonymous channel. The parent process can send messages to the child process, and the child process can also send messages to the parent process. Receiving the message and printing it, such as sending quit, ends both processes.
#include <stdio.h> #include <sys/types.h> #include <error.h> #include <stdlib.h> #include <sys/wait.h> #include <string.h> int main(int argc, char const *argv[]) {// Create an unnamed pipe int fd[2]; int ret = pipe(fd); if (ret == -1) { perror("pipe"); return -1; } printf(" Nameless pipe created successfully \n"); //fd[0]: read //fd[1]: write int s; Pid_t x = fork(); char buf[100] = {0}; char w_buf[100] = {0}; char buf1[100] = {0}; char w_buf1[100] = {0}; If (x == 0) {printf(" child starts running \n"); while(1) { bzero(buf, sizeof(buf)); read(fd[0], buf, sizeof(buf)); Printf (" received parent message: %s\n", buf); if (strcmp(buf, "quit") == 0) { return 0; } scanf("%s", w_buf1); write(fd[1], w_buf1, strlen(w_buf1)); if (strcmp(w_buf1, "quit") == 0) { return 0; } sleep(1); } printf(" child process ends running \n"); } else if (x > 0) {printf(" parent starts running \n"); while(1) { scanf("%s", w_buf); write(fd[1], w_buf, strlen(w_buf)); if (strcmp(w_buf, "quit") == 0) { return 0; } sleep(1); bzero(buf1, sizeof(buf1)); read(fd[0], buf1, sizeof(buf1)); Printf (" Received child process message: %s\n", buf1); if (strcmp(buf1, "quit") == 0) { return 0; } } wait(&s); Printf (" parent finishes running \n"); } else { perror("fork"); return -1; } return 0; }Copy the code
1.2. Named pipes
Features of a named pipe FIFO:
1, have a name, stored in the ordinary file system. 2. Any process with the appropriate permissions can use open() to get the FIFO file descriptor. 3. Use the same read()/write() method as normal files. 4, unlike normal files: cannot use lseek() to locate the file because of the same reason as PIPE. 5, with write atomicity, support multiple writers to write operations at the same time and data will not tread on each other. The data written into the FIFO is read First In First Out.Copy the code
Read/write blocking:
Note: PIPE files (including PIPE,FIFO, and SOKET) cannot be opened with only the read side or only the write side
The sample code
To implement communication between two different processes through a named pipe, enter quit to terminate the two processes.
Jack rose
Application:
#include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <error. H > #include <stdlib.h> #include <sys/wait.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> #define FIFO_PATHNAME "/tmp/myfifo" Int main(int argc, char const *argv[]) {// Create a named pipe if(access(FIFO_PATHNAME, F_OK)) {int ret = mkFIFo (FIFO_PATHNAME, 0644); if (ret == -1) { perror("mkfifo"); return -1; Int fd = open(FIFO_PATHNAME, O_RDWR); if (fd == -1) { perror("open"); return -1; } printf(" named pipe opened successfully \n"); char w_buf[1024] = {0}; char r_buf[1024] = {0}; while(1) { bzero(r_buf, sizeof(r_buf)); bzero(w_buf, sizeof(w_buf)); // Write data scanf("%[^\n]", w_buf); write(fd, w_buf, strlen(w_buf)); if (strcmp(w_buf, "quit") == 0) { return 0; } usleep(10); read(fd, r_buf, sizeof(r_buf)); printf("rose:%s\n", r_buf); if (strcmp(r_buf, "quit") == 0) { return 0; } } return 0; }Copy the code
//rose #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <error.h> #include <stdlib.h> #include <sys/wait.h> #include <string.h> #include <fcntl.h> #define FIFO_PATHNAME "/tmp/myfifo" int main(int Argc, char const *argv[]) {// create a named pipe if(access(FIFO_PATHNAME, F_OK)) {int ret = mkFIFo (FIFO_PATHNAME, 0644); if (ret == -1) { perror("mkfifo"); return -1; Int fd = open(FIFO_PATHNAME, O_RDWR); if (fd == -1) { perror("open"); return -1; } printf(" named pipe opened successfully \n"); char r_buf[1024]; char w_buf[1024]; While (1) {// read data bzero(r_buf, sizeof(r_buf)); bzero(w_buf, sizeof(w_buf)); read(fd, r_buf, sizeof(r_buf)); if (strcmp(r_buf, "quit") == 0) { return 0; } printf("jack:%s\n", r_buf); scanf("%[^\n]", w_buf); write(fd, w_buf, strlen(w_buf)); if (strcmp(w_buf, "quit") == 0) { return 0; } usleep(10); } return 0; }Copy the code
2, signal
Signal is a special type of IPC, an asynchronous communication mechanismCopy the code
Signal life cycle:
Processes generate signals, register signals, respond to and process signals, and deregister signalsCopy the code
Signal response mode:
(1) ignore the signal, (2) capture the response signal function, (3) perform the default actionCopy the code
Special signal:
Signals SIGKILL (-9) and SIGSTOP (-19) are two special signals that cannot be ignored, blocked, or captured and can only be responded to by default. In other words, for signals other than these two, the target process that receives the signal reacts in the following order:
A) If the signal is blocked, the signal is suspended without any processing until it is unblocked. Otherwise enter B. B) If the signal is captured, then further determine the type of capture: B1) If the response function is set, then execute the response function. B2) If set to ignore, the signal is discarded directly. Otherwise enter C. C) Perform the default action of the signal.Copy the code
Signal classification
Run the kill -l command to check the system signal
The first 31 signals are unreliable signals and non-real-time signals
Features:
1. Non-real-time signals do not queue, and their responses will be nested with each other. 2. If the target process does not respond to a non-real-time signal in time, subsequent incoming signals will be discarded. 3. Each non-real-time signal corresponds to a system event, which will be generated when the event occurs. 4. If the pending signal of a process contains real-time and non-real-time signals, the process responds to real-time signals first and will respond accordingly from large to small, while non-real-time signals have no fixed order.Copy the code
Reliable signal, real-time signal after 31 signals
1. The response order of real-time signals is queued according to the receiving order without nesting. 2. Even if the same real-time signal is sent several times at the same time, it will not be discarded, but will respond one by one. 3. Real-time signals have no special system events corresponding to them.Copy the code
Signal correlation function:
Signal transmission
Signal capture
// Signal response function
void func(int sig)
{
Printf (” signal response function \n”);
printf(“sig = %d\n”, sig);
}
Sig is the value of the sent signal
Kill Sends signal receives
The sample code
#include <sys/types.h> #include <signal.h> #include <stdio.h> #include <error.h> #include <unistd.h> #include Void func(int sig) {printf(" signal response function \n"); printf("sig = %d\n", sig); } int main(int argc, char const *argv[]) { pid_t x = fork(); int i = 0; int s; If (x > 0) // parent {sleep(5); kill(x, 2); wait(&s); } else if (x = = 0) / / the child {/ / signal (2, SIG_IGN); // signal(2, SIG_DFL); // Execute the default action signal(2, func); While (1) {printf(" I = %d\n", I ++); sleep(1); if (i == 10) { break; } } } else { perror("fork"); } return 0; }Copy the code
Send yourself:
The sample code
#include <sys/types.h> #include <signal.h> #include <stdio.h> #include <error.h> #include <unistd.h> #include <sys/wait. H > int main(int argc, char const *argv[]) {printf(" ID: %d\n", getpid()); sleep(3); raise(2); printf("11111\n"); return 0; }Copy the code
Suspend this process:
The sample code
#include <sys/types.h> #include <signal.h> #include <stdio.h> #include <error.h> #include <unistd.h> #include <sys/wait. H > int main(int argc, char const *argv[]) {printf(" ID: %d\n", getpid()); int ret = pause(); printf("ret=%d\n", ret); return 0; }Copy the code
Signal sets operate on clusters of functions
To block or disengage one or more signals
The sample code
#include <sys/types.h> #include <signal.h> #include <stdio.h> #include <error.h> #include <unistd.h> #include Void func(int sig) {printf(" catch signal %d\n", sig); } int main(int argc, char const *argv[]) { sleep(1); pid_t x = fork(); If (x > 0) // parent {sleep(1); Printf (" send signal \n"); for (int i = 1; i < 64; ++i) { if (i==9 || i==19 || i==32 || i==33) { continue; } kill(x, i); } printf(" All signals finished \n"); } else if (x == 0) {// register a signal response function for (int I = 1; i < 64; ++i) { if (i==9 || i==19 || i==32 || i==33) { continue; } signal(i, func); } sigset_t mysig_set; // emptySet sigemptySet (&mysig_set); // Add the signal to the signal set sigfillSet (&mysig_set); // Add sigprocmask(SIG_BLOCK, &mysig_set, NULL); sleep(5); Sigprocmask (SIG_UNBLOCK, &mysig_set, NULL); Printf (" child exits \n"); } else { perror("fork"); } return 0; }Copy the code
Realization of log system, 5 clients, to a server to send log information, each client to send log information time is inconsistent, will get the log information, stored in TXT text file, send format:
Client1: Time [2021/9/1 xx:xx:xx]
client2:
client3:
client4:
client5:
Send signals with data
Receiving signals and data
The extended response function interface is as follows:
void (*sa_sigaction)(int, siginfo_t *, void *);
The argument list for this function is as follows:
The first argument, int, is the signal that triggers the function.
The second argument, siginfo_t, points to the following structure:
siginfo_t
To determine if sigqueue is sent;
Sinfo ->si_code == SI_QUEUE
Sinfo ->si_int = sinfo->si_int
Sigqueue differs from kill:
Sigqueue: a queue with data
Kill: one without data
Signal differs from SigAction:
Signal: One cannot receive data, and one uses standard response functions
Void (*sa_sigaction)(int, siginfo_t *, void *);
Signal kernel data structure
The System object of IPC
Multiple processes communicate using the same key value
Key value acquisition:
A few things to note about the function:
1. If the two parameters are the same, the resulting key value is the same.
2. The first parameter usually takes the directory where the process resides, because several processes that need to communicate in a project usually appear in the same directory.
3. If a process in the same directory needs more than one IPC object, it can be identified by the second parameter.
4. There is only one set of key identifiers in the system, that is, different types of IPC objects cannot be repeated
You can use the following commands to view or delete IPC objects in the current system:
Look at the message queue: IPcs -q
View shared memory: IPcs -m
View semaphore: the IPCS -S function retrieves the key of a currently unused IPC
View all IPC objects: IPCS -A
Delete the specified message queue: ipcrm-q MSG_ID or ipcrm-q msg_key
To delete the specified shared memory, run the ipcrm -m SHM_ID or ipcrm -m shm_key command
Delete the specified semaphore: ipcrm-s SEM_ID or ipcrm-s sem_key
Message Queue (MSG)
A message queue provides a special pipeline with a data identifier that makes each piece of data written into a message with an identifier that can be correctly read by the process reading the message without interference from other messagesCopy the code
Message queues are typically used in the following ways:
1. Sender:
A) get the ID of the message queue B) put the data into A special structure with an identifier and send it to the message queue.Copy the code
2. Recipient:
A) gets the ID of the message queue B) reads the message with the specified identity.Copy the code
Gets the message queue ID
Send and receive data
Closing the message queue
The sample code
Create three processes, xiao Hong, Xiao Ming and Xiao Liang. Xiao Ming and Xiao Liang say “I like you” to Xiao Hong respectively, xiao Hong receives their messages, Xiao Hong says “I like you too” to Xiao Ming and “Sorry,you are a goodboy” to Xiao Liang. Use message queues to do this.
Copy the code
The Shared memory
The general steps for using shared memory are:
1. Obtain the ID of the shared memory object 2 and map the shared memory to a certain area of the virtual memory space of the process 3. When the shared memory is no longer needed, remove the mapping relationship 4.Copy the code
The shared memory ID is obtained
Shared memory mapping
Deleting a Shared Memory
A semaphore
1. Resources (variables, linked lists, files, etc.) that can be accessed simultaneously by multiple processes or threads are called shared resources, also known as critical resources. 2. The code that accesses these resources is called critical code, and these code areas are called critical zones. 3. Before the program enters the critical area, it must apply for resources. This action is called P operation, which is just like you need to apply for a parking card to the security guard before you drive into the parking lot. If your application fails, either wait at the door or leave. 4. After the program leaves the critical area, it must release corresponding resources. This action is called V operation, which is just like returning the parking card to the security guard after you drive out of the parking lot.Copy the code
Resolve resource race issues
System semaphores share the same write atomicity characteristic as named pipes
Semaphore ID acquisition
Initialize the semaphore
P/V operation on semaphore
Named semaphore:
(1) Name: “/ myName” Note: The name must start with a /
(2) How to use a named semaphore using the pThread library?
Sem_close (); sem_unlink(); sem_unlink(); sem_unlink()Copy the code
Sem_open() #include <fcntl.h> /* For O_* constants */ #include <sys/stat.h> /* For mode constants */ #include <semaphore.h> sem_t *sem_open(const char *name, int oflag); sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); #include <semaphore.h> int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem); int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); #include <semaphore. H > int sem_post(sem_t *sem); #include <semaphore. H > int sem_close(sem_t *sem); #include <semaphore. H > int sem_unlink(const char *name); Gcc name. C -- o name -lpthreadCopy the code