Linux进程间通信的方式大全(PIPE,FIFO,XSI消息队列,XSI 共享内存,XSI 信号量,域间套接字,信号 , 互斥量 ,sendmse)

linux-IPCs

Linux进程间通信方式汇总

目前已包含的方式

  1. 管道(PIPE) 无名管道
  2. FIFO(有名管道)
  3. XSI消息队列
  4. XSI信号量
  5. XSI共享内存
  6. POSIX信号量
  7. 域套接字(Domain Socket)
  8. 信号(Signal)
  9. 互斥量(Mutex)
  10. sendmsg

其中信号(signal)和信号量(semaphore)本质上并不算是进程间通信方式,应该是进程间同步的方式,但是也可以起到一定的通信作用,故也列在上面。

另外普通的mutex是作用线程间同步用的,但是可以将进程A和进程B共享的内存中初始化一个mutex,这样就可以用将此mutex用作进程间通信用了。

无名管道 Pipe

同主机进程间数据交互机制:

  • 无名管道(PIPE):只有创建该管道的程序,才能够找到该管道(亲缘进程通信)
  • 单向传输
  • 控制进程只有pipe的一端
  • pipe的阻塞操作
    *fd[0] 读 fd[1]写
    SIGPIPE
  • 无名管道的内核资源在通信两进程退出后会自动释放,不能像普通文件一样存储信息,不能使用lseek来修改当前pipe的使用位置

read write 返回-1
会收到sigpipe信号

Demo

#include<stdio.h>
#include<errno.h>
#include<sys/wait.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>

int main()
{
	
    int pipe_fd[2] = {-1,-1};
	
	
    if(pipe(pipe_fd) < 0){
        printf("pipe error, %s\n",strerror(errno));
        return 1;
    }

    pid_t id = fork();
	
    if(id == 0){
        //child write
        close(pipe_fd[0]);

        char *msg = "child write:enenshiwo\n";
        while(1){
            write(pipe_fd[1],msg,strlen(msg));
            sleep(1);
        }
		
		
    }else{
        //father read
        close(pipe_fd[1]);

        char buf[1024];
        while(1){
            buf[0] = '\0';
			 char *msg_f = "father write:enenshiwo\n";
            ssize_t _sz = read(pipe_fd[0],buf,sizeof(buf) - 1);
            if(_sz > 0){
                buf[_sz] = '\0';
            }

            printf("father read : %s\n",buf);
        }
        wait(NULL);
    }
    return 0;
}

有名管道 fifo

  • FIFO 是依赖于文件系统,

依赖于文件系统,像普通文件一样具有磁盘路径,文件权限和其他属性,所有程序都可以通过path找到有名管道

  • fifo是一个文件,存储信息在内存,当两个进程都消失,数据消息,文件只有接口的作用
  • mkfifo
    int mkfifo(const char* pathname, mode_t mode);
  • 单项通信
  • 只能够进行少量数据传输
  • 只能一对一进行传输
  • 两个进程,中途退出了一个进程,未退出的一端如果是写操作的话,返回sigpipe信号
  • 未退出的一端如果是阻塞读操作的话,该读操作不会继续阻塞,直接返回0

Demo

fifo_write.c

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

#define FIFO_NAME "/tmp/my_fifo"

int main(int argc,char *argv[])
{
    int pipe_fd;
    int res;
    char buffer[]="hello world!";

    if (access(FIFO_NAME, F_OK) == -1) 
	{
        res = mkfifo(FIFO_NAME, 0766);
        if (res != 0) 
		{
            fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
            exit(EXIT_FAILURE);
        }
    }

	printf("Process %d opening FIFO O_WRONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, O_WRONLY);
    printf("the file's descriptor is %d\n", pipe_fd);

    if (pipe_fd != -1) 
	{
            res = write(pipe_fd, buffer, sizeof(buffer));
            if (res == -1) 
			{
                fprintf(stderr, "Write error on pipe\n");
                exit(EXIT_FAILURE);
            }
		printf("write data is %s,%d bytes is wirte\n",buffer,res);
        (void)close(pipe_fd); 
    }
    else 
        exit(EXIT_FAILURE);        
    printf("Process %d finished\n", getpid());
    exit(EXIT_SUCCESS);
}

fifo_read.c

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

#define FIFO_NAME "/tmp/my_fifo"

int main(int argc,char *argv[])
{
    int pipe_fd;
    int res;
    char buffer[4096];
    int bytes_read = 0;

    memset(buffer, '\0', sizeof(buffer));
    
    printf("Process %d opening FIFO O_RDONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, O_RDONLY);
    printf("the file's descriptor is %d\n",pipe_fd);

    if (pipe_fd != -1) 
	{
            bytes_read = read(pipe_fd, buffer, sizeof(buffer));
			printf("the read data is %s\n",buffer);
        	close(pipe_fd);
    }
    else 
        exit(EXIT_FAILURE);
    printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
    exit(EXIT_SUCCESS);
}

system v ipc

ipcs

  • key:通信双方约定好的KEY值,32位的整形数据
  • id :系统会为IPC分配一个唯一的ID,通信双方都需要知道ID才能使用当前IPC方法,创建者会返回当前ID值,
 	#include <sys/types.h>
  	#include <sys/ipc.h>
	key_t ftok(const char *pathname, int proj_id);
	
    .  proj_id 
    key 31-24 proj_id 低8位
	key 23-16 st_dev属性的低8位
	key 15-0  st_ino属性的低16
  • 对象
  • 拥有者及权限对象
struct ipc_perm
{
	__kernel_key_t	key;   
	__kernel_uid_t	uid;
	__kernel_gid_t	gid;
	__kernel_uid_t	cuid;
	__kernel_gid_t	cgid;
	__kernel_mode_t	 mode; 
	unsigned short	seq;
};

system v ipc

ipcs

  • key:通信双方约定好的KEY值,32位的整形数据
  • id :系统会为IPC分配一个唯一的ID,通信双方都需要知道ID才能使用当前IPC方法,创建者会返回当前ID值,
 	#include <sys/types.h>
  	#include <sys/ipc.h>
	key_t ftok(const char *pathname, int proj_id);
	
    .  proj_id 
    key 31-24 proj_id 低8位
	key 23-16 st_dev属性的低8位
	key 15-0  st_ino属性的低16
  • 对象
  • 拥有者及权限对象
struct ipc_perm
{
	__kernel_key_t	key;   
	__kernel_uid_t	uid;
	__kernel_gid_t	gid;
	__kernel_uid_t	cuid;
	__kernel_gid_t	cgid;
	__kernel_mode_t	 mode; 
	unsigned short	seq;
};

XSI消息队列

  • 对象:
    struct msqid_ds
	struct msqid_ds {
	   struct ipc_perm msg_perm;     /* Ownership and permissions */
	   time_t          msg_stime;    /* Time of last msgsnd(2) */
	   time_t          msg_rtime;    /* Time of last msgrcv(2) */
           time_t          msg_ctime;    /* Time of last change */
	   unsigned long   __msg_cbytes; /* Current number of bytes in
	                                    queue (nonstandard) */
	   msgqnum_t       msg_qnum;     /* Current number of messages
	                                    in queue */
	   msglen_t        msg_qbytes;   /* Maximum number of bytes
	                                    allowed in queue */
	   pid_t           msg_lspid;    /* PID of last msgsnd(2) */
	   pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
	   struct msg *    msq_first ;
 	   struct msg *    msq_last ;
	};

在这里插入图片描述

优点和缺点

优点:
1. 消息队列是两个不相关进程之间传递数据的有效渠道.
2. 与命名管道相比,具有的优势是:
独立于发送以及接收进程而存在;
消除命名管道的打开以及关闭而存在的困难;
可以提前查看紧急信息;
避免命名管道的同步以及阻塞问题;
缺点:
与命名管道一样,每个数据块都有一个最大长度限制;
系统中所有队列包含的全部数据块长度也有一个上限;

  • 创建对象:
    int msgget(key_t key, int msgflg);

   int msgget(key_t key, int msgflg);
   

key_t key
int msgflg 确定消息队列的访问权限,

IPC_CREAT  找到当前的key对应的IPC,如果没有IPC则进行创建
IPC_EXCL   如果存在则返回失败
IPC_NOWAIT  返回值为当前ipc对应的id值 
  • 设置对象:
   int msgctl(int msqid,int cmd,struct msqid_ds *buf);
	cmd:
		IPC_RMID  删除 
		IPC_SET  设置ipc_perm参数 
		IPC_STAT 获取ipc_perm参数
		IPC_INFO 获取ipc信息  和ipcs
  • 使用对象:

    1. 发送
         int msgsnd(int msqid,const void *msg_ptr,size_t sz,int msgflg);
        struct msgbuf 
      {
         long mtype;       /* message type, must be > 0 */
         char mtext[1];    /* message data */
        };
    
    1. 接收
       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                      int msgflg);

msgtyp 0 默认接收第一条消息
msgtyp > 0 接收第一条msgtyp消息
msgtyp < 0 接收小于msgtyp绝对值的消息

早期的版本,现在根据Ubuntu的版本不一样可能会有出入
msgmni 最大消息队列数 16
msgmax 最大消息长度(字节数) 8192
msgmnb 消息队列中的最大字节数 16384

demo

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<string.h>
#include<sys/msg.h>
#define BUFSIZE 128
struct msg_buf
{
	long type;
	char msg[BUFSIZE];
};

int main(int argc,char *argv[])
{
	key_t key;
	int msgid;
	struct msg_buf msg_snd,msg_rcv;
	struct msginfo buf;
	char *ptr="helloworld";

	memset(&msg_snd,'\0',sizeof(struct msg_buf));
	memset(&msg_rcv,'\0',sizeof(struct msg_buf));

	msg_rcv.type=1;

	msg_snd.type=1;
	memcpy(msg_snd.msg,ptr,strlen(ptr));
	if((key=ftok(".",'A'))==-1)
	{
		perror("ftok");
		exit(EXIT_FAILURE);
	}

	if((msgid=msgget(key,0600|IPC_CREAT))==-1)
	{
		perror("msgget");
		exit(EXIT_FAILURE);
	}
	printf("msgsnd_return=%d\n",msgsnd(msgid,(void *)&msg_snd,strlen(msg_snd.msg),0));
	
	msgctl(msgid,MSG_INFO,&buf);
	printf("buf.msgmax=%d\n",buf.msgmax);
	printf("buf.msgmnb=%d\n",buf.msgmnb);
	printf("buf.msgpool=%d\n",buf.msgpool);
	printf("buf.semmap=%d\n",buf.msgmap);
	printf("buf.msgmni=%d\n",buf.msgmni);
	printf("buf.msgssz=%d\n",buf.msgssz);
	printf("buf.msgtql=%d\n",buf.msgtql);
	printf("buf.msgseg=%u\n",buf.msgseg);
	
	printf("msgrcv_return=%d\n",msgrcv(msgid,(void *)&msg_rcv,BUFSIZE,msg_rcv.type,0));
	printf("rev msg:%s\n",msg_rcv.msg);
	printf("msgctl_return=%d\n",msgctl(msgid,IPC_RMID,0));
}

XSI信号量

概念:

  • 实现进程间同步
  • 表示系统可用资源的个数

对象:

struct semid_ds {
	struct ipc_perm sem_perm;  /* Ownership and permissions */
	time_t          sem_otime; /* Last semop time */
	time_t          sem_ctime; /* Last change time */
	unsigned long   sem_nsems; /* No. of semaphores in set */
	struct   sem *	sem_base 
};
struct sem{
	int semval  信号量的值
	int sempid  最近一个操作的进程号
}
struct ipc_perm
{
	__kernel_key_t	key;   
	__kernel_uid_t	uid;
	__kernel_gid_t	gid;
	__kernel_uid_t	cuid;
	__kernel_gid_t	cgid;
	__kernel_mode_t	 mode; 
	unsigned short	seq;
};

对象操作

  • 创建对象:
 int semget(key_t k,int n,int semflag);
  • 设置对象:
 int semctl(int sid,int semnum,int cmd,union semun a);

	
	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) */
	};

cmd 如果对于整个信号量集合,则有如下设置方法
IPC_STAT ipc_perm
IPC_SET ipc_perm
IPC_RMID
IPC_INFO 信号量的系统信息

int semctl(int sid,int semnum,int cmd,…);

cmd 如果对于单个信号量集合,则有如下设置方法
GETPID 返回操作IPC的PID
GETVAL 返回操作IPC的VAL
GETALL返回操作IPC的所有值
SETVAL 设置操作IPC的VAL
SETALL 设置操作IPC的所有值

  • 使用对象
int semop(int s,struct sembuf *sb,size _t n);

struct sembuf{
unsigned short int sem_num; 
short int sem_op; 
short int sem_flg;};

sem_op

+ 表示进行加
- 表示进行减

0 表示测试是否为0

  • 如果当前信号量的操作后的值为负数,则进行阻塞,一直到满足条件为止

sem_flg:

IPC_NOWAIT 如果操作信号集合中任意一个失败,立即返回,并且不会对其他的信号量做操作
SEM_UNDO :进程退出后,该进程对sem进行的操作将被撤销


struct sembuf sops[4]
sops[0].sem_num  =1
sops[0].sem_op   =-1			/ P操作/
sops[0].sem_flag  = 0
sops[1].sem_num   =2
sops[1].sem_op    =1		/ V操作/
sops[1].sem_flag  =0

demo

生产者:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
int sem_id;

void init()
{
	key_t key;
	int ret;
	unsigned short sem_array[2];
	union semun 
	{
		int val;
		struct semid_ds *buf;
		unsigned short *array;
	}arg;

	key=ftok(".",'s');

	sem_id=semget(key,2,IPC_CREAT|0644);

	sem_array[0]=0;				//identify the productor
	sem_array[1]=100;			//identify the space
	//printf("set the productor init value is 0\nset the space init value is 100\n");
	arg.array = sem_array;
	ret = semctl(sem_id, 0, SETALL, arg);	
	if (ret == -1) 
		printf("SETALL failed (%d)\n", errno); 
	//printf("\nread the number\n");
	printf("productor init is %d\n",semctl(sem_id,0,GETVAL));
	printf("space init is %d\n\n",semctl(sem_id,1,GETVAL));

}
void del()
{
	semctl(sem_id,IPC_RMID,0);
}
#define produce   1 
#define customer  0
#define P   1 
#define V   0
int main(int argc,char *argv[])
{
	struct sembuf sops[2];
	
	sops[0].sem_num = 0;
	sops[0].sem_op = 1;
	sops[0].sem_flg = 0;

	sops[1].sem_num = 1;
	sops[1].sem_op = -1;
	sops[1].sem_flg = 0;

	init();
	

	printf("this is productor\n");
	while(1)
	{
		printf("\n\nbefore produce:\n");
		printf("productor number is %d\n",semctl(sem_id,0,GETVAL));
		printf("space number is %d\n",semctl(sem_id,1,GETVAL));
		
		//生产者的操作
		semop(sem_id,(struct sembuf *)&sops[P],produce);		//get the space to instore the productor
		printf("now producing......\n");
		semop(sem_id,(struct sembuf *)&sops[V],customer);		//now tell the customer can bu cusume
		printf("\nafter produce\n");
		printf("spaces number is %d\n",semctl(sem_id,1,GETVAL));
		printf("productor number is %d\n",semctl(sem_id,0,GETVAL));
		sleep(4);
	}
	del();
}

消费者:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
int sem_id;

void init()
{
	key_t key;

	key=ftok(".",'s');
	sem_id=semget(key,2,IPC_CREAT|0644);
	//printf("sem id is %d\n",sem_id);
}
#define produce   1 
#define customer  0
#define P   1 
#define V   0
int main(int argc,char *argv[])
{

	init();

	struct sembuf sops[2];
	
	sops[0].sem_num = 0;
	sops[0].sem_op = 1;
	sops[0].sem_flg = 0;

	sops[1].sem_num = 1;
	sops[1].sem_op = -1;
	sops[1].sem_flg = 0;


	printf("this is customer\n");
	while(1)
	{
		printf("\n\nbefore consume:\n");
		printf("productor is %d\n",semctl(sem_id,0,GETVAL));
		printf("space  is %d\n",semctl(sem_id,1,GETVAL));
		
		semop(sem_id,(struct sembuf *)&sops[P],customer);		//get the productor to cusume
		printf("now consuming......\n");
		semop(sem_id,(struct sembuf *)&sops[V],produce);		//now tell the productor can bu produce
		
		printf("\nafter consume\n");
		printf("products number is %d\n",semctl(sem_id,0,GETVAL));
		printf("space number is %d\n",semctl(sem_id,1,GETVAL));
		sleep(3);
	}
}

共享内存

在这里插入图片描述

创建共享内存

int shmget(key_t key, size_t size, int shmflg);
共享内存的权限
SHM_R 读权限
SHM_W 写权限

设置共享内存

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
SHM_INFO

 struct shm_info {
     int           used_ids; /* # of currently existing
                                segments */
     unsigned long shm_tot;  /* Total number of shared
                                memory pages */
     unsigned long shm_rss;  /* # of resident shared
                                memory pages */
     unsigned long shm_swp;  /* # of swapped shared
                                memory pages */
     unsigned long swap_attempts;
                             /* Unused since Linux 2.4 */
     unsigned long swap_successes;
                             /* Unused since Linux 2.4 */
 };

SHM_STAT 共享内存状态
SHM_LOCK 锁定共享内存
SHM_UNLOCK 解锁共享内存

使用对象

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

SHM_RDONLY
SHM_REMAP
SHM_EXEC

int shmdt(const void *shmaddr);

注意

1.父子进程的共享内存约定
fork函数
exec执行一个新的程序 自动卸载
exit

demo

共享内存和信号量结合的一个例子

recv

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

#include "shmemory.h"

int main(int argc,char *argv[])
{
	int running=1;
	char *shm_p=NULL;
	int shmid;	
	int semid;
	int value;

	struct sembuf  sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_flg = SEM_UNDO;
				
	if((semid=semget((key_t)123456,1,0666|IPC_CREAT))==-1)
	{
		perror("semget");
		exit(EXIT_FAILURE);
	}
	shmid=shmget((key_t)654321,(size_t)2048,0600|IPC_CREAT);
	if(shmid==-1)
	{
		perror("shmget");
		exit(EXIT_FAILURE);
	}
	shm_p=shmat(shmid,NULL,0);
	if(shm_p==NULL)
	{
		perror("shmat");
		exit(EXIT_FAILURE);
	}
	
	while(running)
	{
		if((value=semctl( semid, 0, GETVAL ))==1)
		{
			printf("read data operate\n");
			sem_b.sem_op = -1;
			if (semop(semid, &sem_b, 1) == -1) 
			{
				fprintf(stderr, "semaphore_p failed\n");
				exit(EXIT_FAILURE);
			}
			printf("%s\n",shm_p);
		}
		if(strcmp(shm_p,"end")==0)
			running--;
	}
	shmdt(shm_p);
	if(shmctl(shmid,IPC_RMID,0)!=0)
	{
		perror("shmctl");
		exit(EXIT_FAILURE);
	}

	if(semctl(semid,0,IPC_RMID,0)!=0)
	{
		perror("semctl");
		exit(EXIT_FAILURE);
	}
	return 0;
}

send


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>


int main(int argc,char *argv[])
{
	int running=1;
	int shid;
	int semid;
	int value;
	void *sharem=NULL;

	struct sembuf  sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_flg = SEM_UNDO;
	
	if((semid=semget((key_t)123456,1,0666|IPC_CREAT))==-1)
	{
		perror("semget");
		exit(EXIT_FAILURE);
	}
	
	if (semctl(semid, 0, SETVAL, 0) == -1)
	{
		printf("sem init error");
        if(semctl(semid,0,IPC_RMID,0)!=0)
		{
			perror("semctl");
			exit(EXIT_FAILURE);
		}
	exit(EXIT_FAILURE);	
	}
	shid=shmget((key_t)654321,(size_t)2048,0600|IPC_CREAT);
	if(shid==-1)
	{
		perror("shmget");
		exit(EXIT_FAILURE);
	}

	
	sharem=shmat(shid,NULL,0);
	if(sharem==NULL)
	{
		perror("shmat");
		exit(EXIT_FAILURE);
	}
	
	while(running)
	{
		if((value=semctl( semid, 0, GETVAL ))==0)
		{	
			printf("write data operate\n");
			printf("please input something:");
			scanf("%s",sharem);
			sem_b.sem_op = 1;
			if (semop(semid, &sem_b, 1) == -1) 
			{
				fprintf(stderr, "semaphore_p failed\n");
				exit(EXIT_FAILURE);
			}
		}
		if(strcmp(sharem,"end")==0)
			running--;
	}
		shmdt(sharem);
		return 0;
}

域间套接字(socketpair)

demo

#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
void err_sys(const char *errmsg);
//父进程与子进程的通信
int main(void)
{
		int sockfd[2];
		pid_t pid;
		if ((socketpair(AF_LOCAL, SOCK_STREAM,0, sockfd))<0)
		    err_sys("socketpair");
		if ((pid = fork()) == -1)
	    	err_sys("fork");
    //子进程
		else if (pid == 0)
		{
        // use sockfd[0]
              char s[1000];
        		ssize_t n;
        	    close(sockfd[1]); //write port
        		if ((n = read(sockfd[0], s, sizeof(s))) <0)
        		err_sys("read error!\n");
        		printf("read:%s\n",s);
        		close(sockfd[0]);
        		exit(0);
		}
    //父进程
		else if (pid > 0)
		{ /* parent process */
        		char buf[] = "hello china";
        		ssize_t n;
        		close(sockfd[0]); //read port
        		if((n = write(sockfd[1], buf, sizeof(buf)))<0)
        		err_sys("write error!\n");
        		close(sockfd[1]);
        		wait(NULL);
		}
		return 0;
}
void err_sys(const char *errmsg)
{
		perror(errmsg);
		exit(1);
}

猜你喜欢

转载自blog.csdn.net/weixin_45309916/article/details/107743249