Presentation is loading. Please wait.

Presentation is loading. Please wait.

Linux 下进程间通信 管道 信号 信号量 共享内存 消息队列 套接字. 无名管道 管道是基于文件描述符的通信方式,当一个管 道建立时,它会创建两个文件描述符 fds[0] 和 fds[1] ,其中 fds[0] 固定用于读管道,而 fd[1] 固 定用于写管道,这样就构成了一个半双工的通 道。

Similar presentations


Presentation on theme: "Linux 下进程间通信 管道 信号 信号量 共享内存 消息队列 套接字. 无名管道 管道是基于文件描述符的通信方式,当一个管 道建立时,它会创建两个文件描述符 fds[0] 和 fds[1] ,其中 fds[0] 固定用于读管道,而 fd[1] 固 定用于写管道,这样就构成了一个半双工的通 道。"— Presentation transcript:

1 Linux 下进程间通信 管道 信号 信号量 共享内存 消息队列 套接字

2 无名管道 管道是基于文件描述符的通信方式,当一个管 道建立时,它会创建两个文件描述符 fds[0] 和 fds[1] ,其中 fds[0] 固定用于读管道,而 fd[1] 固 定用于写管道,这样就构成了一个半双工的通 道。 创建管道可以通过调用 pipe() 来实现。 管道关闭时只需使用普通的 close() 函数逐个关 闭各个文件描述符。

3

4 父子进程之间的管道通信( 1 ) 用 pipe() 函数创建的管道两端处于一个进程中, 由于管道是主要用于在不同进程间通信的,因 此这在实际应用中没有太大意义。实际上,通 常先是创建一个管道,再通过 fork() 函数创建 一子进程,该子进程会继承父进程所创建的管 道 。

5 父子进程之间的管道通信( 2 ) 父子进程分别拥有自己的读写通道,为了实现 父子进程之间的读写,只需把无关的读端或写 端的文件描述符关闭即可。此时,父子进程之 间就建立起了一条 “ 子进程写入父进程读取 ” 的 通道。

6 示例 阅读并运行示例。 阅读并运行示例

7 有名管道 ( 1 )

8 有名管道 ( 2 ) 在创建管道成功之后,就可以使用 open() 、 read() 和 write() 这些函数了。与普通文件的开 发设置一样,对于为读而打开的管道可在 open() 中设置 O_RDONLY ,对于为写而打开的 管道可在 open() 中设置 O_WRONLY ,在这里 与普通文件不同的是阻塞问题。 非阻塞标志可以在 open() 函数中设定为 O_NONBLOCK

9 有名管道 ( 3 ) 对于读进程  若该管道是阻塞打开,且当前 FIFO 内没有数据, 则对读进程而言将一直阻塞到有数据写入。  若该管道是非阻塞打开,则不论 FIFO 内是否有数据, 读进程都会立即执行读操作。即如果 FIFO 内没有数 据,则读函数将立刻返回 0 。 对于写进程  若该管道是阻塞打开,则写操作将一直阻塞到数据 可以被写入。  若该管道是非阻塞打开而不能写入全部数据,则写 操作进行部分写入或者调用失败。

10 示例  读管道示例 读管道示例 写管道

11 信号

12 信号是 UNIX 中所使用的进程通信的一种最古 老的方法。它是在软件层次上对中断机制的一 种模拟,是一种异步通信方式。信号可以直接 进行用户空间进程和内核进程之间的交互,内 核进程也可以利用它来通知用户空间进程发生 了哪些系统事件。它可以在任何时候发给某一 进程,而无需知道该进程的状态。如果该进程 当前并未处于执行态,则该信号就由内核保存 起来,直到该进程恢复执行再传递给它为止; 如果一个信号被进程设置为阻塞,则该信号的 传递被延迟,直到其阻塞被取消时才被传递给 进程。 不可靠信号和可靠信号

13 信号概述( 2 ) 一个不可靠信号的处理过程是这样的:如果发 现该信号已经在进程中注册,那么就忽略该信 号。因此,若前一个信号还未注销又产生了相 同的信号就会产生信号丢失。而当可靠信号发 送给一个进程时,不管该信号是否已经在进程 中注册,都会被再注册一次,因此信号就不会 丢失。所有可靠信号都支持排队,而所有不可 靠信号都不支持排队。

14 一个完整的信号生命周期可以分为 3 个重 要阶段,这 3 个阶段由 4 个重要事件来刻 画的:信号产生、信号在进程中注册、 信号在进程中注销、执行信号处理函数

15 信号概述( 3 ) 用户进程对信号的响应可以有 3 种方式。  忽略信号,即对信号不做任何处理,但是有 两个信号不能忽略,即 SIGKILL 及 SIGSTOP 。  捕捉信号,定义信号处理函数,当信号发生 时,执行相应的自定义处理函数。  执行缺省操作, Linux 对每种信号都规定了 默认操作。

16 信号概述( 3 )

17

18 信号发送与捕捉( 2 )

19 信号发送与捕捉( 1 ) kill() 函数同读者熟知的 kill 系统命令一样,可 以发送信号给进程或进程组(实际上, kill 系 统命令只是 kill() 函数的一个用户接口)。这里 需要注意的是,它不仅可以中止进程(实际上 发出 SIGKILL 信号),也可以向进程发送其他 信号。 与 kill() 函数所不同的是, raise() 函数允许进程 向自身发送信号。

20 示例 阅读并运行示例。 阅读并运行示例

21 信号发送与捕捉( 3 ) alarm() 也称为闹钟函数,它可以在进程中设置 一个定时器,当定时器指定的时间到时,它就 向进程发送 SIGALARM 信号。要注意的是, 一个进程只能有一个闹钟时间,如果在调用 alarm() 之前已设置过闹钟时间,则任何以前的 闹钟时间都被新值所代替。 pause() 函数是用于将调用进程挂起直至捕捉到 信号为止。这个函数很常用,通常可以用于判 断信号是否已到。

22

23 信号发送与捕捉( 4 ) 示例

24 signal() ( 1 ) 信号处理的主要方法有两种,一种是使用简单 的 signal() 函数,另一种是使用信号集函数组。

25 示例

26 struct sigaction { void (*sa_handler)(int signo); sigset_t sa_mask; int sa_flags; void (*sa_restore)(void); } sa_handler 是一个函数指针,指定 信号处理函数,这里除可以是用户 自定义的处理函数外,还可以为 SIG_DFL (采用缺省的处理方式) 或 SIG_IGN (忽略信号)。它的处 理函数只有一个参数,即信号值。 sa_mask 是一个信号集,它可以指 定在信号处理程序执行过程中哪些 信号应当被屏蔽,在调用信号捕获 函数之前,该信号集要加入到信号 的信号屏蔽字中。 sa_flags 中包含了许多标志位,是对 信号进行处理的各个选择项。

27 示例

28 信号集函数组 使用信号集函数组处理信号时涉及一系列的函数,这 些函数按照调用的先后次序可分为以下几大功能模块: 创建信号集合、注册信号处理函数以及检测信号。

29 信号集函数组

30 其中,创建信号集合主要用于处理用户感兴趣的一些 信号,其函数包括以下几个。  sigemptyset() :将信号集合初始化为空。  sigfillset() :将信号集合初始化为包含所有已定义的 信号的集合。  sigaddset() :将指定信号加入到信号集合中去。  sigdelset() :将指定信号从信号集合中删去。  sigismember() :查询指定信号是否在信号集合之中。

31 信号集函数组 ( 4 )

32 示例

33 Linux 下进程间通信 管道 信号 信号量 共享内存 消息队列 套接字

34 信号量概述 信号量是用来解决进程之间的同步与互斥问题 的一种进程之间通信机制,包括一个称为信号 量的变量和在该信号量下等待资源的进程等待 队列,以及对信号量进行的两个原子操作( PV 操作)。其中信号量对应于某一种资源,取一 个非负的整型值。信号量值指的是当前可用的 该资源的数量,若它等于 0 则意味着目前没有 可用的资源。

35 PV 原子操作的具体定义为: P 操作:如果有可用的资源(信号量值 >0 ), 则占用一个资源(给信号量值减去一,进入临 界区代码) ; 如果没有可用的资源(信号量值等 于 0 ),则被阻塞到,直到系统将资源分配给 该进程(进入等待队列,一直等到资源轮到该 进程)。 V 操作:如果在该信号量的等待队列中有进程 在等待资源,则唤醒一个阻塞进程。如果没有 进程等待它,则释放一个资源(给信号量值加 一)。

36 信号量概述( 3 ) 使用信号量访问临界区的伪代码所下所示: { /* 设 R 为某种资源, S 为资源 R 的信号量 */ INIT_VAL(S);/* 对信号量 S 进行初始化 */ 非临界区 ; P(S);/* 进行 P 操作 */ 临界区(使用资源 R ) ; /* 只有有限个(通常只有一个)进程被允许进入该区 */ V(S);/* 进行 V 操作 */ 非临界区 ; }

37 信号量的使用( 1 ) 第一步:创建信号量或获得在系统已存在的信号量, 此时需要调用 semget() 函数。不同进程通过使用同一个 信号量键值来获得同一个信号量。 第二步:初始化信号量,此时使用 semctl() 函数的 SETVAL 操作。当使用二维信号量时,通常将信号量 初始化为 1 。 第三步:进行信号量的 PV 操作,此时调用 semop() 函数。 这一步是实现进程之间的同步和互斥的核心工作部分。 第四步:如果不需要信号量,则从系统中删除它,此 时使用 semclt() 函数的 IPC_RMID 操作。此时需要注意, 在程序中不应该出现对已经被删除的信号量的操作。

38 信号量的使用( 2 )

39 信号量的使用( 3 )

40 信号量的使用( 4 )

41 示例 主程序示例示例 函数示例示例 头文件

42 Linux 下进程间通信 管道 信号 信号量 共享内存 消息队列 套接字

43 共享内存概述 共享内存是一种最为高效的进程间通信方式。 因为进程可以直接读写内存,不需要任何数据 的拷贝。为了在多个进程间交换信息,内核专 门留出了一块内存区。这段内存区可以由需要 访问的进程将其映射到自己的私有地址空间。 因此,进程就可以直接读写这一内存区而不需 要进行数据的拷贝,从而大大提高了效率。当 然,由于多个进程共享一段内存,因此也需要 依靠某种同步机制,如互斥锁和信号量等 。

44

45 共享内存的应用 ( 1 ) 共享内存的实现分为两个步骤, – 第一步是创建共享内存,这里用到的函数是 shmget() ,也就是从内存中获得一段共享内存区域。 – 第二步映射共享内存,也就是把这段创建的共享内 存映射到具体的进程空间中,这里使用的函数是 shmat() 。到这里,就可以使用这段共享内存了,也 就是可以使用不带缓冲的 I/O 读写命令对其进行操作。 除此之外,当然还有撤销映射的操作,其函数为 shmdt() 。

46 共享内存的应用 ( 2 )

47 共享内存的应用 ( 3 )

48 示例

49 Linux 下进程间通信 管道 信号 信号量 共享内存 消息队列 套接字

50 消息队列( 1 ) 消息队列就是一些消息的列表。用户可以从消息 队列中添加消息和读取消息等。从这点上看,消 息队列具有一定的 FIFO 特性,但是它可以实现消 息的随机查询,比 FIFO 具有更大的优势。同时, 这些消息又是存在于内核中的,由 “ 队列 ID” 来标 识。 消息队列的实现包括创建或打开消息队列、添加 消息、读取消息和控制消息队列这四种操作。其 中创建或打开消息队列使用的函数是 msgget() , 这里创建的消息队列的数量会受到系统消息队列 数量的限制;添加消息使用的函数是 msgsnd() 函 数,它把消息添加到已打开的消息队列末尾;读 取消息使用的函数是 msgrcv() ,它把消息从消息 队列中取走,与 FIFO 不同的是,这里可以指定取 走某一条消息;最后控制消息队列使用的函数是 msgctl() ,它可以完成多项功能。

51 消息队列( 2 )

52 消息队列( 3 )

53 消息队列( 4 )

54 消息队列( 5 )

55 示例 消息发送 消息接收


Download ppt "Linux 下进程间通信 管道 信号 信号量 共享内存 消息队列 套接字. 无名管道 管道是基于文件描述符的通信方式,当一个管 道建立时,它会创建两个文件描述符 fds[0] 和 fds[1] ,其中 fds[0] 固定用于读管道,而 fd[1] 固 定用于写管道,这样就构成了一个半双工的通 道。"

Similar presentations


Ads by Google