Linux – five communication methods between processes – (half-duplex pipes, named pipes, message queues, signals, shared memory), plus semaphores. Go directly to the code:

Unnamed pipe (half-duplex): (only the same program can run)

Creating an unnamed pipe will generate a special file that only exists in memory

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
//int pipe(int pipefd[2]);

//ssize_t write(int fd, const void *buf, size_t count);
int main()
{
        int pid;
        int fd[2];
        char buf[128];

        if(pipe(fd) == -1){ //Create an unnamed pipe
                printf("create pipe failed!\\
");
        }

        pid = fork();//Create a process
        if(pid < 0){
                printf("creat fork failed!\\
");
        }else if(pid > 0){//parent process
                sleep(3); //Delay for 3 seconds
                printf("This is father!\\
");
                close(fd[0]); //Close the read channel
                write(fd[1],"Hello , From Father!",strlen("Hello , From Father!"));
                //write channel to child process
                wait();//Wait for the child process to exit and collect the exit signal
        }else{ //subprocess
                printf("This is Child!\\
");
                close(fd[1]);//Close the write channel
                read(fd[0],buf,128);//Read the data sent by the parent process of the channel
                printf("read from father:%s\\
",buf);//print the read data
                exit(0);//exit signal
        }
        return 0;
}

The running effect is as follows: (Because the parent process waits for 3 seconds, the child process runs first, and the parent process runs after waiting for 3 seconds)

Named pipe fifo: (can be run by different programs)

Creating a named pipe will exist in the file system as a special device file, such as the file I created below

Program A: (mkfifo) create pipe (read) read

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
// int mkfifo(const char *pathname, mode_t mode);
int main()
{
        char buf[30] = {0};
        int nread = 0;
        if((mkfifo("./file",0600) == -1) & amp; & amp; errno!= EEXIST)//Create a named pipe, readable and writable
        {
                printf("mkfifo file failed!\\
");
                perror("why:");//perror function gets failure information
        }
        int fd = open("./file",O_RDONLY);//Open the pipe file in read-only mode
        while (1) {
                nread = read(fd,buf,30);//read the data in the pipeline
                printf("read=?yte from buf:%s\\
",nread,buf);//print the number of bytes read and data content
        }
        close(fd);//close the file
        return 0;
}

Program B: (write) write

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
// int mkfifo(const char *pathname, mode_t mode);
int main()
{
        int cnt = 0;
        char *buf = "Ivan hen shuai!"; //string to be written
        int fd = open("./file",O_WRONLY);//Write-only way to punch file
        printf("write open success\\
");
        while(1){ //Break out of the loop after writing 5 times
                write(fd,buf,strlen(buf));//write
                sleep(1);//Delay for one second to wait for writing to the cache
                if(cnt == 5){
                        break;
                }
        }

        close(fd);//close the file
        return 0;
}

The operation effect is as follows: (run program A first, wait for the data to be written into the read data, then run program B, write the data, and finally program A prints the read data) (fiforead is program A, fifowrite is program B)

Message queue: msg (can be run by different programs)

In the Linux kernel, there is a special structure linked list to manage and store message queues

Ideas: 1. How does A add messages to the queue

2. How does B get the message from the queue

Program A: msgget

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

struct msgbuf {
        long mtype; /* message type, must be > 0 */
        char mtext[128]; /* message data */
};
struct msgbuf readbuf;

int main()
{
        int key;
        key = ftok(".",'z');//Analyzed into key by ftok function
        printf("key=%x\\
",key);
        //Each message queue has an id number
        int msgID = msgget(key,IPC_CREAT|0777);//create/get message queue, readable, writable and executable
        if(msgID == -1){
              printf("get que failed!\\
");
        }
        //Read the content written into the queue, the message type length is 888, and the number is 0
        msgrcv(msgID, &readbuf,sizeof(readbuf.mtext),888,0);
        // print the read data
        printf("read from que:%s\\
",readbuf.mtext);
        
        //Create data, which is a structure type, and the length of the message type is 988
        struct msgbuf sendbuf = {988,"Thank you for reach!\\
"};
        //The msgsend function writes the data of the structure into the queue
        msgsnd(msgID, & amp; sendbuf, strlen(sendbuf.mtext), 0);

        //After the program is used up, remove the unused garbage queue
        msgctl(msgID, IPC_RMID, NULL);
        return 0;
}

Program B: msgsend (the whole is similar to msgget, but it depends on who sends it first and who reads it first)

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

struct msgbuf {
        long mtype; /* message type, must be > 0 */
        char mtext[128]; /* message data */
};
struct msgbuf readbuf;
struct msgbuf sendbuf = {888,"This is maessage from queue!\\
"};
int main()
{
        int key;
        key = ftok(".",'z');
        printf("key=%x\\
",key);

        int msgID = msgget(key,IPC_CREAT|0777);
        if(msgID == -1){
              printf("get que failed!\\
");
        }

        //msgsend function reads the data in the queue
        msgsnd(msgID, & amp; sendbuf, strlen(sendbuf.mtext), 0);
        printf("send over!\\
");
        //Send data to the queue

        msgrcv(msgID, &readbuf,sizeof(readbuf.mtext),988,0);
        printf("return from que:%s\\
",readbuf.mtext);
        
        // Talk about garbage queue removal
        msgctl(msgID, IPC_RMID, NULL);
        return 0;
}

The operation effect is as follows: (run program A first, wait for the data to be written into the read data, then run program B, write the data, and finally program AB prints the read data) (msgget is program A, msgsend is program B)

Memory sharing: shm (can be run by different programs)

Memory sharing, as the name implies, they can share a certain memory

Idea: 1. First create/acquire a shared memory

2. The file is connected to this memory

3. Release memory

4. Remove the memory

Program A: Shaget

#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// int shmget(key_t key, size_t size, int shmflg);

int main()
{

    int shmID;
    key_t key;
    char *shmaddr;
    key = ftok(".",1);//Get key value
    shmID = shmget(key,1024*4,IPC_CREAT|0666);//Create shared memory, readable and writable, generate a memory id number
    if(shmID == -1){
          printf("get failed!\\
");
    }
    /*Connect this memory, the first 0 is the default, the second line 0 means that the mapped content is readable and writable
    Generate a pointer variable and point to memory*/
    shmaddr = shmat(shmID,0,0);
    printf("shmat ok!\\
");//Connect successfully
    strcpy(shmaddr,"Ivan nb123!");//Write the string into the pointer variable
    sleep(5);//wait for 5 seconds

    shmdt(shmaddr);//release memory
    shmctl(shmID,IPC_RMID,0);//Remove the memory

    printf("quit\\
");
    return 0;
}

Program B: share

#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// int shmget(key_t key, size_t size, int shmflg);

int main()
{

    int shmID;
    key_t key;
    char *shmaddr;
    key = ftok(".",1);
    shmID = shmget(key,1024*4,0);//Get
    if(shmID == -1){
          printf("get failed!\\
");
    }
    shmaddr = shmat(shmID,0,0);//connect this memory
    printf("data:%s\\
",shmaddr);//print out the data in memory
    printf("shmat ok!\\
");
// sleep(5);

    shmdt(shmaddr);//release memory
    //shmctl(key,IPC_RMID,0);//Because the program has already removed the memory operation, so it is not used here
    printf("quit\\
");
    return 0;
}

The operation effect is as follows: (run program A first, wait for the data to be written into the memory, then run program B, read the data, and finally program B prints the read data) (shmg is program A, shmr is program B)

Signal: sig (can be run by different programs)

For Linux, the signal is like a hardware interrupt in a microcontroller, this signal is a “soft interrupt”

There are three ways of signal processing: ignore, capture signal, default action

(SIGKILL and SIGSTOP cannot be ignored, they are ctl+C and ctl+Z)

Each signal has a name and a number, all beginning with SIG

Use kill -l to see what signals are in linux

For example, when program A cannot exit, we can call it on the other end of cmd this signal

Such as KILL (pid:-9, which is SIGKILL) (pid number of A program)

The premise is that we let program A bind the KILL signal to kill the process

The above is the operation of the low-level signal, and the following is directly on the high-level signal operation:

Go directly to the code demo

Program A: sigaction receiving signal

#include <signal.h>
#include <stdio.h>
//int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
//Function pointer, the first is the num of the program, the second is the structure, and the third is non-empty and can receive data
void handler(int signum, siginfo_t *info, void *context)
{
    printf("get signum:%d\\
",signum);//print out the num of the program
    if(context != NULL){//Non-empty data can be received
            printf("get data:%d\\
",info->si_int);//print out the integer data in the structure
            printf("get data:%d\\
",info->si_value.sival_int);// optional
            printf("form:%d\\
",info->si_pid);//Print out the pid of program B
    }
}

int main()
{

    struct sigaction act;
    printf("pid = %d\\
",getpid());//Print out the pid number of this program

    act.sa_sigaction = handler;//Additional receiving data, which is a function pointer
    act.sa_flags = SA_SIGINFO;// Receive signal default

    //The first parameter is a custom semaphore, the second is a structure, binding function, and the third is a backup, which can be NULL
    sigaction(SIGUSR1, & amp;act,NULL);//Process signal function registration

    while(1);
    return 0;
}

Program B: sigaqueue signaling

#include <signal.h>
#include <stdio.h>

//int sigqueue(pid_t pid, int sig, const union sigval value);
int main(int argc, char **argv)
{
    int signum;
    int pid;
    signum = atoi(argv[1]); //Because signum is an integer, call atoi to force conversion of ASI code
    pid = atoi(argv[2]); //Because pid is an integer, call atoi to force conversion of ASI code

    union sigval value;
    value.sival_int = 100;
   

    sigqueue(pid,signum,value); //Signal, the third parameter is the union, which stores the data to be sent
    printf("pid=%d,done\\
",getpid());
    return 0;
}

The running effect is as follows: (program B is for sending signals, and program A is for receiving signals)

Semaphore

It is not a communication method, it is a usage that is attached to the above other communication methods in the form of a lock

It decides who runs first and who runs later,

It can be understood as:

key: semaphore, semaphore set

Room: Critical Resources (Printers)

There are two operations:

1. P operation (pgetkey) to get the key

2. V operation (vputbackkey) to put the key back

The above code understanding:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
//int semget(key_t key, int nsems, int semflg);
//int semop(int semid, struct sembuf *sops, unsigned nsops)
union semun {
               int val; /* Value for SETVAL */
               struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
               unsigned short *array; /* Array for GETALL, SETALL */
               struct semiinfo *__buf; /* Buffer for IPC_INFO
                                           (Linux-specific) */
};
void pGetKey(int id)
{
        struct sembuf sops;
        sops.sem_num = 0;//The number of the semaphore
        sops.sem_op = -1; //Get the key operation
        sops.sem_flg = SEM_UNDO;//When the process automatically terminates, cancel the lock operation (blocking)
        semop(id, &sops, 1);
        printf("get keys!\\
");
}
void vPutBackKey(int id)
{
        struct sembuf sops;
        sops.sem_num = 0;//The number of the semaphore
        sops.sem_op = 1; //Get the key operation
        sops.sem_flg = SEM_UNDO;//When the process automatically terminates, cancel the lock operation (blocking)
        semop(id, &sops, 1);
        printf("put back keys!\\
");
}

int main()
{
        int semi;
        key_t key;

        key = ftok(".",2);
                      // There is a semaphore in the semaphore collection
        semid = semget(key,1,IPC_CREAT|0666);//create/get semaphore

        union semun initsem;
        initsem.val = 0;
                   //Operate the 0th semaphore number 0
        semctl(semid,0,SETVAL,initsem);
                   //SETVAL sets the value of the semaphore, set to initsem
        int pid = fork();
        if(pid > 0){
                // go get the lock
                pGetKey(semid);
                printf("This is Father\\
");
                // put the lock back
                vPutBackKey(semi);
                semctl(semid,0,IPC_RMID);//Destroy the lock
        }else if(pid == 0){
                //There is no lock here, because there is no lock in the initial state, wait until the parent process does not execute, come here
                printf("This is Child\\
");
                // At this time, the lock operation is called, and there is a lock in the lock pool
                vPutBackKey(semi);
        }else{
                printf("fork error!\\
");
        }
        return 0;
}
                                                                                                          65,1 Bottom

The operation effect is as follows: (Because there is no key for initialization, the parent process does not get the key, the child process runs first, then puts the key in, and the parent process gets the key and then runs)