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

18 篇文章 0 订阅
16 篇文章 0 订阅

一、进程间的通信

1. 管道

(1)概念和原理:

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

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

工作原理:
在这里插入图片描述

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

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

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

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

创建无名管道

#include <unistd.h>

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

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

特点:

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

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

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

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

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

创建有名管道

#include <sys/types.h>
#include <sys/stat.h>

 int mkfifo(const char *pathname, mode_t mode);
返回值:成功  0   失败   -1

参数:
     pathname --- 生成的有名管道的路径
     mode --- 权限 0777

特点:

①有名管道会生成对应的管道文件(用于等一会进程间通信)

②有名管道不能在同一个路径下重复创建,并且只能在纯粹的linux环境中创建,不能在共享文件夹中新建

③有名管道可以用于任意两个进程间通信(可以是父子进程,也可以是没有任何关系的两个进程)

④有名管道没有固定读写端,只要打开成功,直接读写操作

有名管道在创建之前必须判断一下管道文件是否存在

方法一:使用errno去判断

#include <errno.h>

方法二:使用access函数判断

#include <unistd.h>

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

信号缺省动作备注
SIGHUP1终止控制终端被关闭时产生
SIGINT2终止从键盘按键产生的中断信号(比如Ctrl+c)
SIGQUIT3终止并产生转储文件从键盘按键产生的退出信号(比如Ctrl+\)
SIGILL4终止并产生转储文件执行非法指令时产生
SIGTRAP5终止并产生转储文件遇到进程断点时产生
SIGABRT6终止并产生转储文件调用系统函数abort()是产生
SIGBUS7终止并产生转储文件总线错误时产生
SIGFPE8终止并产生转储文件处理器出现浮点运算错误时产生
SIGKILL9终止系统杀戮信号
SIGUSR110终止用户自定义信号
SIGSEGV11终止并产生转储文件访问非法内存时产生
SIGUSR212终止用户自定义信号
SIGPIPE13终止向无读者的管道输入数据时产生
SIGALRM14终止定时器到点儿时产生
SIGTERM15终止系统终止信号
SIGSTKFLT16终止已废弃
SIGCHLD17忽略子进程暂停或终止时产生
sIGCONT18恢复运行系统恢复运行信号
SIGSTOP19暂停系统暂停信号
SIGTSTP20暂停由控制终端发起的暂停信号
SIGTTIN21暂停后台进程发起输入请求时控制终端产生该信号
SIGTTOU22暂停后台进程发起输出请求时控制终端产生该信号
SIGURG23忽略套接字上出现紧急数据是产生
SIGXCPU24终止并产生转储文件处理器占用时间超出限制值时产生
SIGXFSZ25终止并产生转储文件文件尺寸超出限制值时产生
SIGVTALRM26终止由虚拟定时器产生
SIGPROF27终止profiling定时器到点儿时产生
SIGWINCH28忽略窗口大小变更时产生
SIGIO29终止I/O变得可用时产生
SIGPWR30终止启动失败时产生
SIGUNUSED31终止并产生转储文件同SIGSYS
34) SIGRTMIN35) SIGRTMIN+136) SIGRTMIN+237) SIGRTMIN+3
38) SIGRTMIN+439) SIGRTMIN+540) SIGRTMIN+641) SIGRTMIN+7
42) SIGRTMIN+843) SIGRTMIN+944) SIGRTMIN+1045) SIGRTMIN+11
46) SIGRTMIN+1247) SIGRTMIN+1348) SIGRTMIN+1449) SIGRTMIN+15
50) SIGRTMAX-1451) SIGRTMAX-1352) SIGRTMAX-1253) SIGRTMAX-11
54) SIGRTMAX-1055) SIGRTMAX-956) SIGRTMAX-857) SIGRTMAX-7
58) SIGRTMAX-659) SIGRTMAX-560) SIGRTMAX-461) SIGRTMAX-3
62) SIGRTMAX-263) SIGRTMAX-164) SIGRTMAX总共62个信号

(2)相关的接口函数

(一)信号的发送
#include <signal.h>

int kill(pid_t pid, int sig);
参数:pid --- 你要发送信号的那个进程的id

     sig --- 你要发送的那个信号的序号

注:linux规定的信号绝大部分默认动作都是终止进程

(二)捕捉信号并改变信号的响应动作(重点,重点)
#include <signal.h>

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 <signal.h>

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 <signal.h>

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 <signal.h>

int raise(int sig);  // 等价于  kill(getpid(),sig)

定时器,间隔指定时间自己给自己发送SIGALRM信号

#include <unistd.h>
 
unsigned alarm(unsigned seconds);
参数:seconds --- 间隔时间,单位为秒

比如:alarm(5); //间隔5秒,给当前进程发送SIGALRM信号

(五)信号的阻塞(重点)

目前学习的所有关于信号的响应方式

方式一: 默认动作

方式二: 指定动作signal()或者sigaction()

方式三: 忽略信号,直接舍弃掉该信号(当做信号从来都没有发送过)

方式四: 阻塞信号,暂时把信号挡在外面(把信号挂起),等到解除阻塞的时候,依然能够响应 

无论是阻塞信号还是忽略信号,目的都是为了防止信号去干扰我们的进程

设置信号阻塞(屏蔽信号)

sigset_t类型:信号阻塞掩码集(用来存放所有你想要阻塞的信号)

#include <signal.h>

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 <signal.h>

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
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java.L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值