进程可以通过kill函数向包括它本身在内的其他进程发送一个信号,如果程序没有发送这个信号的权限,对kill函数的调用就将失败,而失败的常见原因是目标进程由另一个用户所拥有。
#include
#include
int kill(pid_t pid, int sig);
#include
#include
#include
#include
#include
void fa(int signo)
{
printf("捕获到了信号%d\n", signo);
}
int main(void)
{
//使用fork函数创建子进程
pid_t pid = fork();
if(-1 == pid)
{
perror("fork"),exit(1);
}
if(0 == pid) //子进程
{
printf("pid = %d\n", getpid());
//设置对信号50进行自定义处理
signal(50, fa); // 捕捉信号
while(1);
}
sleep(1);
//判断子进程是否存在
if(0 == kill(pid,0))
{
printf("父进程发送信号50\n");
kill(pid,50); //使用kill函数发送信号
}
return 0;
}
kill pid的作用是向进程号为pid的进程发送SIGTERM(这是kill默认发送的信号),该信号是一个结束进程的信号且可以被应用程序捕获。若应用程序没有捕获并响应该信号的逻辑代码,则该信号的默认动作是kill掉进程。这是终止指定进程的推荐做法。
#include
int raise(int sig);
#include
#include
#include
#include
int main (void)
{
printf("按回车键,终止进程\n");
getchar();
raise(SIGTERM); // 进程自杀
while(1)
pause();
return 0;
}
#include
unsigned int alarm(unsigned int seconds);
#include
#include
int main()
{
int cnt;
alarm(1); // 设置定时器,在将来的某个时刻定时器超时,程序结束
for (cnt = 0; 1; ++cnt)
printf("%d ", cnt); // 计算系统在一秒内可以计算到多大的数
return 0;
}
#include
int pause(void);
#include
#include
#include
#include
#include
void sigint (int signo) // 信号处理函数
{
printf ("\n中断符号被发送\n");
}
int main(void)
{
if (signal(SIGINT, sigint) == SIG_ERR)
perror("signal"), exit(1);
printf("按 ctrl+c 继续\n");
// pause要么不返回,要么返回-1,且errno设置为enter
if (pause() != -1 && errno != EINTR)
perror("pause"), exit(1);
printf("进程继续\n");
return 0;
}
输出结果:
按 ctrl+c 继续
^C
中断符号被发送
进程继续
#include
unsigned int sleep(unsigned int seconds);
#include
#include
#include
#include
#include
void sigint (int signo)
{
printf ("\n中断符号被发送\n");
}
int main (void)
{
if (signal (SIGINT, sigint) == SIG_ERR)
perror ("signal"), exit (1);
printf ("按 ctrl+c 继续\n");
int res = sleep (60);
if (res)
printf ("进程被提前%d秒叫醒\n", res);
printf ("进程继续\n");
return 0;
}
输出结果:
按 ctrl+c 继续
^C
中断符号被发送
进程被提前57秒叫醒
进程继续
#include
void (*signal(int sig, void (*func)(int)))(int);
【Note】:因为子进程在开始时复制了父进程的内存映像,所以子进程继承父进程的信号处理方式。
#include
#include
#include
// 信号处理函数
void signal_handler(int signo)
{
if(signo == SIGINT){
printf("recv SIGINT\n");
}else if(signo == SIGQUIT){
printf("recv SIGQUIT\n");
}
}
int main(int argc, char *argv[])
{
printf("wait for SIGINT OR SIGQUIT\n");
/* SIGINT: Ctrl+c ; SIGQUIT: Ctrl+\ */
// 信号注册函数
signal(SIGINT, signal_handler);
signal(SIGQUIT, signal_handler);
// 等待信号
pause();
pause();
return 0;
}
sigaction结构体:
struct sigaction
{
//信号处理函数
void (*sa_handler)(int);
// 调用信号捕捉函数前,该信号集被加到信号屏蔽字中,
// 从而在调用信号捕捉函数时,能阻塞某些信号。
sigset_t sa_mask;
// ;信号处理修改器。
int sa_flags;
// 替代的信号处理程序,一次只能使用sa_handler和sa_sigaction中的一个。
void (*sa_sigaction)(int, siginfo_t *, void *);
};
【Note】:同一信号多次发生,并不将它们加入队列;如:某种信号阻塞时发生了5次,解除阻塞后,信号处理函数只调用一次。
sigaction函数的原型为:
#include
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
#include
#include
#include
#include
void oldsigint(int signum) // 信号处理函数
{
printf ("\n%d进程:收到%d信号\n", getpid (), signum);
}
int main()
{
struct sigaction sigact = {}; // 定义sigaction结构体
// 指定信号处理函数
sigact.sa_handler = oldsigint;
// 用于设置在信号处理函数的执行期间,需要屏蔽的信号
sigaddset(&sigact.sa_mask, SIGINT);
// 设置信号处理的标志
// SA_NODEFER表示解除对相同信号的屏蔽
// SA_RESETHAND表示处理信号后恢复默认处理方式
sigact.sa_flags = SA_NODEFER | SA_RESETHAND;
//sigact.sa_flags = 0; // 不适用处理标志
// 注册信号和结构体
if (sigaction(SIGINT, &sigact, NULL) == -1)
{
perror("sigaction");
exit(1);
}
while (1)
{
printf("************\n");
sleep(1);
}
return 0;
}
我们已经知道,我们可以通过信号来终止进程,也可以通过信号来在进程间进行通信,程序也可以通过指定信号的关联处理函数来改变信号的默认处理方式,也可以屏蔽某些信号,使其不能传递给进程。那么我们应该如何设定我们需要处理的信号,我们不需要处理哪些信号等问题呢?信号集函数就是帮助我们解决这些问题的。
实际执行信号的处理动作称为信号递送(Delivery),信号从产生到递送之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号,SIGKILL 和 SIGSTOP 不能被阻塞。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递送的动作。
每个进程都有一个信号掩码,它实际上是一个信号集,位于该信号集中的信号一旦产生,并不会被递送给相应的进程,而是会被阻塞在未决状态。在信号处理函数执行期间,这个正在被处理的信号总是处于信号掩码中,如果又有该信号产生,则会被阻塞,直到上一个针对该信号的处理过程结束以后才会被递送。
#include
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
#include
int sigpending(sigset_t *set);
#include
// 清空信号集,即将信号集的全部信号位清 0。
int sigemptyset(sigset_t *set);
// 填满信号集,即将信号集的全部信号位置 1。
int sigfillset(sigset_t *set);
// 加入信号,即将信号集中与指定信号编号对应的信号位置 1。
int sigaddset(sigset_t *set, int signum);
// 删除信号,即将信号集中与指定信号编号对应的信号位清 0。
int sigdelset(sigset_t *set, int signum);
// 以上4 个函数返回值:若成功,返回 0,;若出错,返回 -1
// 判断信号集中是否有某信号,即检查信号集中与指定信号编号
// 对应的信号位是否为 1。
int sigismember(const sigset_t *set, int signum);
// 返回值:若真,返回 1;若假,返回 0
【Demo】:
#include
#include
#include
#include
static void sig_int(int)
{
printf("caught SIGINT\n");
if (signal(SIGINT, SIG_DFL) == SIG_ERR)
perror("can't reset SIGINT");
}
int main()
{
sigset_t newmask, oldmask, pendmask;
if (signal(SIGINT, sig_int) == SIG_ERR)
perror("can't catch SIGINT");
sigemptyset(&newmask); // 清空信号集
sigaddset(&newmask, SIGINT); // 加入信号(阻塞 SIGQUIT 信号)
// 保存当前的信号屏蔽字
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
perror("SIG_BLOCK error");
sleep(5); // 休眠五秒,在此期间,SIG_QUIT信号都会被阻塞
if (sigpending(&pendmask) < 0) // 获取当前信号的未决信号集
perror("sigpending error");
if (sigismember(&pendmask, SIGINT)) // 判断信号集中有没有SIG_QUIT信号
printf("\nSIGQUIT pending\n");
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) // 不再阻塞原信号
perror("SIG_SETMASK error");
printf("SIGINT unblocked\n");
sleep(5);
return 0;
}
#include
void abort(void);
功能: abort 函数的功能是使程序异常终止。如果 abort 函数导致进程终止,则所有打开的流都将关闭并刷新。
#include
#include
#include
#include
void fa(int signo)
{
printf("捕捉到了信号%d\n", signo);
}
int main (void)
{
signal(SIGABRT, fa);
printf("1111111111111\n");
abort();
printf("2222222222222\n");
return 0;
}
#include
int sigqueue(pid_t pid, int sig, const union sigval value);
#include
#include
#include
#include
#include
void fa(int signo, siginfo_t* info, void* p)
{
printf("进程%d发送来了信号%d,附加数据是:%d\n",
info->si_pid, signo, info->si_value);
}
int main(void)
{
//定义结构体变量进行初始化
struct sigaction action = {};
//给第二个函数指针进行初始化
action.sa_sigaction = fa;
//给处理标志进行赋值
//表采用结构中第二个函数指针处理
action.sa_flags = SA_SIGINFO;
//使用sigaction对信号40自定义处理
sigaction(40, &action, NULL);
//创建子进程给父进程发信号和数据
pid_t pid = fork();
if(-1 == pid)
{
perror("fork"),exit(-1);
}
if(0 == pid) //子进程
{
int i = 0;
for(i = 0; i < 100; i++)
{
//定义联合进行初始化
union sigval v;
v.sival_int = i;
//发送信号和附加数据
sigqueue(getppid(), 40, v);
}
sleep(1);
exit(1); //终止子进程
}
//父进程等待处理信号和附加数据
while(1);
return 0;
}
参考:https://www.cnblogs.com/wuchanming/p/4381574.html
https://blog.csdn.net/column/details/14909.html?&page=1
https://blog.csdn.net/lianghe_work/article/details/46804469
https://blog.csdn.net/ZX714311728/article/details/53056927