进程间的通信之管道与信号(系统编程二)

一、进程间的通信

1. 管道

(1)概念和原理:

概念:多个进程之间如何实现数据的收发

分为两种:有名管道,无名管道

工作原理:
进程间的通信之管道与信号(系统编程二)_第1张图片

全双工:通信双方在任意时刻都能互相收发信息,收发信息可以同时进行不受任何影响(QQ、微信)

半双工:通信双方在任意时刻都只能是一方发送,一方接收,不能同时两边都收发(对讲机)

单工:通信双方只能单向传递数据,一方发送,一方接受(收音机)

(2) 无名管道(PIPE)相关的操作

创建无名管道

#include 

int pipe(int fildes[2]);
返回值:成功  0   失败   -1

参数:fildes[2] --- 存放是无名管道读写端的文件描述符
                   fildes[0]    读端的文件描述符
                   fildes[1]    写端的文件描述符

特点:

①如果管道中没有数据可读,read会阻塞当前进程

②无名管道不会生成管道文件

③无名管道有固定的读写端

④无名管道只能用于具有血缘关系多个进程之间(父子进程,兄弟进程)

(3) 有名管道(FIFO)相关的操作

创建有名管道

#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   判断文件是否存在

2.信号

(1) 信号引入:

红灯 --》停下来(默认动作,交通法规定)

闯红灯(忽略法律规定,左耳进右耳出)

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个信号

(2)相关的接口函数

(一)信号的发送
#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型指针,该指针指向一个上下文环境,一般很少使用
(四) raise()和alarm()

自己给自己发信号

#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

你可能感兴趣的:(Linux,编程语言,C语言,开发语言,linux,c语言)