Linux第十次课

1、什么是信号?

信号signal机制是UNIX系统中最为古老的进程之间的通信机制。它用于在一个或多个进程之间传递异步信号。

是进程间通信唯一的异步通信方式。

进程可以屏蔽掉大多数的信号,除了SIGSTOP和SIGKILL
SIGSTOP:使正在运行的进程暂停
SIGKILL:使正在运行的进程退出

QT中应用signal和slot

2、信号的作用?

进程之间的通信
通知进程发生了异步事件

3、什么情况下引发信号?

1.键盘事件 ctrl +c ctrl +
2.非法内存 如果内存管理出错,系统就会发送一个信号进行处理
3.硬件故障 同样的,硬件出现故障系统也会产生一个信号
4.环境切换 比如说从用户态切换到其他态,状态的改变也会发送一个信号,这个信号会告知给系统

通常信号的来源:
1、程序的错误,如非法访问内存
2、外部信号,如按下了CTRL+C
3、通过kill或sigqueue向另一个进程发送信号

4、信号的种类?

kill -l

可以查看到64个信号

信号的值定义在signal.h中,在Linux中没有32和33这两个信号。
其中编号34以上的是实时信号,34以下的信号是普通信号。
而这些信号各自在什么条件下产生,默认的处理动作是什么,
在signal(7)中都有详细说明,在命令行上输入man 7 signal:

可靠信号和不可靠信号

可靠的信号,有排队等待的机制。
不可靠信号是从unix继承而来的,没有有排队等待的机制,默认情况下是处理第1个等待的信号,剩下的丢弃掉。

可靠信号又称为实时信号
非可靠信号又称为非实时信号

可靠信号和不可靠信号

在Linux系统中,信号的可靠性是指信号是否会丢失,或者说该信号是否支持排除。
SIGHUP( 1 ) ~ SIGSYS( 31 )之间的信号都是继承自UNIX系统是不可靠信号。
Linux系统根据POSIX标准定义了SIGRTMIN(34) ~ SIGRTMAX(64)之间的信号,它们都是可靠信号,也称为实时信号。

5、信号的优先级

信号实质上是软中断,中断有优先级,信号也有优先级。
如果一个进程有多个未决信号,则对于同一个未决的实时信号,内核将按照发送的顺序来递送信号。
如果存在多个未决信号,则值(或者说编号)越小的越先被递送。
如果即存在不可靠信号,又存在可靠信号(实时信号),
虽然POSIX对这一情况没有明确规定,但Linux系统和大多数遵循POSIX标准的操作系统一样,将优先递送不可靠信号。

6、信号的处理

进程收到信号后,其可选的处理动作有以下三种:
1.忽略此信号。
2.执行该信号的默认处理动作(终止该信号)。
3.提供一个信号处理函数(自定义动作),要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉(Catch)一个信号。

7、与信号处理相关的函数和结构

signal:设置信号处理函数

SIGINT:键盘中断
CTRL+C
SIGQUIT:键盘退出
CTRL+\会触发SIGQUIT信号

SIGINT捕获

SIGINT: 程序中止信号,在用户按下Ctrl+C时发出

注意:可以使用Ctrl+\ 来退出(SIGQUIT)。
当我们按下:ctrl+\或kill –SIGQUIT pid发送SIGQUIT信号时,程序退出,
那是因为进程对SIGQUIT信号的默认处理动作是退出程序。

signal函数用于截取系统的信号,对此信号挂接用户自己定义的处理函数
#include<signal.h>
typedef void (*sighandler)(int);
sighandler signal(int signum,sighandler handler);

signum 信号编号

sighandler 信号的处理函数
SIG_IGN 忽略信号
SIG_DFL 使用默认的信号处理函数

kill 命令:
-l 查看所有的信号信息
-n pid n是信号编号,想pid进程发送n信号
int kill(pid_t pid, int sig);
向pid进程发送sig信号
pid 进程ID
sig 信号编号
pid_t getpid(void);
返回调用这个函数的进程的PID

sigset_t 
    unsigned long int __val[_SIGSET_NWORDS];
int sigemptyset(sigset_t *set);
    清空信号集合
int sigfillset(sigset_t *set);
    将所有信号都添加进入信号集合中
int  sigprocmask(int  how, const sigset_t *set, sigset_t *old‐set);
    对信号集合当中的信号进行操作
    how:
        SIG_BLOCK   屏蔽信号集合当中的信号
        SIG_UNBLOCK 解除信号集合当中的信号
        SIG_SETMASK 设置信号集合当中的信号操作

信号会打断程序的所有阻塞

struct sigaction
    struct sigaction {
        void     (*sa_handler)(int);
        void     (*sa_sigaction)(int, siginfo_t *, void
                *);
        sigset_t   sa_mask;
        int        sa_flags;
        void     (*sa_restorer)(void);
    };
sa_flags:
    0       默认信号处理方式-打断当前操作后,不会重新调用原先阻塞的操作
    SA_SIGINFO  在传递信号的时候有可能信号会带参数,因此,回掉函数需要指定为sa_sigaction类型
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
    为信号指定操作
    signum      信号编号
    act         对应的回调操作
    oldact      将信号原先的操作进行存储
int  sigqueue(pid_t  pid, int sig, const union sigvalvalue);
    向指定进程发送指定信号,并且可以携带一个参数
    pid         进程ID
    sig         信号编号
    value  : 为信号传递同时携带一个参数    
    typedef union sigval
    {
        int sival_int;
        void *sival_ptr;
    } sigval_t;

8、案例

进程收到信号后,会中断进程正在执行的代码,
转到信号的处理函数中去执行。

alarm2.c
alarm:设置信号传送闹钟
触发SIGALRM信号

头文件
#include<unistd.h>
定义函数
unsigned int alarm(unsigned int seconds);
函数说明
alarm()用来设置信号SIGALRM在经过参数seconds指定的秒数后传送给目前的进程。
如果参数seconds 为0,则之前设置的闹钟会被取消,并将剩下的时间返回。
返回值
返回之前闹钟的剩余秒数,如果之前未设闹钟则返回0。

alarm3.c

kill函数

表头文件
#include<sys/types.h>
#include<signal.h>
定义函数
int kill(pid_t pid,int sig);
函数说明
kill()可以用来送参数sig指定的信号给参数pid指定的进程。参数pid有几种情况:
pid>0 将信号传给进程识别码为pid 的进程。
pid=0 将信号传给和目前进程相同进程组的所有进程
pid=-1 将信号广播传送给系统内所有的进程
pid<0 将信号传给进程组识别码为pid绝对值的所有进程
参数sig代表的信号编号可参考附录D
返回值
执行成功则返回0,如果有错误则返回-1。
错误代码
EINVAL 参数sig 不合法
ESRCH 参数pid 所指定的进程或进程组不存在
EPERM 权限不够无法传送信号给指定进程

pause函数

表头文件
#include<unistd.h>
定义函数
int pause(void);
函数说明
pause()会令当前的进程暂停(进入睡眠状态),直到被信号(signal)所中断。
返回值
只返回-1。
错误代码
EINTR 有信号到达中断了此函数。

fork函数

进程复制

父进程和子进程
调用fork函数的进程是父进程,fork函数产生的进程是子进程

sigaction

头文件
#include<signal.h>
定义函数
int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
函数说明
sigaction()会依参数signum指定的信号编号来设置该信号的处理函数。参数signum可以指定SIGKILL和SIGSTOP以外的所有信号。
如参数结构sigaction定义如下
struct sigaction
{
void (*sa_handler) (int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer) (void);
}
sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数,其他意义请参考signal()。
sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号搁置。
sa_restorer 此参数没有使用。
sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。
OR 运算(|)组合
A_NOCLDSTOP : 如果参数signum为SIGCHLD,则当子进程暂停时并不会通知父进程
SA_ONESHOT/SA_RESETHAND:当调用新的信号处理函数前,将此信号处理方式改为系统预设的方式。
SA_RESTART:被信号中断的系统调用会自行重启
SA_NOMASK/SA_NODEFER:在处理此信号未结束前不理会此信号的再次到来。
如果参数oldact不是NULL指针,则原来的信号处理方式会由此结构sigaction 返回。
返回值
执行成功则返回0,如果有错误则返回-1。

sigqueue函数和kill类似

sigqueue()是比较新的发送信号系统调用,主要是针对实时信号提出的(当然也支持前32种),
支持信号带有参数,与函数sigaction()配合使用。

注意:signal和kill一起使用
sigaction和sigqueue一起使用

对比:
sigqueue()比kill()传递了更多的附加信息,
但sigqueue()只能向一个进程发送信号,
而不能发送信号给一个进程组。
如果 signo=0,将会执行错误检查,
但实际上不发送任何信号,0值信号可用于检查pid的有效性以及当前进程
是否有权限向目标进程发送信号。

sigset_t sigset;//用于记录屏蔽字
sigset_t ign;//用于记录被阻塞的信号集

//清空信号集
sigemptyset(&sigset);
sigemptyset(&ign);

//向信号集中添加信号SIGINT
sigaddset(&sigset, SIGINT);

//当前进程将屏蔽掉sigset信号集中的所有信号
sigprocmask(SIG_SETMASK, &sigset, 0);

//在信号集中删除信号SIGINT
sigdelset(&sigset, SIGINT);

//将进程的屏蔽字重新设置,即取消对SIGINT的屏蔽
//并挂起进程
sigsuspend(&sigset);

猜你喜欢

转载自blog.csdn.net/weixin_44545549/article/details/89601419