Linux(入门基础):16---信号(2)定义、种类、signal、SIGCLD、kill、raise

不存在编号为0的信号
kill函数对信号编号为0有特殊的应用

一、产生信号的条件

在这里插入图片描述

(2) SIGINT   ctrl +c  终止信号
(3)                ctrl  +\  暂停信号,放入后台 
(4)                非法指令
(5)            abort   进程异常终止
(7) SIGBUS  (虚实关系建立)       总线错误(从写的位置到物理内存,操作系统没有将磁盘的开始位置到物理内存之间建立      联系   mmap(把虚拟内存和磁盘文件的关系映射起来,如果磁盘大小大于0,就建立这种关系
(9) SIGKILL  kill - 9 pid  杀死进程
(11) SIGSEGV 段错误
(13) 管道破裂
(14)闹钟
(15) 缺省终止某个进程,终止掉
(17)子进程死的时候会给父进程发送这个信号
(19)进程暂停
(23) SIGURG 紧急数据
(29) 异步 IO 

2.信号处理过程

图片

三、 进程收到信号的三种处理方式

默认:如果是系统默认的话,那就会终止这个进程
忽略 :信号来了我们不处理,装作没看到 SIGKILL SIGSTOP 不能忽略
捕获并处理 :当信号来了,执行我们自己写的代码(捕获信号这个动作是需要我们完成的) SIGKILL SIGSTOP 不能捕获

四、signal

#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int);

    第一个参数:signo是一个整型数
    第二个参数:
        可以是信号处理函数
        可以是后面两个宏定义。常数SIG_IGN(向内核表示忽略此信号)或是常数SIG_DFL(表示接到此信号后的动作是系统默认动作)
        处理函数无返回值,且以参数1位参数
typedef  void  Sigfunc(int);
 然后,可将signal函数原型写成: Sigfunc   *signal(int, Sigfunc *);


//案例 :pause函数,它使调用进程睡眠(挂起)

#include<signal.h>
#include<stdio.h>
#include <unistd.h>
static void sig_usr(int);
 
int main(void)
{
    if (signal(SIGUSR1, sig_usr) == SIG_ERR)
        perror("can’t catch SIGUSR1");
    if (signal(SIGUSR2, sig_usr) == SIG_ERR)
        perror("can’t catch SIGUSR2");
    for (;;)
        pause();
}
 
static void sig_usr(int signo)
{
    if (signo == SIGUSR1)
        printf("received SIGUSR1\n");
    else if (signo == SIGUSR2)
        printf("received SIGUSR2\n");
    else
        printf("received signal %d\n", signo);
}


kill -USR1 2325:向进程发送SIGUSR1信号
kill -USR2 2325:向进程发送SIGUSR2信号
kill 2325:向进程发送SIGTREM(因为该进程没有设置该信号捕获函数,所以使用默认动作终止程序)

五、SIGCLD、SIGCHLD

1、产生信号条件
子进程结束,
子进程收到SIGSTOP信号
子进程收到SIGSCONT信号

2、信号作用
使用SIGCHLD完成对子进程的回收可以避免父进程阻塞等待而不能执行其他操作,只有当父进程收到SIGCHLD信号才能去调用信号捕捉函数完成对子进程的回收,未收到信号之前可以处理其他操作。
子进程状态改变(终止或停止)后产生此信号(SIGCLD/SIGHCLD),传递给父进程;
如果父进程想要捕捉此信号,可以捕获该信号并进行处理

处理:

SIGCLD信号的捕捉处理必须在子进程fork之前完成 
   方式一:
   如果进程明确地将该信号配置为SIG_IGN,则调用进程的子进程将不产生僵死进程(注意:SIG_IGN与默认处理方式SIG_DFL不同)。子进程在终止时,将其状态丢弃
   如果调用进程随后调用一个wait()函数,那么它将阻塞直到所有的子进程都终止,然后该wait返回-1,并将errno设置为ECHILD
    方式二:
    父进程为SIGCLD设置一个信号捕捉函数,在信号捕捉函数中调用wiat()waittpid()获取子进程的终止状态与ID
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
 
static void sig_cld(int);
int main()
{
    pid_t pid;
    if (signal(SIGCLD, sig_cld) == SIG_ERR)
        perror("signal error");
 
    if ((pid = fork()) < 0) {
        perror("fork error");
    } else if (pid == 0) { 
        sleep(2);
        _exit(0); //子进程终结
    }
 
    /* parent */
    //pause:使进程挂起,直到接收到一个信号并从信号处理函数中返回才结束挂起状态
    pause(); 
 
    exit(0);
}
 
static void sig_cld(int signo) 
{
    pid_t pid;
    int status;
    printf("SIGCLD received\n");
 
    if (signal(SIGCLD, sig_cld) == SIG_ERR) 
        perror("signal error");
 
    if ((pid = wait(&status)) < 0) //阻塞等待子进程,获得其进程号和终止状态
        perror("wait error");
    printf("pid = %d\n", pid);
}


//配合SIGCHLD信号,父进程完成对子进程的回收



六、 KILL

向进程或进程组发送指定的信号

#include <signal.h>
int kill(pid_t pid, int signo);

pid > 0:将信号发送给进程ID为pid的进程
pid== 0:将信号发送给调用此kill函数的进程所在进程组的其它所有进程
pid < 0:将信号发送给其进程组ID等于pid绝对值,而且发送进程有许可权向其发送信号的所有进程。
pid== -1: 将该信号发送给除init进程外的所有进程,但发送者必须拥有对目标进程发送信号的权限(很危险,可能bash会被杀死)

返回值:

    成功:返回0
    失败:返回-1,并设置errno。常见的几种errno如下
        EINVAL:无效的信号
        EPERM:该进程没有权限发送信号给任何一个目标进程
        ESRCH:目标进程或进程组不存在

编号0定义为空信号。如果signo参数是0,则kill仍执行正常的错误检查,但不发送信号。这常被用来确定一个特定进程是否仍旧存在;

七、raise函数

进程向自身发送信号
#include <signal.h>
int raise(int signo);

参数:

   要发送给自己的信号

返回值:

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

raise(signo); 等价于 kill(getpid(), signo);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值