【操作系统-进程】进程间通信:管道

进程间通信

  • 操作系统底层工作的整体认识
    • CPU
      • CPU结构
      • CPU运行安全等级
    • 线程模型
      • 线程上下文切换:切换时会把执行结果保存到内存TSS(任务状态段)Task State Segment
    • 虚拟机指令集架构
    • 进程
      • 管道

操作系统底层工作的整体认识

【操作系统-进程】进程间通信:管道_第1张图片
-输入和输出设备:就是我们的磁盘的IO设备(如:键盘,U盘,显示器)
使用这些设备最终反馈的都是电信号,电信号被反馈到内部处理组件里面转成数字信号,所谓的数字信号就是我们的芯片,芯片集成了大量电路,电路通过高低压(高低电阻)判断电信号的开关(1和0),所以这就是我们的CPU为什么只认0101这种二进制的数据。
【操作系统-进程】进程间通信:管道_第2张图片

CPU

  • 控制单元 :指令计数器、指令寄存器、地址寄存器
  • 运算单元:ALU
  • 数据单元:存储单元(寄存器、CPU缓存):主要存指令、存数据…(要算的时候从内存中捞过来)

【操作系统-进程】进程间通信:管道_第3张图片
【操作系统-进程】进程间通信:管道_第4张图片

CPU结构

【操作系统-进程】进程间通信:管道_第5张图片

  • L3 cache是一个CPU上的多个内核(Core0和Core1)共享.
  • L1和L2是内核独享。
  • 还有一点值得注意:缓存是由最小的存储区块-缓存行(cacheline),缓存行大小通常为64byte
    在这里插入图片描述
    long(8字节)那么一个缓存行只能装64/8=8个long

【操作系统-进程】进程间通信:管道_第6张图片
先看一个空间局部性原则的例子:
【操作系统-进程】进程间通信:管道_第7张图片

  • 运行结果是第一种比第二种快
    【操作系统-进程】进程间通信:管道_第8张图片
  • 第一种是按行加,
    【操作系统-进程】进程间通信:管道_第9张图片
  • 把二维数组的一维(第一行)读取到cache缓存,只需要跟内存交互一次。
    【操作系统-进程】进程间通信:管道_第10张图片
    【操作系统-进程】进程间通信:管道_第11张图片

CPU运行安全等级

【操作系统-进程】进程间通信:管道_第12张图片

  • CPU在为java程序创建一个线程时(JAT),会从ring3切换到ring0,这里是状态切换

线程模型

  • 内核线程模型(KLT):线程的创建调度和销毁由OS完成
  • 用户线程模型(ULT):线程的创建调度和销毁由用户程序自己完成
    【操作系统-进程】进程间通信:管道_第13张图片
    在这里插入图片描述
  • 堆栈一般有两个:一个在用户空间,一个在内核空间
  • JVM运用的是KLT线程模型:java启动200个线程时,cpu的线程会立马+200
    【操作系统-进程】进程间通信:管道_第14张图片
    【操作系统-进程】进程间通信:管道_第15张图片

线程上下文切换:切换时会把执行结果保存到内存TSS(任务状态段)Task State Segment

【操作系统-进程】进程间通信:管道_第16张图片

虚拟机指令集架构

【操作系统-进程】进程间通信:管道_第17张图片

进程

每个进程的用户地址空间都是独立的,一般而言是不能互相访问的,但内核空间是每个进程都共享,所以进程之间要通信必须通过内核。
Linux内核提供了不少进程间通信的机制:

  • 管道
  • 消息队列
  • 共享内存
  • 信号量
  • 信号
  • Socket

管道

linux中的“|”竖线

$ ps auxf| grep mysql

上面命令行里的|竖线就是一个管道,它的功能是将一个命令(ps auxf)的输出,作为后一个命令(grep mysql)的输入,从这功能描述,可以看出管道传输数据是单向的
如果想互相通信,我们需要创建两个管道才行。

同时,|这种管道是没有名字,表示匿名管道,用完了就销毁。

所以相应地,管道还有另外一个类型是命名管道,也叫做FIFO,因为数据是先进先出的传输方式。
在使用命名管道前,先需要通过mkfifo命令来创建,并且指定管道名字:

$ mkfifo myPipe
//myPipe就是管道的名字

基于Linux一切皆文件的理念,所以管道也是文件的方式存在,我们可以用ls查看一下,这个文件的类型是p,也就是pipe(管道)的意思:

$ ls -l
pew-r--r--.
1 root root     0 Aug  11  02:45  myPipe

接下来,我们往myPipe这个管道写入数据:

$ echo "hello" > myPipe    //将数据写进管道
                           //停住了...

你会发现,操作后,就停在这了,这是因为管道里内容没有被读取,只有当管道里的数据被读取后,命令才可以正常退出???
于是我们在另一个终端执行另外一个命令来读取这个管道里的数据:

$ cat   <   myPipe     //读取管道里数据
hello

可以看到,管道里的内容被读取出来了,并打印在了终端上,另外一个方面,echo那个命令也正常退出了。

我们可以看出,管道这种通信方式效率低,不适合进程间频繁地交换数据。当然它的好处自然就是简单哈哈哈。同时我们也很容易得出管道里的数据已经被另外一个进程读取了!!!。完成了进程间的通信!!!

那么,上面讲了命名管道,下面讲讲匿名管道的创建,需要通过下面这个命令调用:

int pipe(int fd[2])

这里表示创建一个匿名管道,并返回了两个描述符,一个是管道的读取端描述符fd[0],另一个是管道的写入端描述符fd[1]。注意,这个匿名管道是特殊文件,只存在内存,不存在文件系统中。
【操作系统-进程】进程间通信:管道_第18张图片

其实,所谓的管道,就是内核里面的一串缓存
从管道的一端写入数据,实际上是缓存在内核中的,另一端的读取,也就是从内核中读取这段数据。
另外,管道传输的数据是无格式的流且大小受限

但是了解到这,我们不能忘了主题!!!进程间的通信!!!
所以我们这时要会质问:这两个描述符都是在一个进程里面,并没有起到进程间通信的作用,怎么样才能使得管道是跨过两个进程的呢???
我们可以使用fork创建子进程,woc创建的子进程会复制父进程的文件描述符,这样就做到了两个进程各有两个(fd[0]与fd[1]),两个进程就可以通过各自的fd写入和读取同一个管道文件!!!实现跨进程通信了。
【操作系统-进程】进程间通信:管道_第19张图片
但是,管道只能一端读取另一端写入,所以上面这种模式容易造成混乱,因为父进程和子进程都可以同时写入,也可以同时读取。那么为了避免这种情况,通常的做法是:

  • 父进程关闭读取的fd[0],只保留写入的fd[1];
  • 子进程关闭写入的fd[1],只保留读取的fd[0].
    【操作系统-进程】进程间通信:管道_第20张图片
    所以说如果要双向通信,则应该创建两个管道。

你可能感兴趣的:(操作系统,操作系统,shell,linux)