进程间通信
数据传输:一个进程需要将它的数据发送给另一个进程
资源共享:多个进程间共享同样的资源
通知事件
进程控制
POXIX:可移植操作系统接口
LINUX使用的进程间通信方式包括:
1、 管道(pipe)和有名管道(FIFO)
2、 信号(sigal)
3、 消息队列
4、 共享内存
5、 信号量
6、 套接字(socket)
一、管道通信
特点:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
1、管道是单向的,先进先出的
2、管道包括无名管道和有名管道,前者用于父进程和子进程间的通信,后者可用于同一系统中的任意两个进程间的通信
无名管道由pipe()函数创建 intpipe(int filedis [2]);
3、当一个管道建立时,它会创建两个文件描述符:filedis[0]用于读管道,filedis[1]用于写管道
4、管道关闭用close逐个关闭
5、管道读写
6、管道用于不同进程间通信,通常先创建一个管道,再通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道
注意:必须在系统调用fork()前调用pipe(),否则子进程将不会继承文件描述符
7、命名管道(FIFO)
不想关的进程也能交换数据
创建:int mkfifo(const char*pathname,mode_t mode)
Pathname:FIFO文件名
Mode:属性
一旦创建了一个FIFO,就可以用open打开它,一般的文件访问函数(close、read、write等)都可用于FIFO操作。当打开FIFO时,非阻塞标志(O_NONBLOCK)将对以后的读写产生如下影响:
(1) 没有使用O_NONBLOCK:访问要求无法满足时进程将阻塞。如试图读取空的FIFO,将导致进程阻塞
(2) 使用O_NONBLOCK:访问要求无法满足时不阻塞,立刻出错返回,errno是ENXIO
二、信号通信
特点: 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
1、 几种常见的信号
SIGHUP:从终端上发出的结束信号
SIGINT:来自键盘的中断信号(Ctrl+C)
SIGKILL:该信号结束接受信号的进程
SIGTERM:kill命令发出的信号
SIGCHLD:标识子进程停止或结束的信号
SIGSTOP:来自键盘(Ctrl+Z)或调试程序的停止执行信号
2、 信号处理
(1) 忽略此信号(有两种信号不能被忽略,SIGKILL和SIGSTOP)
(2) 执行用户希望的动作:通知内核在某种信号发生时,调用一个用户函数,在用户函数中执行用户希望的处理
(3) 执行系统默认动作:对大多数的系统默认动作是终止该进程
3、 信号发送
发送信号的主要函数有kill和raise
区别:kill既可以自身发送信号,也可以向其他进程发送信号,raise函数向进程自身发送信号
int kill(pid_t pid,int signo);
int raise(int signo);
kill的pid参数有四种不同的情况:
(1) pid>0:将信号发生给进程ID为pid的进程
(2) pid==0:将信号发送给同组的进程
(3) pid<0:将进程发送给其进程组ID等于pid绝对值的进程
(4) pid==-1:将信号发送给所有进程
Alarm:使用alarm函数可以设置一个时间值(闹钟时间),当所设置的时间到了时,产生SIGALRM信号,如果不捕捉此信号,则默认动作是终止该进程
Unsigned Int alarm(unsigned int seconds);
经过了指定seconds秒后会产生信号SIGALAM
Pause:Pause函数使调用进程挂起直至捕获到一个信号:int pause(void);
只有执行了一个信号处理函数后,挂起才结束
Signal函数:
Void (*signal(int signo,void(*func)(int)))(int)
Func可能的值是:
(1) SIG_IGN:忽略此信号
(2) SIG_DFL:按系统默认方式处理
(3) 信号处理函数名:使用该函数处理
三、共享内存
特点:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信
共享内存分为两个步骤
1、 创建共享内存,使用shmget函数
2、 映射共享内存, 这段创建的共享内存映射到具体的进程空间去,使用shmat函数
Int shmget(key_t key,int size,int shmflg);
Int shmat(int shmid,char *shmaddr,int flag);
当一个进程不再需要共享内存时,需要把它从进程地址空间中脱离
Int shmdt(char*shmaddr);
四、消息队列
特点:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点
消息队列就是一个消息的链表
消息队列的内核持续性要求每个消息队列都在系统范围内对应唯一的键值,所以,要获得一个消息队列的描述字,必须提供该消息队列的键值
Key_t ftok(char *pathname,char proj)
功能:返回文件名对应的键值
Pathname:文件名
Proj:项目名(不为0即可)
获取消息队列的描述字:
Int msgget(key_t key,int msgflg)
Key:键值,由ftok获得
Msgflg:标志位
返回值:与键值key相对应的消息队列描述字
标志位:IPC_CREAT 创建新的消息队列,IPC_EXCL与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误,IPC_NOWAIT读写消息队列要求无法得到满足时,不阻塞
在以下两种情况下,将创建一个新的消息队列:
(1)如果没有与键值key相对应的消息队列,并且msgflg中包含了IPC_CREAT标志位(2)key参数为IPC_PRIVATE
Int msgsnd(int msgid,struct msgbuf*msgp,int msgsz,int msgflg);
功能:向消息队列中发送一条消息
Msqid:已打开的消息队列id
Msgp:存放消息的结构
Msgsz:消息数据长度
Msgflg:发送标志,有意义的msgflg标志为NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待
消息格式:struct msgbuf
{
Long mtype;//消息类型>0
Char mtext[1];//消息数据的首地址
}
接收消息:
Int msgrcv(int msqid,structmysbuf *msgp,int msgsz,long msgtyp,int msgflg)
功能:从msqid代表的消息队列中读取一个msgtyp类型的消息,并把消息存储在msgp
指向的msgbuf结构中,在成功的读取了一条消息以后,队列中的这条消息将被删除
五、信号量
特点:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
与其他进程方式不大相同,主要用途是保护临界资源,进程可以根据它判定是否能够访问某些资源,除了用于访问控制外,还可以用于进程同步
分类:
二值信号灯:信号灯的值只能取0或1
计数信号灯:信号灯的值可以取任意非负值
创建/打开:int semget(key_t key,int nsems,int semflg)
Key:键值,由ftok获得
Nsems:指定打开或者创建的信号灯集中将包含信号灯的数目
Semflg:标识,同消息队列
操作:int semop(int semid,struct sembuf *sops,unsigned nsops);
功能:对信号量进行控制
Semid:信号量集的ID
Sops:是一个操作数组,表明要进行什么操作
Nsops:sops指向的数组的元素个数