signal
为信号注册捕获信号后的处理函数
头文件:
#include <signal.h>
原型:
typedef void (*sighandler_t)(int); //数据类型,指针类型,数据类型的名字是sighandler_t;
sighandler_t signal(int signum, sighandler_t handler);
参数:
int signum:要捕获的信号的值,可以填数值,也可以填对应的宏。例如可以填10,或者SIGUSR1;如果填以下宏:
SIG_IGN:忽略该信号;
SIG_DFL:执行默认处理函数;
sighandler_t handler:handler为函数指针变量,该函数指针指向信号处理函数,信号处理函数的原型: void handler(int sig);
返回值:
成功,返回设置之前的信号处理函数handler的地址;
失败,返回SIG_ERR,更新errno;
例程:
typedef void (*sighadler_t)(int);
void handler(int sig)
{
printf("成功收到%d号信号\n", sig);
}
int main(void){
sighadler_t s = signal(SIGUSR1, handler);
if(SIG_ERR == s)
{
perror("signal");
return -1;
}
}
kill
发送信号给指定进程或进程组
头文件:
#include <sys/types.h>
#include <signal.h>
原型:
int kill(pid_t pid, int sig);
参数:
pid_t pid:指定进程的进程号或所在的进程组
pid > 0: 发送信号给指定进程号的进程;
pid == 0: 发送信号给当前进程组下的所有进程;
pid == -1: 发送信号给系统内的所有权限所能发送到的进程,除了init进程;
pid < -1: 发送信号给进程组ID为pid的绝对值的所有进程;
int sig:指定要发送的信号,可以填数值,也可以填宏;
sig == 0; 没有发送信号,可以用于判断pid进程或者进程组是否存在;
返回值:
成功,返回0;
失败,返回-1,更新errno;
例程:
if(kill(pid, 10) < 0)
{
perror("kill");
return -1;
}
raise
给自身发送一个信号
头文件:
#include <signal.h>
原型:
int raise(int sig); 等同于kill(getpid(), sig);
参数:
int sig:指定要发送的信号,可以填数值,也可以填宏;
返回值:
成功,返回0;
失败,返回非0;
alarm
设置一个定时器,等到时间结束,产生一个 14) SIGALRM 时钟信号
头文件:
#include <unistd.h>
原型:
unsigned int alarm(unsigned int seconds);
参数:
unsigned int seconds:设置定时时间,单位为秒;
seconds == 0;删除定时器,并且不会产生 SIGALRM 信号;
返回值:
成功,返回上一个定时器没有走完的秒数;
失败,返回0,如果没有预先设置定时器;
注意:
1.alarm是一个非阻塞的函数,会立即返回;
2.alarm会返回上一次设定后,没有走完的时间,所以一个进程只能设置一个定时器;
3.alarm(0),会删除定时器,并且不会产生SIGALRM信号;
pause
使进程阻塞,直到进程收到任意信号,并且执行了信号处理函数后结束阻塞
头文件:
#include <unistd.h>
原型:
int pause(void);
返回值:
pause会一直阻塞,直到执行了信号处理函数,并且从信号处理函数返回后,
pause才会结束阻塞,返回-1,并设置errno为EINTR;
例程:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
typedef void (*sighandler_t)(int);
void handler(int sig)
{
printf("Ring......\n");
}
int main(int argc, const char *argv[])
{
//捕获14号信号
sighandler_t s = signal(SIGALRM, handler);
if(s == SIG_ERR)
{
perror("signal");
return -1;
}
unsigned int res ;
res = alarm(3);
pause();
return 0;
}
strsignal
打印指定信号的默认处理函数功能
void handler(int signum)
{
printf("触发%d号信号:%s\n", signum, strsignal(signum));
}头文件:
#include <string.h>
原型:
char *strsignal(int sig);
参数:
int sig:指定要发送的信号,可以填数值,也可以填宏;
返回值:
返回指定信号的默认处理函数功能的描述字符串,如果信号编号无效,则返回未知信号消息。
例程:
void handler(int signum)
{
printf("触发%d号信号:%s\n", signum, strsignal(signum));
}
应用例程1
用信号方式回收僵尸进程
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
typedef void (*sighandler_t)(int);
void handler(int sig)
{
pid_t pid;
//非阻塞方式,如果子进程退出,则返回被回收的子进程的pid;如果没有子进程退出,则返回0
//循环,只要父进程能够收到最后一个信号,就能把前面(信号丢失时)未回收的僵尸进程回收完
while((pid = waitpid(-1, NULL, WNOHANG)) > 0)
{
printf("回收了一个子进程 %d 的资源\n",pid);
}
}
int main(int argc, const char *argv[])
{
//注册 SIGCHLD信号
sighandler_t s;
s = signal(SIGCHLD, handler);
if(s == SIG_ERR)
{
perror("signal");
return -1;
}
pid_t pid = fork();
if(pid > 0)
{
while(1)
{
printf("this is parent....\n");
sleep(1);
}
}
else if(0 == pid)
{
int i = 0;
for(i=0; i<3; i++)
{
printf("this is child\n");
sleep(1);
}
exit(1);
}
else
{
perror("fork");
return -1;
}
return 0;
}
应用例程2
创建两个没有血缘关系的进程A,B。
- A进程拷贝图片的前半部分,拷贝结束后,退出。
- 等待A进程拷贝结束后,B进程拷贝后半部分。
- 不能使用sleep函数。
提示:使用有名管道,信号,pause函数。
A进程拷贝前半部分
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
int main(int argc, const char *argv[])
{
//创建有名管道
if(mkfifo("./myfifo",0664) < 0)
{
if(errno != EEXIST)
{
perror("mkfifo");
return -1;
}
}
int fd = open("./myfifo",O_RDONLY);
if(fd < 0)
{
perror("open");
}
printf("管道创建成功,并打开了读端\n");
//打开原图
int picture = open("/home/linux/Pictures/p1.jpg",O_RDONLY);
int size = lseek(picture,0,SEEK_END);
lseek(picture,0,SEEK_SET);
//打开要拷贝的图片
int picture_copy = open("/home/linux/Pictures/4_6.jpg",O_WRONLY|O_CREAT|O_TRUNC,0664);
//拷贝一半图片
int i=0;
int res;
for(i=0;i<size/2;i++)
{
if(read(picture,&res,1) < 0)
{
perror("read");
return -1;
}
if(write(picture_copy,&res,1) < 0)
{
perror("write");
return -1;
}
}
printf("size = %d\n",size);
printf("size/2 = %d\n",size/2);
int s1 = lseek(picture,0,SEEK_CUR);
printf("s1 = %d\n",s1);
int s2 = lseek(picture_copy,0,SEEK_CUR);
printf("s2 = %d\n",s2);
printf("前半部分拷贝完毕\n");
//传递信号给进程class2
pid_t pid;
if(read(fd,&pid,sizeof(pid)) < 0)
{
perror("read");
return -1;
}
printf("class2's pid = %d\n",pid);
if(kill(pid,SIGUSR1) < 0)
{
perror("kill");
return -1;
}
printf("信号发送完毕\n");
return 0;
}
B进程拷贝后半部分
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
typedef void (*sighandler_t)(int);
int flag = 0;
void handler(int signum)
{
flag = 1;
printf("接收到信号%d了\n",signum);
}
int main(int argc, const char *argv[])
{
//捕获10号信号
sighandler_t signum = signal(SIGUSR1,handler);
if(signum == SIG_ERR)
{
perror("signal");
return -1;
}
printf("10号信号注册完毕\n");
//创建有名管道
if(mkfifo("./myfifo",0664) < 0)
{
if(errno != EEXIST)
{
perror("mkfifo");
return -1;
}
}
int fd = open("./myfifo",O_WRONLY);
if(fd < 0)
{
perror("open");
return -1;
}
printf("管道创建成功,并打开了写端\n");
//打开原图
int picture = open("/home/linux/Pictures/p1.jpg",O_RDONLY);
int size = lseek(picture,0,SEEK_END);
printf("size = %d\n",size);
int s1 = lseek(picture,size/2,SEEK_SET);
printf("s1 = %d\n",s1);
//打开要拷贝的图片
int picture_copy = open("/home/linux/Pictures/4_6.jpg",O_WRONLY|O_CREAT|O_TRUNC,0664);
int s2 = lseek(picture_copy,size/2,SEEK_SET);
printf("s2 = %d\n",s2);
//发送pid给管道
pid_t pid = getpid();
if(write(fd,&pid,sizeof(pid)) < 0)
{
perror("write");
return -1;
}
printf("pid发送完毕\n");
//阻塞,等待class1拷贝完成信号
pause();
//接收到信号,拷贝后半部分图片
int i;
int res;
if(flag)
{
printf("size/2 = %d\n",size/2);
for(i=size/2;i<size;i++)
{
if(read(picture,&res,1) < 0)
{
perror("read");
return -1;
}
if(write(picture_copy,&res,1) < 0)
{
perror("write");
return -1;
}
}
}
printf("后半部分拷贝完毕\n");
return 0;
}
结果
本文链接:https://shengto.top/c/signal_routine.html
转载时须注明出处及本声明