信号——C

进程间通讯

引入

我们要如何将A程序中的数据传入B程序中呢?

这时候就要使用进程间通讯

进程间通讯

别名:IPC(inter processes communication)

作用

数据传输

一个进程需要将他的数据发送给另一个进程

资源共享

多个进程之间共享同样的资源

事件通知

一个进程向另一个或另一组进程通知一个事件

进程控制

有些进程希望完全控制另一个进程的执行,此时控制进程希望拦截另一个进程的所有操作,并能够及时知道他的状态改变

Linux间进程通讯机制

分类

信号量

管道

消息队列

内存共享

套接字

信号

概述

信号是Linux最古老的通信方式

特点

简单

不能携带大量信息

满足某个特设条件才发出

注意

信号是一种异步通信方式

信号是软件中断,他是在软件层次上对中断机制的一种模拟

信号可以导致某个进程被另一个异步进程中断,转而处理某个突发案件

进程不必等待信号的到达,也不知道信号什么时候到达

信号可以直接进行用户空间进程和内核空间进程的转换,内核空间进程可以通过信号来通知用户空间进程发生了哪些系统事件

同步和异步

同步

事件、操作或进程是有序的,一个操作必须在另一个操作结束后才能进行的

异步

事件、操作或进程是独立的,可以不等待其他操作便执行

中断机制

概念
是现代计算机系统中的基本机制之一,完成了对计算机各个事件(如时钟、
键盘等)响应工作
硬件中断
是由硬件设备触发的中断,如时钟中断、串口接收中断、外部中断等。当硬
件设备有数据或事件需要处理时,会向 CPU 发送一个中断请求, CPU 在收到中断请求后,
会立即暂停当前正在执行的任务,进入中断处理程序中处理中断请求。硬件中断具有实
时性强、可靠性高、处理速度快等特点。
软件中断
是由软件程序触发的中断,如系统调用、软中断、异常等。软件中断不是由
硬件设备触发的,而是由软件程序主动发起的,一般用于系统调用、进程切换、异常处
理等任务。软件中断需要在程序中进行调用,其响应速度和实时性相对较差,但是具有
灵活性和可控性高的特点。

完整的信号周期

注册、产生、处理函数(默认/忽略/自定义)、注销

注意:注册、产生、注销不是函数,而是信号的内部机制

信号的编号

每个信号的名字都以字符 SIG 开头。
每个信号和一个数字编码相对应,在头文件 signum.h 中,这些信号都被定义为正整
数。
信号名定义路径: /usr/include/i386-linux-gnu/bits/signum.h
Linux 下,要想查看这些信号和编码的对应关系,可使用命令: kill -l
不存在编号为 0 的信号。
1-31 号信号称之为常规信号 ( 也叫普通信号 , 标准信号 )
34-64 号信号称之为实时信号 ( 自定义信号 )
通过 man 7 signal 查看信号帮助文档
注意:9) SIGKILL 19) SIGSTOP 信号,不允许忽略和捕捉,只能执行默认动作
SIGQUIT 信号由 :Ctrl + \ 触发

信号的产生

当用户按某些终端键时,将产生信号

ctrl+c:SIGINT

ctrl+/:SIGQUIT

ctrl+z:SIGSTOP

kill函数

概念

给指定进程发送指定信号(但不一定杀死该进程)

语法

头文件

#include <signal.h>

#include <sys/types.h>

函数

int kill(pid_t pid,int sig)

参数

pid

        pid > 0,将信号传送给进程号为pid的进程

        pid = 0,将信号传送给当前进程所在进程组中的所有进程
        pid = -1,将信号传送给系统中的所有进程

        pid < -1,将信号传送给指定进程组中的所有进程,该进程组号为pid的绝对值

sig

        信号的编号(推荐使用宏名而不是数字,因为不同操作系统数字编号所对应的宏名不同)

返回值

成功:0

失败:-1

示例

运行结果

raise函数

概念

给当前进程发送指定信号(自己给自己发)

等价于kill(getpid(),sig)

语法

int raise(int sig);

示例

运行结果

abort函数

概念

给自身进程发送异常终止SIGABRT信号,产生core文件

等价于kill(getpid(),SIGABRT);

core文件
core 就是内核
core 文件会包含了程序运行时的内存,寄存器状态,堆栈指针,内存管理信息还有各种
函数调用堆栈信息等,我们可以理解为是程序工作当前状态存储生成第一个文件,许多
的程序出错的时候都会产生一个 core 文件,通过工具分析这个文件,我们可以定位到程
序异常退出的时候对应的堆栈调用等信息,找出问题所在并进行及时解决。
语法

#include <stdlib.h>

void abort(void);

注意

        1.如果使用abort函数结束进程,进程在结束前会冲刷缓冲区,然后关闭所有文件描述符(0、1、2)

        2.即使SIGABRT信号被加入信号阻塞集,一旦进程调用了abort函数,进程也还是会被终止

示例

运行结果

alarm函数

概念

设置定时器,在指定时间后,内核会给当前进程发送一个SIGALRM信号,进程收到该信号后,默认终止。每个进程有且只有唯一的一个定时器。

语法

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

参数

        seconds 秒

返回值

        若以前没有设置过定时器,或者设置的定时器已超时,返回0;

        若设置过定时器,则返回剩余秒数,并重新设置定时器

注意

        定时,与进程状态无关(即自然定时法)!就绪、运行、挂起(阻塞、暂停)、终止、僵

        尸…… 无论进程处于何种状态, alarm 都计时 。不阻塞当前进程
示例

运行结果

setitimer函数

概念

设置定时器,可以代替alarm,单位为us(微秒),可以实现周期定时

语法

#include <sys/time.h>

int setitimer(int which,const struct itimerval *newvalue,struct itimerval *oldvalue)

参数

        which:指定定时器类型,可以是以下三个值之一:

a) 自然定时: ITIMER_REAL → 14 SIGALRM 计算自然时间
b) 虚拟空间计时 ( 用户空间 ) ITIMER_VIRTUAL → 26 SIGVTALRM 只计算
进程占用 cpu 的时间
c) 运行时计时 ( 用户 + 内核 ) ITIMER_PROF → 27 SIGPROF 计算占用 cpu
及执行系统调用的时间
        newvalue:一个指向itimerval结构体的指针,间隔时间和启动时间
        oldvalue:一个指向itimerval结构体的指针,用于获取旧的定时器的值,如果不需要获取,则传入NULL。
itimerval结构体
        struct itimerval
        {
                struct timerval it_interval;        //间隔时间
                struct timerval it_value;        //延迟时间
timerval结构体
        struct timerval
        {
                long tv_sec;        //秒
                long tv_usec;        //微妙
        }
示例

运行结果

先间隔五秒,随后每隔一秒打印一次

pause函数

概念

将正在调用的进程挂起直到捕捉到信号为止

通常用来判断信号是否已到

语法

#include <unistd.h>

int pause(void)

返回值

        捕捉到信号才返回-1

示例

运行结果

程序间隔了七秒才打印啦啦啦和-1

信号处理

方式

一个进程收到一个信号的时候,可以用如下方法进行处理:
1 SIG_DFL 执行系统默认动作 对大多数信号来说,系统默认动作是用来终止该进程。
2 SIG_IGN 忽略此信号 ( 丢弃 ) 接收到此信号后没有任何动作。
3 )信号处理函数名 : 执行自定义信号处理函数 ( 捕获 ) 用用户定义的信号处理函数处理
该信号。
【注意】: SIGKILL SIGSTOP 不能更改信号的处理方式,因为它们向用户提供了一种
使进程终止的可靠方法
捕捉信号并且信号信号的处理方式有两个函数 ,signal sigaction

signal函数

概念
注册信号处理函数(不可用于 SIGKILL SIGSTOP 信号),即确定收到信号后处理函
数的入口地址。此函数不会阻塞
语法
#include <signal.h>
// 定义的一个函数指针
typedef void(*sighandler_t)(int);
//signal 函数
sighandler_t signal(int signum, sighandler_t handler);
参数 :
signum :信号的编号,这里可以填数字编号,也可以填信号的宏定义,可以通过
命令 kill - l("l" 为字母 ) 进行相应查看。
handler : 取值有 3 种情况:
SIG_IGN :忽略该信号
SIG_DFL :执行系统默认动作
信号处理函数名:自定义信号处理函数
返回值 :
成功:第一次返回 NULL ,下一次返回此信号上一次注册的信号处理函数的地址。
如果需要使用此返回值,必须在前面先声明此函数指针的类型。 失败:返回 SIG_ERR
示例1

运行结果

示例2

运行结果

按下ctrl+c时,程序会自动结束

sigacation函数

概念

检查或修改指定信号的设置

语法
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct
sigaction* oldact);
参数 :
signum :要操作的信号。
act : 要设置的对信号的新处理方式(传入参数)。
oldact :原来对信号的处理方式(传出参数)。
注意 :
如果 act 指针非空 , 则要改变指定信号的处理方式 ( 设置 ), 如果 oldact 指针非空 ,
系统将此前指定信号的处理方式存入 oldact
返回值 :
成功: 0
失败: -1
struct sigaction
{
void ( * sa_handler )( int ); // 旧的信号处理函数指针
void ( * sa_sigaction )( int , siginfo_t * , void * ); // 新的信号处理函数指针
sigset_t sa_mask ; // 信号阻塞集
int sa_flags ; // 信号处理的方式
void ( * sa_restorer )( void ); // 已弃用
};
/*
1,sa_handler,sa_sigaction: 信号处理函数指针 , signal() 里的函数指针用法
一样,应根据情况给 sa_sigaction sa_handler 两者之一赋值,其取值如下:
a SIGIGN :忽略该信号
b SIGDFL :执行系统默认动作
c 处理函数名:自定义信号处理函数
2,sa_mask :信号阻塞集,在信号处理函数执行过程中,临时屏蔽指定的信号。
3,sa_flags :用于指定信号处理的行为,通常设置为 0 ,表使用默认属性。它可
以是以下值的 按位或 组合:
SA_RESTART :使被信号打断的系统调用自动重新发起(已经废弃)
SA_NOCLDSTOP :使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD
信号。
SA_NOCLDWAIT :使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这
时子进程如果退出也不会成为僵尸进程。 SA_NODEFER :使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这
个信号。
SA_RESETHAND :信号处理之后重新设置为默认的处理方式。
SA_SIGINFO :使用 sa_sigaction 成员而不是 sa_handler 作为信号处理
函数
void(*sa_sigaction)(int signum, siginfo_t *info, void *context);
参数说明:
signum: 信号的编号。
info: 记录信号发送进程信息的结构体。
context: 可以赋给指向 ucontext_t 类型的一个对象的指针 , 以引用在传递信
号时被中断的接收进程或线程的上下文。
*/
示例

运行结果

示例2

运行结果

可重入函数

概念

函数可以同时被多个进程调用,并且每个进程都能得到期望的结果

注意
1 、不使用或返回 静态的数据、全局变量(除非用信号量互斥)。
2 、不调用动态内存分配、释放的函数。
3 、不调用任何不可重入的函数(如标准 I/O 函数)

信号集

概念
PCB 中有两个非常重要的信号集。一个称之为 阻塞信号集 ,另一个称之为 未决信号
。 这两个信号集都是内核使用位图机制来实现的。但操作系统不允许我们直接对其
进行位操作。而需自定义另外一个集合,借助信号集操作函数来对 PCB 中的这两个信
号集进行修改
信号阻塞集:将某些信号加入 集合,对他们设置屏蔽,当屏蔽 x 信号后,再收到该信
号,该信号的处理将暂缓。
未决信号集:未被处理的信号集合。

自定义信号集函数

#include <signal.h>
int sigemptyset(sigset_t *set); // set 集合置空
int sigfillset(sigset_t *set) // 将所有信号加入 set 集合
int sigaddset(sigset_t *set, int signo); // signo 信号加入到 set 集合
int sigdelset(sigset_t *set, int signo); // set 集合中移除 signo 信号
int sigismember(const sigset_t *set, int signo); // 判断信号是否存在于 集合中

信号阻塞集

概念
信号阻塞集也称信号屏蔽集、信号掩码。
每个进程都有一个阻塞集,创建子进程时子进程将继承父进程的阻塞集。
信号阻塞集用来描述哪些信号递送到该进程的时候被阻塞(在信号发生时记住它 , 直到
进程准备好时再将信号通知进程)。 所谓阻塞并不是禁止传送信号 , 而是暂缓信号的
传送。若将被阻塞的信号从信号阻塞集中删除 , 且对应的信号在被阻塞时发生了,进程
将会收到相应的信号。
我们可以通过 sigprocmask() 修改当前的信号阻塞集来改变信号的阻塞情况。

sigprocmask函数

概念
检查或修改信号阻塞集,根据 how 指定的方法对进程的阻塞集合进行修改,新的信号
阻塞集由 set 指定,而原先的信号阻塞集合由 oldset 保存。
语法
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 参数
how: 信号阻塞集合的修改方法 , 3 种情况 :
SIG_BLOCK: 添加
向信号阻塞集合中添加 set 信号集 , 新的信号掩码是 set 和旧信号掩码的并
. 相当于 mask=mask|set
SIG_UNBLOCK: 删除
从信号阻塞集合中删除 set 信号集 , 从当前信号掩码中去除 set 中的信号 .
相当于 mask=mask&~set
SIG_SETMASK: 替换
将信号阻塞集合设为 set 信号集 , 相当于原来信号阻塞集的内容清空 , 然后
按照 set 中的信号重新设置信号阻塞集。相当于 mask=set
set: 要操作的信号集地址。若 set NULL, 则不改变信号阻塞集合 , 函数只把当前信
号阻塞集合保存到 oldset
oldset: 保存原先信号阻塞集地址
示例

运行结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值