进程间通信方式(IPC)

因为进程的独立性,所以进程间通信变得麻烦,但有时又需要进程之间的通信:数据传输、资源共享、通知事件(进程退出时需要告知父进程)、进程控制。

系统就提供了以下几种进程间通信的方式:

一:管道

                                

  1. 匿名管道,能在父、子进程间传递数据,一个进程创建一个管道,fork()子进程后,int pipe(int fd[2])创建管道。两个管道文件描述符(fd[0]和fd[1])都打开,因为管道是半双工的,所以一对这样的文件描述符只能保证一个方向的数据传输,要是先父进程向子进程写入数据。若要实现双向通信,就必须有两个管道。并且内核会对管道操作进行同步与互斥(一端操作的时候,另一端不能操作;并且当一方操作完成后,另一方才能操作)。
  2. 命名管道:没有关系的进程间通信,用 int mkfifo(const char *pathname, mode_t mode)。函数可以创建一个命名管道(特殊类型的文件),pathname是创建这个文件的名字,mode是这个文件的权限。命名管道与匿名管道的区别只有创建和打开的方式不同,在工作原理上是相同的。当两个进程通信后,会产生一个       文件,相当于一个缓冲区,两个进程对其进行(读/写)操作。 
  3. 这里是管道进行通信的demo        

二:消息队列

消息队列是在两个无关的进程间的传递数据的一种简单、高效的通信方式。消息队列是一种消息链表,由消息队列标识符组成,提供进程之间数据块传输的方法,用ipcs -q查看消息队列,ipcrm -q msgid来销毁。

下图是消息队列的结构:

 

消息队列和管道的区别:

  1. 进程结束后,用于通信的管道也就没有了;而消息队列还会存在(除了用IPC_RMID删除消息队列)。
  2. 管道是文件,在磁盘上,访问效率低;消息队列是数据结构,在内存中,访问效率高。
  3. 管道是流式存取,需要给出访问长度;而消息队列是块式存取。

消息队列的相关函数:

msgget:用来创建和访问一个消息队列
int msgget(key_t key, int msgflg);
    key: 某个消息队列的名字
    msgflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是⼀一样的
    返回值:成功返回一个非负整数,即该消息队列的标识码;失败返回-1

msgctl:对消息队列的控制
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    msqid: 由msgget函数返回的消息队列标识码
    cmd:是将要采取的动作,(有三个可取值)
           IPC_STAT:把msqid_ds中数据设为当前关联值
           IPC_RMID:删除消息队列 
    返回值:成功返回0,失败返回-1

msgsend:将消息添加到消息队列当中
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    msgid: 由msgget函数返回的消息队列标识码
    msgp:是一个指针,指针指向准备发送的消息,
    msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
    msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情
    msgflg=IPC_NOWAIT表⽰示队列满不等待,返回EAGAIN错误。
    返回值:成功返回0;失败返回-1

msgrcv:从消息队列中获取消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    msgid: 由msgget函数返回的消息队列标识码
    msgp:是一个指针,指针指向准备接收的消息,
    msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
    msgtype:它可以实现接收优先级的简单形式
    msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事
    返回值:成功返回实际放到接收缓冲区里去的字符个数,失败返回-1

三:共享内存

      创建一个共享内存区是最快的进程间通信方式效率高。不需要进行多次的数据拷贝,直接从内存中进行读写。指同一块物理内存通过页表被映射到A、B两个进程地址空间。两个进程可以相互看到一方对于共享内存中数据的更新,鉴于此,就必须有一种同步机制(互斥锁、信号量)使双方能够知道数据的修改,因为共享内存本身没有任何的同步与互斥机制,所以用到了信号量实现对共享内存的存取的同步。所以共享内存的效率高,直接对内存进行读写。两个进程通信完毕之前,共享内存一直保持着。Linux支持多种共享内存方式,mmap( )系统调用、Posix共享内存、SystemV共享内存

   共享内存被映射到进程空间中,存在于进程的数据段最大共享内存为0x2000 0000Byte。

      mmap()文件映射(文件存储映射IO):将文件映射到一段内存上(虚拟内存),通过对这段内存的修改,而达到对文件的修改,mmap系统调用可以是进程之间映射一个普通的文件来实现共享内存。内存映射是将用户的一段空间映射到内核空间,成功后,用户对这段内存的修改,可以直接反映到内核空间。从而达到用户和内核之间进行大量数据传输的效率是非常高的。mmap并不是完全为了共享内存,本身是提供了一种不同于一般对文件的访问方式(read、write),进程可以像读写内存一样对文件进行操作。

System V共享内存映射:

         

SystemV共享内存:以文件的形式组织在特殊文件系统shm中的。通过shmget可以创建或获得共享内存的标识符。取得共享内存标识符后,通过shmat将内存区映射到本进程的虚拟地址空间,可以通过mmap( )映射文件(将一个文件映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中的一段虚拟地址一一对应)

shmget:用来创建共享内存
int shmget(key_t key, size_t size, int shmflg);
    key:这个共享内存段名字
    size:共享内存大⼩小
    shmflg:由九个权限标志构成,它们的⽤用法和创建⽂文件时使⽤用的mode模式标志是一样的
    返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

shmat:将共享内存段连接到进程地址空间
void *shmat(int shmid, const void *shmaddr, int shmflg);
    shmid: 共享内存标识
    shmaddr:指定连接的地址
    shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
    返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

shmdt:将共享内存段与当前进程脱离(并不是删除共享内存)
int shmdt(const void *shmaddr);
    shmaddr: 由shmat所返回的指针
    返回值:成功返回0;失败返回-1
    
shmctl:用于控制共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    shmid:由shmget返回的共享内存标识码
    cmd:将要采取的动作(有三个可取值)
    buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
    返回值:成功返回0;失败返回-1


两者对比:

  • mmap是在磁盘上创建一个文件,每个进程都开辟一块空间进行映射。而相对于shm来说,其每个进程映射会映射到同一块物理内存,使读写速率快,但是不能存储太多。
  • 在机器重启之后,因为mmap将文件保存在磁盘中,所以不会丢失;但shm不能保证。
  • mmap更加简单,调用起来方便。 

四:信号量

       System V版本信号量也是IPC中的一种方法,相当于一个计数器。当多个进程同时访问系统上的某个资源时,需要考虑到进程的同步问题,确保一个时间只有一个只有一个进程拥有对该资源的访问权。

首先来了解一下进程相关的一些知识:

  • 临界资源:临界资源说的是一次只能提供一个进程使用的资源。
  • 互斥:是指某一个共享资源同时只允许一个访问者对其进行操作。
  • 同步:按照一定的访问顺序对共享资源操作。 
  • 原子性:一个事务包含多个操作,这些操作要么全部执行,要么全都不执行。若处理事务中途遇到突发状况为能完成事务,就要回滚到最初状态,保证原子性。
  • 锁:给临界区加锁,为了保护临界区,防止两个进程同时对其操作。
  • 并行:同一时刻多个任务同时进行(进程)
  • 并发:一段时间内,多个任务交替执行,不能同时处理(多线程)

     信号量是为了解决有多个进程想要对共享资源进程访问,为了保证访问的同步与互斥,引入了信号量来保证同一时刻只有一个进程对共享资源操作,信号量具有原子性。程序对其操作是原子性的额,进行P/V操作。

     信号量是由一个值和一个指针构成的,指针指向等待该信号量的进程,若为空,该信号量不被继承;值表示资源的使用情况,当值S>=0时,表示可用资源的个数,P操作是分配一个资源给某个进程使用,S值减1;当S<0时,其绝对值代表等待资源的进程个数,等待进程必须等待到有资源被释放时,被唤醒后才能继续运行,V操作是释放一个资源,S值加1。P/V操作必须成对出现。

     进程通常处于就绪、运行和阻塞三个状态,三者转换是靠P/V操作控制的。

信号量和信号量集的相关操作:   信号量集是可以同时把多个共享资源设置为互斥资源

semget:创建和访问一个信号量集
int semget(key_t key, int nsems, int semflg);
    key: 信号集的名字
    nsems:信号集中信号量的个数
    semflg: 由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
    返回值:成功返回一个非负整数,即该信号集的标识码;失败返回-1


semctl:用于控制信号量集
int semctl(int semid, int semnum, int cmd, ...);
    semid:由semget返回的信号集标识码
    semnum:信号集中信号量的序号,用semget的返回值接收所得
    cmd:将要采取的动作(有三个可取值):关联、删除
    返回值:成功返回0;失败返回-1

semop:用来创建和访问一个信号量集
int semop(int semid, struct sembuf *sops, unsigned nsops);
    semid:是该信号量的标识码,也就是semget函数的返回值
    sops:是个指向一个结构数值的指针
    nsops:信号量的个数
    返回值:成功返回0;失败返回-1

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值