一、概念
信号通信,其实就是内核向用户空间进程发送信号,只有内核才能发信号,用户空间进程不能发送信号。信号已经是存在内核中的了,不需要用户自己创建。
信号通信的框架
* 信号的发送(发送信号进程):kill
、
raise
、
alarm
* 信号的接收(接收信号进程) : pause()
、
sleep
、
while(1)
* 信号的处理(接收信号进程) :signal
二、相关函数
1.信号的发送(发送信号进程)
所需头文件:
#include<signal.h>
#include<sys/types.h>
函数原型:
int kill(pid_t pid, int sig);
参数:
函数传入值:
pid
正数:要接收信号的进程的进程号
0:信号被发送到所有和
pid
进程在同一个进程组的进程
‐1:信号发给所有的进程表中的进程
(
除了进程号最大的进程外
)
sig
:信号
函数返回值:成功
0
出错
‐1
示例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc,char *argv[])
{
int pid,sig;
if(argc != 3)
{
printf("输入错误\n");
return -1;
}
sig = atoi(argv[1]);
pid = atoi(argv[2]);
printf("pid = %d,sig = %d\n",pid,sig);
int rnt = kill(pid,sig);
if(rnt == 0)
{
printf("杀死进程%d成功\n",pid);
}
return 0;
}
~
运行结果:
2.raise: 发信号给自己 == kill(getpid(), sig)
所需头文件:
#include<signal.h>
#include<sys/types.h>
函数原型: int raise(int sig);
参数:
函数传入值:
sig
:信号
函数返回值:
成功
0
出错
‐1
示例:
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("hello;\n");
//int rnt = raise(9);
int rnt = kill(getpid(),9);
if(rnt == 0)
{
printf("杀死自己成功\n");
}
printf("world\n");
return 0;
}
运行结果:
3.alarm : 发送闹钟信号的函数
alarm 与 raise 函数的比较:
相同点:让内核发送信号给当前进程
不同点:
alarm 只会发送SIGALARM
信号
alarm 会让内核定时一段时间之后发送信号, raise
会让内核立刻发信号
所需头文件
#include <unistd.h>
函数原型 unsigned int alarm(unsigned int seconds)
参数:
seconds
:指定秒数
返回值:
成功:如果调用此alarm()
前,进程中已经设置了闹钟时间,则 返回上一个闹钟时间的剩余时间,否则返回
0
。
出错:‐1
示例:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main()
{
int i;
alarm(5);
printf("alarm start\n");
for(i =0 ;i<20;i++)
{
sleep(1);
printf("i = %d\n",i);
}
return 0;
}
运行结果:
4.信号的接收
接收信号的进程,要有什么条件:要想使接收的进程能收到信号,这个进程不能结束
: sleep
pause:进程状态为
S (休眠)
函数原型
int pause(void);
函数返回值:
成功:
0
,出错:
‐1
示例:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main()
{
int i,rnt;
printf("pause start\n");
pause();
for(i =0 ;i<20;i++)
{
sleep(1);
printf("i = %d\n",i);
}
return 0;
}
运行结果:
5.信号的处理
收到信号的进程,应该怎样处理? 处理的方式:
1.进程的默认处理方式(内核为用户进程设置的默认处理方式)
A:忽略B
:终止进程
C:
暂停
2.自己的处理方式:
自己处理信号的方法告诉内核,这样你的进程收到了这个信号就会采用你自己的的处理方式、
函数原型 void (*signal(int signum, void (*handler)(int)))(int);
参数:
signum
:指定信号
handler:
1.SIG_IGN:忽略该信号。
2.SIG_DFL:采用系统默认方式处理信号
3.自定义的信号处理函数指针
函数返回值
成功:设置之前的信号处理方式
出错:
‐1
signal
函数有二个参数,第一个参数是一个整形变量(信号值),第二个参数是一个函数指针,是我们自己写的处理函
数;这个函数的返回值是一个函数指针。
1.示例:自定义的信号处理函数指针
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void myfun(int signum)
{
int i;
while(i<5)
{
printf("signum = %d,i = %d\n",signum,i);
i++;
sleep(1);
}
}
int main()
{
int i,rnt;
signal(14,myfun);//14信号是定时器到时发出
printf("alarm start\n");
alarm(7);
for(i =0 ;i<10;i++)
{
sleep(1);
printf("i = %d\n",i);
}
return 0;
}
运行结果:
PS:回调函数执行完之后会重新回到主函数继续执行
2.示例:忽略该信号
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void myfun(int signum)
{
int i;
while(i<5)
{
printf("signum = %d,i = %d\n",signum,i);
i++;
sleep(1);
}
}
int main()
{
int i,rnt;
signal(14,myfun);//14信号是定时器到时发出
printf("alarm start\n");
alarm(7);
signal(14,SIG_IGN);
for(i =0 ;i<10;i++)
{
sleep(1);
printf("i = %d\n",i);
}
return 0;
}
运行结果:
3.示例:采用系统默认方式处理信号
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void myfun(int signum)
{
int i;
while(i<5)
{
printf("signum = %d,i = %d\n",signum,i);
i++;
sleep(1);
}
}
int main()
{
int i,rnt;
signal(14,SIG_DFL);
printf("alarm start\n");
alarm(7);
for(i =0 ;i<10;i++)
{
sleep(1);
printf("i = %d\n",i);
}
return 0;
}
运行结果:
三、信号灯
信号灯集合(可以包含多个信号灯)
IPC
对象是一个信号的集合(多个信号量) ,
一个信号灯有多个信号量。
1.相关函数
1.函数原型: int semget(key_t key, int nsems, int semflg);
功能:创建一个新的信号量或获取一个已经存在的信号量的键值。
函数参数:
key
:和信号灯集关联的
key
值
nsems:
信号灯集中包含的信号灯数目
semflg
:信号灯集的访问权限
函数返回值:
成功:信号灯集
ID
出错:
‐1
2.函数原型:int semctl ( int semid, int semnum, int cmd,…union semun arg(不是地址));
功能:控制信号量,删除信号量或初始化信号量
函数参数:
semid
:信号灯集
ID
semnum:
要修改的信号灯编号
cmd :
GETVAL:获取信号灯的值
SETVAL:设置信号灯的值
IPC_RMID:从系统中删除信号灯集合
函数返回值:
成功:
0
出错:
‐1
三、PV操作
int semop(int semid ,struct sembuf *sops ,size_t nsops);
功能:用户改变信号量的值。也就是使用资源还是释放资源使用权
参数:
semid :
信号量的标识码。也就是
semget
()的返回值
sops
是一个指向结构体数组的指针。
struct sembuf{
unsigned short sem_num; //
信号灯编号;
short sem_op; //对该信号量的操作。‐1 ,P
操作,
1
,
V
操作
short sem_flg; //0阻塞,1非阻塞
};
sem_op :
操作信号灯的个数
//
如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;如果
sem_op
的值为负 数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op
的绝对值。通常用于获取资源的使用 权;如果sem_op
的值为
0
,则操作将暂时阻塞,直到信号的值变为
0
。