signal的第1个参数signum表示要捕捉的信号,第2个参数是个函数指针,表示要对该信号进行捕
捉的函数,该参数也可以是SIG_DFL(表示交由系统缺省处理,相当于白注册了)或SIG_IGN(表示忽略掉
该信号而不做任何处理)。

返回值:signal如果调用成功,返回以前该信号的处理函数的地址,否则返回 SIG_ERR。

sighandler_t是信号捕捉函数,由signal函数注册,注册以后,在整个进程运行过程中均有效,并且
对不同的信号可以注册同一个信号捕捉函数。该函数只有一个整型参数,表示信号值。

eg:捕捉终端 CTRL+c 产生的 SIGINT 信号

#include <unistd.h>

#include <stdio.h>

#include <sys/wait.h>

#include <sys/types.h>

#include <signal.h>

void SignHandler(int iSignNo)

{

printf(“Capture sign no:%d\n”,iSignNo);

}

int  
main()

{

signal(SIGINT,SignHandler);

while(1)

sleep(1);

return 0;

}

该程序可以通过 Ctrl+\终止, 因为
组合键 Ctrl+\能够产生 SIGQUIT 信号,而该信号的捕捉函数尚未在程序中注册。

3、sigaction 信号处理机制

在 signal 处理机制下, 还有许多特殊情况需要考虑:

1、注册一个信号处理函数,并且处理完毕一个信号之后,是否需要重新注册,才能够捕捉下一个
信号;(不需要)

2、如果信号处理函数正在处理信号, 并且还没有处理完毕时,又发生了一个同类型的信号,这时
该怎么处理;(挨着执行),后续相同信号忽略(会多执行一次)。

3、如果信号处理函数正在处理信号,并且还没有处理完毕时,又发生了一个不同类型的信号,这
时该怎么处理;(跳转去执行另一个信号,之后再执行剩下的没有处理完的信号)

4、如果程序阻塞在一个系统调用(如 read(…))时,发生了一个信号,这时是让系统调用返回错误再
接着进入信号处理函数,还是先跳转到信号处理函数,等信号处理完毕后,系统调用再返回。—>  由下面具体说明:

**总结:**信号都是平等的无优先级。但是对于singal接口来说最多执行2次、打断。默认不能打断当前相同信号,可以被其他信号打断。另外一个信号会立即唤醒正在睡眠的进程。

eg:

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <signal.h>

int g_iSeq = 0;

void SignHandler(int iSignNo)

{

int iSeq = g_iSeq++;

printf(“%d Enter SignHandler,signo:%d\n”,iSeq,iSignNo);

sleep(3);

printf(“%d Leave SignHandler,signo:%d\n”,iSeq,iSignNo);

}

int main()

{

char szBuf[8];

int iRet;

//不同的信号调用同一个处理函数

signal(SIGINT,SignHandler);

signal(SIGQUIT,SignHandler);

do{

iRet = read(STDIN_FILENO,szBuf,sizeof(szBuf)-1);

if(iRet < 0){

perror(“read fail.”);

break;

}

s
zBuf[iRet] = 0;

printf(“Get: %s”,szBuf);

}while(strcmp(szBuf,“quit\n”) != 0);

return 0;

}

程序运行时,针对于如下几种输入情况,看输出结果:

1、 [CTRL+c] [CTRL+c] (一个一个挨着执行)

2、 [CTRL+c] [CTRL+] (先执行 c 的进入, 被\打断, 转而执行\,

最后执行c的退出)

3、 hello [CTRL+] [Enter] (先执行中断, 没有任何输出)

4、 [CTRL+] hello [Enter] (先执行中断, 输出内容)

5、 hel [CTRL+] lo[Enter] (先执行中断, 只输出 lo)

4、sigaction 信号处理注册

函数原型:

#include <signal.h>

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

  1. sigaction 也用于注册一个信号处理函数
  2. 参数 signum 为需要捕捉的信号
  3. 参数 act 是一个结构体, 里面包含信号处理函数地址、 处理方式等信息
  4. 参数 oldact 是一个传出参数, sigaction 函数调用成功后, oldact 里面包含以前对 signum 的处理方式的信息,通常为 NULL,如果函数调用成功,将返回 0,否则返回-1

**1、**结构体 struct sigaction的原型为:

struct sigaction {

void (*sa_handler)(int); //老类型的信号处理函数指针

void (*sa_sigaction)(int, siginfo_t *,void *);//新类型的信号处理函数指针

sigset_t sa_mask;         //将要被阻塞的信号集合

int sa_flags;            
//信号处理方式掩码(SA_SIGINFO)

void(*sa_restorer)(void);       
//保留, 不要使

};

1、字段sa_handler是一个函数指针,用于指向原型为void handler(int)的信号处理函数地址,
即老类型 的信号处理函数(如果用这个再将 sa_flags = 0,就等同于 signal()函数)

2、字段 sa_sigaction 也是一个函数指针,用于指向原型为:
void handler(int iSignNum, siginfo_t *pSignInfo, void *pReserved);
的信号处理函数,即新类型的信号处理函数

  • iSignNum: 传入的信号
  • pSignInfo: 与该信号相关的一些信息,它是个结构体
  • pReserved: 保留,现没用,通常为NULL

3、字段sa_handler和sa_sigaction只应该有一个生效,如果想采用老的信号处理机制,就应该让
sa_handler指向正确的信号处理函数,并且让字段sa_flags为0;否则应该让sa_sigaction指向正确
的信号处理函数,并且让字段sa_flags包含SA_SIGINFO选项。

4、字段sa_mask是一个包含信号集合的结构体,该结构体内的信号表示在进行信号处理时,将要
被阻塞的信号。

  • 针对sigset_t 结构体,有一组专门的函数对它进行处理,它们是:#include <signal.h>
    • int sigemptyset(sigset_t *set);   //清空信号集合 set
      • int sigfillset(sigset_t *set);                           //将所有信号填充进set中
      • int sigaddset(sigset_t *set, int signum);            //往set中添加信号signum
      • int sigdelset(sigset_t *set, int signum);          //从 set 中移除信号 signum
      • int sigismember(const sigset_t *set, int signum); //判断signum是否包含在set中(是:返回1,否:0)
      • int sigpending(sigset_t *set); //将被阻塞的信号集合由参数set指针返回(挂起信号)
  • 其中,对于函数sigismember而言,如果signum在set集中,则返回 1;不在,则返回0;出错时返回-1.其他的函数都是成功返回0,失败返回-1。

eg: 如果打算在处理信号SIGINT时,只阻塞对SIGQUIT信号的处理,可以用如下方法:

struct sigaction act;

act.sa_flags = SA_SIGINFO;

act.sa_sigaction = newHandler;

sigemptyset(&act.sa_mask);

sigaddset(&act.sa_mask, SIGQUIT);

sigaction(SIGINT,&act,NULL);

5、字段 sa_flags是一组掩码的合成值,指示信号处理时所应该采取的一些行为,各掩码的含义为:

eg:用sigaction 实现和signal(只能传递一个参数)一样的功能。

#include<signal.h>

#include<stdio.h>

void handle(int signo)

{

printf(“signo: %d\n”,signo);

}

m
ain()

{

struct sigaction st;

st.sa_handler = handle;

st.sa_flags = 0;

sigaction(SIGINT,&st,NULL);

while(1)

{

sleep(1);

}

}

2、用sigaction 实现调用新的信号处理函数

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <signal.h>

int g_iSeq = 0;

void SignHandlerNew(int iSignNo,siginfo_t *pInfo,void *pReserved)

{

int iSeq = g_iSeq++;

printf("%d Enter

SignHandlerNew,signo:%d\n",iSeq,iSignNo);

sleep(3);

printf(“%d Leave SignHandlerNew,signo:%d\n”,iSeq,iSignNo);

}

in
t main()

{

struct sigaction act;

act.sa_sigaction = SignHandlerNew;

act.sa_flags = SA_SIGINFO;

sigaction(SIGINT,&act,NULL);

sigaction(SIGQUIT,&act,NULL);

while(1)

{

sleep(1);

}

r
eturn 0;

}

5、sigprocmask 信号阻塞

函数sigaction中设置的被阻塞信号集合只是针对于要处理的信号,例如

struct sigaction act;

sigemptyset(&act.sa_mask);

sigaddset(&act.sa_mask,SIGQUIT);

sigaction(SIGINT,&act,NULL);

表示只有在处理信号SIGINT时,才阻塞信号 SIGQUIT;(重点区分)

函数sigprocmask 是全程阻塞,在 sigprocmask 中设置了阻塞集合后, 被阻塞的信号将不能再被信号
处理函数捕捉,直到重新设置阻塞信号集合。

原型为:

#include <signal.h>

为了做好运维面试路上的助攻手,特整理了上百道 【运维技术栈面试题集锦】 ,让你面试不慌心不跳,高薪offer怀里抱!

这次整理的面试题,小到shell、MySQL,大到K8s等云原生技术栈,不仅适合运维新人入行面试需要,还适用于想提升进阶跳槽加薪的运维朋友。

本份面试集锦涵盖了

  • 174 道运维工程师面试题
  • 128道k8s面试题
  • 108道shell脚本面试题
  • 200道Linux面试题
  • 51道docker面试题
  • 35道Jenkis面试题
  • 78道MongoDB面试题
  • 17道ansible面试题
  • 60道dubbo面试题
  • 53道kafka面试
  • 18道mysql面试题
  • 40道nginx面试题
  • 77道redis面试题
  • 28道zookeeper

总计 1000+ 道面试题, 内容 又全含金量又高

  • 174道运维工程师面试题

1、什么是运维?

2、在工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?

3、现在给你三百台服务器,你怎么对他们进行管理?

4、简述raid0 raid1raid5二种工作模式的工作原理及特点

5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?

6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?

7、Tomcat和Resin有什么区别,工作中你怎么选择?

8、什么是中间件?什么是jdk?

9、讲述一下Tomcat8005、8009、8080三个端口的含义?

10、什么叫CDN?

11、什么叫网站灰度发布?

12、简述DNS进行域名解析的过程?

13、RabbitMQ是什么东西?

14、讲一下Keepalived的工作原理?

15、讲述一下LVS三种模式的工作过程?

16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?

60道dubbo面试题

  • 53道kafka面试
  • 18道mysql面试题
  • 40道nginx面试题
  • 77道redis面试题
  • 28道zookeeper

总计 1000+ 道面试题, 内容 又全含金量又高

  • 174道运维工程师面试题

1、什么是运维?

2、在工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?

3、现在给你三百台服务器,你怎么对他们进行管理?

4、简述raid0 raid1raid5二种工作模式的工作原理及特点

5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?

6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?

7、Tomcat和Resin有什么区别,工作中你怎么选择?

8、什么是中间件?什么是jdk?

9、讲述一下Tomcat8005、8009、8080三个端口的含义?

10、什么叫CDN?

11、什么叫网站灰度发布?

12、简述DNS进行域名解析的过程?

13、RabbitMQ是什么东西?

14、讲一下Keepalived的工作原理?

15、讲述一下LVS三种模式的工作过程?

16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?

17、如何重置mysql root密码?

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐