Linux信号机制(二)

目录

一、信号的阻塞

二、信号集操作函数

三、sigprocmask函数

四、pause函数 

 五、sigsuspend函数


一、信号的阻塞

        有时候不希望在接到信号时就立即停止当前执行,去处理信号,同时也不希望忽略该信号,而是延时一段时间去调用信号处理函数。这种情况可以通过阻塞信号实现。

        信号的阻塞概念:信号的”阻塞“是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。

        信号的状态:

        信号递达(Delivery ):实际信号执行的处理过程(3种状态:忽略,执行默认动作,捕获)、信号未决(Pending):从产生到递达之间的状态。

二、信号集操作函数

sigset_t set;  自定义信号集。  是一个32bit  64bit  128bit的数组。

sigemptyset(sigset_t *set);	清空信号集

sigfillset(sigset_t *set);	全部置1

sigaddset(sigset_t *set, int signum);	将一个信号添加到集合中

sigdelset(sigset_t *set, int signum);	将一个信号从集合中移除

sigismember(const sigset_t *set,int signum); 判断一个信号是否在集合中。

三、sigprocmask函数

#include <signal.h>
int sigprocmask( int how, 
                const sigset_t *restrict set, 
                sigset_t *restrict oset );

返回值:若成功则返回0,若出错则返回-1

首先,若oset是非空指针,那么进程的当前信号屏蔽字通过oset返回。

其次,若set是一个非空指针,则参数how指示如何修改当前信号屏蔽字。

how可选用的值:(注意,不能阻塞SIGKILL和SIGSTOP信号)

SIG_BLOCK :   把参数set中的信号添加到信号屏蔽字中
SIG_UNBLOCK: 从信号屏蔽字中删除参数set中的信号
SIG_SETMASK: 把信号屏蔽字设置为参数set中的信号
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>

void handle(int sig)
{
	printf("I get the sig = %d\n",sig);
}

int main()
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0; 
	sigemptyset(&act.sa_mask);

	sigaction(SIGINT,&act,NULL);
	sigset_t set;
	sigemptyset(&set);
	sigaddset(&set,SIGINT);
	
	sigprocmask(SIG_BLOCK,&set,NULL);

	sleep(5);
	sigprocmask(SIG_UNBLOCK,&set,NULL);
	while(1)
	{
		sleep(1);
	}
	return 0;
}

这段代码注册了一个信号处理函数 handle() 来处理 SIGINT 信号。
然后它创建了一个 sigset_t 类型的信号集 set,并将 SIGINT 添加到这个信号集中。

接着,通过 sigprocmask(SIG_BLOCK, &set, NULL) 调用,程序阻塞了 SIGINT 信号。
这意味着在这个代码块中,SIGINT 信号将被暂时屏蔽,不会触发信号处理函数。

随后,程序调用 sleep(5) 函数来暂停执行 5 秒钟。在此期间,由于 SIGINT 被阻塞,即使用户发送 SIGINT 信号(通常是通过按下 Ctrl+C),信号处理函数 handle() 也不会执行。

然后,通过 sigprocmask(SIG_UNBLOCK, &set, NULL) 调用,解除了对 SIGINT 信号的阻塞。

最后,程序进入一个无限循环,每次循环调用 sleep(1) 函数来保持进程处于活动状态。

 运行结果:

linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ ./sigmask_new_t
^C^C^C^C^C^C^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^\Quit (core dumped)

四、pause函数 

调用该函数可以造成进程主动挂起,等待信号唤醒。
调用该系统调用的进程将处于阻塞状态(主动放弃cpu) 直到有信号递达将其唤醒。

 int pause(void);     返回值:-1 并设置errno为EINTR
pause() 函数是一个系统调用,它的作用是使当前进程挂起,直到收到一个信号为止。
在收到信号之前,pause() 函数会一直阻塞当前进程。
一旦收到信号,pause() 函数会返回,并且不会执行任何其他代码,直接返回到信号处理函数(如果有的话)或者程序的主体部分。
如下代码中,pause() 函数用于等待SIGINT信号的到来。
一旦收到SIGINT信号(通常由用户在终端上按下Ctrl+C触发),pause() 函数会返回,然后程序会执行信号处理函数handle()。
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>

void handle(int sig)
{
	printf("I get the sig = %d\n",sig);
}

int main()
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);

	pause();
	printf("after pause\n");
	while(1)
	{
		sleep(1);
	}
	return 0;
}

注意:第一次CTRL+C会调用handle回调函数且打印after pause,但是第二次CTRL+C后就不会打印after pause。

linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ ./pause_t
^CI get the sig = 2
after pause
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^\Quit (core dumped)
linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ 

我们用一个测试程序测试一下:

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

void handle(int sig)
{
	printf("I get the sig = %d\n",sig);
}

int main()
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);

	pause();
	printf("after pause\n");
	while(1)
	{
		printf("test\n");
		sleep(1);
		printf("sleep\n");
	}
	return 0;
}

运行结果:

linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ ./pause_t
^CI get the sig = 2
after pause
test
sleep
test
sleep
test
sleep
test
sleep
test
sleep
test
sleep
test
^CI get the sig = 2
sleep
test
sleep
test
sleep
test
^CI get the sig = 2
sleep
test
^CI get the sig = 2
sleep
test
sleep
test
^\Quit (core dumped)

可以发现,当我用CTRL+C,接着运行,之后程序就运行到while(1)里了,当我再CTRL+C因为信号捕获的关系才会打印句柄里的语句I get the sig = 2。

而对于如下代码:

        每次CTRL+C都会触发mytask中的语句和handle句柄中的打印语句。

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

void handle(int sig)
{
	printf("I get the sig = %d\n",sig);
}

void mytask()
{
	printf("woshigedashabi\n");
}

int main()
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);

	pause();
	printf("after pause1\n");
	while(1)
	{
		mytask();
		pause();
	}
	printf("after pasue2\n");
	while(1)
	{
		sleep(1);
	}
	return 0;
}

 运行结果:

linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ ./test
^CI get the sig = 2
after pause1
woshigedashabi
^CI get the sig = 2
woshigedashabi
^CI get the sig = 2
woshigedashabi
^CI get the sig = 2
woshigedashabi
^CI get the sig = 2
woshigedashabi
^CI get the sig = 2
woshigedashabi
^\Quit (core dumped)

对代码进行一定的修改后:


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

void handle(int sig)
{
	printf("I get the sig = %d\n",sig);
}

void mytask()
{
	printf("My task start\n");
	sleep(3);
	printf("My task end\n");
}

int main()
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);
	sigaction(SIGHUP,&act,NULL);

	sigset_t set;
	sigaddset(&set,SIGHUP);
	sigaddset(&set,SIGINT);
	pause();
	printf("after pause1\n");

	while(1)
	{
		sigprocmask(SIG_BLOCK,&set,NULL);
		mytask();
		sigprocmask(SIG_UNBLOCK,&set,NULL);
		pause();
	}
/*	while(1)
	{
		mytask();
		pause();
	}*/

	printf("after pasue2\n");
	return 0;
}

运行结果:

第一次CTRL+C触发,打印完after pause1,程序进入while(1)循环,在5s内再按下CTRL+C会被堵塞,直达sigprocmask(SIG_UNBLOCK,&set,NULL);只要在5s内按下了CTRL+C就会信号捕获打印handle中的语句,且这个时候因为pause(),再按下CTRL+C会再次运行mytask()。

linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ ./test
^CI get the sig = 2
after pause1
My task start
^CMy task end
I get the sig = 2
^CI get the sig = 2
My task start
My task end
^CI get the sig = 2
My task start
My task end
^CI get the sig = 2
My task start
^C^C^C^C^CMy task end
I get the sig = 2
^CI get the sig = 2
My task start
My task end
^CI get the sig = 2
My task start
^C^C^C^C^C^C^C^CMy task end
I get the sig = 2
^CI get the sig = 2
My task start
^C^\Quit (core dumped)

如果上述代码去掉pause(),则输出结果为:则会一直运行mytest(),只是CTRL+C触发运行了handle中的打印语句。

linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ gcc -o test pause_t_new.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ ./test
^CI get the sig = 2
after pause1
My task start
My task end
My task start
My task end
My task start
My task end
My task start
^CMy task end
I get the sig = 2
My task start
^C^C^C^C^C^C^C^C^C^CMy task end
I get the sig = 2
My task start
My task end
My task start
My task end
My task start
My task end
My task start
My task end
My task start
^\Quit (core dumped)

 五、sigsuspend函数

int sigsuspend(const sigset_t *sigmask);

功能:将进程的屏蔽字替换为由参数sigmask给出的信号集,然后挂起进程的执行

参数:sigmask:希望屏蔽的信号

对比如下代码:

 运行结果的区别:

左边运行结果表示你在阻塞期间按下CTRL+C只会捕获一次信号,但是不会认为你需要再执行一次mytask()。只有当运行了sigprocmask(SIG_UNBLOCK,&set,NULL)才有效。

但是右边在任务中间会接收任务,这是因为sigsuspend函数,set2是一个空的信号集。sigsuspend(&set2); 函数允许程序在任务执行的过程中等待信号,一旦收到信号,程序就会立即响应。

详细代码如下: 

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

void handle(int sig)
{
	printf("I get the sig = %d\n",sig);
}

void mytask()
{
	printf("My task start\n");
	sleep(3);
	printf("My task end\n");
}

int main()
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);
	sigaction(SIGHUP,&act,NULL);

	sigset_t set;
	sigset_t set2;
	sigemptyset(&set2);
	sigaddset(&set,SIGHUP);
	sigaddset(&set,SIGINT);
	pause();
	printf("after pause1\n");
	

	while(1)
	{
		sigprocmask(SIG_BLOCK,&set,NULL);
		mytask();
		sigsuspend(&set2);
	}

	printf("after pasue2\n");
	return 0;
}
先注册了两个信号处理函数 handle,分别用于处理 SIGINT 和 SIGHUP 信号
。然后定义了一个自定义函数 mytask(),它模拟了一个长时间运行的任务。

在 main() 函数中,创建了两个信号集 set 和 set2,set 中包含了 SIGHUP 和 SIGINT 信号。
然后调用了 pause() 函数来挂起进程,直到收到信号为止。

接着进入一个无限循环,在循环中,先将 set 中的信号阻塞,然后执行 mytask() 函数,模拟长时间运行的任务。
然后使用 sigsuspend() 函数挂起进程,等待收到 set2 中的信号。

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux信号机制是进程间通信的一种方式。它通过向进程发送信号来通知进程发生了某些事件,例如用户按下了某个键,或者内核发现了一个错误等等。进程可以选择忽略信号,或者处理信号并执行一些特定的操作。 为了进行Linux信号机制的实验,可以按照以下步骤进行: 1. 编写一个简单的C程序,用于演示信号的发送和接收。程序可以使用signal()函数来处理信号。在该程序中,可以使用kill()函数向指定进程发送信号,或者使用raise()函数向当前进程发送信号。 2. 运行该程序,并使用ps命令查看进程的PID。然后,可以使用kill命令向该进程发送信号,例如: ``` kill -SIGINT <PID> ``` 这将向指定的进程发送一个SIGINT信号,该信号通常用于中断进程。 3. 在程序中添加信号处理函数,以便在收到信号时执行特定的操作。例如,可以使用sigaction()函数来注册信号处理程序,如下所示: ``` struct sigaction sigact; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigact.sa_handler = handle_signal; sigaction(SIGINT, &sigact, NULL); ``` 这将注册一个名为handle_signal()的函数,用于处理SIGINT信号。 4. 可以编写多个程序,并使用信号来实现进程间通信。例如,一个进程可以向另一个进程发送信号,以通知它执行某些操作。 5. 最后,可以尝试使用其他类型的信号,例如SIGTERM、SIGKILL等,以了解它们的不同之处。 总之,Linux信号机制是一个非常强大的工具,可以用于实现进程间通信、错误处理等。通过实验,我们可以更深入地了解信号的工作原理,并学会如何使用它们来编写更高效的程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值