Linux-C 进程通信之信号

Linux-C 进程通信之信号

一、简述

      记--进程之间使用信号进行通信。异步信号包含: 非实时信号 、实时信号。

       实时信号一定会响应,非实时信息号不一定会响应(可能会被忽略,或丢失)

     信号一般有以下设定:
            1,捕捉    (收到某个信号,做指定的动作,而不是做默认的)
            2,忽略    (收到某个信号,不做什么动作)
            3,阻塞    (收到某个信号,先做完当前事情,然后在响应信号)
            4,按照默认动作

     注:SIGKILL,SIGSTOP不能被捕捉

       关于信号的详细说明请参考man手册:man  7  signal。

二、signal()与kill()

signal()函数
功能 简单的信号处理函数(信号捕捉函数)
头文件 #include <signal.h>
原型

typedef void (*sighandler_t)(int); //信号处理函数的 指针

sighandler_t signal(int signum, sighandler_t handler);

参数

signum:要捕捉的信号

handler:处理方式

               1、SIG_IGN:忽略这个信号
               2、SIG_DFL:按照默认动作执行这个信号
               3、如果是一个函数,则是捕捉这个信号,收到这个信号的时候去执行这个函数。

                

返回值

成功:返回信号处理程序的先前值

出错:返回SIG_ERR,并设置errno

备注 详细请查看man手册:man 2 signal (第7本也有)。可以配合kill()函数使用,kill()函数向某个进程发送某个指定的信号。

测试代码1:捕捉中断信号(SIGINT),接收到中断信号后,打印一句话"receive signal  %d\n"。其中在终端按下Ctrl+c就发出终端信号,未捕捉之前,终端信号默认结束程序。

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

void SIGINT_handle(int sig_num)
{
	printf("reveice signal %d \n", sig_num);
}

int main(int argc, char argv[])
{
	int i;
	signal(SIGINT, SIGINT_handle);

	for(i=0; i<10; i++)
	{
		printf("i=%d \n", i);
		sleep(1);
	}
	return 0;
}

 运行结果

捕捉终端信号:

    

未捕捉的情况

kill()函数
功能 向一个进程发送信号
头文件 #include <sys/types.h>
#include <signal.h>
原型 int kill(pid_t pid, int sig);
参数

pid:进程ID  (process id)

sig:要发送的信号   

返回值

成功:返回0

失败:返回-1,并设置error。

备注 详细请查看man手册:man 2 kill

测试代码2:使用kill()函数向signal程序发送SIGUSR1信号,a程序捕捉到之后打印一句话"receive signal  %d\n",然后退出。

kill.c文件

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

int main(int argc, char *argv[])
{
	int pid, ret;
	if(argc <2)
	{
		printf("arg error![./kill pid]\n");
		return -1;
	}
	
	sscanf( argv[1], "%d", &pid);
	ret = kill(pid, SIGUSR1);
	if(ret != 0 )
	{
		perror("kill SIGUSR1 error");
		return -1;
	}
	printf("send SIGUSR1 to [pid]%d successful!\n", pid);

	return 0;
}

signal.c文件

#include <stdio.h>
#include <unistd.h> //sleep()
#include <signal.h> 
#include <stdlib.h> //exit()

void SIGUSR1_handle(int sig_num)
{
	printf("reveice signal %d \n", sig_num);
	exit(0);
}

int main(int argc, char argv[])
{
	int i;
	signal(SIGUSR1, SIGUSR1_handle);

	for(i=0; i<30; i++)
	{
		printf("i=%d \n", i);
		sleep(1);
	}
	return 0;
}

运行结果:

三、sigprocmask()信号的阻塞

         信号的阻塞:在阻塞过程中,信号不会丢失,会被挂起,等解开阻塞的时候再响应。

       

sigprocmask()函数
功能 阻塞信号,延迟响应 。(检查并更改阻塞的信号)
头文件 #include <signal.h>
原型 int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数

how:对信号的操作(应用于信号集合中的所有函数)

          SIG_BLOCK:添加信号集合里面的信号进行阻塞(原本的设置上添加设置)
          SIG_UNBLOCK:解除信号集合里面的信号的阻塞。
          SIG_SETMASK: 直接阻塞信号集合里面的信号,原本的设置直接被覆盖。

set:要设置的信号集合
            int sigemptyset(sigset_t *set);//清空信号集合
            int sigfillset(sigset_t *set);//将所有信号登记进集合里面
            int sigaddset(sigset_t *set, int signum);//往集合里面添加signum信号
            int sigdelset(sigset_t *set, int signum);// 往集合里面删除signum信号
            int sigismember(const sigset_t *set, int signum);//测试信号集合里面有无signum信号

oldset:用来保存旧的信号集合设置
            如果这里是一个具体的指针,则将原本的设置保存在这里
            如果是NULL则不保存。

返回值

成功:返回0

失败:返回-1,并设置error

备注 在阻塞状态时,非实时信号可能会丢失

例子:对中断信号、退出信号进行阻塞。然后向sigprocmask程序发送中断信号(Ctrl+c)、退出信号(Ctrl+\)。那么sigprocmask程序收到信号后,不会立即响应,直到解除阻塞,才进行响应。其中,发送了两次中断信号(姑且命名为中断A,中断B),但是只响应了一次,是因为: 在某个时刻或阻塞时 接收到多个同一种非实时信号时,只会响应一次,其余丢弃,如果有一个已经开始响应了但是还没有结束,然后又来一个,那么这一个也会响应。

测试代码:

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


void signal_handle(int sig_num)
{
	printf("receive signal=%d\n", sig_num);

}

int main(void)
{
	int i;
	sigset_t sigset;

	signal(SIGINT, signal_handle);
	signal(SIGQUIT, signal_handle);

	sigemptyset(&sigset);//清空集合

	sigaddset(&sigset, SIGINT);//将中断信号添加到 阻塞集合中
	sigaddset(&sigset, SIGQUIT);//将退出信号添加到 阻塞集合中

	sigprocmask(SIG_BLOCK, &sigset, NULL);//对信号进行阻塞

	for(i=0; i<10; i++)
	{
		printf("i=%d \n", i);
		sleep(1);
	}

	
	sigprocmask(SIG_UNBLOCK, &sigset, NULL);//解开对信号的阻塞

	printf("hello\n");

	return 0;
}

运行结果:

四、带数据的信号的发送与处理:sigaction()与sigqueue()

sigaction()函数
功能 检查并改变信号动作(捕捉信号,指定处理动作)
头文件 #include <signal.h>
原型 int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
参数

signum:捕捉的信号

act:信号动作结构体,用来登记对这个信号进行处理的动作。

          

    struct sigaction 
    {
       void     (*sa_handler)(int);    //不带参数的信号处理函数,默认执行这个不带参数的信号处理函数
       void     (*sa_sigaction)(int, siginfo_t *, void *);//带参的信号处理函数,如果想要使能这个信号处理函数,
                                                         //需要设置一下sa_flags为SA_SIGINFO
       sigset_t   sa_mask;        //信号阻塞设置
                                             //1,在信号处理函数执行的过程当中阻塞掉指定的信号,

                                                     //指定的信号过来将会被挂起,等函数结束后再执行
                                             //2,sigset_t信号集合类型,参照sigprocmask中sigset的使用方式。


       int        sa_flags;        //信号的操作标识,例如设置使用的是带参信号处理函数,还是不带参数的
       void     (*sa_restorer)(void);    //被遗弃的设置
    };

oldact:用来保存原来的设置,如果是NULL则不保存。

返回值

成功:返回0

失败:返回-1,并设置error

备注 siginfo_t 
{
               int      si_signo;     /* Signal number */
               int      si_errno;     /* An errno value */
               int      si_code;      /* Signal code */
               int      si_trapno;    /* Trap number that caused
                                         hardware-generated signal
                                         (unused on most architectures) */
               pid_t    si_pid;       /* Sending process ID */
               uid_t    si_uid;       /* Real user ID of sending process */
               int      si_status;    /* Exit value or signal */
               clock_t  si_utime;     /* User time consumed */
               clock_t  si_stime;     /* System time consumed */
               sigval_t si_value;     /* Signal value */
               int      si_int;       /* POSIX.1b signal */
               void    *si_ptr;       /* POSIX.1b signal */
               int      si_overrun;   /* Timer overrun count;
                                         POSIX.1b timers */
               int      si_timerid;   /* Timer ID; POSIX.1b timers */
               void    *si_addr;      /* Memory location which caused fault */
               long     si_band;      /* Band event (was int in
                                         glibc 2.3.2 and earlier) */
               int      si_fd;        /* File descriptor */
               short    si_addr_lsb;  /* Least significant bit of address
                                         (since Linux 2.6.32) */
               void    *si_lower;     /* Lower bound when address violation
                                         occurred (since Linux 3.19) */
               void    *si_upper;     /* Upper bound when address violation
                                         occurred (since Linux 3.19) */
               int      si_pkey;      /* Protection key on PTE that caused
                                         fault (since Linux 4.6) */
               void    *si_call_addr; /* Address of system call instruction
                                         (since Linux 3.5) */
               int      si_syscall;   /* Number of attempted system call
                                         (since Linux 3.5) */
               unsigned int si_arch;  /* Architecture of attempted system call
                                         (since Linux 3.5) */
};
sigqueue函数
功能 将信号和数据发送到指定进程
头文件 #include <signal.h>
原型 int sigqueue(pid_t pid, int sig, const union sigval value);
参数

pid:进程ID

sig:要发送的信号

value:附加数据

           union sigval 
           {
                int   sival_int;//只附加一个int数据
                void *sival_ptr;//附加更多数据(附加一个地址) 
            };

         //注:这是共用体、两者取其一
        

返回值

成功:返回0

失败:返回-1,并设置error

备注 跟sigaction结合使用

例子:sigqueue程序向sigaction程序发送SIGUSR1信号,并附带int型数据123,sigaction捕捉到信号后,打印出信号发送方的PID和附加的数据,然后退出。

       测试代码:

sigaction.c文件

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


void sigaction_handle(int signum, siginfo_t *info, void * ucontext)
{

	printf("info.si_pid=%d\n", info->si_pid);//打印信号发送方的pid
	printf("info.si_int=%d\n", info->si_int);//打印信号发送方的附加数据
	exit(0);

}

int main(void)
{
	int i;
	struct sigaction act, oldact;
	
	act.sa_flags = SA_SIGINFO;//使用带参数的信号处理函数
	act.sa_sigaction = sigaction_handle;

    //sigemptyset(&act.sa_mask);//清空原来集合
	//sigfillset(&act.sa_mask);//将所有信号添加到集合

	sigaction(SIGUSR1, &act, &oldact);//捕捉sigqueue发送的SIGUSR1信号

	for(i=0; i<30; i++)
	{
		printf("i=%d\n", i);
		sleep(1);
	}
	return 0;
}

sigqueue.c文件

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

#include <sys/types.h>//getpid()
#include <unistd.h>

int main(int argc, const char *argv[])
{
	if(argc < 2)
	{
		printf("arg error\n");
		return -1;
	}
	union sigval value;
	value.sival_int = 123;

	int sig_num, pid;
	
	sscanf(argv[1], "%d", &pid);
	sigqueue(pid, SIGUSR1, value);//向指定的pid发送SIGUSR1信号

	printf("mypid=%d\n", getpid());//打印当前进程的ID号

	return 0;
}

       运行结果: 

五、补充

  1、查看进程相关信息

        ps:查看进程状态   (一个瞬间的进程状态,静态)
        top:“实时查看” ,按q退出    (实时动态显示)
        pstree  树状查看  (可以看出所属子进程)   

        

 2、对某个进程发送信号

    kill  -s   指定发送一个信号      进程的pid
    killall:指定一个应用程序名字去发送信号

    kill  -l   查看系统有什么信号 

   


其中SIGKILL,SIGSTOP只能按照系统默认动作执行,不能捕捉,阻塞及忽略

前面31个是非实时信号:
                1,每一个非实时信号对应一个信号名
                2,每一个非实时信号基本上都会对应一个系统事件
                3,信号有可能会丢失

后面的31个是实时信号:
               1,信号不会丢失
               2,优先级比非实时信号要高

2、信号发送:
    1,自己发给自己: raise()函数
    2,发送信号给其他进程:
              kill -s 信号 进程号
              killall -s 信号 进程名字


3、信号的继承
    1,信号初始化的设置是会被子进程继承的
    2,信号的阻塞设置是会被子进程继承的
    3,被挂起的信号是不会被子进程继承的

4、信号处理函数里面应该注意的地方:
    1,不应该在信号处理函数里面操作全局变量(如果要操作,记得加锁)
    2,应该注意一下一些函数调用是否安全,可以查看man 7 signal

5、测试所有信号的响应优先级

        实时信号34-64:数字大的优先响应;

       从1号信号开始发送

       测试代码:

kill.c文件

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

int main(int argc, const char *argv[])
{
	int signum, pid;
	if(argc < 2)
	{
		printf("arg error\n");
		return -1;
	}
	
	sscanf(argv[1], "%d", &pid);
	//向指定的进程发送所有的信号(不发送SIGKILL、SIGSTOP)
	for(signum=1; signum<=64; signum++)
	{
		//系统没有32、33编号的信号,避免程序被kill掉或停止,不发送SIGKILL、SIGSTOP
		if(signum == 33 || signum == 32 || signum == SIGKILL || signum == SIGSTOP)
                          continue;
		kill(pid, signum );
	}

	printf("send signal over\n");

	return 0;
}

signal.c文件

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

void signal_handle(int signum)
{
	printf("signal_handle signum=%d\n", signum);
}

int main(void)
{
	int signum, i;
	struct sigaction act, oldact;

	//捕捉所有的信号
	for(signum = 1; signum<65; signum++)
	{		
		if(signum == 33 || signum == 32)
			continue;

		signal(signum, signal_handle);

	}

	printf("wait signal\n");	
	//延时30秒,等待信号处理完毕
	for(i=0; i<30; i++)
	{
		sleep(1);
	}
	
	return 0;
}

      运行结果:

     

signal_handle signum=64
signal_handle signum=63
signal_handle signum=62
signal_handle signum=61
signal_handle signum=60
signal_handle signum=59
signal_handle signum=58
signal_handle signum=57
signal_handle signum=56
signal_handle signum=55
signal_handle signum=54
signal_handle signum=53
signal_handle signum=52
signal_handle signum=51
signal_handle signum=50
signal_handle signum=49
signal_handle signum=48
signal_handle signum=47
signal_handle signum=46
signal_handle signum=45
signal_handle signum=44
signal_handle signum=43
signal_handle signum=42
signal_handle signum=41
signal_handle signum=40
signal_handle signum=39
signal_handle signum=38
signal_handle signum=37
signal_handle signum=36
signal_handle signum=35
signal_handle signum=34
signal_handle signum=30
signal_handle signum=29
signal_handle signum=28
signal_handle signum=27
signal_handle signum=26
signal_handle signum=25
signal_handle signum=24
signal_handle signum=23
signal_handle signum=22
signal_handle signum=21
signal_handle signum=20
signal_handle signum=17
signal_handle signum=16
signal_handle signum=15
signal_handle signum=14
signal_handle signum=13
signal_handle signum=12
signal_handle signum=10
signal_handle signum=6
signal_handle signum=3
signal_handle signum=2
signal_handle signum=1
signal_handle signum=31
signal_handle signum=11
signal_handle signum=8
signal_handle signum=7
signal_handle signum=5
signal_handle signum=4

================================================================================================

 从64号开始发送

       运行结果:

          

signal_handle signum=64
signal_handle signum=63
signal_handle signum=62
signal_handle signum=61
signal_handle signum=60
signal_handle signum=59
signal_handle signum=58
signal_handle signum=57
signal_handle signum=56
signal_handle signum=55
signal_handle signum=54
signal_handle signum=53
signal_handle signum=52
signal_handle signum=51
signal_handle signum=50
signal_handle signum=49
signal_handle signum=48
signal_handle signum=47
signal_handle signum=46
signal_handle signum=45
signal_handle signum=44
signal_handle signum=43
signal_handle signum=42
signal_handle signum=41
signal_handle signum=40
signal_handle signum=39
signal_handle signum=38
signal_handle signum=37
signal_handle signum=36
signal_handle signum=35
signal_handle signum=34
signal_handle signum=30
signal_handle signum=29
signal_handle signum=28
signal_handle signum=27
signal_handle signum=26
signal_handle signum=25
signal_handle signum=24
signal_handle signum=23
signal_handle signum=22
signal_handle signum=21
signal_handle signum=20
signal_handle signum=17
signal_handle signum=16
signal_handle signum=15
signal_handle signum=14
signal_handle signum=13
signal_handle signum=12
signal_handle signum=10
signal_handle signum=6
signal_handle signum=3
signal_handle signum=2
signal_handle signum=1
signal_handle signum=31
signal_handle signum=11
signal_handle signum=8
signal_handle signum=7
signal_handle signum=5
signal_handle signum=4

猜你喜欢

转载自blog.csdn.net/nanfeibuyi/article/details/81945408
今日推荐