1. kill函数
#include<sys/types.h>
#include<signal.h>
int kill(pid_t pid, int sig);
/*
功能:
给进程pid发送信号sig
参数:
pid:取值有4种情况:
> 0:将sig信号发送给进程号为pid的进程;
= 0:将sig信号发送给当前进程组中的所有进程;
= -1:将sig信号发送给系统内的所有进程;
< -1:将sig信号发送给进程组号为pid绝对值的进程组中的所有进程。
sig:信号。推荐使用信号宏。
返回值:
成功:0;
失败:-1
*/
a)root权限用户可给任意用户发信号,普通用户不可给root权限用户发信号;
b)普通用户不允许:kill -9 root用户进程的pid; 普通用户也不允许向其他普通用户发送信号终止其进程,只能向自己创建的进程发送信号;普通用户只能给自己的进程发送信号。
kill函数示例:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
int main(int argc, const char* argv[]) {
pid_t pid = -1;
int ret = -1;
// 创建一个子进程
pid = fork();
if (-1 == pid) {
perror("fork");
return 1;
}
// 父进程杀死子进程
if (pid > 0) { // 父进程
sleep(3);
ret = kill(pid, SIGTERM);
if (-1 == ret) {
perror("kill");
return 1;
}
printf("子进程被杀死!\n");
} else { // 子进程
while (1) {
printf("子进程运行中...\n");
sleep(1);
}
exit(0);
}
return 0;
}
运行结果:
2. raise函数
#include<signal.h>
int raise(int sig);
/*
功能:
当前进程给自己发送信号sig,相当于kill(getpid(), sig);
参数:
sig:信号宏;
返回值:
成功:0
失败:非0
*/
其中,sig为信号的编号,见 Linux信号:信号 & 信号集 & 信号集函数 中的 “2. 信号的编号”。
raise函数示例:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
int main(int argc, const char* argv[]) {
int i = 4;
while (1) {
printf("进程%d自杀倒计时: %d\n", getpid(), i);
sleep(1);
if (0== --i) {
printf("进程%d杀死自己.\n", getpid());
raise(SIGTERM);
}
}
return 0;
}
运行结果:
3. abort函数
#include<stdlib.h>
void abort(void);
/*
功能:
给自己发送异常终止信号,即6号信号SIGABTR,并产生core文件。
等价于kill(getpid(), SIGABRT);
*/
abort函数示例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main(int argc, const char* argv[]) {
int i = 3;
while (1) {
printf("终止倒计时:%d\n", i--);
sleep(1);
if (0 == i) {
printf("进程终止!\n");
abort();
}
}
return 0;
}
运行结果:
4. alarm函数
定时器
#include<unistd.h>
unsigned int alarm(unsigned int seconds);
/*
功能:
设置定时器:指定seconds后,内核给当前进程发送14号信号SIGALRM信号,
进程收到该信号,默认终止进程。
每个进程有且只有唯一的定时器。
参数:
seconds:定时时间,单位s;
为0时则表示取消定时器,并返回旧闹钟剩余秒数。
返回值:
0或剩余秒数。
*/
注意:alarm函数与调用进程状态无关。进程处于任何状态(退出除外),alarm都会计时。
alarm函数示例:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
int main(int argc, const char* argv[]) {
unsigned int ret = 0;
ret = alarm(5);
sleep(3); // 进程的sleep状态不会影响闹钟的计时
ret = alarm(4); // 覆盖之前的闹钟,重新计时
/* 结果为2. 第一次计时5秒,sleep了3秒,
执行到第二个闹钟时,第一次闹钟剩余2s*/
printf("上次闹钟剩下的时间:%u\n", ret);
getchar();
return 0;
}
运行结果:
前后总共计时7s
5. setitimer函数
多功能定时器
#include<sys/time.h>
int setitimer(int which, const struct itimerval* new_value, struct itimerval* old_value);
/*
功能:
设置定时器。可替代alarm,精度μs。可实现周期定时。
参数:
which:定时方式:
a) 自然定时:ITIMER_REAL(14号信号,SIGALRM),计算自然时间,默认终止进程;
b) 虚拟空间计时(用户空间):ITIMER_VIRTUAL(26号信号,SIGVTALRM),只计算占CPU时间;
c) 运行时计时(用户+内核):ITIMER_PROF(27号信号,SIGPROF),计算占用CPU+系统调用时间;
new_value:超时时间;
itimerval.it_value:第一次执行function延迟的秒数;
itimerval.it_interval:以后执行function的时间间隔
old_value:存放旧的timeout值,一般为NULL
返回值:
成功:0;
失败:-1
*/
struct itimerval {
struct timerval it_value; // 闹钟触发时间
struct timerval it_interval; // 闹钟触发周期
}
struct timeval {
long tv_sec; // 秒
long tv_usec; // 微秒
}
setitimer示例:
#include<stdio.h>
#include<sys/time.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, const char* argv[]) {
int ret = -1;
struct itimerval tmo;
// 第一次触发时间: 3s
tmo.it_value.tv_sec = 3;
tmo.it_value.tv_usec = 0;
// 触发周期: 2s一次。 此处无效果,需结合后面的信号捕捉使用。
tmo.it_interval.tv_sec = 2;
tmo.it_interval.tv_usec = 0;
// 设置定时器, 默认动作:终止进程
ret = setitimer(ITIMER_REAL, &tmo, NULL);
if (-1 == ret) {
perror("setitimer");
return 1;
}
printf("按下任意键继续\n");
getchar();
return 0;
}
运行结果:
6. signal函数
注册信号处理函数。
注:由于历史原因,signal函数在不同版本的类Unix系统中含义不同,应避免使用signal函数。
#include<signal.h>
typedef void(*sighandler_t)(int);
/*
函数指针。
名字:sighandler_t,返回值:void,形参:一个int类型;
*/
sighandler_t signal(int signum, sighandler_t handler);
/*
功能:
注册信号处理函数:收到signum信号,则执行handler函数。
不可用于SIGKILL、SIGSTOP信号。
参数:
signum:信号编号,建议写信号的宏;
handler:3中取值情况:
SIG_IGN:忽略该信号;
SIG_DFL:执行默认处理方式;
回调函数名:自定义处理方式;
回调函数定义如下:
void func(int signo) {
// signo为触发的信号,为signal函数的第一个参数
}
返回值:
成功:第一次返回NULL,下一次返回此信号上次注册的信号处理函数的地址;
若需要使用此返回值,则必须在前面声明此函数的指针类型。
失败:SIG_ERR
*/
signal示例1:
捕捉Ctrl+c、Ctrl+\信号,并自定义各自的处理方式。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<unistd.h>
void func0(int signum) {
printf("捕捉到信号:%d\n", signum);
}
void func1(int signum) {
printf("捕捉到信号:%d\n", signum);
}
int main(int argc, const char* argv[]) {
/*注册信号处理函数。*/
// SIGINT: Ctrl + c
signal(SIGINT, func0); // 处理异步信号
/*SIGQUIT: Ctrl + \*/
signal(SIGQUIT, func1);
while (1) {
getchar();
}
return 0;
}
运行结果:
signal示例2:
捕捉setitimer信号,修改它默认终止进程的行为。
#include<stdio.h>
#include<sys/time.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
void func(int signum) {
printf("捕捉到SIGALRM信号.\n");
}
int main(int argc, const char* argv[]) {
int ret = -1;
struct itimerval tmo;
// 第一次触发时间: 3s
tmo.it_value.tv_sec = 3;
tmo.it_value.tv_usec = 0;
// 触发周期: 2s一次。 此处无效果,需结合后面的信号捕捉使用。
tmo.it_interval.tv_sec = 2;
tmo.it_interval.tv_usec = 0;
// 捕捉信号SIGALRM,修改它的默认处行为。
signal(SIGALRM, func);
// 设置定时器, 默认动作:终止进程
ret = setitimer(ITIMER_REAL, &tmo, NULL);
if (-1 == ret) {
perror("setitimer");
return 1;
}
printf("按下任意键继续\n");
getchar();
return 0;
}
运行结果:
7. sigaction函数
改变指定信号的处理方式,功能类似于signal。
#include<signal.h>
int sigaction(int signum, const struct sigaction* act, struct sigaction* oldact);
/*
功能:
修改或检查signum信号的处理方式。
参数:
signum:要操作的信号;
act:信号的新处理方式;传入参数;
oldact:信号原先的处理方式;传出参数;
若act非空,则将信号原处理方式变为act;若oldact非空,则将先前的处理方式存入oldact。
返回值:
成功:0
失败:-1
*/
struct sigaction {
void(*sa_handler)(int); // 旧的信号处理函数指针
void(*sa_sigaction)(int, siginfo_t*, void*); // 新的信号处理函数指针
sigset_t sa_mask; // 阻塞信号集
int sa_flags; // 信号处理方式
void(*sa_restorer)(void); // 已弃用
}
/*
(1) sa_handler、sa_sigaction:信号处理函数指针,与signal函数中的函数指针用法一样,
根据情况给两者之一赋值。
a) SIG_IGN:忽略该信号
b) SIG_DFL:执行默认处理方式
c) 回调函数名:自定义处理方式
(2) sa_mask:阻塞信号集。信号处理函数执行过程中,临时屏蔽指定信号。
(3) sa_flags:指定信号处理行为,通常设置为0(使用旧的信号处理函数). 也可是以下值的"按位或"组合
SA_NOCLDSTOP:使父进程在其子进程暂停或继续运行时不会收到SIGCHLD;
SA_NOCLDWAIT:使父进程在其子进程退出时不会收到SIGCHLD信号,此时子进程也不会成为僵尸进程;
SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能收到该信号;
SA_RESETHAND:信号处理之后重新设置为默认的处理方式;
SA_SIGINFO:使用sa_sigaction成员而不是sa_handler作为信号处理函数;
SA_RESTART:使被信号打断的系统调用自动重新发起(已废弃);
*/
void(*sa_sigaction)(int, siginfo_t*, void*);
/*
功能:
新的信号处理函数指针,
参数:
signum:信号编号;
info:记录信号发送进程信息的结构体;
context:赋给指向ucontext_t类型的一个对象指针,
以引用在传递信号是被中断的接收进程或线程的上下文。
*/
sigaction示例1:
旧的信号处理函数:
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
// 信号处理函数
void func(int signum) {
printf("捕捉到信号%d.\n", signum);
}
int main(int argc, const char* argv[]) {
int ret = -1;
struct sigaction act;
act.sa_handler = func; // 自定义旧的信号处理函数
act.sa_flags = 0; // 使用旧的信号处理函数
ret = sigaction(SIGINT, &act, NULL);
if (-1 == ret) {
perror("sigaction");
return 1;
}
printf("按下任意键继续...\n");
getchar();
return 0;
}
运行结果:
sigaction示例2:
新的信号处理函数:
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
// 信号处理函数
void func(int signum, siginfo_t* info, void* context) {
printf("捕捉到信号%d.\n", signum);
}
int main(int argc, const char* argv[]) {
int ret = -1;
struct sigaction act;
act.sa_sigaction = func; // 自定义新的信号处理函数
act.sa_flags = SA_SIGINFO; // 使用新的信号处理函数
ret = sigaction(SIGINT, &act, NULL);
if (-1 == ret) {
perror("sigaction");
return 1;
}
printf("按下任意键继续...\n");
getchar();
return 0;
}
运行结果:
8. sigqueue函数
#include<signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
/*
功能:
给指定进程发送信号。
参数:
pid:进程号;
sig:信号;
value:通过信号传递的参数。
union sigval {
union sigval {
int sival_int;
void* sival_ptr;
}
}
返回值:
成功:0
失败:-1
*/