C语言-进程间通信
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
写操作返回1,errno设置为EPIPE 产生SIGPIPE信号
多个进程写一个管道,如果我们写的数据小于 PIPE_BUF 数据相互之间不会交叉;否则就可能交叉 利用管道同步进程 演示pipesyn
命名管道(FIFO)
命名管道和一般的无名管道基本相同,但也有一些显著的不 同: 命名管道是在文件系统中作为一个特殊的文件而存在的。 不同祖先的进程之间可以通过命名管道共享数据。 当共享管道的进程执行完所有的I / O操作以后,命名管 道将继续保存在文件系统中以便以后使用。 无名管道只能由相关进程使用,它们共同的祖先进程创 建了管道。但是,通过FIFO,不相关的进程也能交换数 据。
key_t ftok(const char *path, int id);
信号量的物理含义
信号量中的整型变量S表示系统中某类资 源的数目。 当其值大于0时,表示系统中当前可用资 源的数目; 当其值小于0时,其绝对值表示系统中因 请求该类资源而被阻塞的进程数目。
P操作
设S为一个信号量,P(S)执行时主要完成下 述动作:
S=S-1; if(S< 0) {设置进程状态为等待; 将进程放入信号量等待队列; 转调度程序;}
getpid()函数介绍 #include <sys/types.h> #include <unistd.h> pid_t getpid(); pid_t getppid();
进程复制fork()
产生进程的方式比较多,fork()是其中的一种方式。fork()函 数以父进程为蓝本复制一个进程,其ID号和父进程号不同。 在Linux环境中,fork()是以写复制方式实现的,只有内存等 与父进程不同,其它与父进程共享,只有在父进程或者子进 程进行了修改后,才重新生成一份。 fork()函数介绍 fork()的原型如下,当成功时,fork()函数的返回值是进程的 ID;失败则返回-1 #include <sys/types.h> #include <unistd.h> pid_t fork(void); fork()的特点是执行一次,返回两次。父进程和子进程中返回 的是不同的值。父进程返回的是子进程的ID号,而子进程返 回的是0。
P操作和V操作是不可中断的程序段,称为原语.P,V 原语中P是荷兰语Passeren,相当于英文的pass,V是荷 兰语的Verhoog,相当于increment。
信号量的定义
信号量由两个成员(s,q)组成,其中s是 一个具有非负初值的整型变量,q是一个初 始状态为空的队列。又称信号灯。 除信号量的初值外,信号量的值仅能由P操 作(又称为 wait 操作)和 V 操作(又称为 signal操作)改变。
V操作
V(S)执行时主要完成下述动作:
S=S+1; if(S≤0){将信号量等待队列中的第一个进程移出;
设置其状态为就绪状态并插入就绪队列; 然后再返回原进程继续执行;}
注意
P操作可能阻塞执行进程,而V操作可以 唤醒其他进程。 P、V操作在封锁中断的情况下执行,即 一个进程在信号量上操作时,不会有别 的进程同时修改该信号量。也就是说P、 V操作是原语。
信号量说明:信号量就是操作系统中所用到的PV原语,广泛用 于进程或线程间的同步与互斥。本质上是一个非负的整数计数器, 被用来控制对公共资源的访问。 PV原语是对整数计数器信号量sem的操作。一次P操作使sem 减一,而一次V操作使sem加一。进程或线程根据信号量的值 来判断是否对公共资源具有访问权限 当信号量sem的值大于等于零时,该进程或线程具有公共 资源的访问权限 相反,当信号量sem的值小于零时,该进程或线程就将阻 塞直到信号量sem的值大于等于0为止 PV原语主要用于进程或线程间的同步和互斥两种典型情况。 如用于互斥,几个进程或线程往往只设置一个信号量sem 当信号量用于同步操作时,往往会设置多个信号量,并安 排不同的初始值来实现他们之间的顺序执行
管道通信
管道有一些固有的局限性:
因为读数据的同时也将数据从管道移去,因 此,管道不能用来对多个接收者广播数据。 管道中的数据被当作字节流,因此无法识别 信息的边界。 如果一个管道有多个读进程,那么写进程不 能发送数据到指定的读进程。同样,如果有 多个写进程,那么没有办法判断是它们中那 一个发送的数据。
system()方式
system()函数调用shell的外部命令在当前进程中开始另一个进程。 system()函数介绍 system()函数调用”/bin/sh-c command”执行特定的命令,阻塞当前进程 直到command命令执行完毕。System()的原型如下: #include <stdlib.h> int system(const char * command); 执行system()函数时,会调用fork、execve、waitpid等函数,其中任意 一个调用失败将导致system()函数调用失败。System()函数的返回值如 下: 失败返回-1; 当sh不能执行时,返回127; 成功返回进程状态值;
进程间通信概述
进程间通信有如下一些目的: 数据传输:一个进程需要将它的数据发送给另一个进程。 共享数据:多个进程想要操作共享数据,一个进程对共 享数据的修改,别的进程应该立刻看到。 通知事件:一个进程需要向另一个或一组进程发送消息, 通知它(它们)发生了某种事件(如进程终止时要通知 父进程)。 资源共享的同步:多个进程之间共享同样的资源。为了 作到这一点,需要内核提供锁和同步机制。 进程控制:有些进程希望完全控制另一个进程的执行 (如Debug进程),此时控制进程希望能够拦截另一个 进程的所有陷入和异常,并能够及时知道它的状态改变。
同步与互斥
同步:多个相互合作的进程在一些关键点 上可能需要互相等待或互相交换信息,这 种相互制约关系称为进程同步。 互斥:当一个进程正在使用某资源时,其他 希望使用该资源的进程必须等待,当该进 程用完资源并释放后,才允许其他进程去 访问此资源,我们称进程之间的这种相互 制约关系为互斥。
信号量
C语言-进程间通信
上海****通信技术有限公司 Mr Jim(seniordba@sina.com) 2014-04
培训大纲
基础概念 进程产生的方式 进程间通信和同步 Linux下的线程
同步与互斥的概念
临界资源与临界区 临界资源:一段时间内仅允许一个进程 使用的资源称为临界资源。 如:打印机、共享变量。 临界区:进程中访问临界资源的那段代 码称为临界区,又称临界段。 同类临界区:所有与同一临界资源相关 联的临界区。
所有用户态进程的产生进程init
在Linux系统中,所有的进程都是由父子或者堂 兄关系的,没有哪个进程与其它进程完全独立。 除了初始进程init,系统中每个进程都有一个父 进程,新的进程不是被全新得创建,通常是从 一个原有的进程进行复制或者克隆的。
Linux操作系统下的每一个进程都有一个父进程 或者兄弟进程,并且有自己的子进Baidu Nhomakorabea.可以在 Linux下使用命令pstree来查看系统中运行的进 程之间的关系,如下所示.可以看出init进程是 所有进程的祖先,其他的进程都是由init进程直 接或者间接fork()出来的。
父进程
子进程 fd[0] fd[1]
fd[0] fd[1] fd[1]
fd[0]
管道 内核
管道 内核
单个进程中的管道
父进程
父子进程共享的管道
子进程 fd[0]
fd[1]
管道 内核
父子进程共享的管道
半双工管道
如果一个管道的写端口关闭了,读操作返回0 可以复制写描述符,让多个进程向同一个管道里面写 如果一个管道的读端口关闭了
管道
最早的一种进程间通信方式 半双工方式 只能在有共同祖先的进程之间使用,一般是父子进程 创建出管道后,调用fork产生新进程,在父子进程间使 用 shell命令中的管道:ls | grep “music”
管道通信
管道通信例如:
ps | grep vsftpd 管道是单向的、先进先出的、无结构的字节流,它 把一个进程的输出和另一个进程的输入连接在一起。 写进程在管道的尾端写入数据,读进程在管道的首端 读出数据。数据读出后将从管道中移走,其它读进程 都不能再读到这些数据。管道提供了简单的流控制机 制。进程试图读空管道时,在有数据写入管道前,进 程将一直阻塞。同样,管道已经满时,进程再试图写 管道,在其它进程从管道中移走数据之前,写进程将 一直阻塞。 管道主要用于不同进程间通信。
培训大纲
程序、进程和线程的概念 进程产生的方式 进程间通信和同步 Linux下的线程
进程号
每个进程在初始化的时候,系统都分配了一个 ID号,用于标识此进程。在Linux中,进程号 是唯一的,系统可以用这个值来表示一个进程, 描述进程的ID号通常叫做PID,即进程 ID(process id)。PID的变量类型是pid_t。
命名管道(FIFO)
在shell的两个管道之间传送数据
mkfifo fifo1 prog3 < fifo1 & prog1 < infile | tee fifo1 | prog2
客户端和服务器间互相传数据
IPC结构
消息队列、信号量和共享内存有很多共同之处 每一种结构都有一个内部的非负整数标识,从小到大 分配 外部使用key_t类型标识符 每创建一个结构体就指定一个key_t类型标识符 服务器使用IPC_PRIVATE 分配一个结构体,把返回的 标识符存在一个地方让客户端来了读取,或是父子进 程 服务器和客户端约定一个公共的标识 服务器和客户端约定一个路径(已存在的文件 )和 (0-255)的数来生成一个标识
半双工管道
#include <unistd.h> int pipe(int fd[2]); 功能:创建一个简单的管道,若成功则为数组fd分配两个文 件描述符,其中fd[0] 用于读取管道,fd[1] 用于写入管道。 返回值:成功返回0,失败返回- 1。
pipe调用成功后的状况
半双工管道
进程执行exec
在使用fork()函数和system()函数的时候,系统中都会建立 一个新的进程,执行调用者的操作,而原来的进程还会存在,直 到用户显示的退出; 而exec()函数与之前的fork()和system()函数不同,exec() 函数会用新进程代替原有的进程,系统会从新的进程运行,新的 进程的PID值会与原来进程的PID值相同. exec()函数,其原型如下: #include <unistd.h> int execve(const char *path,char *const argv[]); 与fork()函数不同,exec()函数执行成功后不会返回,这 是因为执行的新程序已经占用了当前进程的空间和资源,这 些资源包括代码段、数据段和堆栈段等,它们都已经被新的 内容取代,而进程的ID等标识性的信息仍然是原来的东西, 即exec()函数在原来进程的壳上运行了自己的程序,只有程序 调用失败了,系统才会返回-1.
培训大纲
程序、进程和线程的概念 进程产生的方式 进程间通信和同步 Linux下的线程
进程间通信概述
现在linux使用的进程间通信方式: (1)无名管道(pipe)和有名管道(FIFO) (2)消息队列 (3)共享内存 (4)信号量 (5)信号(signal) (6)套接字(socket)
创建命名管道
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char * pathname, mode_t mode) ; 功能:创建命名管道 返回:若成功则为0,若出错则为- 1 建立管道后,就可以像普通文件一样读写操作 若不指定O_NONBLOCK只读方式打开时会阻塞 直到有进程以写方式打开.同样写方式打开时会 阻塞直到有进程以读方式打开