概念:多个进程之间如何实现数据的收发
分为两种:有名管道,无名管道
全双工:通信双方在任意时刻都能互相收发信息,收发信息可以同时进行不受任何影响(QQ、微信)
半双工:通信双方在任意时刻都只能是一方发送,一方接收,不能同时两边都收发(对讲机)
单工:通信双方只能单向传递数据,一方发送,一方接受(收音机)
创建无名管道
#include
int pipe(int fildes[2]);
返回值:成功 0 失败 -1
参数:fildes[2] --- 存放是无名管道读写端的文件描述符
fildes[0] 读端的文件描述符
fildes[1] 写端的文件描述符
特点:
①如果管道中没有数据可读,read会阻塞当前进程
②无名管道不会生成管道文件
③无名管道有固定的读写端
④无名管道只能用于具有血缘关系多个进程之间(父子进程,兄弟进程)
创建有名管道
#include
#include
int mkfifo(const char *pathname, mode_t mode);
返回值:成功 0 失败 -1
参数:
pathname --- 生成的有名管道的路径
mode --- 权限 0777
特点:
①有名管道会生成对应的管道文件(用于等一会进程间通信)
②有名管道不能在同一个路径下重复创建,并且只能在纯粹的linux环境中创建,不能在共享文件夹中新建
③有名管道可以用于任意两个进程间通信(可以是父子进程,也可以是没有任何关系的两个进程)
④有名管道没有固定读写端,只要打开成功,直接读写操作
有名管道在创建之前必须判断一下管道文件是否存在
方法一:使用errno去判断
#include
方法二:使用access函数判断
#include
int access(const char *path, int amode);
参数:path --- 你要判断的那个文件的路径
amode --- R_OK 判断文件是否可读
W_OK 可写
X_OK 可执行
F_OK 判断文件是否存在
红灯 --》停下来(默认动作,交通法规定)
闯红灯(忽略法律规定,左耳进右耳出)
linux --》进程收到信号(默认动作)
进程收到信号(忽略信号)
kill -l 查看到linux系统中定义的所有信号,程序员是不可以自定义信号
shell命令发送信号:
kill -信号的序号 进程的id
killall -信号的序号 进程的名字
比如: kill -9 20000
kill -KILL 20000
kill -SIGKILL 20000
ctrl+c 默认给当前进程发送了SIGINT信号
信号 | 值 | 缺省动作 | 备注 |
---|---|---|---|
SIGHUP | 1 | 终止 | 控制终端被关闭时产生 |
SIGINT | 2 | 终止 | 从键盘按键产生的中断信号(比如Ctrl+c) |
SIGQUIT | 3 | 终止并产生转储文件 | 从键盘按键产生的退出信号(比如Ctrl+\) |
SIGILL | 4 | 终止并产生转储文件 | 执行非法指令时产生 |
SIGTRAP | 5 | 终止并产生转储文件 | 遇到进程断点时产生 |
SIGABRT | 6 | 终止并产生转储文件 | 调用系统函数abort()是产生 |
SIGBUS | 7 | 终止并产生转储文件 | 总线错误时产生 |
SIGFPE | 8 | 终止并产生转储文件 | 处理器出现浮点运算错误时产生 |
SIGKILL | 9 | 终止 | 系统杀戮信号 |
SIGUSR1 | 10 | 终止 | 用户自定义信号 |
SIGSEGV | 11 | 终止并产生转储文件 | 访问非法内存时产生 |
SIGUSR2 | 12 | 终止 | 用户自定义信号 |
SIGPIPE | 13 | 终止 | 向无读者的管道输入数据时产生 |
SIGALRM | 14 | 终止 | 定时器到点儿时产生 |
SIGTERM | 15 | 终止 | 系统终止信号 |
SIGSTKFLT | 16 | 终止 | 已废弃 |
SIGCHLD | 17 | 忽略 | 子进程暂停或终止时产生 |
sIGCONT | 18 | 恢复运行 | 系统恢复运行信号 |
SIGSTOP | 19 | 暂停 | 系统暂停信号 |
SIGTSTP | 20 | 暂停 | 由控制终端发起的暂停信号 |
SIGTTIN | 21 | 暂停 | 后台进程发起输入请求时控制终端产生该信号 |
SIGTTOU | 22 | 暂停 | 后台进程发起输出请求时控制终端产生该信号 |
SIGURG | 23 | 忽略 | 套接字上出现紧急数据是产生 |
SIGXCPU | 24 | 终止并产生转储文件 | 处理器占用时间超出限制值时产生 |
SIGXFSZ | 25 | 终止并产生转储文件 | 文件尺寸超出限制值时产生 |
SIGVTALRM | 26 | 终止 | 由虚拟定时器产生 |
SIGPROF | 27 | 终止 | profiling定时器到点儿时产生 |
SIGWINCH | 28 | 忽略 | 窗口大小变更时产生 |
SIGIO | 29 | 终止 | I/O变得可用时产生 |
SIGPWR | 30 | 终止 | 启动失败时产生 |
SIGUNUSED | 31 | 终止并产生转储文件 | 同SIGSYS |
34) SIGRTMIN | 35) SIGRTMIN+1 | 36) SIGRTMIN+2 | 37) SIGRTMIN+3 |
38) SIGRTMIN+4 | 39) SIGRTMIN+5 | 40) SIGRTMIN+6 | 41) SIGRTMIN+7 |
42) SIGRTMIN+8 | 43) SIGRTMIN+9 | 44) SIGRTMIN+10 | 45) SIGRTMIN+11 |
46) SIGRTMIN+12 | 47) SIGRTMIN+13 | 48) SIGRTMIN+14 | 49) SIGRTMIN+15 |
50) SIGRTMAX-14 | 51) SIGRTMAX-13 | 52) SIGRTMAX-12 | 53) SIGRTMAX-11 |
54) SIGRTMAX-10 | 55) SIGRTMAX-9 | 56) SIGRTMAX-8 | 57) SIGRTMAX-7 |
58) SIGRTMAX-6 | 59) SIGRTMAX-5 | 60) SIGRTMAX-4 | 61) SIGRTMAX-3 |
62) SIGRTMAX-2 | 63) SIGRTMAX-1 | 64) SIGRTMAX | 总共62个信号 |
#include
int kill(pid_t pid, int sig);
参数:pid --- 你要发送信号的那个进程的id
sig --- 你要发送的那个信号的序号
注:linux规定的信号绝大部分默认动作都是终止进程
#include
void (*signal(int sig, void (*func)(int)))(int);
void (*函数名(参数1, 参数2)(int);
返回值:函数指针 void (*)(int)
参数: sig --- 你要捕捉的信号的序号或者名字
void (*func)(int) --- 你捕捉到信号以后,需要改变的动作就通过这个函数实现
注意:
①有两个信号是不能改变它们的默认动作也不能忽略,SIGKILL和SIGSTOP
②signal函数总共有三种用法
用法一: 改变信号的默认动作 signal(某个信号,函数);
用法二: 忽略信号 signal(某个信号,SIG_IGN);
忽略信号,信号发送过来以后直接舍弃,左耳进,右耳出,当它不存在
用法三: 使用信号的默认动作 signal(某个信号,SIG_DFL);
两组常用的信号收发函数
第一组:kill和signal
第二组:sigqueue和sigaction
信号的发送(是kill的升级版,发送信号的时候可以携带额外数据)
#include
int sigqueue(pid_t pid, int sig, const union sigval value);
参数:pid --- 你要发送信号的那个进程的id
sig --- 你要发送的那个信号的序号
value --- 发送信号需要携带的额外数据就保存在这个联合体
union sigval {
int sival_int; //你要携带的整数
void *sival_ptr; //你要携带的指针
};
信号的捕捉(是signal的升级版,重点)
#include
int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oldact);
参数: sig --- 你要发送的那个信号的序号
struct sigaction{
void(*) (int) sa_handler //函数指针,跟signal的第二个一模一样
sigset_t sa_mask //先不管
int sa_flags //开关,让程序员选择使用哪个函数指针
设置为0表示我要使用 void(*) (int) sa_handler
设置为SA_SIGINFO表示我要使用 void(*) (int, siginfo_t *, void *) sa_sigaction
void(*) (int, siginfo_t *, void *) sa_sigaction //函数指针,当你使用sigqueue携带额外数据的时候就需要使用这个版本
}
void( * ) (int siginfo_t*,void*)函数指针
参数:int --- 捕捉到的信号的序号
siginfo_t --- 结构体
{
int si_int;//存放额外发送过来的那个整数
void *si_ptr;//存放额外发送过来的指针
}
第三个参数:一个void型指针,该指针指向一个上下文环境,一般很少使用
自己给自己发信号
#include
int raise(int sig); // 等价于 kill(getpid(),sig)
定时器,间隔指定时间自己给自己发送SIGALRM信号
#include
unsigned alarm(unsigned seconds);
参数:seconds --- 间隔时间,单位为秒
比如:alarm(5); //间隔5秒,给当前进程发送SIGALRM信号
目前学习的所有关于信号的响应方式
方式一: 默认动作
方式二: 指定动作signal()或者sigaction()
方式三: 忽略信号,直接舍弃掉该信号(当做信号从来都没有发送过)
方式四: 阻塞信号,暂时把信号挡在外面(把信号挂起),等到解除阻塞的时候,依然能够响应
无论是阻塞信号还是忽略信号,目的都是为了防止信号去干扰我们的进程
设置信号阻塞(屏蔽信号)
sigset_t类型:信号阻塞掩码集(用来存放所有你想要阻塞的信号)
#include
int sigemptyset(sigset_t *set); //清空集合
int sigfillset(sigset_t *set); //把linux中所有的信号一次性全部加入集合set中
int sigaddset(sigset_t *set, int signum); //把signum信号加入到集合set中
int sigdelset(sigset_t *set, int signum); //将signum信号从集合set中剔除
int sigismember(const sigset_t *set, int signum); //判断signum在不在集合set中 在 --- 返回1 不在 --- 返回0
#include
int sigprocmask(int how, const sigset_t *restrict set,sigset_t *restrict oset);
参数:how --- SIG_BLOCK//设置信号阻塞 把set和oset中所有的信号都阻塞
SIG_UNBLOCK//设置信号解除阻塞
SIG_SETMASK//设置信号阻塞 用set替换掉oset中信号,然后把set中的信号设置成阻塞
set --- 你想要设置阻塞或者解除阻塞的信号集合
oset --- 保存当前进程原本的信号阻塞集合,通常设置为NULL