进程间通信(IPC)之信号

1.信号:

每个信号都有一个自己的名字和编号,这些名字都以“SIG”开头,例如“SIGIO”,“SIGCHLD”等。

信号定义在#include <signal.h>中,信号名都定义为正整数。具体信号名称可以用kill -l来查询信号的名字和序号,信号都是从1开始编号,不存在0信号,因为程序中kill 0有额外的特殊应用。
在这里插入图片描述

信号的处理:

信号的处理一般有三种方法:忽略、捕捉、默认动作

  1. 忽略:大多数信号多可以使用它,但是SIGKILLSIGSTOP无法被忽视
  2. 捕捉:信号产生的时候,由内核来调用用户自定义的信号处理函数,来处理某种信号
  3. 系统默认动作:发生什么信号,系统执行默认的处理动作(具体可查阅man 7 signal查看系统信号默认动作,新手不建议看)

当系统中有需要结束的进程,不仅可以使用ctrl+c了,现在也可以使用kill -9 + 进程id。而其中的9就是信号SIGKILL。(可以自己去写个死循环然后试试)

信号处理的注册函数:
signal(入门)、sigaction(高级)

信号处理的发送函数:
kill(入门)、sigqueue(高级)

signal的demo:

#include <signal.h>
#include <stdio.h>

//typedef void (*sighandler_t)(int);
//sighandler_t signal(int signum, sighandler_t handler);

void handler(int signum){
    printf("signum = %d\n",signum);
    printf("Quit failure!\n");
}
//handler函数可以不带参数,收到后信号的处理方式也是可以自己定义的

int main(){
    signal(SIGINT,handler);
    //signal相当于是修改该指令
	//ctrl+c终止程序是信号SIGNINT
    while(1){};                                                                                                                                                 

    return 0;
}

在这里插入图片描述
对于SIGKILL来说是无法进行更改的(捕捉到之后无法修改原有的指令)

kill的demo:

#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
 
int main(int argc,char** argv){
    pid_t pid;
    int signal;

    pid = atoi(argv[1]);
    signal = atoi(argv[2]);
    //这两个位置用atoi是因为在命令行里定义的是char**类型,所以要转换成int类型,所以用asc to int函数
                                                                                                                                                                
    kill(pid,signal);
    //这里kill还可以换成system的写法
    //char cmd[128];
    //sprintf(cmd,"kill -%d %d",pid,signal);
    //system(cmd);
    printf("Done successfully!\n");
    return 0;
}
~      

在这里插入图片描述
sigaction:

int sigaction(int signnum,const struct sigaction* act,const struct sigaction* oldcast);
第二个参数就是捕捉到指令后要做什么,第三个参数是备份(一般回传个一个NULL),代表原信号的动作

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()的参数handler相同,代表新的信号处理函数
2.sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置
3.sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。

  1. SA_SIGINFO 指定信号处理函数需要三个参数,所以应使用sa_sigaction替代sa_handler。
  2. SA_NODEFER 在信号处理函数处置信号的时段中,核心程序不会把这个间隙中产生的信号阻塞。
  3. SA_INTERRUPT 由此信号中断的系统调用不会自动重启
  4. SA_RESTART 核心会自动重启信号中断的系统调用,否则返回EINTR错误值。(重启被中断的系统调用)
  5. SA_RESETHAND 信号处理函数接收到信号后,会先将对信号处理的方式设为预设方式,而且当函数处理该信号时,后来发生的信号将不会被阻塞。
  6. SA_ONSTACK 如果利用sigaltstack()建立信号专用堆栈,则此标志会把所有信号送往该堆栈。
  7. SA_NOCLDSTOP 假如signum的值是SIGCHLD,则在子进程停止或恢复执行时不会传信号给调用本系统调用的进程。
  8. SA_NOCLDWAIT 当调用此系统调用的进程之子进程终止时,系统不会建立zombie进程。

siginfo_t(sa_sigaction的第二个参数)
{
   int si_signo; /* 信号值,对所有信号有意义*/
   int si_errno; /* errno值,对所有信号有意义*/
   int si_code; /* 信号产生的原因,对所有信号有意义*/
   int si_trapno; /* Trap number that caused hardware-generated signal (unused on most architectures) /
   pid_t si_pid; /
发送信号的进程ID,对kill(2),实时信号以及SIGCHLD有意义 /
   uid_t si_uid; /
发送信号进程的真实用户ID,对kill(2),实时信号以及SIGCHLD有意义 /
   int si_status; /
退出状态,对SIGCHLD有意义*/
   clock_t si_utime; /* 用户消耗的时间,对SIGCHLD有意义 /
   clock_t si_stime; /
内核消耗的时间,对SIGCHLD有意义 /
   sigval_t si_value; /
信号值,对所有实时有意义,是一个联合数据结构,可以为一个整数(由si_int标示,也可以为一个指针,由si_ptr标示)/
   int si_int; /
POSIX.1b signal /
   void si_ptr; / POSIX.1b signal /
   int si_overrun;/
Timer overrun count; POSIX.1b timers /
   int si_timerid;/
Timer ID; POSIX.1b timers /
   void * si_addr; /
触发fault的内存地址,SIGILL,SIGFPE,SIGSEGV,SIGBUS 信号有意义
/
   int si_band; /* 对SIGPOLL信号有意义 /
   int si_fd; /
对SIGPOLL信号有意义 */
}

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

//int sigaction(int signum,  const struct sigaction *act,  struct sigaction *oldact);
void handler(int signum, siginfo_t* info, void* context){
    printf("signum = %d\n",signum);

    if(context != NULL){
        printf("Signal number is %d\n",info->si_signo);                                                                                                         
        printf("Receive signal from %d\n",info->si_pid);
        printf("POSIX.1b signal = %d\n",info->si_int);
        printf("get data = %d\n",info->si_value.sival_int);
    }
    return;
}   
    
int main(){
    struct sigaction act;
    act.sa_sigaction = handler;
    act.sa_flags = SA_SIGINFO;
    //SA_SIGINFO是接受信号的宏
        
    printf("pid = %d\n",getpid());
    sigaction(SIGUSR1,&act,NULL);
    while(1);
    
    return 0;
}

sigqueue:

int sigqueue(pid_t pid, int sig, const union sigval value);

//sigval不用自己定义
union sigval
{
  int sival_int;
  void* sival_ptr;
};

 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <signal.h>
 #include <stdlib.h>

 //int sigqueue(pid_t pid, int sig, const union sigval value);
 
 int main(int argc,char** argv){

    pid_t pid = atoi(argv[1]);
    int sig = atoi(argv[2]);
    union sigval value;
    value.sival_int = 100;
    //传递字符串的话,就用value.sival_ptr = "Hello world!";

    if(sigqueue(pid,sig,value) == 0){
        printf("Send successfully!\n");
    }else{ 
        printf("ERROR!\n");
        perror("Why");
    }                                                                                                                                                           

    return 0;
 }

运行结果:
在这里插入图片描述
在这里插入图片描述
sigaction和sigqueue函数的用法相对比较复杂,需要结合API去进行使用,里边的结构体嵌套,和一些变量的意义需要多看多写去领悟。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值