linux下C的信号函数(sig开头的那一簇)的实践。
背景知识:
linux操作系统中每个进程有一个信号中断列表,列表中每个信号对应一段操作逻辑。
进程收到信号(比如Ctlr+c,比如kill)后查询自己的中断列表,如果有该信号对应的处理逻辑,进程中断当前正在运行的处理逻辑,执行中断处理逻辑。
如果中断处理逻辑不是退出进程,那么它执行完毕后,原先被中断的处理逻辑继续运行。
进程的信号中断列表有一套C的信号函数( sig开头的这一 簇 )可以修改。
我实际要解决的问题如下:
1,进程A通知进程B中断当前操作, 执行一段中断逻辑;
2,但是进程B在进行关键操作时,不可以被A中断;
使用到的函数:
- sigemptyset
- signal
- sigaddset
- sigaction
- sigprocmask
- kill
//=======================进程B实现逻辑================================
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
//自定义的中断处理函数
void bProcessSignal(int signo, siginfo_t *info, void *context)
{
printf("in bProcessSignal/n");
}
int main(int arg, char* argv[])
{
// 将当前进程的进程号写入文件 /home/admin/.b_pid, 以便进程A读取
char pidfilestr[256];
char* homepath = getenv("HOME");
if(NULL == homepath)
{
homepath = ".";
}
sprintf(pidfilestr, "%s/%s", homepath, ".b_pid");
FILE *fp = fopen(pidfilestr, "w+");
if (fp == NULL)
{
printf("in process B, fail to fopen =%s/n", pidfilestr);
return 0;
}
fprintf(fp, "%d/n", getpid());
fclose(fp);
/*设置SIGPIPE信号被忽略
当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统 会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。根据信号的默认处理规则SIGPIPE信号的默认执行动作是terminate(终止、退出),所以client会退出。若不想客户端退出可以把 SIGPIPE设为SIG_IGN
*/
signal(SIGPIPE,SIG_IGN);
/*设置SIGHUP信号被忽略
SIGHUP和控制台操作有关,当控制台被关闭时系统会向拥有控制台sessionID的所有进程发送HUP信号,默认HUP信号的action是 exit
*/
signal(SIGHUP,SIG_IGN);
//action用于描述信号对应的操作逻辑
struct sigaction action;
//清空初始化action的sa_mask成员(附加的屏蔽信号,)
sigemptyset(&action.sa_mask);
//设置中断处理逻辑函数入口
action.sa_sigaction=bProcessSignal;
/* 默认的中断处理函数形式 void handler(int signo);
如果设置sa_flags=SA_SIGINFO, 中断处理逻辑函数形式为 void handler(int signo, siginfo_t *info, void *context);
即设 置sa_flags=SA_SIGINFO,将使得中断处理函数获得更多的信息
*/
action.sa_flags=SA_SIGINFO;
//为信号SIGUSR2设置中断处理逻辑,成功设置返回0
//sigaction成功调用后,进程就可以被信号SIGUSR2中断而且使用指定的处理逻辑了
if(sigaction(SIGUSR2, &action, NULL) < 0)
{
printf("in sig_install : 安装信号处理函数错误/n");
return 0;
}
//sigset用于存放需要更新(添加)到进程的信号集, sigold用于保存进程更新前的信号集
sigset_t sigset, sigold;
//清空初始化信号集sigset
sigemptyset(&sigset);
//在信 号集sigset中加入信号SIGUSR2
sigaddset(&sigset, SIGUSR2);
for(int i=0;i<100;++i)
{
/*使用SIG_BLOCK调用sigprocmask,并且有第二和第三个参数,效果是:
调用前的信号集合存入结构体sigold
sigset中的信号被屏蔽(block),即调用成功后,进程阻塞信号集sigset中的信号,单线程适用
因为在 sigset中存入了 信号SIGUSR2,所以下面的调用后, SIGUSR2信号对已进程是阻塞的
*/
sigprocmask(SIG_BLOCK, &sigset, &sigold));
{
//需要屏蔽 SIGUSR2中断处理逻辑的操作
key_operation_func();
}
/*使用SIG_SETMASK调用sigprocmask,并且只有第二个参数,效果是:
第二个参数表示的信号集合被设置为进程的信号集
因为在sigold是信号SIGUSR2被屏蔽前的快照,所以以下的函数被调用后,SIGUSR2 信号将被进程处理
*/
sigprocmask(SIG_SETMASK, &sigold, NULL));
sleep(1);
}
return 0;
}
//=======================进程A实现逻辑================================
int main(int arg, char* argv[])
{
// 从文件 /home/admin/.b_pid中读取进程B的进程号
char pidfilestr[256];
char* homepath = getenv("HOME");
if(NULL == homepath)
{
homepath = ".";
}
sprintf(pidfilestr, "%s/%s", homepath, ".b_pid");
FILE* fp = fopen(apcupfile, "r");
if (fp != NULL)
{
char szData[50];
memset(szData, '/0', 50);
fgets(szData, 50, fp);
fclose(fp);
pid_t bpid = atoi(szData);
//使用0调用kill,不发送信号,检测bpid进程是否存在
if(kill(bpid , 0) == 0)
{
//向bpid进程发送信号SIGUSR2
kill(bpid , SIGUSR2);
printf("send SIGUSR2 to %d %s", apcuppid, apcupfile);
}
}
return 0;
}