Linux:进程间通信——信号

信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动。信号是软中断,通常信号是由一个错误产生的。但它们还可以作为进程间通信或修改行为的一种方式,明确地由一个进程发送给另一个进程

目录

一、信号种类

1.常见的信号

2.不可靠信号和可靠信号

注意

二、信号捕捉

三、进程休眠号

四、信号集和信号阻塞

五、附带数据信息的信号处理


一、信号种类

在终端输入kill -l 命令可以看到liunx支持的所有信号类型。

Linux:进程间通信——信号_第1张图片

1.常见的信号

SIGINT(2)

        默认动作:终止

        来之键盘的中断信号,Ctrl+C或者break键被按下。

SIGQUIT(3)

        默认动作:终止并进行Core dump(当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中,这种行为就叫做Core dump)

        来自键盘的退出信号,与SIGINT类似,但是由Ctrl+\产生

SIGKILL(9)

        默认动作:终止

        kill信号,该信号不可被忽略,阻塞以及自定义处理方式,如果某个进程结束不了可以用这个信号杀死进程。

SIGSTOP(19)

        默认动作:暂停运行

        暂停进程。该信号不能被忽略、阻塞以及自定义处理方法

2.不可靠信号和可靠信号

    建立在早期的信号处理机制上(1~31)的信号是不可靠信号

        不支持排队、可能会丢失,如果同一个信号连续产生多次,进程可能只相应了一次

    建立在新的信号处理机制上(34~64)的信号是可靠信号

        支持排队、信号不会丢失       

注意

        没有32和33号信号。

        当信号处理完后可能会返回产生信号的代码继续运行,如果我们捕获并处理段错误、算术异常等信号可能会产生死循环,正确的处理段错误、算数异常信号应该是备份数据并直接结束程序

         有些系统通过signal注册的信号处理函数只能执行一次,如果想要持续有效,可以在信号处理函数中再重新注册一次

        子进程会继承父进程的信号处理方式,但是通过exec系列函数创建的子进程,会恢复默认的信号处理方式

二、信号捕捉

typedef void(*sighandler_t)(int);

        功能:说明信号处理函数的格式

sighandler_t signal(int signum, sighandler_t handler);

        功能:向内核注册一个信号处理函数

        参数说明:

                signum:要处理的信号编号,比如SIGINT(中断信号),SIGTERM(终止信号)等

                handler:自定义的信号处理函数的指针,当指定的信号发生时,这个函数将被调用。

        返回值:

                设置成功:返回以前的信号处理程序的地址

                设置失败:返回SIG_ERR;

下面是一个简单的例子,展示如何使用 signal 函数来处理 SIGINT 信号(通常是由 Ctrl+C 触发

#include 
#include 
#include 
#include 

void handle_signal(int signum) {
    printf("Caught signal %d\n", signum);
    exit(0);
}

int main() {
    // 设置 SIGINT 信号处理器
    if (signal(SIGINT, handle_signal) == SIG_ERR) {
        printf("Error setting signal handler\n");
        return 1;
    }

    // 无限循环,等待信号
    while (1) {
        printf("Running... Press Ctrl+C to stop.\n");
        sleep(1);
    }

    return 0;
}

注意:

        当信号处理完后可能会返回产生信号的代码继续运行,如果我们捕获并处理段错误、算术异常等信号可能会产生死循环,正确的处理段错误、算数异常信号应该是备份数据并直接结束程序

        有些系统通过signal注册的信号处理函数只能执行一次,如果想要持续有效,可以在信号处理函数中再重新注册一次

        子进程会继承父进程的信号处理方式,但是通过exec系列函数创建的子进程,会恢复默认的信号处理方式

信号的发送方式:

        1.键盘

                Ctrl+C

                Ctrl+\

        2.错误

                非法访问内存

                总线错误

        3.命令

        kill -信号编号 进程号

                功能:向指定的进程发送信号

        killall -信号编号 进程号

                可以给同名进程发送同一个信号

        4.函数

        int kill(pid_t pid, int sig);

                功能:向指定进程发送指定信号

                pid:进程号

                sig:信号编号

        int raise(int sig);

                功能:向进程自己发送信号sig

        void abort(void);

                功能:向进程自己发送信号SIGABRT

        unsigned int alarm(unsigned int seconds);

                功能:让内核在seconds秒后向进程发送SIGALRM信号

                返回值:上次alarm设置的剩余秒数

三、进程休眠号

         int pause(void);

                功能:让调用者进入休眠状态,直到进程遇到信号才会唤醒

                返回值:要么不返回在休眠,要么唤醒后返回-1

                相当于没有时间限制的sleep

         unsigned int sleep(unsigned int seconds);

                功能:让调用者进入休眠指定的秒数,当遇到信号时会提前唤醒返回

                返回值:剩余的休眠时间

四、信号集和信号阻塞

        信号集:是一种数据类型,定义的变量可以存储多个信号

                sigset_t 128位的二进制数,每一位都固定代表了一种信号

           相关函数:

                int sigemptyset(sigset_t *set);

                        功能:清空信号集

                int sigfillset(sigset_t *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信号        

                        返回值:

                                0   不存在

                                1   存在

                                 -1  signum信号非法

    信号阻塞:
        当程序执行到一些特殊操作时,不适合处理信号,此时可以让内核先屏蔽信号,等操作执行完成后再解除屏蔽重新发送信号
        当信号产生时,内核会在其维护的信号表中为对应的进程设置与该信号对应的标记,这个过程叫做递送
        从信号产生到完成递送有个时间间隔,处于这个间隔的信号状态称为未决
        信号屏蔽\阻塞就是让被屏蔽的信号先处于未决状态、暂停递送,当屏蔽解除时在继续递送

     int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

                 功能:设置要屏蔽的信号、这些信号是存储在信号集里面

                how:信号屏蔽的方式        

                SIG_BLOCK   把set中的信号添加到要屏蔽的信号集里

                SIG_UNBLOCK 从信号屏蔽集中删除set中的信号 解除

                SIG_SETMASK 把set替换之前的信号屏蔽集

                set:准备好的信号集

                oldset:获取旧的信号屏蔽集

五、附带数据信息的信号处理

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

                功能:向内核注册一个信号处理函数

                signum:要捕获的信号编号

                act:   设置要处理的动作        

                olact: 获取原来的处理动作

你可能感兴趣的:(1024程序员节)