进程通信——信号

信号的概念及类型

信号是Linux系统中用于进程之间通信或操作的一种机制,信号可以在任何时候发送给某一进程,而无须知道该进程的状态。如果该进程并未处于执行状态,则该信号就由内核保存起来,直到该进程恢复执行并传递给他为止。如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:

  1. 第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。
  2. 第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。
  3. 第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。

在linux下我们可以使用 kill -l 命令查看当前系统支持的信号,需要注意的是不同的系统支持的信号是不一样的:

在上面的信号中,我们常见或用到的是:

信号编号信号名信号说明默认动作
2SIGINTCtrl+c按键终止程序运行的信号程序终止
4SIGILL非法的指令程序终止
7SIGBUS运行非本CPU相关编译器编译的程序程序终止
9SIGKILL强制杀死程序信号,任何程序都不可以捕捉该信号程序终止,不可被捕捉
10SIGUSR1用户自定义信号1程序终止
11SIGSEGV段错误系统给程序发送的信号程序终止
12SIGUSR2用户自定义信号2程序终止
13SIGPIPE管道破裂信号程序终止
14SIGALRMalarm()系统调用发送的信号程序终止
15SIGTERMkill命令默认发送的信号,默认动作是终止信号程序终止
17SIGCHLD子进程退出信号忽略该信号

信号的安装

linux下主要有两个函数实现信号的安装:signal()、sigaction()。
1、signal()

#include <signal.h>  //头文件

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

函数说明:注册一个信号捕捉函数
参数说明:① 第一个参数signum指定信号的值。②第二个参数handler制定针对前面函数的处理,可忽略该信号(参数设为SIG_IGN),可以采用系统默认方式处理信号(参数设为SIG_DFL),也可以自己实现处理方式(参数指定一个函数地址),即当指定信号到底时,就会跳转到handler指定的函数执行。
返回值:signal()调用成功时,返回最后一次为安装信号signum而调用signal()时的handler值,失败返回SIG_ERR。

2、sigaction()

#include <signal.h>

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

函数说明:函数可对发来的信号做排队处理(通常在Linux用其来注册一个信号的捕捉函数)
参数说明:①第一个参数signum为要操作的信号,可以为SIGKILL及SIGSTOP外的任何一个特定有效的信号(这两个信号定义自己的处理函数,导致安装错误)。②第二个参数act为指向sigaction结构体的一个指针,在结构体中指定了对信号新的处理方式,当为NULL时,进程会按缺省的方式对信号处理。③第三个参数oldact参数输出先前信号的处理方式,也可指为NULL。若第二个参数和第三个参数都为NULL则该函数用来检查信号的有效性。

struct sigaction结构体如下:

struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
}

 sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数。
 sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置
 sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。

①SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
②SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
③SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。若SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号。

signal()代码示例

/*********************************************************************************
 *      Copyright:  (C) 2020 makun<1394987689@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  signal.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2020年03月20日)
 *         Author:  makun <1394987689@qq.com>
 *      ChangeLog:  1, Release initial version on "2020年03月20日 14时19分06秒"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
int     g_sigstop = 0;

void signal_stop(int signum)
{

    if( SIGTERM == signum )
    {
        printf("SIGTERM signal detected\n");
    }
    else if( SIGALRM == signum )
    {
        printf("SIGALRM signal detected\n");
        g_sigstop = 1;
    }
}
void signal_code(int signum)
{
    if(SIGBUS == signum)
    {
        printf("SIGBUS signal detected\n");
    }
    else if(SIGILL == signum)
    {
        printf("SIGILL signal detected\n");
    }
    else if(SIGSEGV == signum)
    {
        printf("SIGSEGV signal detected\n");
    }
    exit(-1);
}

int main (int argc, char **argv)
{
    char    *ptr=NULL;
    

    signal(SIGTERM,signal_stop);
    /*安装SIGTERM信号,SIGTERM这是由kill(1)命令发送的系统默认终止信号,由于该信号是由应用程序捕获的使用SIGTERM也让程序有机会在退出之前做好清理工作,从而优雅的终止*/
    signal(SIGALRM,signal_stop);
    /*安装SIGALRM,SIGALRM,当用alarm函数设置的定时器超时时,产生此信号,若由setitime(2)函数设置的间隔时间已经超时时,也产生此信号。*/ 
    signal(SIGBUS,signal_code);
    /*指示一个实现定义的硬件故障,当出现某些类型的的内存故障时,实现常常产生此种信号*/
    signal(SIGILL,signal_code);
    /* 此信号表示进程已执行一条非法硬件指令 */
    signal(SIGSEGV,signal_code);
    /*指示进程进行了一次无效的内存引用(通常说明程序有错,比如访问了一个未初始的指针)*/ 


    printf("program start running for 20 seconds...\n");
    alarm(20);//程序执行到这会打印SIGALRM signal detected
    while(!g_sigstop)
    {
        ;
    }
    printf("program start stop running...\n");
    printf("invalid pointer operator will raise SIGSEGV signal\n");

    *ptr = 'h';//程序执行到这会打印SIGSEGV signal detected

    return 0;
}

代码运行结果:
在这里插入图片描述

sigaction()代码示例

/*********************************************************************************
 *      Copyright:  (C) 2020 makun<1394987689@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  sigaction.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2020年03月20日)
 *         Author:  makun <1394987689@qq.com>
 *      ChangeLog:  1, Release initial version on "2020年03月20日 14时19分06秒"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>

void sig_usr(int signum)
{

    if( SIGUSR1 == signum )
    {
        printf("SIGUSR1 signal detected\n");
    }
    else if( SIGUSR2 == signum )
    {
        printf("SIGUSR2 signal detected\n");
        
    }
}

    

int main (int argc, char **argv)
{
    char    buf[512];
    int     n;
    
    struct  sigaction   sa_usr; 
    sa_usr.sa_flags =0;
    sa_usr.sa_handler = sig_usr;    //信号处理函数

    sigaction(SIGUSR1, &sa_usr, 0);
    sigaction(SIGUSR2, &sa_usr, 0);


     printf("My PID is %d\n", getpid());
         
     while(1)
     {
         if((n = read(STDIN_FILENO, buf, 511)) == -1)
         {
             if(errno == EINTR)
             {
                 printf("read is interrupted by signal\n");
             }
         }
         else
         {
             buf[n] = '\0';
             printf("%d bytes read: %s\n", n, buf);
         }
     }

     return 0;
}
  1. 第一次运行结果:进程在这里阻塞,等待另一个进程发送信号

在这里插入图片描述

  1. 进程2给进程1-9709发送信号

在这里插入图片描述 3.进程1收到信号后在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
信号量是一种用于进程通信和同步的机制。它是一个计数器,用于保证在共享资源上的互斥访问。在Linux系统中,可以使用信号量来实现进程间的同步和互斥。以下是信号量的基本概念: - 计数器:信号量的值是一个计数器,它可以被多个进程共享。 - P操作:当一个进程需要访问共享资源时,它必须执行P操作,该操作会将信号量的值减1。如果信号量的值为0,则进程将被阻塞,直到信号量的值大于0。 - V操作:当一个进程使用完共享资源后,它必须执行V操作,该操作会将信号量的值加1。如果有进程正在等待该信号量,则唤醒其中一个进程继续执行。 在ZUCC中,可以使用信号量来实现进程的同步和互斥。首先,需要使用semget函数创建一个信号量集合,并使用semctl函数对信号量进行初始化。然后,可以使用semop函数执行P和V操作。例如,下面是一个简单的示例程序,用于演示信号量的使用: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/sem.h> #define SEM_KEY 1234 union semun { int val; struct semid_ds *buf; unsigned short *array; }; int main() { int semid, pid; union semun arg; struct sembuf sb; // 创建信号量集合 semid = semget(SEM_KEY, 1, IPC_CREAT | 0666); if (semid == -1) { perror("semget"); exit(EXIT_FAILURE); } // 初始化信号量 arg.val = 1; if (semctl(semid, 0, SETVAL, arg) == -1) { perror("semctl"); exit(EXIT_FAILURE); } // 创建子进程 pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } else if (pid == 0) { // 子进程执行P操作 sb.sem_num = 0; sb.sem_op = -1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop P"); exit(EXIT_FAILURE); } printf("Child process\n"); // 子进程执行V操作 sb.sem_num = 0; sb.sem_op = 1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop V"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } else { // 父进程执行P操作 sb.sem_num = 0; sb.sem_op = -1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop P"); exit(EXIT_FAILURE); } printf("Parent process\n"); // 父进程执行V操作 sb.sem_num = 0; sb.sem_op = 1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop V"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } return 0; } ``` 在上述代码中,创建了一个信号量集合,并将其初始化为1。然后,创建了一个子进程和一个父进程,它们分别执行P和V操作。由于信号量的初始值为1,因此父进程和子进程都可以顺利地执行。如果将信号量的初始值改为0,那么父进程和子进程都将被阻塞,直到有一个进程执行V操作为止。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值