【系统编程】进程间通信--信号

linux系统编程大纲
1. 进程概念、进程诞生与死亡、进程函数接口、进程意义。
2. 进程通信方式,有名管道、无名管道、信号、消息队列、共享内存、信号量。
3. 进程信号集、如何设置阻塞属性。
4. 线程概念,线程与进程区别?线程诞生与死亡,函数接口。
5. 线程同步互斥方式,有名信号量,无名信号量,互斥锁,读写锁,条件变量。
6. 拓展 -> 线程池  -> 同时处理多个任务。
 


目录

一、进程之间的通信方式  -> 信号

1、 在linux下,信号有哪些?

二、信号的捕捉

1、 什么是信号的捕捉?

2、如何捕捉信号?  

三、信号捕捉函数signal的第二个参数

四、关于信号两个拓展函数接口

五. 信号集

六、如何设置信号的阻塞属性?


一、进程之间的通信方式  -> 信号

1、 在linux下,信号有哪些?

使用"kill -l"命令查看所有的信号。  "kill"命令在linux下不是"杀死"含义,而是专门用于发送信号。 "-l"  list列表。

        1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
        6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
      11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
      16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP

       1/2/3  -> 信号值
       SIG  -> 信号的前缀
       HUP  -> 信号的名字

其实这些信号是一些宏定义来的,例如: #define SIGHUP 1
这些宏定义是被定义在一个头文件: /usr/include/asm-generic/signal.h  -> 信号专属头文件

常见的信号对应的含义:   查看系统信号---  kill -l   或者   man 7 signal

1>关闭终端产生
2> ctrl +c
3> ctrl + \
4> 非法指令
5> 从用户空间嵌入内核产生
6> abort函数产生
7> 两个设备信号断掉 地址映射出错  pipe(最快)  mmap函数(高最效) socket(最稳定)
8> 浮点数异常
9> 杀死进程
10、12>  由用户定义行为
11> 段错误
13> 管道破裂   <----- 关掉管道读端
14>闹钟、警报
15>进程终止
17> 子进程编程僵尸
18> 继续
19>暂停
23>紧急数据

29> 异步IO信号


2、如何给进程发送信号?


1) 给进程PID号发送信号  -> kill命令
      使用格式: kill -信号值 进程的PID号

     9) SIGKILL  -> 杀死进程,进程收到该信号时,会马上退出。

     例如:查看系统所有进程的PID号  ->  ps -ef
                           PID
             gec      10801  2465 94 19:12 pts/1    00:00:25 ./a
             gec@ubuntu:~$ kill -9 10801   -> 发送9信号给10801进程,10801进程收到信号马上退出!
                                        kill -SIGKILL 10801

2)给进程的名字发送信号   -> killall命令
      使用格式: killall -信号值 进程的名字

       gec@ubuntu:~$ killall -9 a
                                 killall -SIGKILL a

3)在程序中使用kill函数来发送信号给别的进程。   -> kill() -> man 2 kill
      功能: kill - send signal to a process
      使用格式:
              #include <sys/types.h>
              #include <signal.h>

              int kill(pid_t pid, int sig);

           pid: 发送目标的PID号
           sig: 需要发送的信号值

        返回值:
              成功:0
              失败:-1

二、信号的捕捉
 

1、 什么是信号的捕捉?

  其实就是进程在收到某个信号之前,必须提前约定好将来收到该信号时需要处理什么事情。

  进程1先捕捉SIGUSR1,收到信号就处理某件事情。  -->   进程2发送信号给进程1  -->  进程1收到信号,就会马上处理事情。
  进程2发送信号给进程1   ->  进程1捕捉SIGUSR1,收到信号就处理某件事情。   -> 进程1不会处理这个事情。

2、如何捕捉信号?  

参数: 哪个信号?做什么事情?  -> signal()  -> man 2 signal
功能: signal - ANSI C signal handling  -> 处理信号
       提前约定好,将来收到某个信号,我需要做什么事情。

使用格式:
       #include <signal.h>
       typedef void (*sighandler_t)(int);  给void(*)(int)这种指针变量的数据类型取了一个别名叫sighandler_t 

       sighandler_t signal(int signum, sighandler_t handler);

    signum:需要捕捉的信号值
    handler: 是一个函数指针  类型:void (*)(int)  -> 收函数到信号之后,会执行的

    返回值:
        成功:处理函数的地址
        失败:NULL  ->SIGERR

例子:
void fun(int sig)  -> 这个就是捕捉的信号值
{
}

signal(SIGUSR1,fun);  -> fun必须是void返回值,只有一个int类型的参数。

练习1: 编程一个程序,让子进程发送信号给父进程,父进程收到信号后,
       如果是SIGUSR1,则打印hello,如果是SIGUSR2,则打印apple。

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

void fun1(int sig)
{
	printf("hello!\n");
}

void fun2(int sig)
{
	printf("apple!\n");
}

int main()
{
	//产生一个子进程
	pid_t x;
	x = fork();
	if(x > 0)
	{
		//父进程先提前约定好,将来收到信号时需要什么任务。
		signal(SIGUSR1,fun1);
		signal(SIGUSR2,fun2);
		wait(NULL);
		exit(0);
	}
	
	if(x == 0)
	{
		usleep(100000);
		kill(getppid(),SIGUSR1);
		sleep(5);
		kill(getppid(),SIGUSR2);
		exit(0);
	}

	return 0;
}

三、信号捕捉函数signal的第二个参数

 

which is either SIG_IGN, SIG_DFL, or the address of a programmer-defined function  (a "signal handler").

SIG_IGN   -> 收到信号,就会忽略信号。
SIG_DFL   -> 收到信号,则执行信号的默认动作。
the address of a programmer-defined function  -> 信号处理函数    void(*)(int)  -> 收到信号时,会执行函数。

The signals SIGKILL and SIGSTOP cannot be caught or ignored.
//SIGKILL与SIGSTOP信号不可被捕捉/忽略,当进程收到这两个信号时,一定要响应!

  例题: 验证忽略/默认动作。
         "ctrl + C"  等价于  2) SIGINT   按下"ctrl + C" -> 默认动作回到终端上。

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

void fun1(int sig)
{
	printf("helloworld!\n");
}

int main()
{
	//产生一个子进程
	pid_t x;
	x = fork();
	if(x > 0)
	{
		//父进程先提前约定好,将来收到信号时需要什么任务。
		signal(SIGKILL,fun1);   -> 收到SIGKILL信号,不会执行fun1函数,而是直接退出。
		signal(SIGINT,fun1);    -> 收到SIGINT,执行fun1函数
		//signal(SIGINT,SIG_IGN);  -> 收到SIGINT,忽略该信号
		//signal(SIGINT,SIG_DFL);  -> 收到SIGINT,程序马上中断
		wait(NULL);
		printf("hello!\n");
		exit(0);
	}
	
	if(x == 0)
	{
		usleep(100000);
		kill(getppid(),SIGINT);
		kill(getppid(),SIGKILL);
		exit(0);
	}

	return 0;
}

四、关于信号两个拓展函数接口

 

1、挂起进程,直到收到一个信号为止。   pause()  -> 一般与signal连用。  

  signal(SIGUSR1,fun);
  pause();   -> 暂时不会往下运行代码,直到收到某个可以捕捉的信号,就会继续往下执行代码。  
  printf("hello!\n");

功能: wait for signal  -> 等待一个信号   
使用格式:
    #include <unistd.h>

         int pause(void);

    参数: 无
    返回值:
        收到可以捕捉的信号时  -1
        收到致命的信号时,直接退出

2. 自己给自己发信号。  -> raise()  -> man 3 raise
功能: send a signal to the caller  -> 给发送者发送一个信号。
使用格式:
    #include <signal.h>

        int raise(int sig);

    sig: 需要给自己发送的信号值

    返回值:
        成功:0
        失败:非0

 raise(sig)  等价于  kill(getpid(), sig);

五. 信号集

1、 什么是信号集?信号集有什么作用?
      信号集就是一堆信号的集合。信号集为了可以同时操作多个信号。如果将若干个信号加入到信号集中,那么给信号集设置阻塞属性,那么在信号集中所有信号都会阻塞的属性。

2、信号阻塞与信号忽略区别?
信号忽略:如果忽略某个信号,当别的进程发送该信号给自己时,就等于是没有收到该信号。

例子:
signal(SIGUSR1,SIG_IGN);
pause();   -> 程序会阻塞到这个地方,即使收到SIGUSR1,也会阻塞。
printf("hello!\n");

信号阻塞:如果设置了对某个信号阻塞,那么将来收到这个信号时,暂时不能响应该信号,而是将该信号放置在挂起队列上,等到解除对所有信号的阻塞之后,就可以响应挂起队列上的信号。

六、如何设置信号的阻塞属性?


在linux下,只能对信号集设置阻塞属性,所以说对信号设置阻塞之前,首先将信号加入信号集,再设置信号集的属性为阻塞。
信号集其实一个变量,定义了一个变量,等于定义了一个信号集。
信号集数据类型: sigset_t
头文件: #include <signal.h>

1、 关于信号集处理函数
        1)清空信号集中所有的信号
                 int sigemptyset(sigset_t *set);

                 set: 需要清空的信号集的地址

                返回值:
                        成功:0
                       失败:-1

      2)将linux所有信号一次性添加到信号集
             int sigfillset(sigset_t *set);

              set: 需要添加的信号集的地址

           返回值:
                   成功:0
                   失败:-1

      3)将某个信号添加到信号集中
            int sigaddset(sigset_t *set, int signum);
    
            set: 信号集的地址
            signum: 需要添加进信号集的信号

            返回值:
                   成功:0
                   失败:-1    

      4)从信号集中删除某个信号
            int sigdelset(sigset_t *set, int signum);

            set: 信号集的地址
            signum: 需要删除信号集的信号值

            返回值:
                     成功:0
                     失败:-1    

      5)测试某个信号值是否为信号集的成员
            int sigismember(const sigset_t *set, int signum);

            set: 信号集的地址
            signum: 需要测试的信号值

    返回值:
    在信号集中:  1
    不在信号集中:0
    出错:       -1


2、 如何给信号集设置阻塞属性?  -->  sigprocmask()  -> man 3 sigprocmask
功能: sigprocmask - change blocked signals  -> 给信号设置阻塞的属性。
使用格式:
    #include <signal.h>
   int sigprocmask(int how, const sigset_t * set,sigset_t * oset);

    how:
        SIG_BLOCK  -> 设置参数二set为阻塞状态
        SIG_UNBLOCK  -> 设置参数二set为非阻塞状态
    set:  需要设置属性的信号集地址
    oset: 保存原有的状态,如果不关注原有的状态,则设置为NULL。

   返回值:
    成功:0
    失败:-1

  练习: 使用学习过的API,验证信号阻塞情况。
            父进程 -> 对某个信号阻塞10秒,10秒之后解除阻塞
            子进程 -> 在5秒发送该信号给父进程,看看在多少秒会响应。

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

void fun(int sig)
{
	printf("sig = %d\n",sig);
}

int main()
{
	pid_t x;
	x = fork();
	if(x > 0)
	{
		//0. 首先捕捉信号
		signal(SIGUSR1,fun);
		
		//1. 定义一个信号集
		sigset_t set;
		
		//2. 清空信号集
		sigemptyset(&set);
		
		//3. 将SIGUSR1加入到信号集
		sigaddset(&set,SIGUSR1);
		
		//4. 判断信号是否在信号集中
		int ret,i;
		ret = sigismember(&set,SIGUSR1);
		if(ret <= 0)
			printf("not a member!\n");
		
		//5. 设置信号集为阻塞属性
		sigprocmask(SIG_BLOCK,&set,NULL);
		
		//6. 将阻塞的状态持续10秒
		for(i=10;i>0;i--)
		{
			sleep(1);
			printf("%d\n",i);
		}
		
		//7. 解除阻塞状态
		sigprocmask(SIG_UNBLOCK,&set,NULL);
		
		wait(NULL);
		exit(0);
	}
	
	if(x == 0)
	{
		sleep(5);
		kill(getppid(),SIGUSR1);
		printf("I send signal to parent!\n");
		exit(0);
	}
	
	return 0;
}

结果:
5秒: 子进程已经发送信号给父进程。
10秒: 父进程才会响应该信号。

参考博客:

https://blog.csdn.net/bian_cheng_ru_men/article/details/80325959

https://blog.csdn.net/zb1593496558/article/details/80280346

发布了64 篇原创文章 · 获赞 82 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_40602000/article/details/101213022