IPC (管道、FIFO、消息队列、共享内存、信号、信号量)

IPC (管道、FIFO、消息队列、共享内存、信号、信号量)

进程间通信(IPC,InterProcess Communication)的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。

一、管道

管道是基于文件描述符的通信方式。当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1]。其中fd[0]固定用于读管道,而fd[1]固定用于写管道。
①半双工通信模式。
②只用于具有亲缘关系的进程之间的通信。如父子进程、兄弟进程、
③可看成是一种特殊的文件。

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
//int pipe(int fd[2]);
int main()
{
    
    
	int fd[2];
	int ret;
	char *str="Hellow word";
	char buf[30]={
    
    0};
	ret = pipe(fd);//创建管道
	if(ret == -1){
    
    
		printf("have failed\n");
		perror("pipe:");
		exit(-1);
	}
	pid_t pid = fork();//创建子进程
	if(pid < 0){
    
    
		perror("create child failed\n");
		exit(-1);
	}else if(pid > 0){
    
    
		perror("create child failed\n");
		close(fd[0]);
		write(fd[1],srt,strlen(str));//将str内容写入管道
		wait();
	}else{
    
    
		close(fd[1]);
		read(fd[0],buf,30);//将管道内容读到buf中去
		printf("read from parent:%s\n",buf);
		exit(0);
	}
	return 0;
}

二、FIFO

命名管道,又称为有名管道,它可以使不相关的进程实现彼此通信。
①命名管道(FIFO)是在文件系统中作为一个特殊的设备文件而存在的。
②不同祖先的进程之间可以通过管道共享数据。
③当共享管道的进程执行完所有的I/O操作以后,命名管道将继续保存在文件系统中以便以后使用。
读端

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

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

int main()
{
    
    	
	char buf[128];

	if(mkfifo("./file",0600) == -1 && errno != EEXIST){
    
    //创建一个文件名为file的管道
            perror("error\n");
        	exit(-1);
	}
      
	int fd = open("./file",O_RDONLY);//以只读方式打开file管道
	if(fd < 0){
    
    
		perror("open fifo:");
		exit(-1);
	}
	while(1){
    
    
	    read(fd,buf,128);
		printf("read from write.c: %s",buf);
		if(strstr(buf,"quit") != NULL)
                	break;
	}
	close(fd);
	return 0;
}

写端

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main()
{
    
    	
	char *str="Hellow word";
	int fd = open("./file",O_WRONLY);//只写方式打开file管道
	if(fd == -1){
    
    
		perror("fifo:");
		exit(-1);
	}
	while(1){
    
    
		sleep(1);
		write(fd,str,strlen(str));
	}
	close(fd);
	return 0;
}

三、消息队列

消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。

1、特点

(1)消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。

(2)消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。

(3)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

2、原型

#include <sys/msg.h>
// 创建或打开消息队列:成功返回队列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);

在以下两种情况下,msgget将创建一个新的消息队列:

(1)如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志位。
(2)key参数为IPC_PRIVATE。
函数msgrcv在读取消息队列时,type参数有下面几种情况:
(1)type == 0,返回队列中的第一个消息;
(2)type > 0,返回队列中消息类型为 type 的第一个消息;
(3)type < 0,返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息。
可以看出,type值非 0 时用于以非先进先出次序读消息。也可以把 type 看做优先级的权值。
msgrcv.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>


struct msg_form {
    
    
    long mtype;
    char mtext[256];// 消息结构
};
int main()
{
    
    
    int msqid;
    key_t key;
    struct msg_form msg;
    // 获取key值
    if((key = ftok(".",'z')) < 0)// 用于创建一个唯一的key
    {
    
     
        perror("ftok error");
        exit(1);
    }
    printf("Message Queue - Server key is: %x.\n", key);    //16进制方式打印key值
    if ((msqid = msgget(key, IPC_CREAT|0777)) == -1) // 创建消息队列
    {
    
    
        perror("msgget error");
        exit(1);
    }
    // 打印消息队列ID及进程ID
    printf("My msqid is: %d.\n", msqid);
    printf("My pid is: %d.\n", getpid());

    // 循环读取消息
    for(;;)
    {
    
    
        msgrcv(msqid, &msg, 256, 888, 0);// 返回类型为888的第一个消息、256代表读取的大小
        printf("Server: receive msg.mtext is: %s.\n", msg.mtext);
        printf("Server: receive msg.mtype is: %d.\n", msg.mtype);
        msg.mtype = 999; // 客户端接收的消息类型
        sprintf(msg.mtext, "hello, I'm server %d", getpid());//写入到msg.mtext中信息
        msgsnd(msqid, &msg, sizeof(msg.mtext), 0);//发送出去
    }
    return 0;
}

msgsnd.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
struct msg_form {
    
    
    long mtype;
    char mtext[256];// 消息结构
};
int main()
{
    
    
    int msqid;
    key_t key;
    struct msg_form msg;
    if ((key = ftok(MSG_FILE, 'z')) < 0)   // 获取key值
    {
    
    
        perror("ftok error");
        exit(1);
    }
    printf("Message Queue - Client key is: %x\n", key);    //16进制形式打印key值
    if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)    // 打开消息队列
    {
    
    
        perror("msgget error");
        exit(1);
    }
    printf("My msqid is: %d.\n", msqid);   // 打印消息队列ID及进程ID
    printf("My pid is: %d.\n", getpid());
    msg.mtype = 888; // 添加消息,类型为888
    sprintf(msg.mtext, "hello, I'm client %d", getpid());//写入到msg.mtext中信息
    msgsnd(msqid, &msg, sizeof(msg.mtext), 0);//发送出去
    msgrcv(msqid, &msg, 256, 999, 0);  // 读取类型为999的消息
    printf("Client: receive msg.mtext is: %s.\n", msg.mtext);
    printf("Client: receive msg.mtype is: %d.\n", msg.mtype);
    msgctl(msgqid,IPC_RMID,0);//移除消息队列
    return 0;
}

五、共享内存

共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。

1、特点

(1)共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。

(2)因为多个进程可以同时操作,所以需要进行同步。

(3)信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。

2、原型

#include <sys/shm.h>
// 创建或获取一个共享内存:成功返回共享内存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);

sever.c

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

int main()
{
    
    
	int shmid;
	char *shmaddr;
	key_t key;
   if ((key = ftok(".", 'z')) < 0)   // 获取key值
    {
    
    
        perror("ftok error");
        exit(1);
    }
	shmid = shmget(key,1024*4,IPC_CREAT|0666);//创建共享内存(创建的内存大小必须为兆的整数倍)
	if(shmid == -1){
    
    
		printf("create failed\n");
		exit(-1);
	}
	shmaddr = shmat(shmid,0,0);//连接共享内存
	strcpy(shmaddr,"helloha");//输入信息
	sleep(5);//休息五秒,等待接受信息
	shmdt(shmaddr);//断开连接
	shmctl(shmid,IPC_RMID,NULL);//卸载共享内存
	return 0;
}

client.c

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

int main()
{
    
    
	int shmid;
	char *shmaddr;
	key_t key;
	  if ((key = ftok(".", 'z')) < 0)   // 获取key值
    {
    
    
        perror("ftok error");
        exit(1);
    }
	shmid = shmget(key,1024*4,0);//获取共享内存
	shmaddr = shmat(shmid,0,0);//连接共享内存
	printf("context: %s\n",shmaddr);//打印共享内存信息
	shmdt(shmaddr);//断开连接
	return 0;
}

查看所有共享内存

ipcs -m

移除共享内存

ipcrm -m (key)

五、信号

信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
入门级
signal

#include <signal.h>
#include <stdio.h>
void handler(int signum)
{
    
    
	printf("get signum %d\n",signum);//打印捕获的信号
	switch(signum){
    
    
		case 2:
			printf("SIGINT\n");
			break;
		case 9:
			printf("SIGKILL\n");
			break;
	}
}
int main()
{
    
    
	printf("pid = %d\n",getpid());
	signal(SIGKILL,handler);//捕获SIGKILL信号到handler处理
	signal(SIGINT,handler);
	while(1);
	return 0;
}

高级
sigaction
接受信息

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

void handler(int signum,siginfo_t *info, void *context)//info包含很多信息,context表示有无信息传递过来
{
    
    
	printf("get signum %d\n",signum);
	if(context != NULL){
    
    
		printf("get data: %d\n",info->si_int);//指令信息
		printf("get data: %d\n",info->si_value.sival_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;//必须这样配置去获取信息
	sigaction(SIGUSR1,&act,NULL);//捕获信号SIGUSR1,null是代表要不要备份数据
	while(1);
	return 0;
}

sendsigaction
发送信息

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

int main(int argc, char **argv)
{
    
    
	int signum = atoi(argv[1]);//将命令字符串转换成数字
	int pid = atoi(argv[2]);//将pid字符串转换成数字
	
	union sigval value;
	value.sival_int = 100;//信息
	sigqueue(pid,signum,value);//发送信息
	printf("pid = %d done\n",getpid());
	return 0;
}

六、信号量

信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
(1)信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
(2)信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
(3)每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
支持信号量组。
无论何时,进程想要使用资源需要调用p操作或者wait操作,当进程使用完资源需要调用v操作或者signal操作。如果s变为0则则进程不得不执行wait等待操作直到信号量s的值变为正数。举个栗子,假设有p1,p2,p3,p4四个进程并且它们都对信号量s执行了P操作(信号量s的值初始化为4),如果另一个进程p5想要进入临界区想要使用资源,它需要等到p1,p2,p3,p4这四个进程中的任意一个执行v操作使得信号量s的值变为正数。

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

/*	创建或获取一个信号量组:int semget(key_t key, int num_sems, int sem_flags);
	对信号量组进行操作,改变信号量的值:int semop(int semid, struct sembuf semoparray[], size_t numops);  
	控制信号量的相关信息:int semctl(int semid, int sem_num, int cmd, ...);
*/

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

void pgetkey(int id)
{
    
    
	struct sembuf sops;
    sops.sem_num = 0;
    sops.sem_op = -1;//拿锁
    sops.sem_flg = SEM_UNDO;//等待
    semop(id, &sops, 1);
	printf("get key\n");
}

void vputkey(int id)
{
    
    
	struct sembuf sopss;
    sopss.sem_num = 0;
    sopss.sem_op = 1;//放回锁
    sopss.sem_flg = SEM_UNDO;
    semop(id, &sopss, 1);
//	printf("put key\n");
}

int main(int argc,char **argv)
{
    
    
	int semid;
	key_t key;
	key = ftok(".",6);
	semid = semget(key, 1, IPC_CREAT|0666);
	
	union semun initsem;
	initsem.val = 0;//锁的总量,运用PV操作使得其在0与正数之间变化
	semctl(semid,0,SETVAL,initsem);//获取创建信号量
	//'0'代表操作第一个信号量,和数组一样0表示第一个信号量
	//SETVAL设置信号量的值,设置为initsem
	int pid = fork();
	if(pid > 0){
    
    
		pgetkey(semid);//拿锁
		printf("this is father\n");
		vputkey(semid);//放锁
		semctl(semid,0,IPC_RMID);//摧毁锁
	}
	else if(pid == 0){
    
    
		printf("this is child\n");
		vputkey(semid);//放一把锁
	}else{
    
    
		printf("fork error\n");
	} 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_51478436/article/details/113863607
今日推荐