热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

fork子进程shmget共享数据到父进程_理解进程间通信

进程是一个实体,两个实体间的通信就需要介质。使用不同的介质,就对应了不同的通信方式。进程的通信方式分为两种,同主机和不同主机。下面我们来逐

进程是一个实体,两个实体间的通信就需要介质。使用不同的介质,就对应了不同的通信方式。进程的通信方式分为两种,同主机和不同主机。下面我们来逐个分析。

1 匿名管道

匿名管道是进程间通信中比较简单的一种,他只用于有继承关系的进程,因为匿名,非继承关系的进程无法找到这个管道,也就无法完成通信,而有继承关系的进程,是通过fork出来的,父子进程可以获得得到管道。进一步来说,子进程可以使用继承于父进程的资源,但是他无法使用叔伯进程的资源。管道通信的原理如下。

c58b1f0eb33172d00979b2b8b3b7ce66.png

父子进程通过fork后,子进程继承了父进程的文件描述符。所以他们指向同一个数据结构。父子进程通常只需要单向通信,父子进程各关闭自己的一端。当父子进程对管道进程读写的时候,操作系统会控制这一切,包括数据的读取和写入,进程的挂起和唤醒。

2 命名管道

正如1中所说,匿名管道可以完成进程间通信,但是他有一定的限制,他的限制来自于他是匿名的,所有其他进程无法找到他,命名管理就是用来解决这个问题。有名字,进程们就可以通过名字去找到这个管道来通信。原理如下。

34fe597ace06598a8632c5efaa7808fb.png

首先创建一个文件名为my_fifo的文件,然后进程们以读或写的方式去打开这个文件(以什么方式打开则具有对应的能力)。因为一个文件对应一个inode,所以不同的文件以同样的文件名打开一个文件时,他指向的inode是一样的。所以这个inode就是进程间通信的介质。他指向一块内存用于通信。然后其他的就和匿名管道一样了。

3 消息队列

进程间通信的前提是需要共享介质,所以不同的进程间通信,就是找到不同的共享介质。消息队列的原理就是操作系统维护一块数据,然后各个进程通过key来换取一个id,后续通过id进行消息的存取。使用过程。

// 发送进程
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
msgsnd(msgid, (void *)&data, MAX_TEXT, 0)
// 接收进程
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0)

ff8069a576d9a53e02cd246c3e3d15d0.png

进程通过key可以获取或者创建(系统维护)一个msqid_ds结构体。然后操作系统会返回key对应的一个id。后续进程通过id对msqid_ds进行存取。msqid_ds是表示一个消息队列的管理者。各个进程使用一个msqid_ds进行通信。操作系统会有权限控制,大小控制等。

4 共享内存

共享内存的原理和消息队列类型,都是开辟一块内存作为通信的介质。
共享内存的使用步骤。

// 通过key拿到一个id,每个进程都通过同样的key则可以得到同样的id
id= shmget((key_t)key, ...);
/*挂载到进程的地址空间,我们知道进程的地址是使用vma管理的,这里就是插入一个新的vma,拿到共享内存的首地址address,接下来就可以对这块内存进行读写了。
*/
address = shmat(shmid, 0, 0);
// 操作
// 从进程地址空间剥离出来,即不再使用这个空间
shmdt(shm);

1 操作系统有一个全局的结构体数据,每次需要一块共享的内存时(shmget),从里面取一个结构体,记录相关的信息。并返回一个id。
2 调用shmat的时候传入shmget返回的id。shmat根据id找到对应的shmid_ds 结构体。新建一个vm_area_struct结构体。开始地址和结束地址根据shmid_ds 中的信息计算,也就是用户申请的大小。接着把vm_area_struct插入进程中管理vm_area_struct的avl树。并且把一些上下文信息保存到页表项。
3 进程访问共享内存范围中的地址时,触发缺页中断。
4 如果还没分配物理地址则分配,否则直接范围已经分配的地址。如果分配了物理地址,则把物理地址写入进程的页表项。下次就不会缺页中断了。
5 其他进程共享该块内存的时候,如果访问范围内的地址,处理过程是类似的。进程访问某一个地址,发生缺页中断,发现这时候共享内存已经映射了物理地址。最后改写自己的页表项。因为各个进程都对应同一块内存,所以操作的时候会互相感知,实现通信。

f87d25efeb17b15cab26dafe97b308bc.png

5 信号

信号通信是进程通信中最简单的一种,但是他所能携带的信息有限,他只是通知其他进程一个信号,而不能发送具体的数据。我们先看一下进程结构体中关于信号的字段。

struct task_struct {...// 收到的信号long signal;// 每个信号对应的处理函数和一些标记struct sigaction sigaction[32];// 当前屏蔽的信号long blocked;
};

当一个进程发送一个信号给另一个进程的时候,会调用kill函数。我们看看这个函数的逻辑。

int sys_kill(int pid,int sig)
{struct task_struct **p = NR_TASKS + task;int err, retval = 0;/*pid等于0则给当前进程的整个组发信号,大于0则给某个进程发信号,-1则给全部进程发,小于-1则给某个组发信号*/if (!pid) while (--p > &FIRST_TASK) {if (*p && (*p)->pgrp == current->pid) if (err=send_sig(sig,*p,1))retval = err;} else if (pid>0) while (--p > &FIRST_TASK) {if (*p && (*p)->pid == pid) if (err=send_sig(sig,*p,0))retval = err;} else if (pid == -1) while (--p > &FIRST_TASK)if (err = send_sig(sig,*p,0))retval = err;else while (--p > &FIRST_TASK)if (*p && (*p)->pgrp == -pid)if (err = send_sig(sig,*p,0))retval = err;return retval;
}/*发送信号给进程sig是发送的信号,p是接收信号的进程,priv是权限,1是代表可以直接设置,比如给自己发信息,priv为0说明需要一定的权限
*/
static inline int send_sig(long sig,struct task_struct * p,int priv)
{if (!p || sig<1 || sig>32)return -EINVAL;// 这里使用euid&#xff0c;即进程设置了suid位的话&#xff0c;可以扩大权限&#xff0c;即拥有文件属主的权限if (priv || (current->euid&#61;&#61;p->euid) || suser())p->signal |&#61; (1<<(sig-1));elsereturn -EPERM;return 0;
}

我们看到发送信号的逻辑很简单&#xff0c;收到判断要给哪些进程发送信号&#xff0c;然后判断有没有发送权限&#xff0c;最后修改另一些进程结构体中的signal字段。这就表示那些进程收到了信号。那么在某个时机&#xff0c;那些进程就会处理这个信号。这些时机包括&#xff0c;系统调用返回和时钟中断返回等。

6 socket

socket通信的原理比较简单&#xff0c;但是他的实现非常复杂&#xff0c;因为网络的情况比较多样复杂。socket通信的方式有基于连接和不基于连接的。不管是否基于连接&#xff0c;socket通信都是基于四元组&#xff08;源ip、源端口、目的ip、目的端口&#xff09;。
1 不基于连接&#xff0c;比如UDP&#xff0c;那两端就直接发送数据&#xff0c;对端接收就可以。
2 基于连接&#xff0c;基于连接的&#xff0c;流程会比较长&#xff0c;大概分为建立连接&#xff0c;通信&#xff0c;关闭连接三个步骤。
基于连接的进程间通信&#xff0c;首先需要有一个进程在监听某个端口&#xff08;监听型socket&#xff09;&#xff0c;我们叫他为服务进程。如果哪个进程想和这个服务进程通信&#xff0c;那么就要先和服务进程完成三次握手。完成后&#xff0c;服务进程会新建一个通信型socket和客户进程进行数据通信。服务进程继续监听。

7 unix域

unix域是基于socket通信的一个特例&#xff0c;因为他的实现中使用了socket技术&#xff0c;但是他是基于单个主机上的进程间通信。因为在同一个主机内&#xff0c;所以就少了很多网络上的问题&#xff0c;那就减少了复杂度。unix域和传统的socket通信类型&#xff0c;服务器监听&#xff0c;客户端连接&#xff0c;由于在同主机&#xff0c;就不必要使用ip和端口的方式&#xff0c;浪费一个端口。unix域采用的是一个文件作为标记。大致原理如下。
1 服务器首先拿到一个socket结构体&#xff0c;和一个unix域相关的unix_proto_data结构体。
2 服务器bind一个文件。对于操作系统来说&#xff0c;就是新建一个文件&#xff0c;然后把文件路径信息存在unix_proto_data中。
3 listen
4 客户端通过同样的文件路径调用connect去连接服务器。这时候客户端的结构体插入服务器的连接队列&#xff0c;等待处理。
5 服务器调用accept摘取队列的节点&#xff0c;然后新建一个通信socket进行通信。
unix域通信本质还是基于内存之间的通信&#xff0c;客户端和服务器都维护一块内存&#xff0c;然后实现全双工通信&#xff0c;而unix域的文件路径&#xff0c;只不过是为了让客户端进程可以找到服务端进程。而通过connect和accept让客户端和服务器对应的结构体关联起来&#xff0c;后续就可以互相往对方维护的内存里写东西了。就可以实现进程间通信。

1451a6d646906e80381b71534265df6e.png

8 mmap

mmap可以映射文件&#xff0c;从而达到进程间通信的目前&#xff0c;mmap的原理是
1 打开一个文件&#xff0c;拿到一个文件描述符。
2 根据mmap的参数&#xff0c;申请一个vma结构体。并且传入fd表示映射文件。
3 把vma插入到调用进程的vma链表和树中。返回首地址&#xff08;用户指定或者系统默认分配&#xff09;。
4 用户通过3中返回的地址&#xff0c;进行内存的读写&#xff0c;这时候对应的是文件的读写。
5 另一个进程同样执行1-4的步骤&#xff0c;即有两个进程都映射到同一个文件。两个进程进行读写的时候&#xff0c;就完成了进程间通信。

aba76c269889f545333d57d4c2aaa3fd.png



推荐阅读
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
author-avatar
手机用户2502870457
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有