进程通信
进程间通信的方法:
1.管道
2.信号
3.消息队列
4.共享内存
5.信号灯
管道
无名管道:
只能用于具有亲源关系的进程间通信
函数接口:
pipe
int pipe(int pipefd2)
功能:
创建一段用于进程间通讯的内核缓存区
参数:pipedf0: 读文件描述符
pipedf1: 写文件描述符
返回值:成功返回0
失败返回-1
管道特性:
1.管道中如果有写端
管道中有数据则直接读出
管道中没有数据则阻塞等待直到数据写入将数据读出
2.管道中没有写端
管道中有数据则直接读出
管道中没有数据,则直接结束,不需要阻塞等待
3.管道中有读端
如果管道中没有写满64K,直接写入数据
如果管道中写满则需要有数据读出才能继续写入
4.管道中没有读端
写入数据会产生管道破裂的错误,使进程直接退出
有名管道:
可以用于任意两个进程间的通信
必须两端同时加入才能向下执行,如果只有一端,则阻塞等待直到另一端加入
阻塞效果,mkfifo创建的文件,必须同时启动open(O_RDONLY)以及open(O_WRONLY),否则另一端为阻塞
FIFO 的 open 是同步握手操作:O_WRONLY 必须等到另一个进程的 O_RDONLY 才能返回。在同一个进程中顺序调用两者,必然导致死锁。正确做法是使用 fork 分离进程,或利用多进程/多终端配合打开。
mkfifo;
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
功能:创建一个有名管道 管道文件的路径名(如 "/tmp/myfifo")
参数:pathname: 管道名字
mode: 创建管道的权限(同 open 的权限位,如 0666)
返回值:成功返回0
失败返回-1
信号:
1.内核层与应用层通信的一种机制
2.应用层收到信号的处理方式:
1缺省:按照默认的方式处理信号
2忽略:不处理信号
3捕捉:按照指定的方式处理信号
3.信号的种类, kill -l查看
SIGINT信号,键盘可以输入3信号之一,ctrl + c,
SIGQUIT信号,从键盘输入ctrl + \
SIGKILL信号,不能被忽略和捕捉,只能按照默认方式执行处理
SIGSEGV信号:段错误信号
SIGPIPE信号,管道破裂
SIGALRM信号,定时时间到达
SIGCHLD, 子进程结束信号
SIGSTOP, 暂停信号
SIGTSTP,挂起信号,让进程暂停执行。ctrl + z
SLGIO, 异步IO
4.函数接口:
signal:
#include <signal.h>
typedef void (*sighandler_t)(int); 函数指针类型名字重新设置为sighandler_t
sighandler_t signal(int signum, sighandler_t handler);
功能:改变信号的处理方式
参数:signum信号编号
handler:
SIG_IGN 忽略信号
SIG_DFL 缺省(默认)处理信号,恢复信号为系统默认行为(如终止、暂停等)
捕捉:自定义函数 捕捉信号 信号发生时,调用你写的函数
SIGKILL 9 强制终止(不可捕获) kill -9 <PID>
SIGSTOP 19 强制暂停(不可捕获) kill -STOP <PID>
返回值:
成功 返回之前对该信号设置的处理函数指针(可能是 SIG_DFL、SIG_IGN 或之前自定义的函数地址)
失败 返回 SIG_ERR(即 (sighandler_t)-1),并设置 errno
pause:
#include <unistd.h>
int pause(void);
功能:让当前进程暂停执行(进入睡眠状态),直到收到一个信号,且该信号的处理方式不是忽略。
返回:始终返回 -1 pause() 正常工作时永远不会返回,只有被信号中断后才返回,并且总是返回 -1,同时设置 errno = EINTR
signal(SIGINT, handler)
但是handler什么都不写,只捕捉信号,不处理
sleep()实现的基础
alarm
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能:用于设置定时器的函数,它会让内核在指定的秒数后,向当前进程发送 SIGALRM 信号。就发一次
参数:时间秒
返回:返回之前设置的闹钟剩余的秒数(如果之前没有设置,返回 0)
sleep的实现,pause + alarm, 用完之后别忘了恢复默认