linux学习笔记之信号

信号机制是进程之间想回传递消息的一种方法,应用于异步事件的处理,信号全称为软中断信号,它被发送给一个正在被执行的进程以通知该进程有某一件事发生。
发出信号的原因有很多:

1.与进程终止有关的信号。当进程退出或子进程终止时,会发出这类信号
2.与进程列外事件相关的信号。如进程越界,或企图写一个只读的内存区域,或其它各种硬件错误。
3.与在系统调用期间遇到不可恢复条件相关的信号。如执行系统调用exec时,原有资源已经释放,而目前的资源又已经耗尽。
4.与执行系统调用时遇到非预测错误条件相关的信号。如执行一个并不存在的系统调用
5.在用户态下的进程发出的信号。如进程调用系统调用kill向其他进程发送信号
6.与终端交互相关的信号。如用户关闭一个终端..
7.跟踪进程执行的信号

在Linux里面每个进程都是按照进程描述符 task_struct 结构创建的,还有一个叫task vector的东西,从名字上就能看出来这是一个数组,这里面保存的是指向每一个进程的指针,即指向每一个task_struct的指针,因此一个Linux系统最大的进程数,取决于task vector这个数组的大小,一般默认是512个。在进程描述符task_struct里面,其中一项是Signal_Strct,在Signal_Strct这里面有一项list_head的描述符,在这里面有一个sigset_t表,定义了64种信号的所代表的含义。(信号列表)也就是说在每个进程之中,都有存着一个表,里面存着每种信号所代表的含义,而这也是信号机制的根本。由于信号的触发和发送是随机的,也就是异步的。接收进程是无法预知什么时间,会收到哪个信号的。下面就开始讲下信号的详细发送机制,举例说明,如果有A,B两个进程,A进程接收到出发条件,开始发送信号给B进程,信号并不是直接从进程A发送给进程B,而是要通过内核来进行转发。之所以要通过内核来转发,这样做的目的应该也是为了对进程的管理和安全因素考虑。因为在这些信号当中,SIGSTOP和SIGKILL这两个信号是可以将接收此信号的进程停掉的,而这类信号,肯定是需要有权限才可以发出的,不能够随便哪个程序都可以随便停掉别的进程。A进程发送的信号消息,其实就是根据上面的那个信号表,根据需要对相应的表项进行设置。内核接受到这个信号消息后,会先检查A进程是否有权限对B进程的信号表对应的项进行设置,如果可以,就会对B进程的信号表进行设置,这里面信号处理有个特点,就是没有排队的机制,也就是说某个信号被设置之后,如果B进程还没有来及进行响应,那么如果后续第二个同样的信号消息过来,就会被阻塞掉,也就是丢弃。内核对B进程信号设置完成后,就会发送中断请求给B进程,这样B进程就进入到内核态,这个时候进程B根据那个信号表,查找对应的此信号的处理函数,然后设置frame,设置好之后,跳回到用户态执行信号处理函数,处理完成后,再次返回到内核态,再次设置frame,然后再次返回用户态,从中断位置开始继续执行。这个frame其实就是在用户态和内核态之间跳转的时候,对堆栈现场的压栈保存。
linux下进程的信号处理大概就是这么一个过程,在真正使用的时候,就比较简单了,用kill函数发送信号,在接收进程里,通过signal或者signalaction函数调用sighandler(处理函数),来启动对应的函数处理信号消息。

kill函数
函数原型 int kill(pid_t,int signum); // 成功返回0,否则返回-1

pid>0 将信号发送给进程号为pid的进程
pid=0 将信号发送给当前进程所在进程组的所有进程
pid<0&&pid!=-1 向ID为pid绝对值的进程组中的所有进程发送信号
pid=-1 除自身外向所有进程ID大于1的进程发送信号
signum代表发送的信号,参见信号列表。signum为0时不发送任何信号,但照常进行错误检查,可用于检查目标进程是否存在以及当前进程是否有向目标进程发送信号的权限。

父进程向其子进程发送SIGABRT信号使子进程非正常结束

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

int main()
{
    pid_t pid;
    int status;
    if( !( pid=fork())){
        printf("this is child process!");
        sleep(10);
        printf("this is child process,again!"); //子进程非正常结束  这句并没有输出
        return 1;
    }
    else{
        printf("send signal to child process(%d)\n",pid);
        sleep(1);
        if( kill( pid,SIGABRT) == -1){
            printf("kill failed");
         }
        wait(&status);
        if(WIFSIGNALED(status)){
            printf("child process receive signal %d\n",WTERMSIG(status));   //参见进程笔记中status的宏定义及含义
        }
    }
    return0;
}

类似的发送信号函数还有
int raise(int signum); //signum为将要发送的信号值
int sigqueue(pid_t pid,int signum,const union sigval val); //pid为指定进程的ID signum为要发送的信号值,第三个参数指定了传递的参数
unsigned int alarm(unsigned int seconds); //此函数专为SIGALRM信号而设 使系统在一定时间后发送信号
int setitimer(int which,const struct itimerval *value,struct itimerval *oldvalue); //与alarm函数类似 也可以使系统在某一时刻发送信号,但更精确。
void abort(void); // 想进程发送SIGABORT信号,默认情况下进程会异常退出

signal函数
通过调用signal函数来注册某个特定信号的处理程序,函数原型
void(*signal(int signum,void(*handler)(int)))(int);
如果signal()调用成功,返回最后一次为安装信号signum而调用signal()时的handler值;失败则返回SIG_ERR。

POSIX定义:
typedef void(*sighandler_t)(int);
signhandler_t signal(int signum,sighandler_t handler);
但这种格式在不同的系统中有不同的定义。第一个参数指定信号的值,第二个参数指定针对前面信号值的处理,可以忽略该信号(参数设为SIG_IGN);SIGSTOP/SIGKILL这俩信号无法捕获和忽略,可以采用系统默认方式处理信号(参数设为SIG_DFL);也可以自己实现处理方式(参数指定一个函数地址)。

/*实现进程的软中断通信*/
/*按Ctrl+c键,可发送SIGINT信号给当前进程*/
/*使用系统调用fork()创建两个子程序,再用系统调用signal()注册父进程和子进程分别对待从键盘上来的中断信号(SIGINT信号)的行为;当有中断信号后,父进程用系统调用Kill()向两个子进程发出信号,子进程收到信号后分别输出
Child Processll is Killed by Parent!
Child Processl2 is Killed by Parent!
父进程等待两个子进程终止后,输出如下的信息后终止:
Parent process is Killed!
*/


#include<stdio.h>
#include<signal.h>
#include <stdlib.h>

int wait_mark;

void waiting()
{
    while(wait_mark==1);
}
void  stop()
{
    wait_mark=0;
}

int main()
{
    int p1,p2;
    while((p1=fork())==-1);//创建子进程1如出错会进入循环
    if(p1==0)
    {
                   wait_mark=1;
                   signal(SIGINT,SIG_IGN);//注册SIGINT信号发生后的行为(忽略)
                   signal(16,stop);//注册16号信号发生后的处理函数
                   waiting();
                   printf("Child Process 1 is Killed by Parent!\n");
                   exit(0);
    }
    else
    {
                   while((p2=fork())==-1);//创建子进程2如出错会进入循环
                   if(p2==0)
                   {
                      wait_mark=1;
                      signal(SIGINT,SIG_IGN);//注册SIGINT信号发生后的行为(忽略)      
                      signal(17,stop);//注册17号信号发生后的处理函数
                      waiting();

                      printf("Child Process 2 is Killed by Parent!\n");

                      exit(0);
                    }
                   else
                   {
                      wait_mark=1;
                      signal(SIGINT,stop);//注册SIGINT信号发生后的处理函数
                      waiting();
                      kill(p1,16);//发送16号信号给p1
                      kill(p2,17);//发送17号信号给p1
                      wait(0);
                      wait(0);
                      printf("Parent Process is Killed!\n");
                      exit(0);
                   }
           }

sigaction函数
此函数的功能是修改或检查与指定信号相关联的处理动作,函数原型
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact); //成功返回0 出错返回-1 。参数signum为需要捕捉的信号。函数act是一个结构体。包含的信息如下:

struct sigaction{
    void (*sa_handler)(int); //信号处理函数指针(old)
    void (*sa)sigaction)(int,siginfo_t *,void *); //信号处理函数指针(new) 
    sigset_t sa_mask;  //将要被阻塞的信号集合
    int sa_flags;      //信号处理方式的掩码
    void (*sa_restorer)(void);    //保留 并不使用
}
/*sigaction系统调用的使用方法。sigaction函数的功能是检查或修改与制定信号相关联的处理动作,可以完全代替signal函数*/


#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

int g_iSeq=0;

void SignHandlerNew(int iSignNo,siginfo_t *pInfo,void *pReserved)
{
    int iSeq=g_iSeq++;
    printf("%d Enter SignHandlerNew,signo:%d.\n",iSeq,iSignNo);
    sleep(3);      /*睡眠3秒钟*/
    printf("%d Leave SignHandlerNew,signo:%d\n",iSeq,iSignNo);
}

int main(void)
{
    char szBuf[20];      /*输入缓冲区,长度为20*/
    int iRet;
    struct sigaction act;   /*包含信号处理动作的结构体*/
    act.sa_sigaction=SignHandlerNew;  /*指定信号处理函数*/
    act.sa_flags=SA_SIGINFO;   /*表明信号处理函数由sa_sigaction指定*/
    sigemptyset(&act.sa_mask);
    /*信号集处理函数,将act.sa_mask所指向的信号集清空,*/
    /*即不包含任何信号*/
    sigaction(SIGINT,&act,NULL);   /*注册SIGINT信号*/
    sigaction(SIGQUIT,&act,NULL);  /* 注册SIGQUIT信号*/
    do{
       iRet=read(STDIN_FILENO,szBuf,sizeof(szBuf)-1);  /*从标准输入读入数据*/
       if(iRet<0)
       {
           perror("read fail.");
           break;   /* read出错退出*/
       }
       szBuf[iRet]=0;
       printf("Get: %s",szBuf);    /*打印终端输入的字符串*/
       }while(strcmp(szBuf,"quit\n")!=0);   /*输入"quit"时退出程序*/
     return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值