进程间通讯

来源:互联网 发布:ubuntu卸载不了软件 编辑:程序博客网 时间:2024/06/04 01:09

进程间通讯:多个进程之间数据相互交换

进程间通讯的方式:

                                 信号

                                 管道

                                 信号量 

                                 消息队列

                                 共享存储(共享内存)

                                 套接字

在总结与对比之前先来说说我在学习的时候遇到的一些问题:

1.信号和信号量一样吗?

      当然不一样。信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动, 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。信号量是用来调协进程对共享资源的访问的。

2.信号量怎么传递数据?

      在刚开始学习的时候对于信号量一些操作很模糊,以为信号量也可以向管道那样发送数据,其实不是的。我们通常通过信号来解决多个进程对同一资源的访问竞争的问题,使在任一时刻只能有一个执行线程访问代码的临界区域,也可以说它是协调进程间的对同一资源的访问权,也就是用于同步进程的。: 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

3.信号量中什么时候用P操作,什么时用V操作?

     首先,在用PV操作之前肯定要用smget先创建或者获取信号量。想申请一个信号量时用P操作,想释放一个信号量时用V操作。另外信号量有几个封装函数在后续也会添加。

4.套接字和其他通讯机制有什么不同?

         socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。也因为这样,套接字明确地将客户端和服务器区分开来。本篇博客主要讲前几种通讯方式,套接字会在后续博客中更新。

5.浅拷贝和深拷贝

       简单的来说就是,在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存,而深拷贝就是增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存,采用深拷贝的情况下,释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误!

6.管道的默认大小是多少??能不能修改管道的大小?管道操作的内核实现。(空间的开辟, 读写操作偏移量的变化)

     这个问题在上篇博客中已经解决过了。

现在进入博客主题:

     1.首先来看管道

                1)管道分为有名管道(注意函数的阻塞运行)和无名管道:

                            有名管道:应用于任意两个进程之间数据的单向传递。(在文件目录树中有一个文件标示(管道文件)。实际不占据磁盘空间。数据缓存在内存上。)

                            无名管道:只能用于父子进程之间(因为:在使用时产生,不使用后释放。并不会在系统上留下任何蛛丝马迹。无名管道因其使用前没有任何的标示。)

                 2)操作:

                             有名管道:

                                                创建:      命令方式:mkfifo 函数方式: mkfifo();
                                                打开:      open
                                                写数据:   write
                                                读数据:   read
                                                关闭:      close

                              无名管道:

                                                   创建        int pipe(int fd[2]);     // fd[0] 读 fd[1] 写
                                                   打开         同上
                                                   读           read(fd[0], buff, size);
                                                   写           write(fd[1], buff, len);
                                                   关闭       close(fd[1]); close(fd[0]);

      2.其次来看信号量

                 1)首先要掌握几个概念

                                    临界资源(同一时刻,只能被一个进程访问的资源。)
                                    临界区(访问临界资源代码区域。)
                                    原子操作(任何情况下都不能被打断的操作。)
                                    内核对象(用于对进程间通讯时,多进程能够访问同一资源的记录。)

                  2)信号量的操作:
                                                     创建或获取: 如果是创建,必须初始化。如果获取,则不能初始化。
                                                     减一操作: P 操作
                                                     加一操作: V 操作
                                                     删除:    
                                                     sem_get(); // 获取信号量, 如果是第一次,则创建信号量并初始化。
                                                     sem_P(); // 执行 P 操作
                                                     sem_V(); // 执行 V 操作
                                                     sem_del(); // 删除信号量

       3.再来看消息队列

                   1)概念:

                                    消息: 数据 & 类型
                                    队列: 数据结构,先进先出
                                    消息队列: 是一种临时存储消息的队列,完成进程间数据传递, 优先级队列

                   2)特点:

                                   与信号量对比: 都以内核对象来确保多进程访问同一个消息队列,信号量进行进程同步控制,消息队列发送实际数据。
                                   与管道对比: 管道发送的数据没有类型,读取数据端无差别从管道中按照数据的前后顺序读取数据,消息队列数据有类型,读端可以根据数据类型读                                  取特定数据。

                    3)操作:

                                    创建或获取:  int msgget((key_t)key, int flag); flag: 权限 以及控制。IPC_CREAT: 如果存在,直接获取;如果不存在,则创建


                                    发送消息:      int msgsnd(int msgid, void *ptr, size_t size, int flag); ptr: ptr 指向一个结构体: 类型 + 数据            size: 数据的大小


                                    获取消息:       int msgrcv(int msgid, void *ptr, size_t size, long type, int flag);


                                      删除消息队列:int msgctl(int msgid, int cmd, struct msgid_ds * buff);

   4.最后看共享内存

                       1)特点:

                                      共享内存是最快的一种 IPC, 在各个进程都有指针直接指向开辟内存区域。访问时当做本进程中的一个内存控制直接操作。

                       2)操作:
                                      创建或者获取int shmget((key_t)key, size_t size, int flag);
                                                                             size: 开辟内存空间的大小,
                                                                              flag: IPC_CREAT 如果存在,则获取
                                                                              如果不存在,则创建


                                       链接:void * shmat(int shmid, void *addr, int flag);返回值一个指向共享内存首地址的指针


                                       断开链接:int shmdt(void *ptr);此函数只完成断开链接操作,并不会删除共享内存。

                                       删除内核对象:int shmctl(int shmid, int cmd , struct shmid_ds *buff);

                       


需要注意的问题:

   1. 管道都是半双工通讯,而无名管道创建后,父进程在 fork 产生子进程后,两个进程分别有一对读写,所以,要在父子进程分别关闭读或写。

   2.共享内存是两个以上的进程能够操作同一块物理空间的内存区域,所以共享的区域就成了临界资源,所以对于共享区域的访问必须做同步控制。(信号量)     

   3.进程之间的通信种类很多,刚开始学习时候比较乱,主要是不清楚每个函数的作用还有参数,要多练习。

扩展:

              1.管道的默认大小是多少??能不能修改管道的大小?管道操作的内核实现。(空间的开辟, 读写操作偏移量的变化)

                     见第一篇博客

               2.半同步/半异步模式

                       1) 引入:   

                                      同步模式编程简单,但是I/O的利用利率低;而异步模式编程复杂,但是I/O利用率高,所以就有了半同步半异步的设计模式。         

                       2)应用场景:半同步半异步模式在下面的场景中使用:

                              A)一个系统中的进程有下面的特征:
                                                                                   系统必须响应和处理外部异步发生的事件,如果为每一个外部资源的事件分派一个独立的线程同步处理I/O,效率很低。
                                                                                   如果上层的任务以同步方式处理I/O,实现起来简单。

                              B) 一个或多个任务必须在单独的控制线程中执行,其它任务可以在多线程中执行:
                                                                               上层的任务(如:数据库查询,文件传输)使用同步I/O模型,简化了编写并行程序的难度。
                                                                               而底层的任务(如网络控制器的中断处理)使用异步I/O模型,提供了执行效率。
                                                                               一般情况下,上层的任务要比下层的任务多,使用一个简单的层次实现异步处理的复杂性,可以对外隐藏异步处理的细节另                                                                          外,同步层次和异步层次任务间的通信使用一个队列来协调。




                        3)实现方案:可以分为三层:同步任务层,队列层,异步任务层。

                        4)优缺点