Linux learning process communication (IPC) - communication between processes

foreword

This article records the learning process and program cases of different communication methods between processes. The learning process benefits from the following two excellent blog posts:

It's cold! Zhang San failed to answer "inter-process communication" and was hung up by the interviewer... (with pictures and texts, concise and meaningful, which is helpful for conceptual understanding)
six common methods of inter-process communication (clear logic, clean and tidy, with program source code) )

insert image description here

1. Pipeline

Pipelines are simple to use , but are inefficient and not suitable for frequent information exchange between processes .

1.1 Anonymous pipes

Anonymous pipe pipe is a half-duplex communication mode, which can only be used between processes with kinship. The kinship of processes generally refers to the parent-child relationship . Close the read port when using the write port, and close the write port when using the read port . You can use ordinary read, write and other functions to perform read and write operations, and the operation follows the first-in-first-out principle.

1.1.1 pipe prototype

   	#include <unistd.h>

    int pipe(int pipefd[2]); 

1.1.2 pipe instance

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
//匿名管道的创建与使用
int main()
{
    
    
        int fd[2];
        char buf[1024]={
    
    ""};
        int pid;
        int states;

        if(pipe(fd)==-1)
        {
    
    
                printf("fail!\n");
        }

        pid=fork();

        if(pid<0)
        {
    
    
                printf("creat child fail!\n");
        }
        else if(pid>0)//父进程
        {
    
    
                printf("This is Father\n");
                sleep(3);
                close(fd[0]);//管道0负责读 1负责写
                write(fd[1],"Hello from father",strlen("Hello from father"));
                wait(&states);
        }
        else//子进程
        {
    
    
                printf("This is child\n");
                close(fd[1]);
                read(fd[0],buf,1000);
                printf("read from father:%s\n",buf);
                exit(0);
        }
        return 0;
}

1.2 Named pipes

The named pipe FIFO exists in the file system in the form of a special device file, and has a path name associated with it. Therefore, data can be exchanged between unrelated processes.

1.2.1 fifo prototype

    #include <sys/types.h>

    #include <sys/stat.h>

    int mkfifo(const char *pathname, mode_t mode);

1.2.2 fifo example

process writes to the pipe

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
//有名管道的写入
int main()
{
    
    
        char *str="message from fife";
        int cnt=0;
        int fd=open("flie",O_WRONLY);
        printf("write open success\n");

        while(1)
        {
    
    

                sleep(1);
                write(fd,str,strlen(str));
                if(cnt>5)
                {
    
    
                        break;
                }
                cnt++;
        }

        close(fd);
        return 0;
}

process reads from the pipe

#include <sys/types.h>
#include <sys/stat.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <unistd.h>
#include<fcntl.h>
#include <errno.h>


//有名管道的读取fifo
int main()
{
    
    
        char buf[30]={
    
    0};
        int nread=0;

        if(mkfifo("flie",0600==-1) && errno!=EEXIST)
        {
    
    
                printf("mkfifo failed\n");
                perror("why");
        }

        int fd=open("flie",O_RDONLY);
        printf("open success\n");
        while(1)
        {
    
    
                nread=read(fd,buf,30);
                printf("read %d byte from fifo,context:%s\n",nread,buf);
        }
        close(fd);

        return 0;
}

Two, the message queue

The message queue is a message linked list stored in the kernel . When sending data, it will be divided into an independent data unit, that is, the message body (data block). The message body is a user-defined data type, and the sender and receiver of the message The party must agree on the data type of the message body, so each message body is a fixed-size storage block, unlike the unformatted byte stream data of the pipeline. If the process reads the message body from the message queue, the kernel will delete the message body. It does not need to follow the first-in-first-out principle , and can read according to the data type.

The life cycle of the message queue depends on the kernel. If the message queue is not released or the operating system is not closed, the message queue will always exist. The life cycle of the anonymous pipe mentioned above is established with the creation of the process and destroyed with the end of the process. However, message queues are not suitable for the transmission of large data
in view of the upper limit of transmission . At the same time, the essence is the copy of information between the user mode and the kernel mode , which increases the workload.

2.1 msg prototype

// 创建或打开消息队列:成功返回队列ID,失败返回-1
int msgget(key_t key, int flag);
// 添加消息:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
// 读取消息:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
// 控制消息队列:成功返回0,失败返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

2.2 msg instance

2.2.1 Example of one-way communication

sender

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

struct msgbuf
{
    
    
        long mtype;
        char mtext[128];
};

int main()
{
    
    
        struct msgbuf  sendbuf={
    
    888,"This is message from quen"};

        //1. int msgget(key_t key, int msgflg);
        int msgid=msgget(0x1234,IPC_CREAT|0777);

        if(msgid==-1)
        {
    
    
                printf("creat que failed!\n");
        }
        //2. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

        //3. ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)

        msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);
        return 0;
}

Receiving end

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>



struct msgbuf
{
    
    
        long mtype;
        char mtext[128];
};

int main()
{
    
    
        struct msgbuf  readbuf;
        //1. int msgget(key_t key, int msgflg);
        int msgid=msgget(0x1234,IPC_CREAT|0777);
        if(msgid==-1)
        {
    
    
                printf("creat que failed!\n");
        }
        // int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

        // ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)

        msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),888,0);
        printf("read from que:%s\n",readbuf.mtext);
        return 0;
}

2.2.2 Example of two-way communication

read first

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
//       int msgget(key_t key, int msgflg);
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
 
//       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 */
};
 
 
int main()
{
    
    
        struct msgbuf sendbuf={
    
    888,"message from send"};
        struct msgbuf readbuf;
 
        key_t key;
 
        if((key = ftok(".",'z')) < 0){
    
    
                printf("ftok error\n");
        }
        int msgId = msgget(key,IPC_CREAT|0777);
 
        if(msgId == -1){
    
    
                printf("get quen failed\n");
        }
 
        msgsnd(msgId,&sendbuf,strlen(sendbuf.mtext),0);
        printf("send over\n");
 
        msgrcv(msgId,&readbuf,sizeof(readbuf.mtext),999,0);
        printf("read from get is:%s\n",readbuf.mtext);
 
        msgctl(msgId,IPC_RMID,NULL);
 
        return 0;
}

read before post

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
//       int msgget(key_t key, int msgflg);
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
 
//       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 */
};
 
int main()
{
    
    
        struct msgbuf readbuf;
        memset(readbuf.mtext, '\0', sizeof(readbuf.mtext));
        struct msgbuf sendbuf={
    
    999,"thank for your reach"};
 
        key_t key;
 
        //获取key值
        if((key = ftok(".",'z')) < 0){
    
    
                printf("ftok error\n");
        }
 
        int msgId = msgget(key,IPC_CREAT|0777);
 
        if(msgId == -1){
    
    
                printf("get quen failed\n");
                perror("why");
        }
 
        msgrcv(msgId,&readbuf,sizeof(readbuf.mtext),888,0);
        printf("read from send is:%s\n",readbuf.mtext);
 
        msgsnd(msgId,&sendbuf,strlen(sendbuf.mtext),0);
 
        msgctl(msgId,IPC_RMID,NULL);
 
        return 0;
}

3. Shared memory

In the process of reading and writing the message queue , there will be a message copy process between the user state and the kernel state . The way of shared memory solves this problem very well. The mechanism of shared memory is to take out a virtual address space and map it to the same physical memory. In this way, the things written by this process can be seen by another process immediately, without copying and copying, and passing them back and forth, which greatly improves the speed of inter-process communication .

3.1 Prototype of shared memory

// 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
int shmget(key_t key, size_t size, int flag);
// 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
void *shmat(int shm_id, const void *addr, int flag);
// 断开与共享内存的连接:成功返回0,失败返回-1
int shmdt(void *addr); 
// 控制共享内存的相关信息:成功返回0,失败返回-1
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);

3.2 shm example

send data

#include <string.h>
#include <stdlib.h>
#include <unistd.h>
//共享内存的创建

//int shmget(key_t key, size_t size, int shmflg);

//void *shmat(int shmid, const void *shmaddr, int shmflg);
//int shmdt(const void *shmaddr);

//void *shmat(int shmid, const void *shmaddr, int shmflg);
//int shmdt(const void *shmaddr);

int main()
{
    
    
        key_t key;
        key=ftok(".",1);

        int shmid=shmget(key,1024*4,IPC_CREAT|0777);//创建,内存大小必须得是MB的整数倍
        if(shmid==-1)
        {
    
    
                printf("failed!\n");
                exit(-1);
        }

        char *shmadder=shmat(shmid,0,0);//映射挂载
        printf("shmat ok\n");
        strcpy(shmadder,"message frome send");

        sleep(5);//等待五秒

        shmdt(shmadder);//卸载
        shmctl(shmid,IPC_RMID,0);//删除空间

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

read data

#include <sys/shm.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
//共享内存的创建

//int shmget(key_t key, size_t size, int shmflg);

//void *shmat(int shmid, const void *shmaddr, int shmflg);
//int shmdt(const void *shmaddr);

//void *shmat(int shmid, const void *shmaddr, int shmflg);
//int shmdt(const void *shmaddr);

int main()
{
    
    
        key_t key;
        key=ftok(".",1);

        int shmid=shmget(key,1024*4,0);//获取
        if(shmid==-1)
        {
    
    
                printf("failed!\n");
                exit(-1);
        }

        char *shmadder=shmat(shmid,0,0);//映射挂载
        printf("shmat ok\n");
        printf("data:%s\n",shmadder);


        shmdt(shmadder);//卸载
        shmctl(shmid,IPC_RMID,0);//删除空间

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

4. Semaphore

The semaphore is different from the IPC structure already introduced, it is a counter . Semaphores are used to implement mutual exclusion and synchronization between processes , not to store inter-process communication data. The semaphore is based on the P and V atomic operations of the operating system and is used for inter-process synchronization . To transfer data between processes, it needs to be combined with shared memory . Each PV operation on the semaphore is not limited to adding or subtracting 1 to the semaphore value, but can also add or subtract any positive integer.

4.1 Semaphore Prototype

// 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
int semget(key_t key, int num_sems, int sem_flags);
// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
int semop(int semid, struct sembuf semoparray[], size_t numops);  
// 控制信号量的相关信息
int semctl(int semid, int sem_num, int cmd, ...);

4.2 Semaphore instance

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
 
//       int semget(key_t key, int nsems, int semflg);
//       int semctl(int semid, int semnum, int cmd, ...);
//       int semop(int semid, struct sembuf *sops, size_t 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 seminfo  *__buf;  /* Buffer for IPC_INFO
                                    (Linux-specific) */
};
 
//P操作,拿钥匙
void PGetKey(int semid)
{
    
    
        struct sembuf sops;
        sops.sem_num = 0;// 信号量组中对应的序号,0~sem_nums-1
        sops.sem_op = -1;// 信号量值在一次操作中的改变量
        sops.sem_flg = SEM_UNDO;// IPC_NOWAIT, SEM_UNDO
 
        semop(semid, &sops, 1);
}
 
//V操作,放回钥匙
void VPutBackKey(int semid)
{
    
    
        struct sembuf sops;
        sops.sem_num = 0;
        sops.sem_op = 1;
        sops.sem_flg = SEM_UNDO;
 
        semop(semid, &sops, 1);
}
 
int main()
{
    
    
        key_t key;
        int semid;
        if((key == ftok(".",6)) < 0)
        {
    
    
                printf("ftok error\n");
        }
 
        semid = semget(key , 1,  IPC_CREAT|0666);//创造信号量集合 钥匙,数量为1
 
        union semun sem;
        sem.val = 0;//初始状态为没有钥匙
        semctl(semid, 0, SETVAL, sem);//SETVAL初始化信号量为一个已知的值,这时就需要第四个参数
                     //0表示操作第一把钥匙
        int pid = fork();
 
        if(pid < 0)
        {
    
    
                printf("fork failed\n");
        }else if(pid == 0)//子进程
        {
    
    
                printf("this is child\n");
                VPutBackKey(semid);//子进程运行结束后把钥匙放回     
        }else//父进程
        {
    
    
                PGetKey(semid);//等子进程将钥匙放回后才会进行操作,保证子进程先执行(最开始无锁状态,父进程拿不到锁,进程堵塞)
                printf("this is father\n");
                VPutBackKey(semid);
                semctl(semid, 0, IPC_RMID);//销毁钥匙
        }
 
        return 0;
}

5. Signal

Signal signal belongs to soft interrupt and belongs to asynchronous communication. When the end user enters ctrl+c to interrupt the program, a program will be stopped through the signal mechanism (SIGINT) . The three methods of signal processing are: ignore , catch and default action
.

5.1 signal

5.1.1 Signal signal prototype

     //接收函数,第二个参数指向信号处理函数
    sighandler_t signal(int signum, sighandler_t handler);

    //发送函数
     int kill(pid_t pid, int sig);

5.1.2 Example of signal sig

take over

#include <stdio.h>
#include <signal.h>
 
//       typedef void (*sighandler_t)(int);
 
//       sighandler_t signal(int signum, sighandler_t handler);
/*接受到信号后,让信号处理该函数*/
void handler(int signum)
{
    
    
        printf("signum = %d\n",signum);
 
        switch(signum){
    
    
                case 2:
                        printf("SIGINT\n");
                        break;
                case 9:
                        printf("SIGKILL\n");
                        break;
                case 10:
                        printf("SIGUSR1\n");
                        break;
        }
}
 
int main()
{
    
    
        signal(SIGINT,handler);
        signal(SIGKILL,handler);
        signal(SIGUSR1,handler);
 
        while(1);
 
        return 0;
}

send

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
 
//       int kill(pid_t pid, int sig);
 
int main(int argc,char **argv)
{
    
    
        int signum;
        int pid;
        char cmd[128]={
    
    0};
 
        signum = atoi(argv[1]);//将字符型转为整型 //消息号       
        pid = atoi(argv[2]);//进程号
 
        kill(pid,signum);//进程号,信号编号
        
 //		sprintf(cmd,"kill -%d%d",signum,pid);       
 //     system(cmd);
        printf("signum = %d,pid = %d\n",signum,pid);
 
        return 0;
}

5.2 sigaction

5.2.1 sigaction prototype

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
 
struct sigaction {
    
    
   void       (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作
   void       (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
   sigset_t   sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。
   int        sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据
 };
//回调函数句柄sa_handler、sa_sigaction只能任选其一

5.2.2 sigaction example

Receiving end

#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
//       int sigaction(int signum, const struct sigaction *act,
//                     struct sigaction *oldact);

void handler(int signum ,siginfo_t *info,void *context)
{
    
    
        printf("get signum%d\n",signum);
        if(context!=NULL)
        {
    
    
                printf("get data=%d\n",info->si_int);
                printf("from:%d\n",info->si_pid);
        }
}


int main()
{
    
    

        struct sigaction act;
        printf("pid:%d\n",getpid());

        act.sa_sigaction = handler;
        act.sa_flags = SA_SIGINFO;//be able to get message

        sigaction(SIGUSR1,&act,NULL);//注册信号
        while(1);
        return 0;
}

sender

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
//  int sigqueue(pid_t pid, int sig, const union sigval value);


int main(int argc,char **argv)
{
    
    
        int signum;

        int pid;

        union sigval value;
        value.sival_int=100;


        signum = atoi(argv[1]);
        pid = atoi(argv[2]);
        sigqueue(pid,signum,value);
        printf("send successful\n");
        printf("pid=%d,done\n",getpid());
        return 0;
}

Six, Socket

Guess you like

Origin blog.csdn.net/ONERYJHHH/article/details/126534712