进程间的通信相关的基础知识
进程间的通信
1. 互斥和同步
--互斥:各个进程之间共享资源,这些资源要排他使用,因此要竞争资源
--同步:各个进程之间为了完成同一个任务而相互协作。
2. 通信的目的
--数据传递
--资源共享
--通知事件
3. 发展历史
--管道
--system IPC
消息队列
共享内存
信息量
--POSIX IPC
互斥量
读写锁
4. 死锁:多个进程之间相互等待对方的资源,在等待对方时,不会释放自己的资源。
这样造成循环等待,所有进程都在等待一个不可能的进程时,进程就会死锁。
--产生的必要条件:
循环等待
互斥性
请求并保持
不可剥夺
--防止死锁的方法:
一次性分够(破坏请求并保持)
可剥夺(破坏不可剥夺)
资源有序分配(破坏循环等待)
--死锁避免
经典的死锁避免方法:银行家算法
(1)当顾客资金需求小于银行家现有的资金总量,接纳客户
(2)顾客分期贷款,贷款总额不能超过最大需求量
(3)当银行现有的资金不能满足顾客的需求,但总能在有限的时间内让顾客得到贷款
(4)当顾客使用完所有的资金,总能在有限的时间内归还所有资金。
--解决办法
哲学家就餐
5.信号量和pv操作
--pv操作
互斥:p和v 操作在同一个进程中
同步:p和v操作不在同一个进程中
--信号量值的含义:
S>0:表示有s个资源在使用
S=0:表示没有资源可以使用,也没有进程在该信号量上等待资源
S<0:表示有s个进程在等待该资源。
Struct semaphore{
Int value;
Struct task_struct *pstr;//等待该信号量的进程,处于等待状态。
};
p原语:
P(s)
{
s.value --;
If(s.value<0)
{
将整体置为等待状态
将该进程的task_struct插入到相信的队列
}
}
v原语:
v(s)
{
s.value++;
If(s.value<=0)
{
唤醒等待队列上的进程
将其改成就绪状态
放入就绪队列
}
6.管道(本质为内核缓存)
--一个进程到另一个进程的数据流
--管道的限制
(1)半双工(类似于对讲机)
(2)只能在有亲缘关系的进程之间使用
--创建管道:
int pipe (int fds[2]);
fd[0] 用来读文件,子进程来完成
fd[1]用来写文件,父进程来完成
Int fds[2];
Char buf[1024];
Pid_t pid=fork();
If(pid<0)
{
Pritnf(“error”);
}
//读取写入的管道中的数据
If(pid>0)
{
Close fds[1];//关闭写的文件数组
Close (0);
Dup(fds[0]);//进行读取后
Close(fds[0]);//父进程关闭掉fds[0]
Execlp(“wc”,”wc”,”-l”,null);
Printf(‘haha’);
}
//将数据写入管道中
Else
{
Close fds[0];//读端关闭
Close 1;
Dup(fds[1]);
}
案例:从键盘读取数据,写入管道,读取管道,写到屏幕(按照步骤一步步来实现)
int main(void)
{
int fds[2];
char buf[100];
int len;
if(pipe(fds)==-1)
perror("make pipe");
exit(1);
//read from stdin
while (fgets(buf,100,stdin)!=len)
{
len =strlen(buf);
//write into pipe
if(write(fds[1],buf,len)!=len)
{
perror("write into pipe");
break;
}
memset(buf,0x00,sizeof(buf));
//read from pipe
if((len=read(fds[0],buf,100))==--1)
{
perror(" read from pipe");
break;
}
//write to stdout
if(write(1,buf,len)!=len)
{
perror("write to stdout");
break;
}
}
}
7. 命名管道
是一种特殊类型的文件
--创建
(1)命令行# mkfifo filename
(2)函数 int mkfifo(const char*filenme,mode_t mode);
--与匿名管道的区别(只有创建与打开方式不同)
·匿名管道由pipe函数创建并打开。
·命名管道由mkfifo函数创建,打开用open
·案例:读取文件,写入命名管道
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main(int argc,char *argv[])
{
mkfifo("tp",0644);//用mkfifo创建
int infd;
int fd=open("abc",O_RDONLY);//用open打开
int outfd;
outfd=open("tp",O_WRONLY);
close(infd);
close(outfd);
}
8. 消息队列
(1) --一条消息最大是多大?
MSGMAX
--消息队列中所有消息的和最大是多大?
MSGMNB
--系能创建多少个消息队列?
MSGMNI
(2) --消息队列的创建
msgget 函数
int msgget(key_t key, //key相当于文件系统文件名,
int msgflag); //打开为0,成功返回标识码,失败返回-1
--查看消息队列
Ipcs -q
--删除消息队列
Ipcrm -Q key
--发送数据
Int msgsnd(int id, //msgget返回的id
Const void*msgp, //数据的起始地址
Size_t msgsz, //有效数据的大小,不包含通道号
Int msgflg); //0
--取出数据
msgrcv
--删除数据
Msgctl
案例:将数据由客户端1通过服务端传给客户端2(主要的目的是要知道使用消息队列的相关函数)
int createmsqueue();//创建队列
int getmsgqueue();//获取队列信息
int destroymsgqueue(int msgid);//清空缓存
int sendmsg(int msgid,int who,char *msg);//发送数据到服务端
7. 共享内存(共享内存,是最快的IPC)
(1)创建和打开共享内存
将共享内存挂载到自己的虚拟地址空间,就可以不用依赖于内核,直接调用函数
int shmget(key_t key,
size_t size, //想分配多大的内存空间
int shmflg); // IPC_CREAT|0644 0
int shmflg,//两个可能值
返回值:成功返回0,失败返回1
(1)控制共享内存
Int shmctl(int shimd,//由shmget返回的共享内存标识码
Int cmd,//将要采取的动作
Struct shimd_ds *buf);//指向保存着共享内存的模式状态和访问权限的数据结构,一般为0
返回值:成功返回0,失败返回-1