12-LINUX--进程间的通信

进程间通信:采用IPC机制(进程间的用户空间相互独立,内核空间共享),有管道,信号量,共享内存,消息队列,套接字。

一.管道

        管道可以用来在两个进程之间传递数据,如: ps -ef | grep “bash”, 其中‘|’就是管
道,其作用就是将 ps 命令的结果写入管道文件,然后 grep 再从管道文件中读出该数据进行
过滤。

1.1有名管道

        有名管道可以在任意两个进程之间通信
有名管道的创建:
        ◼ 命令创建: mkfifo FIFO
        ◼ 系统调用创建
#include <sys/types.h>
 #include <sys/stat.h>
 //filename 是管道名 mode 是创建的文件访问权限
 int mkfifo(const char *filename, mode_t mode);

a.c代码

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>

int main()
{
        int fd = open("./fifo",O_WRONLY);
        if(fd ==-1)
        {
                exit(1);
        }
        printf("fd=%d\n",fd);
        while(1)
        {
        printf("intput:\n");
        char buff[128] ={0};
        fgets(buff,128,stdin);
        if(strncmp(buff,"end",3) ==0)
        {
                break;
        }
        write(fd,buff,strlen(buff));
        }
        close(fd);
}
                                                                  

b.c代码

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>


int main()
{
        int fd = open("./fifo",O_RDONLY);
        printf("fd=%d\n",fd);
        if(fd ==-1)
        {
                exit(1);
        }
        while(1)
        {
        char buff[128];
        int n=read(fd,buff,127);
        if(n==0)
        {
                break;
        }
        printf("buff=%s\n",buff);
        }
        close(0);
        exit(0);
}
      

运行:

1.2无名管道

无名管道主要应用于父子进程间的通信。
无名管道的创建:
1. #include <unistd.h>
2. /*
3. pipe()成功返回 0,失败返回-1
4. fds[0]是管道读端的描述符
5. fds[1]是管道写端的描述符
6. */
7. int pipe(int fds[2]);
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
//无名管到
int main()
{
        int fd[2];//fd[0] r  fd[1]  w
        if(pipe(fd) == -1)
        {
                exit(1);
        }

        write(fd[1],"hello",5);
        char buff[128] ={0};
        read(fd[0],buff,127);
        printf("%s\n",buff);

        close(fd[0]);
        close(fd[1]);
        exit(0);
}

无名管道父子进程间的通信:

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

int main()
{
	int fd[2];//fd[0] r  fd[1]  w
	if(pipe(fd) == -1)
	{
		exit(1);
	}

	pid_t pid =fork();
	if(pid ==-1)
	{
		exit(1);
	}
	if(pid==0)
	{
		
		close(fd[1]);
		while(1)
		{
			char buff[128]={0};
			//read(fd[0],buff,127);
			if(read(fd[0],buff,127) == 0)
			{
				//printf("child read:%s\n",buff);
				break;
			}
			printf("chlid read:%s\n",buff);

		}
		close(fd[0]);
	}
	else
	{
	        close(fd[0]);
		while(1)
		{
			printf("input: ");
			char buff[128] ={0};
			fgets(buff,128,stdin);
			if(strncmp(buff,"end",3)==0)
			{
				break;
			}
			write(fd[1],buff,strlen(buff));
		}
		close(fd[1]);
	}
	exit(0);
}

1.3管道的特点

无论有名还是无名,写入管道的数据都在内存中
管道是一种半双工通信方式(通信方式有单工、半双工、全双工)
有名和无名管道的区别:有名可以在任意进程间使用,而无名主要在父子进程间

1.4管道的实现

二.信号量

由于父子进程间无法确定谁先谁后,所以运行结果会有顺序错乱的情况

提出使用信号量管理,使得父子进程间的通信变得有序

1.信号量

        信号量是一个特殊的变量,一般取正数值。它的值代表允许访问的资源数目,获取资源
时,需要对信号量的值进行原子减一,该操作被称为 P 操作。当信号量值为 0 时,代表没有
资源可用,P 操作会阻塞。释放资源时,需要对信号量的值进行原子加一,该操作被称为 V
操作。信号量主要用来同步进程。信号量的值如果只取 0,1,将其称为二值信号量。如果信
号量的值大于 1,则称之为计数信号量。
         临界资源:同一时刻,只允许被一个进程或线程访问的资源
        临界区:访问临界资源的代码段

2.信号量的使用

操作信号量的接口介绍:
 #include <sys/sem.h>
 #include <sys/types.h>
 #include <sys/ipc.h>
 /*
 semget()创建或者获取已存在的信号量
 semget()成功返回信号量的 ID, 失败返回-1
 key:两个进程使用相同的 key 值,就可以使用同一个信号量
 nsems:内核维护的是一个信号量集,在新建信号量时,其指定信号量集中信号
量的个数
 semflg 可选: IPC_CREAT IPC_EXCL
 */
 int semget(key_t key, int nsems, int semflg);

 /*
 semop()对信号量进行改变,做 P 操作或者 V 操作
 semop()成功返回 0,失败返回-1
 struct sembuf
 {
 unsigned short sem_num; //指定信号量集中的信号量下标
 short sem_op; //其值为-1,代表 P 操作,其值为 1,代表 V 操作
 short sem_flg; //SEM_UNDO
 };
 */
 int semop(int semid, struct sembuf *sops, unsigned nsops);
 /*
 semctl()控制信号量
 semctl()成功返回 0,失败返回-1
 cmd 选项: SETVAL IPC_RMID

 union semun
 {
 int val;
struct semid_ds *buf;
 unsigned short *array;
 struct seminfo *_buf;
 };
 */
int semctl(int semid, int semnum, int cmd, ...);
例题:进程 a 和进程 b 模拟访问打印机,进程 a 输出第一个字符‘a’表示开始使用打印
机,输出第二个字符‘a’表示结束使用,b 进程操作与 a 进程相同。(由于打印机同一时刻
只能被一个进程使用,所以输出结果不应该出现 abab),如图所示:

封装信号量的接口:

          sem.h 的代码如下:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/sem.h>

union semun
{
	int val;
};
void sem_init();
void sem_p();
void sem_v();
void sem_destroy();
sem.c 的代码如下:
#include "sem.h"

static int semid =-1;
void sem_init()
{
	semid =semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);
	if(semid == -1)
	{
		semid = semget((key_t)1234,1,IPC_CREAT|0600);//创建失败,说明已有,获取
		if(semid == -1)
		{
			printf("semget err\n");
			return;
		}
	}
	else//创建成功,初始化
	{
	 	union semun a;
		a.val =1;
		if(semctl(semid,0,SETVAL,a) == -1)
		{
			printf("semcrl setval err\n");
		}
	}
}
void sem_p()
{
	struct sembuf buf;
	buf.sem_num =0; //信号两的下标,目前只有一个,下标为0
	buf.sem_op =-1;//p
	buf.sem_flg = SEM_UNDO;

	if(semop(semid,&buf,1) == -1)
	{
		printf("semop p err\n");
	}
}
void sem_v()
{
	
	struct sembuf buf;
	buf.sem_num =0; //信号两的下标,目前只有一个,下标为0
	buf.sem_op =1;//v
	buf.sem_flg = SEM_UNDO;

	if(semop(semid,&buf,1) == -1)
	{
		printf("semop v err\n");
	}
}
void sem_destroy()//销毁信号量
{
	if(semctl(semid,0,IPC_RMID) == -1)
	{
		printf("semctl destroy err\n");
	}
}
a.c 的代码如下:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/sem.h>
#include"sem.h"
int main()
{
	sem_init();
	for(int i =0;i<5;i++)
	{
		sem_p();
		printf("A");
		fflush(stdout);
		int n = rand() %3;
		sleep(n);
		printf("A");
		fflush(stdout);;
		sem_v();
		n=rand() %3;
		sleep(n);
	}
}
//gcc -o a a.c sem.c
//gcc -o b b.c sem.c 

b.c代码:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/sem.h>
#include"sem.h"
int main()
{
	sem_init();
	for(int i =0;i<5;i++)
	{
		sem_p();
		printf("B");
		fflush(stdout);
		int n = rand() %3;
		sleep(n);
		printf("B");
		fflush(stdout);
		sem_v();
		n=rand() %3;
		sleep(n);
	}
	sleep(10);
	sem_destroy();
}
运行结果如下图所示,输出结果只截了部分:
练习题: 三个进程 a、b、c 分别输入“A”、“B”、“C”,要求输出结果必须是“ABCABCABC…”

3.ipcs/ipcrm 介绍

ipcs 可以查看消息队列、共享内存、信号量的使用情况,使用 ipcrm 可以进行删除操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

果蛋蛋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值