进程间的通信相关的基础知识

进程间的通信

1. 互斥和同步

--互斥:各个进程之间共享资源,这些资源要排他使用,因此要竞争资源

--同步:各个进程之间为了完成同一个任务而相互协作。

2. 通信的目的

--数据传递

--资源共享

--通知事件

3. 发展历史

--管道

--system IPC

  消息队列

  共享内存

  信息量

--POSIX IPC

  互斥量

  读写锁

  

4. 死锁:多个进程之间相互等待对方的资源,在等待对方时,不会释放自己的资源。

 这样造成循环等待,所有进程都在等待一个不可能的进程时,进程就会死锁。

--产生的必要条件:

  循环等待

  互斥性

  请求并保持

  不可剥夺

--防止死锁的方法:

  一次性分够(破坏请求并保持)

   可剥夺(破坏不可剥夺)

   资源有序分配(破坏循环等待)

--死锁避免

  经典的死锁避免方法:银行家算法

(1)当顾客资金需求小于银行家现有的资金总量,接纳客户

(2)顾客分期贷款,贷款总额不能超过最大需求量

(3)当银行现有的资金不能满足顾客的需求,但总能在有限的时间内让顾客得到贷款

(4)当顾客使用完所有的资金,总能在有限的时间内归还所有资金。

--解决办法

  哲学家就餐

  

5.信号量和pv操作

--pv操作

互斥:pv 操作在同一个进程中

同步:pv操作不在同一个进程中

 

--信号量值的含义:

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;

   }

}

}

在开始时,父进程创建了管道,父进程fork出子进程,父进程关闭fd[0],子进程关闭了fd[1]

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);//发送数据到服务端

  int recvmsg(int msgid,int recvtype,char out[]); //服务端接收并发给客户端

7. 共享内存(共享内存,是最快的IPC

(1)创建和打开共享内存

将共享内存挂载到自己的虚拟地址空间,就可以不用依赖于内核,直接调用函数

 int shmget(key_t key,

size_t size,    //想分配多大的内存空间

int shmflg);   // IPC_CREAT|0644    0

 

(2)挂载
 void shmat(int shmid,//共享内存标识
const void *shmaddr,//指定连接的地址,系统挂载,NULL

int shmflg,//两个可能值   

(3)删除共享内存段与当前进程脱离
int shmdt(connst void *shmaddr);//

返回值:成功返回0,失败返回1

(1)控制共享内存

 Int shmctl(int shimd,//shmget返回的共享内存标识码

Int cmd,//将要采取的动作

Struct shimd_ds *buf);//指向保存着共享内存的模式状态和访问权限的数据结构,一般为0

返回值:成功返回0,失败返回-1

 

 

猜你喜欢

转载自blog.csdn.net/Ning_zhi_t/article/details/80586422