系列文章目录
文章一: Linux—进程通信(一)—进程通信概述
文章二: Linux—进程通信(二)—管道
文章三: Linux—进程通信(三)—信号通信
文章四: Linux—进程通信(四)—IPC通信之共享内存
文章五: Linux—进程通信(五)—IPC通信之消息队列
文章六: Linux—进程通信(六)—IPC通信之信号灯
文章目录
前言
本文主要讲解了进程通信的信号通信方式
给出了信号通信所需要的各种操作函数
同时基于Linux系统给出了C语言代码的例子
一、信号通信是什么?
信号通信是进程之间通过信号来互相传递信息的通信方式,信号由内核来发送,用户空间不能发送信号。
区别于管道:
1.信号是已经在Linux内核中存在的对象
2.内核中存在着多个信号
3.信号本身不能改变,但是得到信号的“反应”可以根据需求而改变。
可以通过以下的Linux命令可以查看信号的具体信息:
kill -l
结果为:
1.数子代表每种信号的ID号。
2.名字代表信号的意义。
3.总共有64中信号。
二、信号的使用
1.信号的应用举例
在LInux系统中有如下杀死指定进程的命令:
kill 9 进程的pid号
这里的pid号可以通过如下命令查看:
ps -axj
执行如上命令后,pid号就在第二列。
这个命令的原理就是根据信号通信实现的,其中9代表Linux内核中的第9个信号,根据上面的图可以发现该信号是“SIGKILL”。
2.信号通信的函数
信号发送函数kill
函数原型:
int kill(pid_t pid,int sig);
所需头文件:
#include<signal.h>
#include<sys/types.h>
函数参数:
1.第一个参数是pid:
正数:要接收信号的进程的进程号
0:信号被发送到所有和此进程在同一进程组的进程
-1:信号发送给所有进程表中的进程(除了进程号最大的进程)
2.sig:信号的ID
函数返回值:
成功返回0,出错返回-1
例子1:实现Linux的kill命令
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char *argv[])
{
int sig;
int pid;
if(argc<3)
{
printf("please input param\n");
return -1;
}
sig=atoi(argv[1]);
pid=atoi(argv[2]);
printf("sig=%d,pid=%d\n",sig,pid);
kill(pid,sig);
return 0;
}
信号的发送函数raise
函数原型:
int raise(int sig);
所需头文件:
#include<signal.h>
#include<sys/types.h>
函数功能:
发信号给自己
相当于:kill(getpid(),sig);
参数:
1.sig:信号的ID
返回值:
成功返回0,失败返回-1
注意:
raise(9);终止进程时,调用的是_exit(),不会将库缓存刷入内核空间。
例子2:raise函数的示例
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
printf("raise before\n");
raise(9);
printf("raise after\n");
return 0;
}
执行结果:
raise before打印,raise after不打印,即进程被终止。同时还会打印进程被终止的信息。
信号发送函数alarm
首先要了解一个信号:
信号 | 信号代表的意义 | 默认操作 |
SIGALRM | 该信号是当一个定时器到时的时候发出 | 终止进程 |
函数原型
unsigned int alarm(unsigned int seconds);
所需头文件:
#incldue<unistd.h>
函数功能:
1.只会发送SIGALRM信号给当前进程。
2.alarm函数会让内核定时一段时间后发送该信号
函数参数:
seconds:指定秒数
函数返回值:
1.成功一般返回0,失败一定返回-1
2.如果进程在调用alarm前设置了闹钟时间,则成功会返回上一个闹钟时间剩余的时间。
例子3:闹钟信号杀死进程
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
int i;
i=0;
printf("alarm before\n");
alarm(9);
printf("alarm after\n");
while(i<20)
{
i++;
sleep(1);
printf("process things %d\n",i);
}
return 0;
}
执行结果:
i的值是8的时候会接着打印“Alarm clock”,进程被杀死
信号的接收方法
信号的接收可以用以下的三种方式:
1.pause()
2.sleep()
3.while(1)
这里我们重点介绍pause()函数
函数原型;
int pause(void);
所需头文件:
#incldue<unistd.h>
函数功能:
让进程处于睡眠状态,即S状态
函数的返回值:
成功返回0,失败返回-1
信号的处理函数signal
我们要明确:
1.之前我们没有采用signal函数,系统会采用默认的信号处理方式。
2.默认处理方式一般是终止进程,有些是睡眠进程,也有些是忽略信号。
接下来我们要介绍signal函数:
函数原型:
//返回值是函数指针
//第二个参数也是一个函数指针
void(*signal(int signum,void(*hander)(int)))(int);
所需头文件:
#include<signal.h>
函数的参数:
1.signum:指定信号
2.hander:
SIG_IGN:忽略该信号
SIG_DFL:采用系统默认的方式处理信号
自定义信号处理函数的函数指针
函数的返回值:
1.成功:设置之前的那个信号处理函数的函数指针
2.失败:返回-1
例子4:signal函数的使用
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
void myfun(int signum)
{
int i=0;
while(i<10)
{
printf("process signal signum=%d",signum);
sleep(1);
i++;
}
return;
}
int main()
{
int i;
i=0;
//14号信号是alarm所发送的信号
signal(14,myfun);
printf("alarm before\n");
alarm(9);
printf("alarm after\n");
while(i<20)
{
i++;
sleep(1);
printf("process things %d\n",i);
}
return 0;
}
执行结果:
1.先打印main函数的信息到i=8的那条语句。
2.然后程序跳到myfun函数执行10条打印语句。
3.然后回到main函数继续执行完之后的打印语句。
例子5:以父子进程的方式进行信号通信
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
void myfun(int signum)
{
int i=0;
while(i<5)
{
printf("recive signum=%d",signum);
sleep(1);
i++;
}
return;
}
void myfun1(int signum)
{
printf("recive signum=%d",signum);
//wait函数是等待子进程结束,功能用来回收僵尸进程的资源
wait(NULL);
return;
}
int main()
{
pid_t pid;
pid=fork();
if(pid>0)
{
int i=0;
signal(10,myfun);
signal(17,myfun1);
while(1)
{
printf("parent process things,i=%d\n",i);
sleep(1);
i++;
}
}
if(pid==0)
{
sleep(10);
/*
getppid()是获得父进程的pid号
10号是SIGUSR1信号,是用户自定义信号
*/
kill(getppid(),10);
sleep(10);
exit(0);
//包含一个发送信号函数
//kill(getppid(),17);
}
return 0;
}
总结
多线程编程也有信号通信,感兴趣的可以看:
Linux—多线程编程(二)—多线程的控制