引言
同步和异步的使用。
异步事件处理的两种方法:查询法、通知法。(单核机器不存在异步)
一、信号
1. 信号的概念
信号是软件中断。信号的响应依赖于中断。中断是底层硬件的机制。
2. signal函数
kill -l命令查看所有的信号。1 -31 属于标准信号 34 - 64属于实时信号
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
展示形式为: void (* signal(int signum, void (*func)(int) ) ) (int )
信号会打断阻塞的系统调用。
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void mySig()
{
write(1,".",1);
}
int main()
{
signal(SIGINT,SIG_IGN);//运行过程中忽略这个信号
signal(SIGINT,mySig);
int i;
for(i = 0;i<10;i++)
{
write(1,"*",1);
sleep(1);
}
return 0;
}
重构之前的mycpy.c 对于open write read 函数增加出现系统调用打断的行为判断
mycpy.c
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define BUF_SIZE 1024*100
int main(int argc,char **argv)
{
int fps,fpd;
int ret;
char buf[BUF_SIZE];
int len,pos;
if(argc <3)
{
fprintf(stderr,"Usage:%s <src_file> <dest_file>\n",argv[0]);
exit(1);
}
do
{
fps = open(argv[1],O_RDONLY);
if(fps <0)
{
if(errno != EINTR)
{
perror("open");
exit(1);
}
}
}while(fps<0);
do
{
fpd = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0600 );
if(fpd <0)
{
if(errno != EINTR)
{
close(fps);
perror("open");
exit(1);
}
}
}while(fpd<0);
while(1)
{
len = read(fps,buf,BUF_SIZE);
if(len < 0)
{
if(errno == EINTR)
continue;
perror("read()");
break;
}
if(len ==0)
break;
pos = 0;
while(len > 0)
{
ret = write(fpd,buf+pos,len);
if(ret <0)
{
if(errno == EINTR)
continue;
perror("write()");
exit(1);
}
pos += ret;
len-=ret;
}
}
close(fpd);
close(fps);
return 0;
}
3. 信号的不可靠
标准的信号会丢失,实时信号不会丢失。指的是信号的行为不可靠。(第一次调用还未结束,第二次调用就开始了)
4. 可重入函数
第一次调用还未结束,第二次调用不会出错叫可重入函数(reentrant)。所有的系统调用都是可重入的,一部分库函数也是可重入的,比如说:memcpy rand_r localtime_r memove
5. 信号的响应过程
mask(信号屏蔽字,32位一般情况都是1)pending(32位 位图,初始都为0),收到信号后pending位置1,内核在kernal态进入到user态时,会用mask值 & pending 获取发生的信号,将mask与pending全部置为0,执行函数,执行完毕将mask位置1。
信号从收到到响应有一个不可避免的延迟。
思考:
如何忽略掉一个信号? 将mask位置0
标准信号为什么会丢失? 在执行信号时,pending位置多次1,此时mask位是0
标准信号的响应没有严格的顺序。
不能从信号处理函数中随意的往外跳。(sigsetjmp,siglongjmp)
6. 相关常用函数
kill
发送一个信号到一个进程或进程组
raise
给当前进程或者线程发送一个信号
alarm
指定秒数,发送SIGALRM信号。默认alarm会杀掉当前程序。alarm不能实现多任务的计时器,只能执行最后一次设置的计时器。
alarm.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void mySig()
{
write(1,".",1);
}
int main()
{
alarm(5);
alarm(2);
alarm(10);
while(1)
pause();
#if 0
signal(SIGALRM,mySig);
int i;
for(i = 0;i<10;i++)
{
write(1,"*",1);
alarm(1);
sleep(1);
}
#endif
return 0;
}
5秒进行计数,不用alarm实现
5sec.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
int main()
{
time_t end;
long long count = 0;
end = time(NULL)+5;
while(time(NULL)<= end)
{
count++;
}
printf("count = %lld \n",count);
return 0;
}
使用alarm实现
5sec_signal.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
static int loop = 1;
void alrm_handler(int num)
{
loop = 0;
}
int main()
{
signal(SIGALRM,alrm_handler);
alarm(5);
long long count=0;
while(loop)
{
count++;
}
printf("count = %lld \n",count);
return 0;
}
gcc -S 5sec_signal.c 会产生 5sec_singal.s的汇编文件
使用-O1优化之后,程序会一直执行下去,因为程序中while中没有用到loop,优化后loop的值会直接从寄存器取程序while会一直成立,volatile关键字告诉程序要去真正的地址取数据,不要从内存获取
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
static volatile int loop = 1; //从真实的空间去取数据
void alrm_handler(int num)
{
loop = 0;
}
int main()
{
signal(SIGALRM,alrm_handler);
alarm(5);
long long count=0;
while(loop)
{
count++;
}
printf("