One 、 Overview of interprocess communication

Process communication has the following purposes :
A、 The data transfer : One process needs to send its data to another process , The amount of data sent ranges from one byte to several M Between bytes
B、 Shared data : Multiple processes want to manipulate shared data , Modification of shared data by a process , Other processes should see immediately .
C、 Notification event : A process needs to send messages to another process or group of processes , Notify it ( they ) Something happened ( If you want to notify the parent process when the process terminates ).
D、 Resource sharing : Multiple processes share the same resources . To do this , The kernel is required to provide locking and synchronization mechanisms .
E、 Process control : Some processes want full control over the execution of another process ( Such as Debug process ), At this time, the control process wants to be able to intercept all traps and exceptions of another process , And can know its state change in time .
Linux Interprocess communication (IPC) The following is the development of several parts :
In the early UNIX Interprocess communication 、 be based on System V Interprocess communication 、 be based on Socket Interprocess communication and POSIX Interprocess communication .
UNIX Inter process communication includes : The Conduit 、FIFO、 The signal .
System V Inter process communication includes :System V Message queue 、System V Signal lamp 、System V Shared memory 、
POSIX Interprocess communication includes :posix Message queue 、posix Signal lamp 、posix Shared memory .
Now? linux The inter process communication mode used :

(1) The Conduit (pipe) And famous pipes (FIFO)
(2) The signal (signal)
(3) Message queue
(4) Shared memory
(5) Semaphore
(6) Socket (socket)

Two 、 Pipeline communication

ordinary Linux shell Both allow redirection , And redirection uses pipes . for example :
ps | grep vsftpd . The pipe is one-way 、 First in, first out 、 Unstructured 、 Fixed size byte stream , It connects the standard output of one process to the standard input of another . The write process writes data at the end of the pipe , The read process reads data at the end of the pipe . The data is read out and removed from the pipe , No other read process can read this data any more . Pipes provide a simple flow control mechanism . When a process attempts to read an empty pipe , Before data is written to the pipeline , The process will always block . Again , When the pipe is full , The process tries to write the pipeline again , Before other processes remove data from the pipeline , The write process will always block . Pipes are mainly used for communication between different processes .

Pipe creation and closing
Create a simple pipe , You can use system calls pipe(). It takes a parameter , That is, an array of two integers . If the system call succeeds , This array will include the two file descriptors used by the pipeline . After creating a pipe , In general, a process will produce a new process .

 system call :pipe(); Prototype :int pipe(int fd[2]); Return value : If the system call succeeds , return 0. If the system call fails, return -1:
errno=EMFILE( No empty parent file descriptor )
      EMFILE( System file table full )
      EFAULT(fd Invalid array )

Be careful :fd[0] For reading pipes ,fd[1] Used to write pipes .
The picture is attached
The creation of pipes

#include<unistd.h>#include<errno.h>#include<stdio.h>#include<stdlib.h>int main(){int pipe_fd[2];if(pipe(pipe_fd)<0){printf("pipe create error\n");return -1;}elseprintf("pipe create success\n");close(pipe_fd[0]);close(pipe_fd[1]);}

Reading and writing of pipes
Pipes are mainly used for communication between different processes . actually , Usually a pipe is created first , Re pass fork Function to create a child process . The picture is attached .
Named pipes written by the child and read by the parent : The picture is attached
Notes on pipeline reading and writing :
You can create a two-way pipe by opening two pipes . But you need to set the file descriptor correctly in the subroutine . Must be called in the system fork() Call in pipe(), Otherwise, the child process will not inherit the file descriptor . When using half duplex pipes , Any associated process must share a related ancestor process . Because pipes exist in the system kernel , So any process that is not in the ancestor process of the process that created the pipe will not be able to address it . This is not the case in named pipes .
For example, see :pipe_rw.c

#include<unistd.h>#include<memory.h>#include<errno.h>#include<stdio.h>#include<stdlib.h>int main(){int pipe_fd[2];pid_t pid;char buf_r[100];char* p_wbuf;int r_num;memset(buf_r,0,sizeof(buf_r)); The data in the array is clear 0;if(pipe(pipe_fd)<0){printf("pipe create error\n");return -1;}if((pid=fork())==0){printf("\n");close(pipe_fd[1]);sleep(2);if((r_num=read(pipe_fd[0],buf_r,100))>0){printf("%d numbers read from be pipe is %s\n",r_num,buf_r);}close(pipe_fd[0]);exit(0);}else if(pid>0){close(pipe_fd[0]);if(write(pipe_fd[1],"Hello",5)!=-1)printf("parent write success!\n");if(write(pipe_fd[1]," Pipe",5)!=-1)printf("parent wirte2 succes!\n");close(pipe_fd[1]);sleep(3);waitpid(pid,NULL,0);exit(0);}}

Standard flow pipe
And linux There are file stream standards for file operations in I/O equally , Pipeline operation also supports file stream based mode . The interface functions are as follows :

 Library function :popen(); Prototype :FILE *open (char *command,char *type); Return value : If it works , A new stream file is returned . If you can't create a process or pipeline , return NULL. The direction of the data flow in the pipeline is determined by the second parameter type The control of the . This parameter can be r perhaps w, They stand for reading or writing . But not for reading and writing at the same time . stay Linux  Under the system , The pipe will be in parameter type The first character in represents . therefore , If you're looking at parameters type writes rw, The pipe will open in read mode .

Use popen() The created pipe must use the pclose() close . Actually ,popen/pclose And standard file input / In the output stream fopen()/fclose() Very similar .
Library function :pclose();

 Prototype :int pclose(FILE *stream); Return value : Return to system call wait4() The state of .
If stream Invalid , Or system call wait4() Failure , Then return to -1. Note that this library function waits for the pipeline process to finish running , Then close the file stream . Library function pclose() In the use of popen() Create a process to execute wait4() function , It will destroy pipes and file systems .

An example of a flow pipe .

#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<fcntl.h>#define BUFSIZE 1024int main(){FILE *fp;char *cmd="ps -ef";char buf[BUFSIZE];buf[BUFSIZE]='\0';if((fp=popen(cmd,"r"))==NULL)
 perror("popen");while((fgets(buf,BUFSIZE,fp))!=NULL)
 printf("%s",buf);pclose(fp);exit(0);}

name pipes (FIFO)
Basic concepts
Named pipes are basically the same as general pipes , But there are also some significant differences :

A、 Named pipe exists as a special device file in the file system .
B、 Processes of different ancestors can share data through pipelines .
C、 When the process that shares the pipeline performs all I/O After operation , Named pipes will continue to be saved in the file system for later use .

Pipes can only be used by related processes , Their common ancestor process created the pipeline . however , adopt FIFO, Unrelated processes can also exchange data .
Named pipe creation and operation
Named pipe creation

#include<sys/types.h>#include<sys/stat.h>int mkfifo(const char *pathname,mode_t mode); return : If successful 0, Return in case of error -1 Once used mkfifo Created a FIFO, You can use open To open it . exactly , General documents I/O function (close,read,write,unlink etc. ) Can be used for FIFO. When you open a FIFO when , Nonblocking label (O_NONBLOCK) Have the following effects :
(1) In general ( No explanation O_NONBLOCK), Read only open to block to some other process to open this for write FIFO. similar , Open one for writing FIFO To block to some other process and open it for reading .
(2) If you point to one O_NONBLOCK, Open read only and return immediately . however , If no process has opened one for reading FIFO, Then write only open will return an error , Its errno yes ENXIO. It's like a pipe , If you write a process that has not been opened for reading FIFO, Then it generates a signal SIGPIPE. If a FIFO The last write process of shut down the FIFO, Will be the FIFO The read process of produces an end of file flag .

FIFO Related error messages :

EACCES( No access rights )
EEXIST( The specified file does not exist )
ENAMETOOLONG( The pathname is too long )
ENOENT( The included directory does not exist )
ENOSPC( There is not enough free space in the file system )
ENOTDIR( Invalid file path )
EROFS( The specified file exists in a read-only file system )

fifo_write.c

#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 "/tmp/myfifo"main(int argc,char** argv){char buf_r[100];int fd;int nread;if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))printf("cannot create fifoserver\n");printf("Preparing for reading bytes....\n");memset(buf_r,0,sizeof(buf_r));fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);if(fd==-1){perror("open");exit(1);}while(1){memset(buf_r,0,sizeof(buf_r));if((nread=read(fd,buf_r,100))==-1){if(errno==EAGAIN)printf("no data yet\n");}printf("read %s from FIFO\n",buf_r);sleep(1);}pause();unlink(FIFO);}fifo_read.c#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 "/tmp/myfifo"main(int argc,char** argv){int fd;char w_buf[100];int nwrite;if(fd==-1)if(errno==ENXIO)printf("open error;no reading process\n");fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);if(argc==1)printf("Please send something\n");strcpy(w_buf,argv[1]);if((nwrite=write(fd,w_buf,100))==-1){if(errno==EAGAIN)printf("The FIFO has not been read yet. Please try later\n");}else printf("write %s to the FIFO\n",w_buf);}

3、 ... and 、 The signal

Signal Overview

The signal is a software interrupt . The signal (signal) The mechanism is Unix The oldest trust mechanism between processes in the system . It is used to pass asynchronous signals between one or more processes . Many conditions can produce a signal .
A、 When the user presses some terminal keys , Generate signals . Press... On the terminal DELETE Keys usually generate interrupt signals (SIGINT). This is the way to stop a program that has lost control .
B、 Signal generated by hardware abnormality : Divisor is 0、 Invalid storage access, etc . These conditions are usually detected by hardware , And notify the kernel . The kernel then generates the appropriate signal for the process running when the condition occurs . for example , For a process that performs an invalid storage access, generate a SIGSEGV.
C、 For process kill(2) Function to send a signal to another process or process group . natural , There are some restrictions : All processes of receiving and sending signals must be the same , Or the owner of the signaling process must be a superuser .
D、 User available Kill(ID value ) Commands send signals to other processes . This program is Kill Function interface . This command is often used to terminate a runaway background process .
E、 When a software condition is detected to have occurred , It also generates a signal when it is notified to the relevant process . This is not about hardware generation conditions ( If 0 except ), It's software conditions . for example SIGURG( Upload non specified baud rate data on the network connection )、SIGPIPE( A process writes to the pipeline after the read process of the pipeline has terminated ), as well as SIGALRM( The alarm time set by the process has timed out ).
The kernel signals the process , To respond to different events , These events are the signal sources . The main signal sources are as follows :

(1) abnormal : An exception occurred during the process ;
(2) Other processes : One process can send a signal to another or a group of processes ;
(3) Terminal interrupt :Ctrl-c,Ctro-\ etc. ;
(4) Operation control : The front desk 、 Background process management ;
(5) The allotment :CPU Timeout or file size exceeds limit ;
(6) notice : Notifies the process that an event has occurred , Such as I/O Ready, etc ;
(7) Call the police : The timer expires ;

Linux Signal in

1、SIGHUP 2、SIGINT( End ) 3、SIGQUIT( sign out ) 4、SIGILL 5、SIGTRAP 6、SIGIOT  7、SIGBUS   8、SIGFPE   9、SIGKILL 10、SIGUSER 11、 SIGSEGV SIGUSER 12、 SIGPIPE 13、SIGALRM 14、SIGTERM 15、SIGCHLD 16、SIGCONT 17、SIGSTOP 18、SIGTSTP 19、SIGTTIN 20、SIGTTOU 21、SIGURG 22、SIGXCPU 23、SIGXFSZ 24、SIGVTALRM 25、SIGPROF 26、SIGWINCH 27、SIGIO 28、SIGPWR

Common signals :

  • SIGHUP: The end signal from the terminal ;
  • SIGINT: Interrupt signal from keyboard (Ctrl+c)
  • SIGQUIT: The exit signal from the keyboard ;
  • SIGFPE: Floating point exception signal ( For example, floating point overflow );
  • SIGKILL: This signal ends the process of receiving the signal ;
  • SIGALRM: When the timer of the process expires , Send this signal ;
  • SIGTERM:kill The signal of command ;
  • SIGCHLD: A signal that indicates the stop or end of a child process ;
  • SIGSTOP: From keyboard (Ctrl-Z) Or stop sweeping signal of debugging program

The system can be required to operate in one of the following three ways when a signal appears .
(1) Ignore this signal . Most signals can be processed in this way , But there are two signals that can never be ignored . They are :SIGKILL and SIGSTOP. These two signals can't be ignored , as a result of : They provide the superuser with a reliable way to terminate or stop the process . in addition , If you ignore some signals caused by hardware exceptions ( For example, illegal storage access or divide by 0), Then the behavior of the process is defined .
(2) Capture the signal . To do this, inform the kernel when a signal occurs , Call a user function . In user functions , It can execute the processing that the user wants to do to this kind of event . If you catch SIGCHLD The signal , Indicates that the child process has terminated , So the capture function of this signal can be called waitpid To get the process of the subprocess ID And its termination state .
(3) Perform system default actions . The system default action for most signals is to terminate the process . Each signal has a default action , It's when the process doesn't assign a handler to this signal , Signal processing by kernel .

Yes 5 It's a default action :

(1) Abnormal termination (abort): In the current directory of the process , Put the address space content of the process 、 The contents of the register are stored in a file called core In the file of , And then terminate the process .
(2) sign out (exit): Do not produce core file , Terminate the process directly .
(3) Ignore (ignore): Ignore the signal .
(4) stop it (stop): Suspend the process .
(5) continue (contiune): If the process is suspended , Just resume the process . otherwise , Ignore the signal .
Signal transmission and capture 

kill() and raise()
kill() Not only can you abort the process , You can also send other signals to the process .
And kill The function is different from ,raise() Function to send a signal to the process itself

#include<sys/types.h>#include<signal.h>int kill(pid_t pid,int signo);int raise(int signo); Two functions return : If successful 0, In case of error -1.
kill Of pid There are four different cases of parameters :
(1)pid>0 Send a signal to the process ID by pid The process of .
(2)pid==0 Send the signal to its process group ID Equal to the process group of the sending process ID, And all the processes to which the sending process has permission to send signals .
(3)pid<0 Send the signal to its process group ID be equal to pid The absolute value , And all the processes to which the sending process has permission to send signals . As mentioned above ,“ All processes ” Does not include processes in the system process set .
(4)pid==-1 POSIX.1 There is no case defined 

kill.c

#include<stdio.h>#include<stdlib.h>#include<signal.h>#include<sys/types.h>#include<sys/wait.h>int main(){pid_t pid;int ret;if((pid==fork())<0){perro("fork");exit(1);}if(pid==0){raise(SIGSTOP);exit(0);}else {printf("pid=%d\n",pid);if((waitpid(pid,NULL,WNOHANG))==0){if((ret=kill(pid,SIGKILL))==0)printf("kill %d\n",pid);else{perror("kill");}}}}

alarm and pause function
Use alarm Function can set a time value ( Alarm time ), At some point in the future, the time value will be exceeded . When the set time is exceeded , produce SIGALRM The signal . If the pilot signal is not ignored or captured , The default action is to terminate the process .

#include<unistd.h>unsigned int alarm(unsigned int secondss); return :0 Or the remaining seconds of the alarm time set before .
Parameters seconds The value of is seconds , After a specified seconds A signal is generated in seconds SIGALRM. Each process can only have one alarm time . If you're calling alarm when , The alarm time has been set for this process before , And it hasn't timed out yet , Then the remaining value of the alarm clock time is taken as the current alarm The value of the function call returns . The previously registered alarm clock time is replaced by the new value .
If there is a previously registered alarm time that has not been exceeded , and seconds The value is 0, Then cancel the previous alarm time , The remaining values remain as the return values of the function .

pause The function uses the calling process to suspend until a signal is caught

#include<unistd.h>int pause(void); return :-1,errno Set to EINTR

Only when a signal handler is executed and returned from it ,pause To return to .
alarm.c

#include<unistd.h>#include<stdio.h>#include<stdlib.h>int main(){int ret;ret=alarm(5);pause();printf("I have been waken up.\n",ret);}

Signal processing
When the system catches a signal , You can ignore the signal or use the specified processing function to process the signal , Or use the default mode of the system . There are two main ways of signal processing , One is simple to use signal function , The other is to use signal set function group .

signal()#include<signal.h>void (*signal (int signo,void (*func)(int)))(int) return : Success is the previous signal processing configuration , In case of error SIG_ERR
func The value of is :(a) constant SIGIGN, or (b) constant SIGDFL, or (c) The address of the function to be called when this signal is received . If specified SIGIGN, Indicates to the kernel that this signal is ignored ( There are two signals SIGKILL and SIGSTOP Can't ignore ). If specified SIGDFL, It means that the action after receiving this signal is the default action of the system . When you specify the function address , We call this capturing this signal . We call this function a signal processor (signal handler) Or signal capture function (signal-catching funcgion).signal Function prototypes are too complex , If you use the following typedef, It can be simplified .

type void sign(int);sign *signal(int,handler *); See for example :mysignal.c#include<signal.h>#include<stdio.h>#include<stdlib.h>void my_func(int sign_no){if(sign_no==SIGINT)
 printf("I have get SIGINT\n");else if(sign_no==SIGQUIT)
 printf("I have get SIGQUIT\n");}int main(){
 printf("Waiting for signal SIGINT or SIGQUTI\n");
 signal(SIGINT,my_func);
 signal(SIGQUIT,my_func);
 pasue();
 exit(0);}

Signal set function group
We need to have a signal that can represent multiple signals —— Signal set (signal set) Data type of . Will be in sigprocmask() This data type is used in such functions , To tell the kernel that signals in the signal set are not allowed to occur . The signal set function group contains several modules : Create function sets 、 Register the signal set 、 Detection signal set .
The picture is attached .
Create function sets

#include<signal.h>int sigemptyset(sigset_t* set);int sigfillset(sigset_t* set);int sigaddset(sigset_t* set,int signo );int sigdelset(sigset_t* set,int signo);

Four functions return : If successful 0, In case of error -1

int sigismember(const sigset_t* set,int signo); return : If it is true, it is 1, If false, it means 0;
signemptyset: Initialization signal set is empty .
sigfillset: Initialize the signal set to all signal sets .
sigaddset: Adds the specified signal to an existing set .
sigdelset: Remove the specified signal from the signal set .
sigismember: Query whether the specified signal is in the signal set .

Register the signal set
The register signal processor is mainly used to determine how the process processes the signal . First of all, we need to determine whether the current process block can be passed to the signal set of the signal . This first uses sigprocmask Function judgment detects or changes the signal mask word , And then use sigaction Function to change the behavior of a process after receiving a specific signal .
The signal mask word of a process can specify the signal set that is currently blocked and cannot be delivered to the process . Call function sigprocmask You can detect or change ( Or both ) The signal mask word of the process .

#include<signal.h>int sigprocmask(int how,const sigset_t* set,sigset_t* oset); return : If successful 0, In case of error -1oset Yes no null pointer , Process is the current signal mask word through oset return . secondly , if set It's a non null pointer , The parameter how Indicates how to modify the current signal mask word .
use sigprocmask How to change the current signal mask word .how Parameter setting :
SIG_BLOCK The new signal mask word of the process is its current signal mask word and set The union of a set of directional signals .set Contains additional signals that we want to block .
SIG_NUBLOCK The new signal mask word of the process is its current signal mask word and set The intersection of the set of signals pointed to .set Contains the signals we want to unblock .
SIG_SETMASK The new signal mask for this process is set The value of the point . If set It's a null pointer , The signal mask word of the process is not changed ,how And it doesn't make sense .
sigaction The function is to check or modify ( Or both ) The processing action associated with the specified signal . This function replaces UNIX Earlier versions used signal function .

#include<signal.h>int sigaction(int signo,const struct sigaction* act,struct sigaction* oact); return : If successful 0, In case of error -1 Parameters signo It is the number of signals to detect or modify specific actions . if act The pointer is not empty , You have to modify the action . If oact The pointer is null , Then the system returns the original action of the signal .

This function uses the following structure :

struct sigaction{void (*sa_handler)(int signo);sigset_t sa_mask;int sa_flags;void (*sa_restore);};

sa_handler Is a function pointer , Specify the signal correlation function , It can be a custom handler , just so so SIG_DEF or SIG_IGN;sa_mask It's a signal set , It can specify which signals should be blocked during the execution of the signal processor .
sa_flags It contains many flag bits , It's a variety of options for signal processing . As follows :
SA_NODEFER\SA_NOMASK: When this signal is captured , When executing its signal capture function , The system will not block this signal automatically .
SA_NOCLDSTOP: The process ignores any SIGSTOP、SIGTSTP、SIGTTIN and SIGTOU The signal
SA_RESTART: Can make the system call that restarts work again .
SA_ONESHOT\SA_RESETHAND: Custom signals are executed only once , The system default action of restoring the signal after execution .

Detection signal is the next step of signal processing , But it's not necessary .sigpending Function running process detection “ outstanding “ The signal ( The process is not clear about his existence ), And further decide what to do with them .
sigpending Returns the signal set that cannot be delivered and is currently pending for the calling process .

#include<signal.h>int sigpending(sigset_t * set); return : If successful 0, In case of error -1

Examples of signal sets are shown in :sigaction.c

#include<sys/types.h>#include<unistd.h>#include<signal.h>#include<stdio.h>#include<stdlib.h>void my_func(int signum){printf("If you want to quit,please try SIGQUIT\n");}int main(){sigset_t set,pendset;struct sigaction action1,action2;if(sigemptyse(&set)<0)perror("sigemptyset");if(sigaddset(&set,SIGQUIT)<0)perror("sigaddset");if(sigaddset(&set,SIGINT)<0)perror("sigaddset");if(sigprocmask(SIG_BLOCK,&set,NULL)<0)perror("sigprcmask");esle{printf("blocked\n");sleep(5);}if(sigprocmask(SIG_UNBLOCK,&set,NULL)perror("sigprocmask");elseprintf("unblock\n");while(1){if(sigismember(&set,SIGINT)){sigemptyset(&action1.sa_mask);action1.sa_handler=my_func;sigaction(SIGINT,&action1,NULL);}else if(sigismember(&set,SIGQUIT)){sigemptyset(&action2.sa_mask);action2.sa_handler=SIG_DEL;sigaction(SIGTERM,&action2,NULL);}}}