进程间通信--信号(进程间通信唯一的异步方式)

转载地址:

http://blog.chinaunix.net/uid-25120309-id-3301181.html向原作者致敬^ _ ^


正文:

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

一、信号的介绍

信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了那些系统事件。
如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递个它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞取消时才被传递给进程。


二、linux操作系统支持的信号

A. kill  -l



B.常用信号的含义



三、信号的产生


A.用户在终端按下某些键时,终端驱动程序会发送信号给前台进程,例如ctr+c产生SIGINT,  ctr + \产生SIGQUI信号,ctr + z产生SIGTSTP。

B.硬件异常产生信号,这些条件由硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给当前进程 。

C.一个进程调用int kill(pid_t pid,int sig)函数可以给另一个进程发送信号

D.可以用kill命令给某个进程发送信号,如果不明确指定信号则发送SIGTERM信号,该信号的默认处理动作是终止进程。

E.当内核检测到某种软件条件发生时也可以通过信号通知进程,例如闹钟超时产生SIGALRM信号,向读端已关闭的管道写数据时产生SIGPIPE信号。

四、进程对信号的处理


A.忽略此信号
B.执行该信号的默认处理动作
C .提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式成为捕捉(Catch)一个信号。
 

五、相关信号API


A.通过系统调用向一个指定的进程发送信号
#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);
参数说明:
第一个参数:指定发送信号的接收线程
第二个参数:信号的signum

案例一、
父进程从终端输入signum,然后发给子进程
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>

int main()
{
    int pid;

    if((pid = fork()) < 0)
    {
    
        perror("Fail to fork");
        exit(EXIT_FAILURE);
    
    }else if(pid == 0){
        
        while(1);
    
    }else{
        
        int signum;
        
        while(scanf("%d",&signum) == 1)
        {
            kill(pid,signum);
            system("ps -aux | grep a.out");
        }
    }

    return 0;
}
运行结果如下:
[root@zx signal]# ./a.out 
19
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.7/FAQ
root      8313  0.0  0.0   1732   364 pts/1    S+   19:50   0:00 ./a.out
root      8314  8.7  0.0   1728    72 pts/1    T+   19:50   0:00 ./a.out
root      8315  0.0  0.1   6208  1012 pts/1    S+   19:50   0:00 sh -c ps -aux | grep a.out
root      8317  0.0  0.1   5612   744 pts/1    S+   19:50   0:00 grep a.out
9
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.7/FAQ
root      8313  0.0  0.0   1732   364 pts/1    S+   19:50   0:00 ./a.out
root      8314  5.0  0.0      0     0 pts/1    Z+   19:50   0:00 [a.out] <defunct>
root      8318  0.0  0.1   6208  1012 pts/1    S+   19:50   0:00 sh -c ps -aux | grep a.out
root      8320  0.0  0.1   5612   740 pts/1    S+   19:50   0:00 grep a.out
18
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.7/FAQ
root      8313  0.0  0.0   1732   364 pts/1    S+   19:50   0:00 ./a.out
root      8314  1.9  0.0      0     0 pts/1    Z+   19:50   0:00 [a.out] <defunct>
root      8321  0.0  0.1   6208  1016 pts/1    S+   19:51   0:00 sh -c ps -aux | grep a.out
root      8323  0.0  0.1   5612   744 pts/1    S+   19:51   0:00 grep a.out
^C
[root@zx signal]# 


B.捕捉一个信号



对应的API
       #include <signal.h>

       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);
其原型:
       typedef void (*sighandler_t)(int);
参数说明:
signum  :  指定信号
handler  :  SIG_IGN忽略该信号,SIG_DFL采用系统默认方式处理信号,自定义的信号处理函数指针。

案例探究:

通过异步方式,给子进程收尸

注意:子进程在终止时会给父进程发SIGCHLD,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需要专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>

void child_exit_handler(int signum)
{
    if(signum == SIGCHLD)
    {
        printf("Child exit.\n");
        wait(NULL);
    }
}

int main()
{
    int pid;
    int i = 0;

    //想内核注册,处理 SIGCHLD信号的方式
    signal(SIGCHLD,child_exit_handler);

    if((pid = fork()) < 0)
    {
        perror("Fail to fork");
        exit(EXIT_FAILURE);

    }else if(pid == 0){
        
        for(i = 0;i < 5;i ++)
        {
            printf("child loop.\n");
            sleep(1);
        }
    
    }else{
        
        for(i = 0;i < 5;i ++)
        {
            printf("Father loop.\n");
            sleep(2);
        }

    }

    exit(EXIT_SUCCESS);
}

C.闹钟函数alarm
       #include <unistd.h>

       unsigned int alarm(unsigned int seconds);
alarm()也称为闹钟函数,它可以在进程中设置一个定时器。当定时器指定的时间到时,内核就向进程发送SIGALARM信号。

seconds:指定的秒数,如果参数seconds为0,则之前设置的闹钟会被取消,并将剩下的时间返回。

成功:如果调用此alarm()前,进程中已经设置了闹钟时间,则放回上一个闹钟时间的剩余时间,否则返回0。

alarm(100);
........

......

alarm(5);

出错:-1

案例探究:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

void handler(int signum)
{
    if(signum == SIGALRM)
    {
        printf("Recv SIGALARM.\n");
    }

    exit(EXIT_SUCCESS);
}

int main()
{
    int count = 0;
    int n = 0;

    signal(SIGALRM,handler);

    n = alarm(10);

    printf("n = %d.\n",n);
    
    sleep(2);

    n = alarm(5);

    printf("n = %d.\n",n);
    
    while(1)
    {
        printf("count = %d.\n", ++count);
        sleep(1);
    }

    return 0;
}

案例二、综合案例

使用FIFO实现clientA与clientB之间聊天
A.输入quit后,两个进程退出
B.如果在20秒内,没有等到另一端发来的消息,则认为对方已不在,此时终止。

clientA:(与clientB只是在对fifo a和b的调用上相反)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

#define MAX 100

void signal_handler(int signum)
{
    static int flag = 0;

    switch(signum)
    {
        case SIGALRM:
            if(flag == 0)
            {
                printf("The people is leaving,the system is closed in 10 seconds \
                        and you can input 'ctrl + c' cancel.\n");
                alarm(10);
            }else{
                
                kill(getppid(),SIGKILL);
                usleep(500);
                exit(EXIT_SUCCESS);
            }

            flag = 1;            
            break;

        case SIGINT:
            printf("The alarm is cancel.\n");
            alarm(0);
            break;
    }

}

int child_recv_fifo(char *fifo_name)
{
    int n,fd;
    char buf[MAX];

    if((fd = open(fifo_name,O_RDONLY)) < 0)
    {
        fprintf(stderr,"fail to open %s : %s.\n",fifo_name,strerror(errno));
        return -1;
    }

    signal(SIGALRM,signal_handler);
    signal(SIGINT,signal_handler);
    alarm(15); 
    while(1)
    {
        n = read(fd,buf,sizeof(buf));
        buf[n] = '\0';

        printf("Read %d bytes : %s.\n",n,buf);

        if(strncmp(buf,"quit",4) == 0 || n == 0)
        {
            kill(getppid(),SIGKILL);
            usleep(500);
            exit(EXIT_SUCCESS);
        }

        alarm(15);
    }

    return 0;
}

int father_send_fifo(char *fifo_name,int pid)
{
    int n,fd;
    char buf[MAX];

    if((fd = open(fifo_name,O_WRONLY)) < 0)
    {
        fprintf(stderr,"Fail to open %s : %s.\n",fifo_name,strerror(errno));
        return -1;
    }

    signal(SIGINT,SIG_IGN);

    while(1)
    {
        getchar();
        printf(">");

        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf)-1] = '\0';

        write(fd,buf,strlen(buf));

        if(strncmp(buf,"quit",4) == 0)
        {
            kill(pid,SIGKILL);
            usleep(500);
            exit(EXIT_SUCCESS);
        }
    }

    return 0;
}

int main(int argc,char *argv[])
{
    int pid;

    if(argc < 3)
    {
        fprintf(stderr,"usage %s argv[1].\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
    {
        perror("Fail to mkfifo");
        exit(EXIT_FAILURE);
    }

    if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
    {
        perror("Fail to mkfifo");
        exit(EXIT_FAILURE);
    }
    
    if((pid = fork()) < 0)
    {
    
        perror("Fail to fork");
        exit(EXIT_FAILURE);
    
    }else if(pid == 0){
        
        child_recv_fifo(argv[2]);
    
    }else{

        father_send_fifo(argv[1],pid);
    }

    exit(EXIT_SUCCESS);
}

client B
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

#define MAX 100

void signal_handler(int signum)
{
    static int flag = 0;

    switch(signum)
    {
        case SIGALRM:
            if(flag == 0)
            {
                printf("The people is leaving,the system is closed in 10 seconds \
                        and you can input 'ctrl + c' cancel.\n");
                alarm(10);
            }else{
                
                kill(getppid(),SIGKILL);
                usleep(500);
                exit(EXIT_SUCCESS);
            }

            flag = 1;            
            break;

        case SIGINT:
            printf("The alarm is cancel.\n");
            alarm(0);
            break;
    }

}

int child_recv_fifo(char *fifo_name)
{
    int n,fd;
    char buf[MAX];

    if((fd = open(fifo_name,O_RDONLY)) < 0)
    {
        fprintf(stderr,"fail to open %s : %s.\n",fifo_name,strerror(errno));
        return -1;
    }

    signal(SIGALRM,signal_handler);
    signal(SIGINT,signal_handler);
    alarm(15);
    while(1)
    {
        n = read(fd,buf,sizeof(buf));
        buf[n] = '\0';

        printf("Read %d bytes : %s.\n",n,buf);

        if(strncmp(buf,"quit",4) == 0 || n == 0)
        {
            kill(getppid(),SIGKILL);
            usleep(500);
            exit(EXIT_SUCCESS);
        }

        alarm(15);
    }

    return 0;
}

int father_send_fifo(char *fifo_name,int pid)
{
    int n,fd;
    char buf[MAX];

    if((fd = open(fifo_name,O_WRONLY)) < 0)
    {
        fprintf(stderr,"Fail to open %s : %s.\n",fifo_name,strerror(errno));
        return -1;
    }

    signal(SIGINT,SIG_IGN);

    while(1)
    {
        getchar();
        printf(">");

        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf)-1] = '\0';

        write(fd,buf,strlen(buf));

        if(strncmp(buf,"quit",4) == 0)
        {
            kill(pid,SIGKILL);
            usleep(500);
            exit(EXIT_SUCCESS);
        }
    }

    return 0;
}

int main(int argc,char *argv[])
{
    int pid;

    if(argc < 3)
    {
        fprintf(stderr,"usage %s argv[1].\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
    {
        perror("Fail to mkfifo");
        exit(EXIT_FAILURE);
    }

    if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
    {
        perror("Fail to mkfifo");
        exit(EXIT_FAILURE);
    }
    
    if((pid = fork()) < 0)
    {
    
        perror("Fail to fork");
        exit(EXIT_FAILURE);
    
    }else if(pid == 0){
        
        child_recv_fifo(argv[1]);
    
    }else{

        father_send_fifo(argv[2],pid);
    }

    exit(EXIT_SUCCESS);
}

D.将进程挂起函数pause
       #include <unistd.h>

       int pause(void);
解释如下:
The  pause()  library function causes the invoking process (or thread) to sleep until a signal is received that either terminates it or causes it to call a signal-catching function.

PS:(利用signal模拟中断)

利用signal可以在用户态实现简单的中断:如主进程在执行一项工作,但是主进程希望某一文件非空的时候立马处理其他工作,这个和硬件中断很相似
其实,这个就可以用signal来实现:主进程开一个子进程用来阻塞读取指定文件,若非空,则向父进程发送kill()命令,以通知其异步工作,而父进程处理signal的handler函数之后还会返回之前的调用点继续工作,这个和硬件中断真是很像!
linux下模拟这个硬件中断很方便哦~^ _ ^

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值