10,信号


中断:停止当前执行的程序而去执行新的程序或处理意外的过程;
中断分为两大类:硬件中断,软件中断

信号的处理:
    本质上就是一种软件中断,即可以作为两个进程间通信的一种机制;
    更重要的是,信号总是可以终止一个程序的执行,他更多的被用与处理意外情况;

信号的特性:
1.信号是异步的,进程不知到什么时候知道信号回来;
2.进程即可以发送信号,也可以处理信号;
3.每个信号都有一个名字,用SIG开头

信号的基本命令和分类:
kill -l:查看当前系统所支持的所有信号;

要求掌握的信号:
SIGINT:        信号2,使用ctril + c 产生该信号,默认终止进程
SIGQUIT:    信号3,使用ctril + \ 产生该信号,默认终止进程

SIGKILL:    信号9,使用kill + 9 产生该信号,默认终止进程
            (该信号只能采用默认处理,不能被用户捕捉,也就是不能用signal函数处理)

基本分类:
1~31之间的信号叫做不可考信号,不支持排队,信号可能会丢失,也叫做实时信号;
34~64之间的信号叫做可靠信号,支持排队,信号不会丢失,也叫做实时信号;

信号的处理方式:
1.默认处理:绝大多数信号的默认处理方式都是终止进程
2.忽略处理:
3.自定义处理/捕获处理:

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t   signal(int  signum,  sighandler_t handler);

第一个参数:具体的信号值/信号名称
第二个参数:具体的处理方式
            SIG_IGN:忽略处理
            SIG_DFL:默认处理
            自定义函数的地址:自定义处理

返回值:成功返回之前的处理方式,失败返回SIG_ERR;
函数功能:主要用于设置指定信号的处理方式

父子进程对信号处理方式的比较:

1.对于fork函数创建的子进程来说,完全照搬父进程对信号的处理方式;
即父进程怎么处理子进程就怎么处理;

2.对于vfor函数和execl函数启动的子进程来说
父进程忽略,子进程也忽略
父进程默认,子进程叶默认
父进程子定义,子进程默认处理

发送信号的主要方式

1.采用键盘发送信号(只能发送部分特殊的信号)
2.程序出错发送信号(只能发送部分特殊的信号):段错误  发送信号SIGSEGV 11
3.使用kill命令发送信号(所有信号都可以发送):kill -信号值 进程号
4.采用系统函数发送信号:kill()/raise()/alarm()/......

发送信号系统函数的解析:

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

第一个参数:进程编号
第二个参数:信号值  如果是0,表示不会发送信号;只是检查进程是否存在

函数功能:主要用于给指定进程发送参数指定的信号
返回值:成功0;失败-1;

#include <signal.h>
int raise(int sig);
函数功能:主要用于给当前正在调用的进程/线程发送参数指定的信号;即给自己发送信号
对于单线程的程序来说,等价于kill(getpid(),sig);
返回值:成功返回0;失败返回非0

sleep():主要用于当前正在调用的线程进入睡眠状态;
直到指定的秒数睡了,或者一个不能被忽略的信号达到了;

如果参数指定的秒数过去了,则返回0;否则返回还没有来得及的秒数,也就是剩余的秒数;

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

函数功能:主要用于经过参数指定的秒数之后,给当前正在调用的进程发送SIGALRM信号;
每次重新设置闹钟之后都会取消之前的闹钟,当前参数为0时,专门用于取消闹钟;
如果之前没有闹钟,则返回0;否则返回之前闹钟剩余的秒数;





信号集的基本概念:本质上就是若干个信号组成的集合

操作系统内部使用数据类型:sigset_t 表示信号集的数据类型,该数据类型的大小:128个字节



信号集的基本操作:
1.sigemptyset():    清空信号集
2.sigfillset():        填满信号集
3.sigaddset():        添加信号到信号集中
4.sigdelset():        删除信号集中的信号
5.sigismember():    判断信号是否存在


#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);




*************信号的屏蔽****************

在某些特殊程序的执行过程中,可能不允许被信号打断,
此时就需要使用信号的屏蔽技术来解决该问题;

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

第一个参数:具体的屏蔽方式
    SIG_BLOCK:使用当前屏蔽集 + 参数set屏蔽集
               即 ABC + CDE(set) => ABCDE
    
    SIG_UNBLOCK:当前屏蔽集 - 参数set屏蔽集
               即 ABC - CDE(set) => AB
    
    SIG_SETMASK:使用参数set屏蔽集 替换 当前屏蔽集
               即 ABC + CDE(set) => CDE
    
    
第二个参数:信号集类型的指针,用于指定新的信号集

第三个参数:信号集类型的指针,用于带出之前屏蔽的信号集
          如果不想带出之前的屏蔽集,该参数给NULL即可;

函数功能:
    主要用于检查/修改即将屏蔽的信号集

返回值:成功返回0;失败返回-1;


注意:信号的屏蔽并不是删除信号,而是相当于用一个隔板把信号挡起来;
对于可靠信号来说,产生多少次,则在隔板的另一侧会排队等待多少个;
而对于不可考信号来说,无论产生了多少次,在隔板的另一侧只有一个信号在排队等待,
当信号的屏蔽解除时,相当于将隔板移开,只要进程不结束,则全部依次处理;




#include <signal.h>
int sigpending(sigset_t *set);

函数功能:
    主要用于获取信号屏蔽期间来过但没有来得及处理的信号,通过参数set返回;


****************************************************
计时器:
    在linux系统中,操作系统会为每一个进程管理3中计时器;
    分别是:真实计时器,虚拟计时器,实用计时器;
    一般才有真实计时器;


#include <sys/time.h>
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value,
                               struct itimerval *old_value);

第一个参数:计时器的类型
    ITIMER_REAL:    真实计时器,产生SIGALRM信号
    ITIMER_VIRTUAL:    虚拟计时器,产生SIGVTALRM信号  进程在用户态下消耗的时间
    ITIMER_PROF:    实用计时器,产生SIGPROF信号  进程在用户态和内核态消耗的总时间

第二个参数:机构体指针,用于指定计时器的新值
第三个参数:结构体指针,用于带出设置之前的旧值

struct itimerval {
    struct timeval it_interval; /* 间隔时间/next value */
    struct timeval it_value;    /* 启动时间/current value */
};

struct timeval {
    long tv_sec;                /* 秒数/seconds */
    long tv_usec;               /* 微秒/microseconds */
};



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值