简述几种进程间通讯方式。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_43730678/article/details/89061538

管道

(一)无名管道PIPE

 创建方式:
#include<unistd.h>
int pipe(int filedes[2]);
filedes[0]是读而打开,filedes[1]是写而打开

无名管道借助于文件系统,但不需要管道文件,利用父子进程共享fork()之前的文件描述符。
1.管道是半双工的,数据只能单向流动。
2.管道的只能在有亲缘关系的进制之间传输,比如父子,兄弟进程之间。
3.管道有一个读端和一个写端
调用pipe()函数之后调用fork()函数就创建了一个从父进程到子进程(或反向)的IPC通道。紧接着,因为管道是单项传输的,管道内数据的流输方向取决于我们对读端和写端的设置。对于父进程到子进程的管道,则父进程关闭管道的读端(fd[0]),子进程关闭管道的写端(fd[1]),子进程到父进程的管道则与之相反。

如图为父进程到子进程的管道 如图为父进程到子进程的管道
实例:

#include<stdio.h>
#include<unistd.h>

int main()
{
	int fd[2];
	pid_t pid;
	char buff[128];
	
	if(pipe(fd)<0)
	{
		printf("create pipe erro!");
	}
	if((pid=fork())<0)
	{
		printf("fork erro!");	
	}
	else if(pid>0)    //父进程
	{
		close(fd[0]);			//关闭读端
		write(fd[1],"Hello Hannibal!",16);
	}
	else              //子进程
	{	
		close(fd[1]);   //关闭写端
		read(fd[0],buff,16);
		printf("%s\n",buff);
	}
	return 0;
}

(二)有名(命名)管道FIFO

创建方式:
#inlcude<sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);

在磁盘上有一个管道文件标识文件,但是并不占用磁盘空间,一旦mkfifo创建好一个FIFO后,我们就可以用open打开它。有名管道与管道不同,他没有只能在亲缘进程之间传输的限制,因为每个FIFO都有一个路径名与之相关联,从而允许无亲缘关系的任意两个进程间通过FIFO进行通信。
有名管道的读端与写端必须同时打开。
实例:

fifo_read.c

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

int main()
{
	char buff[128] = {0};

	int fd = mkfifo("fifo.tmp", 0664);//创建有名管道
	if (-1 == fd)
	{
		perror("mkfifo");
		exit(1);
	}
	fd = open("fifo.tmp", O_RDONLY);//只读方式打开文件fifo.tmp
	if (-1 == fd)
	{
		perror("open");
		exit(1);
	}
	while (1)
	{
		int res = read(fd, buff, 127);//从文件中读取数据
		if (-1 == res)
		{
			printf("read erro!");
			exit(1);
		}
		if (!strcmp(buff, "stop"))
		{
			break;
		}
		printf("read:%s\n", buff);
		memset(buff, 0, sizeof(buff));
	}

	close(fd);
	unlink("fifo.tmp");//在管道使用结束后删除文件fifo.tmp
	return 0;
}

fifo_write.c

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

int main()
{

	char buff[128] = {0};

	int fd = open("fifo.tmp", O_WRONLY);//以只写方式打开文件fifo.tmp
	while (1)
	{
		scanf("%s", buff);

		int ret = write(fd, buff, strlen(buff));//写入数据到文件
		if (-1 == ret)
		{
			perror("write");
			exit(1);
		}
		if (!strcmp(buff, "bye"))//输入bye结束写入
		{
			break;
		}
		memset(buff, 0, sizeof(buff));
	}

	close(fd);
	return 0;
}

先运行read再打开新终端运行write

信号量(semaphore)

 创建方式:
 #include<sys/sem.h>
 int semget(key_t key,int nsem,int flag);

信号量是个计数器记录临界资源的个数,用于多进程对共享数据的访问。
进程访问临界资源时控制,用来实现进程的同步控制
nsem是集合内的信号量数,如果是创建新的集合,就必须指定nsem的大小。如果引用一个现存的集合nsem指定为0;
P 操作:表示通过,信号量减一,表示占用了临界资源

V 操作:表示释放,信号量加一,释放临界资源。
实例:

sem.h
#ifndef __SEM_H
#define __SEM_H
typedef union SemUn
{
	int val;
}SemUn;

int SemGet(int key, int semVal, int nsems);
int SemP(int semid, int index[], int sems);
int SemV(int semid, int index[], int sems);
int SemDel(int semid);

#endif



sem.c
#include"sem.h"
#include<sys/sem.h>
#include<malloc.h>
#include<assert.h>
#include<string.h>

int SemGet(int key, int semVal[], int nsems)
{
	int semid = semget((key_t)key, nsems, 0664);     //创建新的共享存储段
	assert(semid != -1);
	for (int i = 0; i < nsems; ++i)
	{
		SemUn un;
		un.val = semVal[i];
		semctl(semid, i, SETVAL, un);
	}
	return semid;
}

int SemP(int semid, int index[], int sems)
{
	struct sembuf* buf = (struct sembuf*)malloc(sizeof(struct sembuf) * sems);
	assert(buf != nullptr);
	for (int i = 0; i < sems; ++i)
	{
		buf[i].sem_num = index[i];
		buf[i].sem_op = -1;                   //p操作
		buf[i].sem_flg = SEM_UNDO;
	}
	if (-1 == semop(semid, buf, sems))
	{
		free(buf);
		return 0;
	}
}
int SemV(int semid, int index[], int sems)
{
	struct sembuf* buf = (struct sembuf*)malloc(sizeof(struct sembuf) * sems);
	assert(buf != nullptr);
	for (int i = 0; i < sems; ++i)
	{
		buf[i].sem_num = index[i];
		buf[i].sem_op = 1;                    //V 操作
		buf[i].sem_flg = SEM_UNDO;
	}
	if (-1 == semop(semid, buf, sems))
	{
		free(buf);
		return 0;
	}
}
int SemDel(int semid)
{
	return semctl(semid, 0, IPC_RMID);
}

信号(signal)

信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身.

实例:

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

int fun()
{
	printf("hello world!");
	signal(SIGINT, SIG_DFL);
}

int main()
{
	pid_t n = fork();  //创建子进程
	signal(SIGINT, fun);		
	if (0 == n)
	{
		printf("child");
	}
	else
	{
		while (1)
		{
			printf("sleep 1s\n");
			sleep(1);
		}
	}
}

共享内存

共享内存允许两个或多个进程共享一定的存储区,因为数据不需要在进程之间复制,所以他是最快的IPC。
共享内存是临界资源,所以要通过信号量完成同步控制。

实例:

SHARE A
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string,h>
#include<assert.h>

#include<sys/shm.h>
#include "sem.h"

int main()
{
	int shmid = shmget((key_t)key1234, 128, IPC_CREAT | 0664);
	assert(shmid != -1);

	int semVal - 1;
	int semid = SemGet(1000, &semVal, 1);

	char* ptr = (char*)shmat(shmid, NULL, 0);
	asssert(ptr != (char*)-1);

	while (1)
	{
		int index = 0;
		SemP(semid, &index, 1);
		printf("please input:");
		fgets(ptr, 128, stdin);

		SemV(semid, &index, 1);
		if (strncmp(ptr, :end, 3) == 0)
		{
			break;
		}
	}
}

SHARE  B
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string,h>
#include<assert.h>

#include<sys/shm.h>
#include "sem.h"

int main()
{
	int shmid = shmget((key_t)key1234, 128, IPC_CREAT | 0664);
	assert(shmid != -1);

	int semVal - 1;
	int semid = SemGet(1000, &semVal, 1);

	char* ptr = (char*)shmat(shmid, NULL, 0);
	asssert(ptr != (char*)-1);

	while (1)
	{
		int index = 0;
		SemP(semid, &index, 1);
		
		if (strncmp(ptr, :end, 3) == 0)
		{
			break;
		}
		printf("lenth=%d:%s", strlen(ptr) - 1, ptr);
		sleep(2);
		SemV(semid, &index, 1);
	}
	shmdt(ptr);
}

消息队列

消息队列是消息的链接表,存放在内核中并有消息队列标识符标识。

实例:

SEND MESSAGE.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string,h>
#include<assert.h>

#include<sys/msg.h>

typedef struct MsgData
{
	long mtType;
	char mText[128];
}MsgData;
int main()
{
	int msgid = msgget((key_t)1234, 0664 | IPC_CREATE);
	assert(-1 != msgid);

	MsgData data;
	memset(data.mText, 0, 127);
	data.mType = 100;
	strcpy(data.mText, "hello");

	msgsnd(msgid, &data, strlen(data.mText), 0);

	data.mtType = 200;			
	strcpy(data.mText, "world");

	msgsnd(msgid, &data, strlen(data.mText), 0);
	exit(0);
}

RECIEVE .C


#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string,h>
#include<assert.h>
#include<sys/msg.h>

typedef struct MsgData
{
	long mtType;
	char mText[128];
}MsgData;

int main()
{
	int msgid = msgget((key_t)1234, 0664 | IPC_CREATE);
	assert(-1 != msgid);

	MsgData data;
	msgrcv(msgid, &data, 127, 100, 0);//接受mType为100的队列消息
	printf("data.type=%d", data.mType);
	printf("data.text=%s", data.mText);
	exit(0);
}

猜你喜欢

转载自blog.csdn.net/weixin_43730678/article/details/89061538