操作系统--进程间通信(一)

原文引用https://www.dazhuanlan.com/2019/08/26/5d632d2a07f58/

IPC 运用范畴

配图与本文无关,纯属有趣

相对于做点什么,我们更想睡懒觉,但是相对于睡觉,我想写总结!
今天把博客的 git 分支搞混了,浪费了一点时间。那么今天介绍一下进程间通信(IPC) 。进程间通信,我们从 << UNIX网络编程卷2:进程间通信 >> 最后一章 Sun RPC 谈起,那为什么呢? 因为我们去掌握一个东西,肯定是因为它有价值,我们才去学习,如果一些技术现在都不用了,淘汰了,那我们就没有必要去深究,只需学习其思想,浅尝辄止即可。

最后一张概述部分是这样介绍的,构筑一个应用进程时,我们首先在以下两者之间作出选择:

(1) 构筑一个庞大的单一进程,完成全部工作;
(2) 把整个应用进程散步到彼此通信的多个进程中。
如果我们选择后者,接下去的抉择是:

这本著作主要关注的是 2a 这种情况,也就是 使用消息传递共享内存、并可能使用某种形式的同步来进行同一台主机上的进程间 IPC。同一进程内不同线程间的 IPC 以及不同进程内各个线程间的 IPC只是这种情形的特殊情况。

而对于不同部分之间需要网络通信的应用进程则大多数使用 显示网络编程explicit network programming)方式编写,也就是我们现在流行的 Socket编程。

那么现在是什么情况呢? 由于互联网大火,导致现在主流热门的进程间通信方式变成了使用 Socket 套接字通信。这个也可以理解,由于互联网就是传统的 C/S(服务器端-客户端)架构,对于客户端开发来说,主要就是移动应用开发、PC端应用开发、Web 浏览器开发,这些是互联网主要的流量入口,应用和浏览器需要通信的对象就是服务器,服务器要做的也是和客户端更好的进行通信,所以如果需要从事互联网的工作,那么对 Socket 编程相对于其他 IPC 手段就需要更熟悉,特别是服务器后台开发,客户端还好,客户端业务逻辑和 UI 体验相对重要,至于一些系统层技术,开发框架都已经封装的很好了,只需掌握使用方法就可以,像androidIOS开发。但是如果你是从事系统开发,那么所有的 IPC 方式都需要了解,特别是消息传递共享内存需要深入了解。

具体的 IPC 手段

信号

软件中断通知事件处理 例如:SIGFPE, SIGKILL, SIGUSR1, SIGSTOP, SIGCONT
接收到信号会发生什么:

  • Catch:指定信号处理函数被调用
  • Ignore:依靠操作系统的默认操作
    example:Abort,memory dump,suspend or resume process
  • Mask:闭塞信号,因此不会发生
    可能是暂时的(处理同样类型的信号)

不足:不能传输要交换的任何数据

特点:

  • signals are only delivered between processes when either: 仅在同一个用户的进程间传递,或者超级用户发出信号

    1. the processes involved are run by the same user
    2. the raising process is run by a superuser
  • signal types and type names are defined in signal.h 定义在 signal.h 文档
  • signals are not queued 信号没有排队
  • handlers “terminate process” (SIG_DFL) and “ignore signal” (SIG_IGN) are already defined in signal.h 常用终止进程和忽略信号定义在 signal.h

unix 主要接口信息:

12345678910

functions:void (int sig_type)description:用信号sig_type发信号。

functions:void signal(int sig_type, void (*sig_handler)(int signal_type))description:sig_type是signal.h中定义的信号值(或名称)之一。 sig_handles是一个指向带有int参数的函数的指针。当进程被指定类型的信号命中时,此函数将在进程上下文中运行。

functions:int kill(pid_t dest_pid, int sig_type)description:向使用PID dest_pid运行的进程发送sig_type类型的信号。 dest_pid的特定类型0和-1 make kill()分别发送与发送方的同一组执行的所有进程,并发信号通知系统中运行的所有进程(仅适用于超级用户凭据)。

管道和FIFO

1.管道,通常指无名管道,是 UNIX 系统IPC最古老的形式
特点:

  • pipes are uni-directional 半双工
  • with file descriptors, they can only be shared between processes with parent-child relationship 只能用于父子进程间
  • atomicity is guaranteed for messages shorter than PIPE_BUF bytes 特定大小缓存保持原子性
  • alf-open pipes are told widowed (or broken) pipes. Writing on them causes a write error, reading from them always returns 0 bytes. 坏的管道,会出现写错误,读取时,返回0字节.

unix 主要接口信息:

1234
#include: unistd.hfunctions: int pipe(int *fd_couple)  description:创建一个管道并将其文档描述符存储到fd_couple [0](读取结束)和fd_couple [1](写入结束)。 return: 成功返回 0,错误返回 -1

模拟使用场景,实现 Linux 系统调用 popen,主要代码:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
FILE * vpopen(const char * cmdstring, const char * type){    int pfd[2];    FILE * fp;    pid_t pid;

if ((type[0] != ‘r‘ && type[0] != ‘w‘) || type[1] != 0)    {        errno = EINVAL;        return (NULL);    }

if(pipe(pfd) != 0)    {        return NULL;    }

if((pid = vfork()) < 0)     {        return NULL;    }    else if(pid == 0)     {        if(*type == ‘r‘)        {            close(pfd[0]);            if (pfd[1] != STDOUT_FILENO)            {                dup2(pfd[1], STDOUT_FILENO);                close(pfd[1]);            }        }        else         {            close(pfd[1]);            if (pfd[0] != STDIN_FILENO)            {                dup2(pfd[0], STDIN_FILENO);                close(pfd[0]);            }        }

if(execl("/bin/bash", "bash", "-c", cmdstring, (char *) 0) < 0)        {            return NULL;        }    }

wait(0); // 等子进程结束,回收子进程

if (*type == ‘r‘)    {        close(pfd[1]);        if ((fp = fdopen(pfd[0], type)) == NULL)        {            return (NULL);        }    }    else    {        close(pfd[0]);        if ((fp = fdopen(pfd[1], type)) == NULL)        {            return (NULL);        }    }

return (fp);}

2.有名管道,FIFO,
特点:

  • 每个FIFO有一个路径名与之关联,允许无亲缘关系进程间的通信
  • 拥有标准管道的属性和特征

unix 主要接口信息:

1234
*  #include: sys/types.h and sys/stat.h*  functions: int mkfifo(char *path, mode_t mode) *  description:path是要创建的FIFO文档的(路径+)名称。 mode是文档权限(请参阅umask(2)和chmod(2))*  return: 成功返回 0,错误返回非 0

模拟使用场景:

1234567
* 模拟场景*  1. 在 shell 端使用命令创建有名管道: mkfifo /tmp/named_pipe*  2. 运行该接口*  3. 在 shell 端写入数据到管道 :echo "hey, this is a message" > /tmp/named_pipe*  4. 测试完毕,删除管道: rm /tmp/named_pipe* *  当然我们也可以自己启动一个任意进程向FIFO管道里面写数据

具体实现代码:

123456789101112131415161718192021222324252627282930313233343536373839404142
int fifo_read(){    int pipe;    char ch;    static char TmpCh;

/* we expect a named pipe has been created     * in /tmp/named_pipe . The command     *   $ mkfifo /tmp/named_pipe     * is a friend to get to this point     */

pipe = open("/tmp/named_pipe", O_RDONLY);    if (pipe < 0) {        printf("open fifo error, maybe you should create a fifo first !n");        exit(1);    }

/* preparing to read from the pipe... */    printf("Waiting data from the pipe... n");

/* reading one char a time from the pipe */    while (1) {        if (read(pipe, &ch, 1) < 0) {            printf("Read the pipe failed! n");            exit(2);        }

if(ch != TmpCh)        {            printf("%c", ch);        }

TmpCh = ch;    }

/* leaving the pipe */    close(pipe);

return 0;}

总结

上述主要对IPC做了一个大体的介绍,以及介绍了信号(signal)和管道两种IPC方式,这两者在Linux系统自带功能实现中用的较多。至于 PosixSystem V的进程间通信方式 消息队列信号共享内存,由于内容较多后面再单独介绍。

原文地址:https://www.cnblogs.com/petewell/p/11410494.html

时间: 2024-12-11 11:05:15

操作系统--进程间通信(一)的相关文章

操作系统--进程间通信

(1) 共享内存:通常由一个进程创建,其余进程对这块内存区进行读写.共享内存区域是被多个进程共享的一部分物理内存.如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信.共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容. (2) 管道:管道是单向的.先进先出的.无结构的.固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起.写进程在管

反应堆模式

反应堆模式 he Reactor:An Object-Oriented Wrapper for Event-Driven Port Monitoring and Service Demultiplexing 反应堆模式:一种应用于事件驱动的端口监控和服务多路化的面向对象封装器 Douglas C. Schmidt An earlier version of this paper appeared in the February 1993 issue of the C++ Report. 这篇文章

反应堆模式最牛的那篇论文--由solidmango执笔翻译

The Reactor:An Object-Oriented Wrapper for Event-Driven Port Monitoring and Service Demultiplexing 反应堆模式:一种应用于事件驱动的端口监控和服务多路化的面向对象封装器 Douglas C. Schmidt An earlier version of this paper appeared in the February 1993 issue of the C++ Report. 这篇文章的早期版本

Java面试题集

前几天,有朋友去面试之前问我关于后端架构相关的问题,但奈于我去年很多其它的工作是在移动SDK开发上,对此有所遗忘,实属无奈,后面准备总结下. 今天要谈的主题是关于求职.求职是在每一个技术人员的生涯中都要经历多次,对于我们大部分人而言,在进入自己心仪的公司之前少不了准备工作,有一份全面仔细面试题将帮助我们降低很多麻烦.在跳槽季来临之前,特地做这个系列的文章,一方面帮助自己巩固下基础,还有一方面也希望帮助想要换工作的朋友. 从12年開始,我先后做过爬虫,搜索,机器学习,javaEE及Android等

转自52 梦回凉亭的她 Java常见问题,面试题

收集整理分享 # 相关概念## 面向对象的三个特征封装,继承,多态.这个应该是人人皆知.有时候也会加上抽象. ## 多态的好处允许不同类对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用).主要有以下优点: 1. 可替换性:多态对已存在代码具有可替换性.2. 可扩充性:增加新的子类不影响已经存在的类结构.3. 接口性:多态是超类通过方法签名,向子类提供一个公共接口,由子类来完善或者重写它来实现的.4. 灵活性:5. 简化性: ### 代码中如何

java工程师面试常问的多线程问题【推荐】

思考题:1.说说进程,线程,协程之间的区别 思考题:希望大家积极的思考,并且可以踊跃的说出自己的想法,想法不管对与错,只要说出来就是一种提高,所以,希望小伙伴们可以把自己的想法在留言区给出,这样大家也可以相互学习,有启发的作用,扩展知识面,提高面试能力~ 2.你了解守护线程吗?它和非守护线程有什么区别 程序运行完毕,jvm会等待非守护线程完成后关闭,但是jvm不会等待守护线程.守护线程最典型的例子就是GC线程 3.什么是多线程上下文切换 多线程的上下文切换是指CPU控制权由一个已经正在运行的线程

bat等大公司常考java多线程面试题

1.说说进程,线程,协程之间的区别 简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程.进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高.线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位.同一进程中的多个线程之间可以并发执行. 2.你了解守护线程吗?它和非守护线程有什么区别 程序运行完毕,jvm会等待非守护线程完成后关闭,但是jvm不会等待守护线程.守护线程最典型的例子就是GC线

Keep面经汇总

一.Java 线程如何终止 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止. 使用stop方法强行终止线程. 使用interrupt方法中断线程. 如何用一个cancel方法停止两个线程 泛型原理.使用场景.优缺点 原理:泛型的实现是靠类型擦除技术,类型擦除是在编译期完成的,在编译期,编译器会将泛型的类型参数都擦除成它的限定类型,如果没有则擦除为object类型之后在获取的时候再强制类型转换为对应的类型. 使用场景:参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口

史上最全Java面试题(带全部答案)

今天要谈的主题是关于求职,求职是在每个技术人员的生涯中都要经历多次.对于我们大部分人而言,在进入自己心仪的公司之前少不了准备工作,有一份全面细致面试题将帮助我们减少许多麻烦.在跳槽季来临之前,特地做这个系列的文章,一方面帮助自己巩固下基础,另一方面也希望帮助想要换工作的朋友. 相关概念 面向对象的三个特征 封装,继承,多态,这个应该是人人皆知,有时候也会加上抽象. 多态的好处 允许不同类对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用).主要