进程通信方式
一.无名管道
(1)创建无名管道
#include <unistd.h>
int pipe(int pipefd[2]);
功能:创建无名管道
参数:
pipefd 获得文件描述符,pipefd[0]:用于读管道,pipefd[1]:用于写管道
传参方法 int pfd[2];
if(pipe(pfd) < 0)
返回值:
成功返回0,失败返回-1,并置errno
注意:无名管道只能用于亲缘间进程通信(原因:亲缘间进程存在数据的拷贝)
练习:用无名管道实现,父子进程通信
父亲进程:循环从键盘输入数据,然后写入管道
子进程 :循环从管道中读取数据,然后打印
如果是"quit",父子进程退出
3.管道的读写规则
1.读端存在,写管道
管道空闲,则写入数据;管道满,写管道则阻塞
2.读端不存在,写管道
此时写管道没有意义,内核会发送SIGPIPE信号,杀死写管道进程
-----------------------------------------------------------------------
1.写端存在,读管道
管道中有数据 >= 要求读取数据 读取要求大小的数据
管道中有数据 < 要求读取数据 读取管道中实际大小数据
管道中没有数据,则读管道阻塞
2.写端不存在,读管道
管道中有数据,读取数据,管道中没有数据,读管道不阻塞,立即返回 0
虽然具有亲缘关系的进程同时拥用无名管道的读端和写端,
但是如果要实现两进程相互双向通信,需要使用两个无名管道。
注意一般利用管道进行读写时读进程先关写端,写进程先关闭读端。
二.有名管道
特点:
1.在文件系统中存在文件名,不同的进程可通过管道文件名找到同一管道进行通信
2.因为上条特点,可用于非亲缘关系进程间通信
A.创建有名管道
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
功能:创建一个有名管道
参数:
pathname 管道文件创建路径
mode 创建的管道文件权限 3位8进制数
返回值:成功返回0,失败返回-1,并置errno
注意:如果有名管道己经存在,再次mkfifo(3)会失败,如下处理
if(mkfifo(path,0666) < 0 && errno != EEXIST)
{
//出错处理
}
B.打开有名管道文件,利用open函数
注意:
1.如果只有一个进程以只读方式打开一个有名管道,则进程阻塞,直到另一进程以写方式打开
2.如果只有一个进程以只写方式打开一个有名管道,则进程阻塞,直到另一进程以读方式打开
3.如果以读写方式打开,不会阻塞
C.管道的读写:管道(有名,无名)有相同的读写规则.
练习:
*1.两个进程利用管道进行读写,A进程读,B进程写。B输入quit后,两进程退出。
(当B进程读stdin到文件结尾,两进程结束)
2.两终端通过两个有名管道进行聊天。需两对父子进程
有名管道与无名管道异同点:
相同:
1.都是基于文件描述符的的进程间通信方式。
2.都可以利用read(),write()函数等文件IO函数进行读写。与普通文件读写区别是管道中数据读完后就没有,数据只能读到一次
3.都不可使用lseek()进行定位操作。
4.数据都是先入先出FIFO。
5.读写都遵循管道读写规则。
不同:
1.无名管道只能用于具有亲缘关系的进程间通信;有名管道可用于并不相关的两个进程之间相互通信。
2.无名管道通过pipe函数创建并获得操作的一对文件描述符,具有亲缘关系的进程才能使用;
而有名管道是通过mkfifo函数创建,任何进程都可以根据有名管道文件名及其所在路径利用open()函数打开,
close()函数关闭,read()/write()读写。
3.无名管道只在内核中有一段缓冲区,只能通过pipe函数创建并获得读写的文件描述符;
而有名管道不光在内核中有一段缓冲区,而且在文件系统中有一个文件名,这个文件名对应了内核中的一段缓冲区。
与普通文本文件区别在于有名管道仅仅在文件系统中有一个名字,数据是存放在内存中(内核空间)。
4.无名管道创建时能获得一对用于读写的文件描述符,所以pipe()函数执行并不会阻塞;
而有名管道创建后,再打开时单独以一种方式打开(读或写),都会阻塞等待另一种方式打开,但以读写方式打开时不会阻塞。
三、信号
信号(软件中断信号)提供了一种进程间异步通信方式,可用于实现进程间的同步!
信号用于内核通知进程发生了异步事件,也可以用于不同进程相互的发送消息实现进程间通信。
1.一个完整的信号生命周期
可以分为三个阶段:1.信号产生;2信号发送或传递;3.信号注销和处理
2.Linux 下信号处理:进程对信号的响应方式
忽略信号: 对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。
捕捉信号: 定义并注册信号处理函数,当信号发生时,执行相应的处理函数。
执行缺省操作: Linux对每种信号都规定了默认操作
3.信号的使用
发送给进程的消息通常是一个数,即信号的编号,并用此标识区分不同信号。
操作系统中用一组前缀为SIG的宏来表示可以由内核或进程发送的信号
使用信号进行通信
1.定义信号处理函数
2.向内核注册信号处理函数
3.向指定进程发送对应信号
参数:
signum 注册处理方式的信号
handler 信号处理方式
handler可以传参的值有哪些
SIG_IGN,忽略信号
SIG_DFL,按照默认方式处理信号
传自己实现信号处理函数的函数名,类型要求为void sig_handler(int signum);
返回值:成功返回信号原处理方式对应函数指针值;失败返回函数指针SIG_ERR
注意:1.sighandler_t类型说明,确定信号处理函数的原型为,void (*sighandler_t)(int);
sighandler函数的参数int为当内核给进程发送信号时,信号编号
2.信号处理函数的有一个int类型参数,作用当其被调用时,保存接收到的信号值
练习:父进程不阻塞,不轮询方式回收僵尸态子进程资源
提示:捕捉SIGCHLD信号,信号处理函数中应调用waitpid,以非阻塞方式调用
linux 系统下调用signal显式忽略子进程的SIGCHLD也可实现,由init进程回收子进程资源,
5.信号的发送
*选做作业:利用有名管道实现文件传输
A进程,将文件内容读出,写入管道,直至文件结尾为止
B进程,从管道中将内容读出,写入另一文件,直到管道为空
一.无名管道
(1)创建无名管道
#include <unistd.h>
int pipe(int pipefd[2]);
功能:创建无名管道
参数:
pipefd 获得文件描述符,pipefd[0]:用于读管道,pipefd[1]:用于写管道
传参方法 int pfd[2];
if(pipe(pfd) < 0)
返回值:
成功返回0,失败返回-1,并置errno
注意:无名管道只能用于亲缘间进程通信(原因:亲缘间进程存在数据的拷贝)
练习:用无名管道实现,父子进程通信
父亲进程:循环从键盘输入数据,然后写入管道
子进程 :循环从管道中读取数据,然后打印
如果是"quit",父子进程退出
3.管道的读写规则
1.读端存在,写管道
管道空闲,则写入数据;管道满,写管道则阻塞
2.读端不存在,写管道
此时写管道没有意义,内核会发送SIGPIPE信号,杀死写管道进程
-----------------------------------------------------------------------
1.写端存在,读管道
管道中有数据 >= 要求读取数据 读取要求大小的数据
管道中有数据 < 要求读取数据 读取管道中实际大小数据
管道中没有数据,则读管道阻塞
2.写端不存在,读管道
管道中有数据,读取数据,管道中没有数据,读管道不阻塞,立即返回 0
虽然具有亲缘关系的进程同时拥用无名管道的读端和写端,
但是如果要实现两进程相互双向通信,需要使用两个无名管道。
注意一般利用管道进行读写时读进程先关写端,写进程先关闭读端。
二.有名管道
特点:
1.在文件系统中存在文件名,不同的进程可通过管道文件名找到同一管道进行通信
2.因为上条特点,可用于非亲缘关系进程间通信
A.创建有名管道
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
功能:创建一个有名管道
参数:
pathname 管道文件创建路径
mode 创建的管道文件权限 3位8进制数
返回值:成功返回0,失败返回-1,并置errno
注意:如果有名管道己经存在,再次mkfifo(3)会失败,如下处理
if(mkfifo(path,0666) < 0 && errno != EEXIST)
{
//出错处理
}
B.打开有名管道文件,利用open函数
注意:
1.如果只有一个进程以只读方式打开一个有名管道,则进程阻塞,直到另一进程以写方式打开
2.如果只有一个进程以只写方式打开一个有名管道,则进程阻塞,直到另一进程以读方式打开
3.如果以读写方式打开,不会阻塞
C.管道的读写:管道(有名,无名)有相同的读写规则.
练习:
*1.两个进程利用管道进行读写,A进程读,B进程写。B输入quit后,两进程退出。
(当B进程读stdin到文件结尾,两进程结束)
2.两终端通过两个有名管道进行聊天。需两对父子进程
有名管道与无名管道异同点:
相同:
1.都是基于文件描述符的的进程间通信方式。
2.都可以利用read(),write()函数等文件IO函数进行读写。与普通文件读写区别是管道中数据读完后就没有,数据只能读到一次
3.都不可使用lseek()进行定位操作。
4.数据都是先入先出FIFO。
5.读写都遵循管道读写规则。
不同:
1.无名管道只能用于具有亲缘关系的进程间通信;有名管道可用于并不相关的两个进程之间相互通信。
2.无名管道通过pipe函数创建并获得操作的一对文件描述符,具有亲缘关系的进程才能使用;
而有名管道是通过mkfifo函数创建,任何进程都可以根据有名管道文件名及其所在路径利用open()函数打开,
close()函数关闭,read()/write()读写。
3.无名管道只在内核中有一段缓冲区,只能通过pipe函数创建并获得读写的文件描述符;
而有名管道不光在内核中有一段缓冲区,而且在文件系统中有一个文件名,这个文件名对应了内核中的一段缓冲区。
与普通文本文件区别在于有名管道仅仅在文件系统中有一个名字,数据是存放在内存中(内核空间)。
4.无名管道创建时能获得一对用于读写的文件描述符,所以pipe()函数执行并不会阻塞;
而有名管道创建后,再打开时单独以一种方式打开(读或写),都会阻塞等待另一种方式打开,但以读写方式打开时不会阻塞。
三、信号
信号(软件中断信号)提供了一种进程间异步通信方式,可用于实现进程间的同步!
信号用于内核通知进程发生了异步事件,也可以用于不同进程相互的发送消息实现进程间通信。
1.一个完整的信号生命周期
可以分为三个阶段:1.信号产生;2信号发送或传递;3.信号注销和处理
2.Linux 下信号处理:进程对信号的响应方式
忽略信号: 对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。
捕捉信号: 定义并注册信号处理函数,当信号发生时,执行相应的处理函数。
执行缺省操作: Linux对每种信号都规定了默认操作
3.信号的使用
发送给进程的消息通常是一个数,即信号的编号,并用此标识区分不同信号。
操作系统中用一组前缀为SIG的宏来表示可以由内核或进程发送的信号
使用信号进行通信
1.定义信号处理函数
2.向内核注册信号处理函数
3.向指定进程发送对应信号
4.设置进程对信号的处理方式--注册信号处理函数
、、================================================================================/*{{{*/
void fun(int );//函数声明
void *fun1(int);//指针函数的声明
void (*pfun)(int) = fun;//函数指针变量定义
typedef int a;
typedef void (*pfun)(int);//给函数指针类型void (*)(int) 另起了类型别名pfun
pfun q;//void (*q)(int)
、、================================================================================/*}}}*/
#include <signal.h>
typedef void (*sighandler_t)(int);
//信号处理函数的类型
sighandler_t signal(int signum, sighandler_t handler);
void (* signal(int signum, void (* handler )(int) )) (int);
功能:注册信号处理函数
参数:
signum 注册处理方式的信号
handler 信号处理方式
handler可以传参的值有哪些
SIG_IGN,忽略信号
SIG_DFL,按照默认方式处理信号
传自己实现信号处理函数的函数名,类型要求为void sig_handler(int signum);
返回值:成功返回信号原处理方式对应函数指针值;失败返回函数指针SIG_ERR
注意:1.sighandler_t类型说明,确定信号处理函数的原型为,void (*sighandler_t)(int);
sighandler函数的参数int为当内核给进程发送信号时,信号编号
2.信号处理函数的有一个int类型参数,作用当其被调用时,保存接收到的信号值
练习:父进程不阻塞,不轮询方式回收僵尸态子进程资源
提示:捕捉SIGCHLD信号,信号处理函数中应调用waitpid,以非阻塞方式调用
linux 系统下调用signal显式忽略子进程的SIGCHLD也可实现,由init进程回收子进程资源,
5.信号的发送
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
kill - send signal to a process
#include <signal.h>
int raise(int sig);
raise - send a signal to the caller
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
alarm - set an alarm clock for delivery of a signal
#include <unistd.h>
int pause(void);
pause - wait for signal
//pause(2)和kill(2)/raise(2)/alarm(2)一起可实现进程间同步
练习:检测用户是否输入,如果用户3s没有输入,则超时一次,如果超时3次自动退出
*选做作业:利用有名管道实现文件传输
A进程,将文件内容读出,写入管道,直至文件结尾为止
B进程,从管道中将内容读出,写入另一文件,直到管道为空