并发基础知识(二)[进程间通信·信号]

1.信号   


    信号是进程间通信的一种方式,这种方式没有数据传输。
    只是在内核中传递一个信号(整数),信号的表示是一个整数。
    不同的信号值,代表不同的含义,当然用户可以自定义信号。
    那么自定义的信号的含义和值由程序员来定和解释
    
                                Term:Terminate 中止
        signal          value        Action        Comment
    --------------------------------------------------------------------------
        SIGHUP        1       Term    Hangup detected on controlling terminal
                                        or death of controlling process
                                        控制终端的挂起操作,或者是控制进程死亡时, 
                                        控制终端上的所有进程都会收到 SIGHUP 信号
                                        
                                        
        SIGINT        2       Term    Interrupt from keyboard
                                      从键盘上收到的中断信号, ctrl + C      
        SIGQUIT       3       Core    Quit from keyboard
                                        CTRL + Z 
        
        SIGILL        4       Core    Illegal Instruction
                                        非法指令
        SIGABRT       6       Core    Abort signal from abort(3)
                                        调用abort这个函数时,进程会收到
                                        SIGABRT这个信号
        SIGFPE        8       Core    Floating-point exception
                                        浮点运算异常的时候,产生SIGFPE信号
        
        SIGKILL       9       Term    Kill signal 任何进程只要收到这两个信号就必死无疑
        SIGSTOP   17,19,23    Stop    Stop process 而且不能捕获的                                    
        
        SIGSEGV      11       Core    Invalid memory reference
                                        非法内存引用时,会收到SIGSEGV信号
                                        =》segamation Fault 
                                        
        SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                        readers; see pipe(7)
                                       当你往一个管道写数据时,没有读端进程时
                                       就会产生SIGPIPE信号。
                                     
        SIGALRM      14       Term    Timer signal from alarm(2)
                                        定时信号,在进程调用alarm时
                                        会在超时的时候,产生SIGALRM信号
        SIGTERM      15       Term    Termination signal
                                        终止信号
        SIGUSR1   30,10,16    Term    User-defined signal 1
        SIGUSR2   31,12,17    Term    User-defined signal 2
                                        用户自定义信号
        SIGCHLD   20,17,18    Ign     Child stopped or terminated
                                        当子进程停止或中止时,父进程会
                                        收到SIGCHLD这个信号
        
        SIGCONT   19,18,25    Cont    Continue if stopped
        SIGTSTP   18,20,24    Stop    Stop typed at terminal
        SIGTTIN   21,21,26    Stop    Terminal input for background process
        SIGTTOU   22,22,27    Stop    Terminal output for background process

    进程在收到一个信号时,通常会有三种处理方式:  
        (1)默认行为
            收到一个信号时,采用操作系统默认的行为。
            大部分信号的默认行为,会把进程给干掉。 
            只有一个SIGCHLD是被忽略的。

        (2)捕捉信号
            把一个信号 与  用户自定义的信号处理函数关联起来。
            那么在收到该信号时,就会自行调用该处理函数。
            

        (3)忽略该信号 

        
2.信号处理过程         


    通过"软件中断/软中断"来实现的,

    你的信号处理函数其实是在 中断上下文 执行,
        信号处理函数  -》 “软中断函数”
        
        //信号处理函数 是 线程安全的/可重入
        
        进程上下文:进程的大环境下,“时间片轮转”
            一个进程的执行状态又分为: 
                用户态:在执行用户自己的代码
                内核态:进入操作系统内核执行的
        中断上下文:就是中断的环境下。“中断模式” 
                中断处理 
                
                interrupt 

当一个进程接收到信号时,操作系统会对该进程进行处理和调度。以下是信号处理的一般过程:

信号传递:当进程接收到信号时,操作系统会将信号传递给进程。这是通过向进程发送一个特定的软中断(software interrupt)或触发硬件中断(hardware interrupt)来实现的。

信号处理函数调用:接收到信号的进程会根据之前对信号进行的处理注册,执行与该信号关联的信号处理函数。每个信号都有一个默认的处理行为,但进程可以选择为特定信号注册自己的信号处理函数。

信号处理:当操作系统调用信号处理函数时,进程会执行与信号关联的操作。这可能包括异常处理、资源回收、状态更新等操作。在处理完信号后,进程可以恢复执行原来的程序。

需要注意的是,操作系统对接收到信号的进程的调度方式可能会根据不同的操作系统和调度策略而有所不同。一般来说,操作系统可能会采用以下方式处理接收到信号的进程:

抢占式调度:操作系统会立即中断当前进程的执行,将控制权转移到信号处理函数。这种方式会立即响应信号,但可能会导致进程的执行中断。

非抢占式调度:操作系统会等待进程主动释放CPU控制权后,再调用信号处理函数。这种方式会等待进程处于合适的执行点,然后才会响应信号。

对于实时操作系统(Real-Time Operating System),会有更灵活的机制来处理信号和调度进程,以满足实时性的要求。

综上所述,当一个进程接收到信号时,操作系统会将信号传递给进程,并根据调度策略执行相应的信号处理函数。具体的调度方式可能会因操作系统和调度策略而有所不同      


3.linux下信号相关的API函数


    1.发送信号  


        1.kill函数 


        头文件 
            #include <sys/types.h>
            #include <signal.h>
        函数功能 
            用来把一个信号发送到一个指定的进程或多个进程
        函数原型
            int kill(pid_t pid, int sig);
        函数参数 
            pid_t pid        //指定信号的接收者(可能是多个进程)
                                pid > 0:pid表示接收者进程 
                                pid = 0:发送信号给调用进程同组的所有进程
                                pid = -1:发送信号给所有进程(有权限发送的所有)
                                pid < -1:发送信号给组id等于 pid绝对值的所有进程
            int sig            //要发送的信号的信号值                    
            
            
        函数返回值
            成功: (至少有一个进程成功接收到信号)返回0
            失败: 返回-1,同时errno被设置 
        练习:  
            1.写一个程序,发送一个SIGKILL 给某一个进程或多个进程
                
    -------------------------------------------------------------


    2.raise:发送信号给自己  


        头文件 
            #include <signal.h>
        函数功能 
            发送一个信号给调用者进程
        函数原型
            int raise(int sig);
        函数参数  
            int sig  //要发送的信号 
        函数返回值 
            成功:返回0
            失败:返回非0
        
            《=》 kill(getpid(),sig);
    ---------------------------------------------------------------


    3.alarm发送一个闹钟信号  


        头文件 
            #include <unistd.h>
        函数功能
            定时发送一个闹钟信号(SIGALRM)给本进程。
            "闹钟":每个进程都有属于自己的一个“闹钟”
            “闹钟”时间到了,进程就会收到SIGALRM的信号,
            但是同一时刻一个进程只有一个“闹钟”生效
        函数原型
            unsigned int alarm(unsigned int seconds);
        函数参数 
            unsigned int seconds        //多少秒后,发送一个“闹钟信号”
                                        0,cancal a alarm 取消一个闹钟
        函数返回值 
            返回上一个闹钟的剩余秒数
            
            例子:  
                alarm(5);
                int r = alarm(10);//10秒后会收到一个"闹钟信号"
                                    //前面的那个闹钟将替换了。 
                ...
                alarm(0);  //取消闹钟     
    --------------------------------------------------
        大部分信号的默认行为,是把收到信号的进程kill掉。
        那么我们能不能改变这个默认行为呢?
            改变信号的处理方式 --》 捕捉信号
            


    (2)捕捉信号:改变信号的处理方式 


        使用这个函数 需要在头文件前面添加#define __GNU__SOURCE 这个宏定义 
        头文件
            #include <signal.h>
        函数功能
            捕捉和改变信号的处理方式
        函数原型
                typedef void (*sighandler_t)(int);
            sighandler_t signal(int signum, sighandler_t handler);
        函数参数
            int signum                //要捕捉的那个信号的信号值
            sighandler_t handler    //sighandler_t是一个函数指针的类型
                                        它可以用来定义函数指针变量。 
                                        信号的处理方式
                                        (1)SIG_IGN:忽略该信号
                                        (2)SIG_DFL:default,采用操作系统默认的处理方式
                                        (3) 自定义的处理函数  
                                            无返回值,但是带一个int的参数(用来保存收到的那个信号的信号值)
                                            void my_sig_handler(int sig)
                                            {
                                            
                                            }
                
        函数返回值
            成功: 返回 用户改变的信号处理方式的上一次的处理方式,就是指向那个处理函数 ,当我们通过 
                    这个函数指针调用那个函数就可以看到上一次的处理方式,如果没有上一次,则指向NULL
            失败: 返回SIG_ERR,同时errno被设置
        
        练习: 
            我们的程序,通常可以被键盘的CTRL + C 干掉,
            请写一个程序,CTRL + C 干不掉你了?
        


    (3)    等待信号到来   


        pause:让进程停在那里,等待某个信号的到来。
            直到收到信号。
            
        头文件 
            #include <unistd.h>
        函数功能 
            让进程停在那里,等待某个信号的到来。直到收到信号。
        函数原型
             int pause(void);
        函数参数 
            无
        函数返回值 
            只在捕捉到信号时返回。返回-1,errno被设置为EINTR。

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


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

	int k = kill(2647, 19);    //指定进程发送信号
	if(k == -1)
	{
		perror("kill send failed\n");
	}
	/*k = kill(2853, 3);
	if(k == -1)
	{
		perror("kill send failed\n");
	}*/

	return 0;
}
#include <stdio.h>
#include <signal.h>
int main(int argc, char * argv [ ])
{
	int r = raise(9);
	if(r != 0)
	{
		printf("raise failed\n");
	}
	while(1);

	return 0;
}
#include <stdio.h>
#include <unistd.h>
int main(int argc, char * argv [ ])
{
	int r = alarm(5);
	printf("r =%d\n",r);
	r = alarm(2);
	printf("r = %d\n",r);
	r = alarm(0);
	printf("r = %d\n",r);
	while(1);


	return 0;
}
#define  _GNU_SOURCE
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

//信号的新的处理方式
void signal_handler(int sig)
{	
	if(sig == 1)
	{
		printf("test last\n");
	}
	if(sig == 2)
	{
		printf("kill buliao I\n");
	}


}


void signal_handlerxx(int sig)
{
	if(sig == 2)
	{
		printf("you kill buliao I\n");
	}
}

int main(int argc, char * argv [ ])
{
	sighandler_t p = signal(SIGINT, signal_handler);  //信号捕获
	if(p == SIG_ERR)
	{
		perror("signal failed\n");
	}
	if(p == NULL)
	{
		printf("no last\n");
	}
	
	p = signal(SIGINT, signal_handlerxx);  //信号捕获
	if(p == SIG_ERR)
	{
		perror("signal failed\n");
	}
	if(p == NULL)
	{
		printf("no last\n");
	}
	else
	{
		p(1);  //===》signal_handler(1);
		
	}
	
	int s = pause();
	if(s == -1)
	{
		perror("");
	}
	printf("qqqqqqqqqqqq\n");

	
	while(1);
	return 0;
}
#include <stdio.h>
int main(int argc, char * argv [ ])
{

	while(1);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值