概述
文章目录
- 常用信号链接
- No.1 信号
- No.2 信号的处理过程
- No.3 高级信号处理:sigaction
- No.4 简单信号发送:kill
- No.5 可以多个信号使用一个函数
- No.6 定时信号:SIGALRM
- alarm函数 只能触发一次
- setitimer 函数,可以多次触发
- No.7 信号屏蔽
- No.8 练习
常用信号链接
=========================================
No.1 信号
-
本质是一个整数
-
用户模式下 信号是用来模拟硬件中断的
-
硬件中断:物理层面的中断
-
软件中断:模拟硬件中断
-
信号的产生:
信号发送: 内核 硬件 进程 类似: qt: 信号与槽 中断:设置一个提示,来信号了提示一下 windows:消息 轮询:一直等待消息,直到信息来了
No.2 信号的处理过程
进程A正在运行
进程B发送了一个SIGINT信号
进程A收到 SIGINT 信号,默认处理(结束进程)
#include <signal.h>
typedef void (*sighandler_t)(int); //声明void的类型并且参数是int的函数指针类型 sighandler_t
//signum为信号类型 handler为sighandler_t类型的一个指针
sighandler_t signal(int signum,sighandler_t handler);
-
Ctrl + c 2 SIGINT
-
Ctrl + 9 SIGQUIT
LINUX提供的信号:64个 + 不可靠信号(非实时) unix提供的 1-31 + 可靠信号(实时) 后来扩充 32-64 + 标准:操作系统提供 + 自定义:用户自定义
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void testSignal(int sign)
{
printf("受到了一个%d信号!n",sign);
}
int main()
{
signal(SIGINT,testSignal);
int n=0;
while(1)
{
printf("<< 你好啊啊啊啊~t%dn",n++);
sleep(2);
}
return 0;
}
No.3 高级信号处理:sigaction
不仅仅可以接受信号做信号处理,还能接收信号的同时接受一些信息
int sigaction(
int signum,
const struct sigaction *act,
struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int); //信号处理函数 signal
void (*sa_sigaction)(int, siginfo_t *, void *); //
sigset_t sa_mask; //信号集,用于信号屏蔽
int sa_flags; //方式 SA_SIGINFO 使用第二个函数的时候要给赋值
void (*sa_restorer)(void); //是否保存
};
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void hander(int sign)
{
printf("基本信息处理函数 sig: %d信号!n",sign);
}
void handAct(int n,siginfo_t* siginfo,void* arg)
{
printf("高级信息处理函数!n");
printf("n:%d,msg:%dn",n,siginfo->si_int);
}
int main()
{
struct sigaction act = {0};
struct sigaction oldAct = {0};
act.sa_handler = hander;
act.sa_sigaction = handAct;
act.sa_flags = SA_SIGINFO;
sigaction(SIGINT,&act,&oldAct);
printf("pid:%dn",getpid());
int n=0;
while(1)
{
printf("<< 你好啊啊啊啊~t%dn",n++);
sleep(2);
}
return 0;
}
No.4 简单信号发送:kill
可以在另外一个终端上使用kill
命令发送信号kill -s 信号 进程id
同时也可以使用kill命令进行信号的发送
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
int main(int argc,char* argv[])
{
int pid = atoi(argv[2]);
int sigNum = atoi(argv[1]);
kill(pid,sigNum);
printf(“<< 发送%d信号给%d进程!n”,sigNum,pid);
return 0;
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/b08d8c064af14120b6dd1dd44e42c8cc.png)
## No.5 高级信号发送:sigqueue
发送的时候可以带点信息
```cpp
#include <signal.h>
int sigqueue(
pid_t pid, //要发送的进程id
int sig, //信号
const union sigval value//一个联合);
union sigval {
int sival_int; //发送黑进程的信息
void *sival_ptr;}; //一个地址
示例:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
int main(int argc,char* argv[])//sigNum pid sival_int
{
int pid = atoi(argv[2]);
int sigNum = atoi(argv[1]);
union sigval uVal={0};
uVal.sival_int = atoi(argv[3]);
sigqueue(pid,sigNum,uVal);
//kill(pid,sigNum);
printf("<< 发送%d信号给%d进程! --附带信息%dn",
sigNum,pid,uVal.sival_int);
return 0;
}
No.5 可以多个信号使用一个函数
修改终端大小时触发的信号:SIGWINCH
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void testSignal(int sign)
{
switch (sign)
{
case 2:
printf("受到了一个%d信号!n",sign);
break;
case 28:
printf("窗口改变了 %d!n",sign);
break;
default:
printf("%d信号!n",sign);
break;
}
}
int main()
{
signal(99,testSignal);
signal(SIGWINCH,testSignal);
signal(SIGINT,testSignal);
int n=0;
printf("PID:%dn",getpid());
while(1)
{
printf("<< 你好啊啊啊啊~t%dn",n++);
sleep(2);
}
return 0;
}
通SIGINT信号使用方法都相同,就是终端大小改变的时候触发
No.6 定时信号:SIGALRM
alarm函数 只能触发一次
setitimer 函数,可以多次触发
int setitimer(
int which,
const struct itimerval *new_value,
struct itimerval *old_value);
struct itimerval {
struct timeval it_interval; /* 周期性计时器的间隔 */
struct timeval it_value; /* 下次到期前的时间 */};
struct timeval {
time_t tv_sec; /* 秒*/
suseconds_t tv_usec; /* 微妙*/ };
setitimer()将value指向的结构体设为计时器的当前值,如果ovalue不是NULL,将返回计时器原有值。
which:间歇计时器类型,有三种选择
ITIMER_REAL //数值为0,计时器的值实时递减,发送的信号是SIGALRM。
ITIMER_VIRTUAL //数值为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM。
ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF。
返回说明:
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EFAULT:value或ovalue是不有效的指针
EINVAL:其值不是ITIMER_REAL,ITIMER_VIRTUAL 或 ITIMER_PROF之一
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
void testSignal(int sign)
{
switch (sign)
{
case 2:
printf("受到了一个%d信号!n",sign);
break;
case 28:
printf("窗口改变了 %d!n",sign);
break;
case 14:
printf("信号%d来了!n",sign);
break;
default:
printf("%d信号!n",sign);
break;
}
}
int main()
{
signal(14,testSignal);
signal(SIGWINCH,testSignal);
signal(SIGINT,testSignal);
int n=0;
struct itimerval tv = {0};
tv.it_value.tv_sec = 3;//第一次发送间隔时间
tv.it_interval.tv_sec = 5;//间隔时间
setitimer(ITIMER_REAL,&tv,NULL);
//alarm(3);
printf("PID:%dn",getpid());
while(1)
{
printf("<< 你好啊啊啊啊~t%dn",n++);
sleep(1);
}
return 0;
}
No.7 信号屏蔽
- 信号集:多个信号的集合 sigset_t类型
- 有一系列的函数
#include <signal.h>
int sigemptyset(sigset_t *set);//清空这个信号集合
int sigfillset(sigset_t *set);//填充
int sigaddset(sigset_t *set, int signum);//添加
int sigdelset(sigset_t *set, int signum);//删除
int sigismember(const sigset_t *set, int signum);//判断某一个信号是不是信号集里面的
- 在os 内核中有一个信号缓冲区,维护一个信号队列,如果产生了信号就入队,注册了信号处理的话,从队列中取出信号进行处理;如果屏蔽某个信号,那么会不从队列中取出处理。直到解除屏蔽再行处理。屏蔽结束后相同信号只响应一次.
int sigprocmask(
int how,//是要屏蔽还是解除屏蔽
/*
SIG_BLOCK:屏蔽
SIG_UNBLOCK:解除屏蔽
*/
const sigset_t *set,//信号集
sigset_t *oldset);//信号
示例代码:
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
void testSignal(int sign)
{
switch (sign)
{
case 2:
printf("受到了一个%d信号!n",sign);
break;
case 28:
printf("窗口改变了 %d!n",sign);
break;
case 14:
printf("信号%d来了!n",sign);
break;
default:
printf("%d信号!n",sign);
break;
}
}
int main()
{
signal(14,testSignal);
signal(SIGWINCH,testSignal);
signal(SIGINT,testSignal);
int n=0;
sigset_t set,oldset;
sigemptyset(&set);
// sigemptyset(&oldset);
sigaddset(&set,SIGINT);
printf("PID:%dn",getpid());
//前十秒钟不加信号屏蔽
while(1)
{
printf("<< 这是前十秒钟,未屏蔽~t%dn",n++);
sleep(1);
if(n==10) break;
}
int r = 0;
if(1==sigismember(&set,SIGINT))
{
printf("屏蔽当前信号集!n");
r = sigprocmask(SIG_BLOCK,&set,&oldset);
if(r==0) printf("屏蔽成功:%m!n");
else printf("屏蔽失败:%m!n");
}
//屏蔽十秒钟
while(1)
{
printf("<< 这是中间的十秒钟,屏蔽~t%dn",n++);
sleep(1);
if(n==20) break;
}
if(1==sigismember(&set,SIGINT))
{
printf("解除当前信号集!n");
r = sigprocmask(SIG_UNBLOCK,&set,&oldset);
if(r==0) printf("解除屏蔽成功:%m!n");
else printf("解除屏蔽失败:%m!n");
}
//十秒钟之后解除屏蔽
while(1)
{
printf("<< 这是最后的十秒钟,解除屏蔽~t%dn",n++);
sleep(1);
if(n==30) break;
}
return 0;
}
No.8 练习
- 用管道传输文件的练习 传输完后要结束进程
用信号来结束进程。
A
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
void header(int sigNum)
{
if(sigNum==SIGINT)
{
printf("接收到关闭信号!n");
exit(-1);
}
}
int main(int argc,char* argv[])
{
signal(SIGINT,header);
if(argc!=2) return 0;
//1. 创建管道文件(mkfifo)
int r = mkfifo("test.pipe",0777);
if(0==r) printf("创建管道文件:%mn");
else printf("创建管道文件失败:%mn"),exit(-1);
//2. 打开管道文件
int fd = open("test.pipe",O_RDWR);
if(-1==fd) printf("打开管道文件失败:%mn"),exit(-1);
else printf("打开管道文件成功:%mn");
//3. 使用管道文件(写)
//获取B进程的pid
int Aid = getpid();
int Bid = 0;
read(fd,&Bid,sizeof Bid);
write(fd,&(Aid),sizeof Aid);
printf("A pid:%dt B pid:%dn",Aid,Bid);
//3.1 获取要传输的文件信息
struct stat st ;
stat(argv[1],&st);
//3.2 传送文件名长度
int Size = strlen(argv[1]);
write(fd,&Size,4);
//3.3 传送文件名
write(fd,argv[1],strlen(argv[1]));
printf("%s %ldn",argv[1],st.st_size);
//3.4 传送文件大小
Size = st.st_size;
write(fd,&Size,sizeof Size);
//3.5 打开文件然后传送文件
int fw = open(argv[1],O_RDONLY);
r=0;
char c;
while(1)
{
r = read(fw,&c,1);
if(r!=1)
{
printf("拷贝完成!n");
break;
}
write(fd,&c,1);
}
//4. 关闭
close(fd);
close(fw);
//5. 删除管道文件
unlink("test.pipe");
kill(Bid,SIGINT);
while(1);
return 0;
}
B
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
int Aid = 0;
void header(int sigNum)
{
if(sigNum==2)
{
kill(Aid,2);
printf("接收到关闭信号!n");
exit(-1);
}
}
int main()
{
signal(SIGINT,header);
//1. 打开管道文件
int fd = open("test.pipe",O_RDWR);
if(-1==fd) printf("打开管道文件失败:%mn"),exit(-1);
else printf("打开管道文件成功:%mn");
//2. 使用管道文件(读)
//获取A管道的pid以及发送自己的pid
int Bid = getpid();
write(fd,&(Bid),sizeof Bid);
read(fd,&Aid,sizeof Aid);
printf("A pid:%dt B pid:%dn",Aid,Bid);
//2.1 获取文件名长度
int r = 0;
read(fd,&r,4);
char* name = (char*)malloc((r+1));
//2.2 获取文件名
r = read(fd,name,r);
name[r+1] = 0;
printf("%sn",name);
//2.3 获取文件内容长度
int Size;
read(fd,&Size,4);
printf("size:%dn",Size);
//2.4 创建文件夹Test
system("mkdir Test");
//2.5 链接文件目录
char demo[1024];
sprintf(demo,"./Test/%s",name);
printf("%sn",demo);
//2.6 创建文件并且进行接受
int fw = open(demo,O_WRONLY|O_CREAT,0666);
int n=0;
char c;
while(n<=Size)
{
read(fd,&c,1);
n++;
write(fw,&c,1);
}
//3. 关闭
close(fd);
close(fw);
while (1);
return 0;
}
最后
以上就是犹豫果汁为你收集整理的Linux - 信号No.8 练习的全部内容,希望文章能够帮你解决Linux - 信号No.8 练习所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复