Linux C编程--进程间通信(IPC)3--信号集和发送信号介绍

时间:2022-05-12 14:55:16

Linux信号集

  1.信号集概念

  信号集是一个能表示多个信号的数据类型,sigset_t set ;set即一个信号集。

  既然是一个集合,就需要对集合进行添加/删除等操作。

  int sigemptyset(sigset_t *set); 将set集合置空

  int sigfillset(sigset_t *set); 将所有信号加入set集合

  int sigaddset(sigset_t *set,int signo); 将signo信号加入到set集合

  int sigdelset(sigset_t *set,int signo); 从set集合中移除signo信号

  int sigismember(const sigset_t *set,int signo); signo判断信号是否存在于set集合中


   2.信号集的操作

   1>sigprocmask(查询或设置信号掩码)
相关函数 

signal,sigaction,sigpending,sigsuspend

表头文件 
#include<signal.h>

定义函数 
int sigprocmask(int how,const sigset_t *set,sigset_t * oldset);

函数说明 
sigprocmask()可以用来改变目前的信号遮罩,其操作依参数how来决定
SIG_BLOCK 新的信号遮罩由目前的信号遮罩和参数set 指定的信号遮罩作联集
SIG_UNBLOCK 将目前的信号遮罩删除掉参数set指定的信号遮罩
SIG_SETMASK 将目前的信号遮罩设成参数set指定的信号遮罩。
如果参数oldset不是NULL指针,那么目前的信号遮罩会由此指针返回。

返回值 
执行成功则返回0,如果有错误则返回-1。

错误代码 
EFAULT 参数set,oldset指针地址无法存取。
EINTR 此调用被中断


注:如果将set设为一空指针,那么进程的信号掩码将不会改变,这时how的位也是没有以意义的。


下面给出一个例子,用来说明这个函数

功能是打印调用进程的信号掩码中的信号的名称

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

void pr_mask()
{
sigset_t sigset;
if(sigprocmask(0, NULL, &sigset)<0)
{
printf("sigprocmask error");
exit(0);
}
if(sigismember(&sigset, SIGINT))
printf("SIGINT \n");

if(sigismember(&sigset, SIGTERM))
{
printf("SIGTERM \n");
exit(0);
}

}

int main()
{
char buffer1[100],buffer2[100];
int i;
if(signal(SIGINT, &pr_mask)==-1)
{
printf("Couldn't register signal hanlder for SIGINT!\n");
exit(1);
}

if(signal(SIGTERM, &pr_mask)==-1)
{
printf("Couldn't register signal hanlder for SIGTERM!\n");
exit(1);
}
printf("Pid of This Process : %d \n",getpid());

for(;;)
{
printf("Please input:\n");
fgets(buffer1, sizeof(buffer1),stdin);
for(i=0;i<100;i++)
{
if(buffer1[i]>=97&&buffer1[i]<=122)
buffer2[i]=buffer1[i]-32;
else
buffer2[i]=buffer1[i];
}
printf("Your input is: %s \n",buffer2);
}
exit(0);
}

2>sleep(让进程暂停执行一段时间)

相关函数 
signal,alarm

表头文件 
#include<unistd.h>

定义函数 
unsigned int sleep(unsigned int seconds);

函数说明 
sleep()会令目前的进程暂停,直到达到参数seconds 所指定的时间,或是被信号所中断。

返回值 
若进程暂停到参数seconds 所指定的时间则返回0,若有信号中断则返回剩余秒数。


3>sigpending(查询被搁置的信号)

相关函数 
signal,sigaction,sigprocmask,sigsuspend

表头文件 
#include<signal.h>

定义函数 
int sigpending(sigset_t *set);

函数说明 
sigpending()会将被搁置的信号集合由参数set指针返回。

返回值执 
行成功则返回0,如果有错误则返回-1。

错误代码 
EFAULT 参数set指针地址无法存取
EINTR 此调用被中断。


下面给出一个实例,说明其用法:

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

static void sig_quit(int);

int main()
{
sigset_t newmask, oldmask, pendmask;
if(signal(SIGQUIT,&sig_quit)==-1)
{
printf("Couldn't register signal hanlder for SIGQUIT!\n");
exit(1);
}
sigemptyset(&newmask);
sigaddset(&newmask,SIGQUIT);

if(sigprocmask(SIG_BLOCK, &newmask, &oldmask)<0)
{
printf("SIG_BLOCK error.\n");
exit(2);
}
sleep(5);
if(sigpending(&pendmask)<0)
{
printf("sigpending error.\n");
exit(3);
}
if(sigismember(&pendmask,SIGQUIT))
printf("SIGQUIT pending \n");
if(sigprocmask(SIG_SETMASK, &oldmask, NULL)<0)
{
printf("SIG_SETMASK error.\n");
exit(4);
}
printf("SIGQUIT unblocked.\n");
sleep(5);
exit(0);
}

static void sig_quit(int signum)
{
printf("catch SIGQUIT.\n");
if(signal(SIGQUIT, SIG_DFL)==-1)
printf("Couldn't reset SIGQUIT!\n");
return;
}


4>sigsuspend函数

函数原型:

#include  <signal.h>
int sigsuspend(const sigset_t *mask);

作用:

用于在接收到某个信号之前,临时用mask替换进程的信号掩码,并暂停进程执行,直到收到信号为止。
也就是说,sigsuspend后,进程就挂在那里,等待着开放的信号的唤醒。系统在接收到信号后,马上就把现在的信号集还原为原来的,然后调用处理函数。

返回值:
sigsuspend返回后将恢复调用之前的的信号掩码。信号处理函数完成后,进程将继续执行。该系统调用始终返回-1,并将errno设置为EINTR.


下面给出一个例子,使用sigsuspend函数使进程挂起等待某个全程变量

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

int quitflag=0;

int main()
{
void sig_int(int);
sigset_t newmask, oldmask, zeromask;

if(signal(SIGINT,&sig_int)==-1)
{
printf("Couldn't register signal hanlder for SIGINT!\n");
exit(1);
}
if(signal(SIGQUIT,&sig_int)==-1)
{
printf("Couldn't register signal hanlder for SIGQUIT!\n");
exit(2);
}
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask,SIGQUIT);
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask)<0)
{
printf("SIG_BLOCK error.\n");
exit(3);
}
while(quitflag==0)
sigsuspend(&zeromask);
printf("process wake up.\n");
quitflag=0;
if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0)
exit(0);
}

void sig_int(int signum)
{
if(signum==SIGINT)
printf("\n interrupt \n");
else if (signum==SIGQUIT)
{
printf("catch SIGQUIT \n");
quitflag=1;
}
return ;
}

发送信号

1. 函数说明:
  kill和raise是用来发送信号的:
  kill把信号发送给进程或进程组;
  raise把信号发送给(进程)自身.
  他们的原型如下:
  #include 
  
int kill(pid_t pid, int signo);
  int raise(int signo);

  成功则返回0, 出错则返回-1
  从原型上可以看出, raise函数是可以通过kill实现的.
  raise(signo);
  等价于:
  kill(getpid(), signo);


2. pid参数:
  kill函数中的pid参数, 它有以下4种情况:
  pid > 0: 将该信号发送给进程ID为pid的进程. 
  pid == 0: 将该信号发送给与发送进程属于同一进程组的所有进程(不包括内核进程和init进程). 此时, 发送进程必须具有向这些进程发送信号的权限. 
  pid < 0: 将该信号发给其进程组ID等于pid绝对值的所有进程(不包括内核进程和init进程). 此时, 发送进程必须具有向这些进程发送信号的权限. 
  pid == -1: 将该信号发送给发送进程有权限向它们发送信号的系统上的所有进程.(不包括内核进程和init进程). 

3. signo参数:
  POSIX.1将编号为0的信号定义为空信号. 如果signo参数是0, 则kill仍执行正常的错误检查, 但不发送信号. 这被用来确定一个进程是否存在.


下面给出两个例子说明这两个函数的使用方法:

1.raise函数的使用

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

void inthandler(int signum);
void continuehandler(int signum);
void terminatehandler(int signum);

int main()
{
char buffer[100];
if(signal(SIGINT,&inthandler)==-1)
{
printf("Couldn't register signal hanlder for SIGINT!\n");
exit(1);
}
if(signal(SIGTSTP, &inthandler)==-1)
{
printf("Couldn't register signal hanlder for SIGTSTP!\n");
exit(2);
}
if(signal(SIGCONT, &continuehandler)==-1)
{
printf("Couldn't register signal hanlder for SIGCONT!\n");
exit(3);
}
if(signal(SIGTERM, &terminatehandler)==-1)
{
printf("Couldn't register signal hanlder for SIGINT!\n");
exit(4);
}
printf("Pid of This Process : %d \n",getpid());

for(;;)
{
printf("Please input:\n");
fgets(buffer, sizeof(buffer),stdin);
if(strcmp(buffer,"int\n")==0)
raise(SIGINT);
else if(strcmp(buffer,"stop\n")==0)
raise(SIGTSTP);
else if(strcmp(buffer,"continue\n")==0)
raise(SIGCONT);
else if(strcmp(buffer,"quit\n")==0)
raise(SIGTERM);
else
printf("Your input is: %s \n",buffer);
}
exit(0);
}

void inthandler(int signum)
{
printf("catch signal %d \n",signum);
}

void continuehandler(int signum)
{
printf("Continue code.\n");
}

void terminatehandler(int signum)
{
printf("signal SIGTERM \n");
exit(0);
}

2.kill函数的使用

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

int usr_interrupt=0;

void synch_signal(int signum)
{
usr_interrupt=1;
}

void child_function()
{
printf(" I'm child process. My pid is %d \n",getpid());
sleep(5);
kill(getppid(),SIGUSR1);
puts("Good bye! \n");
exit(0);
}

int main()
{
struct sigaction usr_action;
sigset_t block_mask;
pid_t child_id;

sigfillset(&block_mask);
usr_action.sa_handler=synch_signal;
usr_action.sa_mask=block_mask;
usr_action.sa_flags=0;
sigaction(SIGUSR1, &usr_action,NULL);
child_id=fork();
if(child_id==0)
child_function();
while(!usr_interrupt);
puts("That's all!");
return 0;
}