1.9 第三章 进程和线程间的通信day3

本文详细介绍了在Linux中使用消息队列、信号和共享内存进行进程间通信的方法,包括进程A和进程B之间的通信示例,以及线程同步和互斥的概念及其实现。同时讨论了fork()调用后的资源共享和僵尸进程的问题。
摘要由CSDN通过智能技术生成

思维导图

 1.使用消息队列完成两个进程之间的相互通信

进程A:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<header.h>
#define SIZE (sizeof(struct msgbuf)-sizeof(long))
struct msgbuf
{
	long mtype;    //消息类型
	char mtext_1[1024];//消息正文
	char mtext_2[1024];
};
int main(int argc, const char *argv[])
{	
	//创建key值
	key_t key=0;
	if((key=ftok("/",'a'))==-1)
	{
		perror("ftok error");
		return -1;
	}
	printf("key=%#x\t",key);

	//使用key值创建消息队列
	int msqid=0;
	if((msqid=msgget(key,IPC_CREAT|0664))==-1)
	{
		perror("msgget error");
		return -1;
	}printf("msqid=%d\n",msqid);
	
	//创建进程
	pid_t pid=fork();
	if(pid>0)
	{
		//父进程写入数据
		struct msgbuf buf={.mtype=100};
		while(1)
		{
			scanf(" %s",buf.mtext_1);
			msgsnd(msqid,&buf,SIZE,0);  //0表示阻塞
			
			if(strcmp(buf.mtext_1,"quit")==0)
				break;

			printf("发送成功\n");

		}
		exit(EXIT_SUCCESS);
	}else if(pid==0)
	{
		//子进程读取数据
		struct msgbuf buf={.mtype=200};
		while(1)
		{
			msgrcv(msqid,&buf,SIZE,200,0); //200指取mtype的值
									 //0表示阻塞
			if(strcmp(buf.mtext_2,"quit")==0)
				break;
			printf("收到了:%s\n",buf.mtext_2);
		}
		wait(NULL);

		//删除消息队列
		if(msgctl(msqid,IPC_RMID,NULL)==-1)
		{
			perror("msgctl error");
			return -1;
		}
	}else
	{
		perror("pid1 create error");
		return -1;
	}
	return 0;
}

进程B:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<header.h>
#define SIZE (sizeof(struct msgbuf)-sizeof(long))
struct msgbuf
{
	long mtype;    //消息类型
	char mtext_1[1024];//消息正文
	char mtext_2[1024];
};
int main(int argc, const char *argv[])
{	
	//创建key值
	key_t key=0;
	if((key=ftok("/",'a'))==-1)
	{
		perror("ftok error");
		return -1;
	}
	printf("key=%#x\t",key);

	//使用key值创建消息队列
	int msqid=0;
	if((msqid=msgget(key,IPC_CREAT|0664))==-1)
	{
		perror("msgget error");
		return -1;
	}printf("msqid=%d\n",msqid);
	
	//创建进程
	pid_t pid=fork();
	if(pid==0)
	{
		//子进程写入数据
		struct msgbuf buf={.mtype=200};
		while(1)
		{
			scanf(" %s",buf.mtext_2);
			msgsnd(msqid,&buf,SIZE,0);  //0表示阻塞
			
			if(strcmp(buf.mtext_2,"quit")==0)
				break;

			printf("发送成功\n");

		}
		exit(EXIT_SUCCESS);
	

	}else if(pid>0)
	{
		//父进程读取数据
		struct msgbuf buf={.mtype=100};
		while(1)
		{
			msgrcv(msqid,&buf,SIZE,100,0); //100指取mtype100的值
									 //0表示阻塞
			if(strcmp(buf.mtext_1,"quit")==0)
				break;
			printf("收到了:%s\n",buf.mtext_1);
		}
		wait(NULL);	
	}else
	{
		perror("pid1 create error");
		return -1;
	}
	return 0;
}

2.将信号通信相关代码重新实现一遍

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<header.h>
//定义信号处理函数
void handler(int signo)
{
	if(signo==SIGUSR1)
	{
		puts("父进程:逆子何至于此,那就跟你一起去了吧");
		raise(SIGKILL);
	}
}
int main(int argc, const char *argv[])
{
	//定义进程号
	pid_t pid=fork();
	if(pid>0)
	{
		//父进程
		//将SIGUSR1信号绑定
		if(signal(SIGUSR1,handler)==SIG_ERR)
		{
			perror("signal error");
			return -1;
		}
		while(1)
		{
			puts("父进程:我真的很想再活五百年");
			usleep(500000);
		}
	}
	else if(pid==0)
	{
		//子进程
		puts("子进程:人生得意须尽欢,莫使金樽空对月");
		sleep(3);
		puts("子进程:我看透了红尘,父亲和我一起去了吧");
		//向父进程发送一个信号
		kill(getppid(),SIGUSR1);
		//退出进程
		exit(EXIT_SUCCESS);
	}
	else
	{
		perror("fork error");
		return -1;
	}
	return 0;
}

3.将共享内存的相关代码重新实现一遍

shmsnd:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<header.h>
#define PAGE_SIZE 4096
int main(int argc, const char *argv[])
{
	//1.成key值
	key_t key=-1;
	if((key=ftok("/",'t'))==-1)
	{
		perror("ftok error");
		return =1;
	}
	printf("key=%#x\n",key);
	//2.将物理内存创建处共享内存段
	int shmid=0;
	if((shmid=shmget(key,PAGE_SIZE,IPC_CREAT))==-1)
	{
		perror("shmget error");
		return -1;
	}
	printf("shmid=%d\n",shmid);
	//3.将共享内存段地址映射到用户空间
	//NULL表示让系统自动选择页的分段
	//0表示当前进程对共享内存具有读写功能
	char *addr=(char *)shmat(shmid,NULL,0);
	if(addr==(void *)-1)
	{
		perror("shmat error");
		return -1;
	}
	printf("addr=%p\n",addr);;
	//4.操作共享映射
	//char buf[128]="";
	while(1)
	{
		fgets(addr,PAGE_SIZE,stdin);//从终端输入数据
		addr[strlen(addr)-1]='\0';
		if(strcmp(addr,"quit")==0)
			break;
	}
	//5.取消映射
	return 0;
}

 shmrcv:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<header.h>
#define PAGE_SIZE 4096
int main(int argc, const char *argv[])
{
	//1.成key值
	key_t key=-1;
	if((key=ftok("/",'t'))==-1)
	{
		perror("ftok error");
		return =1;
	}
	printf("key=%#x\n",key);
	//2.将物理内存创建处共享内存段
	int shmid=0;
	if((shmid=shmget(key,PAGE_SIZE,IPC_CREAT))==-1)
	{
		perror("shmget error");
		return -1;
	}
	printf("shmid=%d\n",shmid);
	//3.将共享内存段地址映射到用户空间
	//NULL表示让系统自动选择页的分段
	//0表示当前进程对共享内存具有读写功能
	char *addr=(char *)shmat(shmid,NULL,0);
	if(addr==(void *)-1)
	{
		perror("shmat error");
		return -1;
	}
	printf("addr=%p\n",addr);;
	//4.操作共享映射
	//char buf[128]="";
	while(1)
	{
		printf("共享内存中的数据为:%s\n",addr);
		sleep(1);
		addr[strlen(addr)-1]='\0';
		if(strcmp(addr,"quit")==0)
			break;
	}
	//5.取消映射
	if(shmdt(addr)==-1)
	{
		perror("shmdt error");
		return -1;
	}
	//6.删除共享内存
	if(shmctl(shmid,IPC_RMID,NULL)==-1)
	{
		perror("shmctl error");
		return -1;
	}
	return 0;
}

面试:

一.线程和进程的区别
不同:
1.进程是资源分配的最小单位,线程是执行任务的最小单位
2.进程分配4G的虚拟内存,0-3G独立的用户内存,3-4G共享的内核空间,线程占用8K内存
3.进程之间的资源相互独立;同一进程中的线程共享进程的资源,可能会产生资源抢占现象(竞态)
4.线程是进程的执行单元,一个进程中可包含多个线程
同:
1.都能实现多任务并发执行
2.都遵循时间片轮巡调度上下文切换

二.如何实现线程同步,举例说明
同步指多个线程有先后顺序的执行,同步是为了防止多任务并发执行时发生的竞态,保护临界资源
实现:无名信号量,条件变量。
无名信号量:本质是维护了一个value值,当每个线程要执行时,先申请该value值。如果申请成功,则value减1;如果申请时value为0,则在申请处阻塞,直到另一个线程将value值增加到大于0。

三.互斥的概念以及实现
互斥指多个线程同一时间只允许一个执行
实现:互斥锁,本质是一个临界资源,当某个线程抢到该资源后,其他线程只能处于等待状态,直到该线程释放了锁资源。

四.fork()调用后,父子进程的执行顺序?如何共享资源?
没有先后顺序。创建子进程后,子进程会继承父进程创建子进程之前的所有资源

五.什么是僵尸进程,如何避免
僵尸进程指子进程退出后,父进程没有为其回收资源
子进程退出时会发出SIGCHLD信号,父进程可以捕获该信号然后将该进程回收

六.linux支持的进程间的通信机制
七种:无名管道、有名管道、信号、消息队列、共享内存、信号灯集、套接字。

七.消息队列在进程间通信是如何工作的
允许一个或多个进程写入消息,并由一个或多个进程读取,该消息读取后就没有了。当程序关闭后,如果消息没有被读取,消息还存在。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值