【IPC通信--信号】

发布时间:2024年01月06日

信号处理函数

? 信号发送函数
kill(), sigqueue(), raise(), alarm(), setitimer(), pause() abort()
? 信号安装函数
signal(), sigaction()
? 信号集操作函数
sigemptyset(), sigfillset(), sigaddset(), sigdelset(), sigismember()

?

信号发送函数—kill()

#include <sys/types.h>?
#include <signal.h>?
int kill(pid_t pid,int signo)?
? 功能
向进程或进程组发送一个信号 ( 成功返回 0 ; 否则,返回 -1
? 参数说明
pid :接收信号的进程(组)的进程号
? pid>0 :发送给进程号为 pid 的进程
? pid=0 :发送给当前进程所属进程组里的所有进程
? pid=-1 :发送给除 1 号进程和自身以外的所有进程
? pid<-1 :发送给属于进程组 -pid 的所有进程
signo :发送的信号
? Signo = 0 :不发送信号 , 可用于检查目标进程是否存在,以及当前进程是否具有向目标 进程 发送信号的权限( root 权限的进程可以向任何进程发送信号,非 root 权限的进程只能向属于同一个 session 或者同一个用户的进程发送信号)。

?

#include <sys/wait.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main( void ) {
    pid_t childpid;
    int status;
    int retval;
    childpid = fork();
    if ( -1 == childpid ){
       perror( "fork()" );
       exit( EXIT_FAILURE );
    }
    else if ( 0 == childpid ){
       puts( "In child process" );
       sleep( 100 );//让子进程睡眠,看看父进程的行为
       exit(EXIT_SUCCESS);
    }
    else{
         if ( 0 == (waitpid( childpid, &status, WNOHANG ))){
             retval = kill( childpid,SIGKILL ); 
             if ( retval ){
                 puts( "kill failed." );
                 perror( "kill" );
                 waitpid( childpid, &status, 0 );
             }
             else{
                 printf( "%d killed\n", childpid );
             }
         }
     }
     exit(EXIT_SUCCESS);
 }

?信号发送函数—sigqueue()

#include <sys/types.h>?
#include <signal.h>?
int sigqueue(pid_t
pid , int signo , const union sigval sigval_t )
? 调用成功返回 0 ;否则,返回 -1
? 功能
主要针对 实时信号 ,支持带有参数信号,与函数 sigaction() 配合使用
? 参数说明
pid :接收信号的进程 ID
signo :待发送信号
sigval_t :信号传递的参数( 4 字节)
? 说明
调用 sigqueue() 时, sigval_t 被拷贝到信号处理函数
sigqueue() 发送 非实时信号
? sigval_t 包含的信息仍然能够传递给信号处理函数

仍然不支持排队,所有相同信号都被合并为一个信号

sigqueue()向信号传递附加消息的流程

? sigqueue() 的第三个参数是 sigval 联合数据结构
调用 sigqueue() 时,该数据结构中的数据将被拷贝到信号处理函数 sigaction() 的第二个参数
这样,就可在发送信号的同时让信号传递一些附加信息

?

Kill()sigqueue()区别

sigqueue() kill() 传递更多的附加信息
sigqueue() 只能向一个进程 发送信号

信号发送函数—raise()

#include <signal.h>?
int raise(int signo)?
? 功能
向自身发送信号?
? 参数说明
signo :发送的信号
? signo=0 :不发送信号
? 返回值
成功时,返回 0
错误时,返回 -1
? 说明
raise() 可以通过 kill( ) 实现, raise(signo) 等价于
? kill(getpid( ), signo);

?

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

int main(void)
{
        printf("kill myself!\n");
        raise(SIGKILL);
        printf("can you see this?\n");
        return 0;
}

信号发送函数—alarm()

? #include <unistd.h>?
unsigned int?? alarm(unsigned int seconds)?
? 功能
设置定时器,当计时时间到达时,向进程发出 SIGALRM 信号
? 参数说明
seconds :等待秒数
? seconds 0 时,取消先前设置的闹钟,返回闹钟剩余时间
若之前未设闹钟,则返回 0
? 说明
alarm() 只发送一次信号
若需重复设定定时器,则要多次调用 alarm() 函数
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <signal.h>
void handler() {
	printf("hellon\n");
}
main()
{
	int i;
	signal(SIGALRM,handler);
	alarm(5);
	for(i=1;i<7;i++){
		printf("sleep %d \n",i);
		sleep(1);
	}
}

信号发送函数—setitimer()

? 函数原型
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
? 参数说明
which :逻辑定时器类型
? ITIMER_REAL :按 实际时间 计时,计时到达时向进程发送 SIGALRM 信号。
? ITIMER_VIRTUAL :计算进程在用户态执行的时间,计时到达将发送 SIGVTALRM 信号给进程。
? ITIMER_PROF :计算进程在用户态和核心态的总时间。计时到达将发送 SIGPROF 信号给进程。
ITIMER_VIRTUAL 是一对,该定时器经常用来统计进程在用户态和核心态花费的时间
value :指明定时器的时间
ovalue :如果不为空,则保存上次调用设定的值
? 功能
指定一段时间后,执行某个 function;

每间格一段时间就执行某个function

? 返回值
成功时,返回 0
错误时,返回 -1
? struct itimerval 结构 定义

struct itimerval {

??? struct?? timeval?? it_interval;???? /*定时器周期*/

??? struct?? timeval?? it_value;??? /*定时器剩的时间,为0时发信号*/

};

? struct timeval 结构定义

struct timeval {

??? long? tv_sec;?? /*秒*/

??? long? tv_usec; /*微秒,1=1000000微秒*/

};

it_value0是不会触发信号的,所以要能触发信号,it_value得大于0;如果it_interval为零,只会延时,不会定时(也就是说只会触发一次信号)

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

void signalHandler(int signo)
{
    switch (signo){
        case SIGALRM:
            printf("Caught the SIGALRM signal!\n");
            break;
   }
}

int main(int argc, char *argv[])
{
    signal(SIGALRM, signalHandler);
    struct itimerval new_value, old_value;
    new_value.it_value.tv_sec = 1;
    new_value.it_value.tv_usec = 0;
    new_value.it_interval.tv_sec = 2;
    new_value.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &new_value, &old_value); 
    for(;;); 
    return 0;
}

信号发送函数—pause()

? 功能
使当前进程暂停,进入睡眠状态,直到被信号所中断,即将进程挂起等待信号到来。
? 函数原型
int pause(void); ?
? 返回值
-1
? 举例
使用 alarm( ) pause( ) 实现 sleep( ) 功能

信号的安装(设置信号关联动作)

? 如果进程要处理某一信号,那么就要在进程中安装该信号。安装信号主要用来确定信号值及进程针对该信号值的动作之间的映射关系,即进程将要 处理哪个信号 ;该信号被传递给进程时,将 执行何种操作
? linux 主要有两个函数实现信号的安装: signal() sigaction() 。其中 signal() 在非可靠信号系统调用的基础上实现 , 是库函数。它只有 两个参数 ,不支持信号传递信息,主要是用于前 32 种非实时信号的安装;而 sigaction() 是较新的函数(由两个系统调用实现: sys_signal 以及 sys_rt_sigaction ), 有三个参数 ,支持信号传递信息,主要用来与 sigqueue() 系统调用配合使用,当然, sigaction() 同样支持非实时信号的安装。 sigaction() 优于 signal() 主要体现在 支持信号带有参数。

信号安装函数—signal()

? 原型定义
void (*signal(int signum, void (*handler)(int))) (int);
? 参数说明
signum :需要安装的信号
handler :与安装信号相关的处理函数,可以是 SIG_IGN SIG_DFL
? SIG_IGN :忽略该信号
? SIG_DFL :执行默认操作函数
? 返回值
成功时,返回新安装信号处理函数 handler 的值
失败时,返回 SIG_ERR
? 底层系统调用
sys_signal(int sig,? __sighandler_t handler)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void sig_usr(int sig);
int main(int argc, char* argv[])
{
        int i=0;
        if(signal(SIGUSR1, sig_usr) == SIG_ERR)
	printf("Can't catch SIGUSR1\n");
       if(signal(SIGUSR2, sig_usr) == SIG_ERR)
                printf(“Can‘t catch SIGUSR2\n”);   
        while(1) {
                printf("%2d\n", i);
                pause();
                i++;
        }
        return 0;
}
void sig_usr(int sig)
{
        if(sig == SIGUSR1)
                printf("Received SIGUSR1\n");
        else if(sig == SIGUSR2)
                printf("Received SIGUSR2\n");
        else
                printf("Undeclared signal %d \n",sig);
}

执行:

? Gcc signal.c –o signal
? ./signal&( 以后台程序运行,并显示 pid )
? Kill? –SIGUSR1 pid

信号安装函数—sigaction()

? 原型定义

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

sigaction函数用于设定进程接收到特定信号后的行为。

第一个参数为信号的值 ,可以为除 SIGKILL SIGSTOP 外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)。
第二个 参数是指向结构 sigaction 的一个实例的指针,在结构 sigaction 的实例中, 指定了对特定信号的处理 ,如果为空,进程会以缺省方式对信号处理;
第三个参数 oldact 指向的对象 用来保存原来对相应信号的处理 ,可指定 oldact NULL 。如果把第二、第三个参数都设为 NULL ,那么该函数可用于检查信号的有效性。
第二个参数最为重要,其中包含了 对指定信号的处理 信号所传递的信息 信号处理函数执行过程中应屏蔽掉哪些函数 等等。
? 底层系统调用
sys_sigaction ( int sig, const struct old_sigaction __user *act, struct old_sigaction __user * oact )

?????????sigaction结构定义如下:

struct sigaction {
    union{
            __sighandler_t _sa_handler;
            void (*_sa_sigaction)(int, struct siginfo *, void *);
         }_u
         sigset_t sa_mask;
         unsigned long sa_flags;
         void (*sa_restorer)(void);
}
? sa_restorer ,已过时, POSIX 不支持它,不应再被使用
? 联合 数据结构中的两个元素 _ sa_handler 以及 *_ sa_sigaction 指定信号 关联函数 即用户指定的信号处理函数 。除了可以是用户自定义的处理函数外,还可以为 SIG_DFL( 采用缺省的处理方式 ) ,也可以为 SIG_IGN (忽略信号)。
? _ sa_handler 指定的处理函数只有一个参数,即信号值,所以信号不能传递除信号值之外的任何信息;
? _ sa_sigaction 指定的信号处理函数带有三个参数,是为实时信号而设的(当然同样支持非实时信号),它指定一个 3 参数信号处理函数。 第一个参数为信号值 第三个参数没有使用 posix 没有规范使用该参数的标准), 第二个参数是指向 siginfo_t 结构的指针,结构中包含 信号携带的数据值 ,参数所指向的结构如下:

?siginfo_t {
? ? ? ? ? ? ? ? ? int ? ? ?si_signo; ?/* 信号值,对所有信号有意义*/
? ? ? ? ? ? ? ? ? int ? ? ?si_errno; ?/* errno值,对所有信号有意义*/
? ? ? ? ? ? ? ? ? int ? ? ?si_code; ? /* 信号产生的原因,对所有信号有意义*/
? ? ? ? ? ? ? ? ? pid_t ? ?si_pid; ? ?/* 发送信号的进程ID,对实时信号以及SIGCHLD有 ? ? ? ? ? ? ? ?意义 */
? ? ? ? ? ? ? ? ? uid_t ? ?si_uid; ? ?/* 发送信号进程的真实用户ID,对kill(2),实时信号以及SIGCHLD有意义 */
? ? ? ? ? ? ? ? ? ?int ? ? ?si_status; /* 退出状态,对SIGCHLD有意义*/
? ? ? ? ? ? ? ? ? clock_t ?si_utime; ?/* 用户消耗的时间,对SIGCHLD有意义 */
? ? ? ? ? ? ? ? ? clock_t ?si_stime; ?/* 内核消耗的时间,对SIGCHLD有意义 */
? ? ? ? ? ? ? ? ? sigval_t si_value; ?/* 信号值,对所有实时有意义,是一个联合数据结构,
? ? ? ? ? ? ?/*可以为一个整数(由si_int标示,也可以为一个指针,由si_ptr标示)*/?? ?
? ? ? ? ? ? ? ? ?void * ? si_addr; ? /* 触发fault的内存地址,对SIGILL,SIGFPE,SIGSEGV,SIGBUS 信号有意义*/
? ? ? ? ? ? ? ? ?int ? ? ?si_band; ? /* 对SIGPOLL信号有意义 */
? ? ? ? ? ? ? ? ? int ? ? ?si_fd; ? ? /* 对SIGPOLL信号有意义 */
}

? sa_mask : 信号的集合。 指定在信号处理程序执行过程中,哪些信号应当被阻塞。缺省情况下当前信号本身被阻塞,防止信号的嵌套发送。
? sigaction ()安装信号的处理函数执行过程中由 sa_mask 指定的信号才被阻塞。
? sa_flags 用于更改指定信号的行为 。比较重要的标志位是 SA_SIGINFO ,当设定了该标志位时,表示信号附带的参数可以被传递到信号处理函数中,因此,应该为 sigaction 结构中的 sa_sigaction 指定处理函数,而不应该为 sa_handler 指定信号处理函数,否则,设置该标志变得毫无意义。即使为 sa_sigaction 指定了信号处理函数,如果不设置 SA_SIGINFO ,信号处理函数同样不能得到信号传递过来的数据,在信号处理函数中对这些信息的访问都将导致段错误( Segmentation fault )。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void myFun(int sig);
int main(int argc, char* argv[])
{
        struct sigaction act, oldact;
        act.sa_handler = myFun;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGUSR1, &act, &oldact);
        while(1){
                printf("Hello world!\n");
                pause();
        }
}
void myFun(int sig)
{
        printf("I got a signal:%d\n",sig);
}
? Gcc sigacton1.c –o sigaction
? ./ sigaction &
? Kill –SIGUSR1 pid
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

void func(int signo, siginfo_t *info, void *p)
{
        printf("signo = %d\n", signo);
        printf("sender pid = %d\n", info->si_pid);
}
int main()
{
        struct sigaction   act,  oldact;
        sigemptyset(&act.sa_mask);
        act.sa_flags = SA_SIGINFO;
        act.sa_sigaction = func;
        sigaction(SIGUSR1, &act, &oldact);
        while(1)
        {
                printf("pid is %d hello!\n",getpid());
                pause();
        }
}

?在另外一个终端发送信号给当前进程?

sigqueue()函数调用示例在不同进程间传递整型参数.信号接收程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
void myFun(int, siginfo_t*, void* myfun);
int main(int argc, char* argv[])
{
        struct sigaction act;
        int sig;
        pid_t pid;
        pid = getpid();
        printf("pid is%d\n",pid);

        sigemptyset(&act.sa_mask);
        act.sa_sigaction = myFun;
        act.sa_flags = SA_SIGINFO;
        if(sigaction(SIGUSR1, &act, NULL)<0)
        {
                printf("install sig error\n");
                return 0;
        }
        while(1)
        {
                sleep(2);
                printf("wait for the signal\n");
        }
}
void myFun(int signo, siginfo_t *info, void* myfun)
{
        printf("the int value is %d\n",info->si_int);
        printf("Recv signum is%d\n",signo);
        //raise(SIGKILL);
}

sigqueue()函数调用示例在不同进程间传递整型参数.信号发送程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
main(int argc, char* argv[])
{
        pid_t pid;
        int signum;
        union sigval mysigval;
        pid = (pid_t)atoi(argv[1]);
        mysigval.sival_int = 8;
        if(sigqueue(pid, SIGUSR1, mysigval)==-1)
                printf("send error\n");
        sleep(2);
        return 0;
}

signal()sigaction()的区别

? 不同点
signal()
? 安装的信号不能向信号处理函数传递信息
sigaction ()
? 可设置进程的信号掩码,返回设置之前的 sigaction 结构
? 安装的信号可以向信号处理函数传递信息
? 相同点
都可以为指定的信号设置信号处理函数
共用同一个内核函数 do_sigaction ()

信号集操作函数

? 信号集定义
? 函数原型
int sigemptyset ( sigset_t *set);
? 初始化信号集合 set ,将 set 设置为空
int sigfillset ( sigset_t *set);
? 初始化信号集合,将信号集 set 设置为包含所有信号的集合
int sigaddset ( sigset_t *set, int signo );
? 将信号 signo 加入到信号集 set
int sigdelset ( sigset_t *set, int signo );
? 将信号 signo 从信号集 set 中删除
int sigismember ( sigset_t *set, int signo );

查询信号signo是否在信号集set

信号操作函数sigprocmask( )

? 功能
通过信号集 set 修改进程的信号阻塞集
? 函数原型
int sigprocmask (int how, const sigset_t *set, sigset_t * oset );
? 参数
how :函数操作方式
? SIG_BLOCK :增加一个信号集到当前进程的阻塞集中
? SIG_UNBLOCK :从当前阻塞集中删除一个信号集
? SIG_SETMASK :将当前信号集设置为信号阻塞集合
set :当前进程的信号集
oset :保存当前进程的信号阻塞集
? 说明
在使用之前需先设置好信号集合 set

其他信号处理函数

? sigpending ( sigset_t *set))
获取当前进程的未决信号集
set 保存返回结果
? sigsuspend (const sigset_t *mask))
在接收到某个信号之前,临时用 mask 替换进程的信号掩码,并暂停进程执行,直到收到信号为止
sigsuspend 返回后将恢复调用之前的信号掩码
信号处理函数完成后,进程将继续执行
该系统调用始终返回 -1 ,并将 errno 设置为 EINTR

task_struct中与信号处理相关的成员

? struct signal_struct *signal
信号 描述符结构 每种信号选择处理函数
? struct sighand_struct * sighand
包含信号处理函数描述符的数组,信号作为数组序号
? sigset_t blocked, real_blocked ;?
被阻塞信号的掩码,每种信号类型对应一个元素
? struct sigpending pending;
维护本进程中的未决信号
? sigset_t saved_sigmask ;
保存信号掩码

定义TIF_RESTORE_SIGMASK时恢复信号掩码

task_struct信号处理相关数据结构关系图

?

文章来源:https://blog.csdn.net/j8267643/article/details/135424662
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。