Linux之进程间通信①——信号(Signal)

一、什么是进程间通信

1.进程间通信的介绍

进程的用户空间是相互独立的,一般而言是不能互相访问的,唯一的例外就是共享内存区,则内核空间是每个进程共享的,因此不同进程之间想要进行通信,就需要通过内核来实现,进程因此可互相交换数据与信息。

2.进程间通信的种类

(1)信号
(2)管道(Linux之进程间通信②——管道(pipe、fifo)
(3)socket
(4)信号量(Linux之进程间通信③——信号量(Semaphore)
(5)共享内存(Linux之进程间通信④——共享内存(Shared Memory)
(6)消息队列
本篇主要讲解使用信号通信,其余内容后续一一进行介绍

二、信号介绍

信号是进程间通信中唯一一种异步通信机制,是软件层次上的中断模拟,可以在任何时刻发送信号给某一进程,且无须知道该进程的状态,用于通知接收进程某个事件已发生。
无论进程是处于未执行或阻塞状态,最终都会在其恢复正常状态后传递给该进程。Linux中含几十种信号,所代表意义也不同,对于这些信号也有着不同的处理方式。

①执行默认操作:对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得该进程终止,进程通过系统调用signal来指定进程对某个信号的处理行为
②捕捉信号:可以为信号定义一个处理函数(安装信号),当该信号发生时,就执行相应的处理函数,类似中断的处理程序
③忽略信号:不希望处理某些信号时,可忽略该信号,不做任何处理

在Linux下可以通过kill -l 查看所有信号的定义,使用信号时可以man 7 signal 查看信号触发条件以及其的默认处理动作。
在这里插入图片描述

1.函数介绍

(1)signal()

#include <signal.h>
typedef void (*sig_handle)(int);//信号处理函数
sighandler_t signal(int signum,sig_handle handle);

①函数功能:对指定的信号编号设置该信号的处理函数;
②函数参数:signum为需要处理的信号代码,handle为与信号关联的动作,其中SIG_IGN表示忽略该信号,SIG_DFL表示恢复对信号的系统默认处理,sig_handle类型的函数指针,利用函数处理该信号;
③函数返回值:成功,返回先前的信号处理函数指针;失败,返回SIG_ERR(-1);

(2)kill()

#include <sys/types.h>
#include <signal.h>
int kill(pid_t,int signum);

①函数功能:给指定进程发送指定信号;
②函数参数:pid>0,为信号发送的进程,pid = 0,为信号送往所有调用kill()的进程所属同一使用组的进程,pid = -1,为信号送往所有调用进程有权给其发送信号的进程,除了进程(init),pid<-1,为信号送往以-pid为组标识的进程;signum为准备发送的信号代码;
③函数返回值:成功返回0,失败返回-1;

(3)wait()

#include <sys/types.h>
#include <sys/wait.h>
int	wait(int *wstatus);

如父进程未使用wait()函数等待已终止的子进程,则子进程会处于无父进程的状态,此时子进程会沦为僵尸进程,依旧占用着系统资源,wait()需与fork()配套出现,且在fork()之后调用wait(),wait()与waitpid()相似,waitpid()具体可查看Linux之多进程详解

①函数功能:阻塞等待子进程退出、回收子进程残留资源、获取子进程结束状态(退出原因);
②函数参数:传出参数wstatus/status 用来保存进程的退出状态,为NULL时,表示忽略子进程退出时的状态
③函数返回值:成功,返回回收的子进程ID;失败,返回 -1(无子进程);

2.进程间通信——信号

当父进程创建子进程后,父、子进程的运行先后顺序无特殊规定,由操作系统的进程调度策略所确定,但可以使用信号来实现进程间同步,能够确保父子进程运行的先后顺序。
以下程序为确保子进程先于父进程执行,又接收父进程通知先退出,可确保父进程能回收子进程避免其沦为僵尸进程,最后才退出。

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

int   child_stop = 0;		//定义全局变量
int   parent_run = 0;

void sig_child(int signum)//信号处理函数1
{
    
    
    if(SIGUSR1 == signum)
    {
    
       
        child_stop = 1; 
    }   
}

void sig_parent(int signum)//信号处理函数2
{
    
    
    if(SIGUSR2 == signum)
    {
    
       
        parent_run = 1;
    }   
}

int main (int argc,char **argv)
{
    
    
    pid_t     pid;//定义进程号
    int       wstatus;//定义传出参数,用来保存进程退出状态
    signal(SIGUSR1,sig_child);//安装信号,用户自定义信号,当发送SIGUSR1则执行sig_child
    signal(SIGUSR2,sig_parent);
	pid = fork();
    if(pid < 0)
  {
    
    
        printf("create child process failure\n");
        return -1;
    }
    else if(pid == 0)//子进程运行
    {
    
    
        printf("child process start running and send signal to parent process\n");
        kill(getppid(),SIGUSR2);//子进程通过kill函数发送SIGUSR2的信号到父进程,则父进程可开始运行

        while(!child_stop)//未收到父进程传的信号则处于循环休眠等待
        {
    
    
            sleep(1);
        }
        printf("child process receive signal from parent process and exit now\n");//接收到SIGUSR1信号,则可退出
        return 0;
    }
    printf("parent hangs up untill receive signal from child!\n");//接收到子进程传送的SIGUSR2信号才可运行
    while(!parent_run)//等待子进程运行后,父进程才进行,确保了子进程先运行
    {
    
    
        sleep(1);
    }
    printf("parent process start running now and send child process signal to exit\n");
    kill(pid,SIGUSR1);		//父进程进行后发送SIGUSR1信号给子进程,使其退出循环

    wait(&wstatus);		//父进程等待子进程退出(若子进程退出父进程不收尾则会使其变为僵尸进程),未找到子进程会一直处于阻塞状态
    printf("parent wait child process die and exit now.\n");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xll102500/article/details/129422486
今日推荐