17.2 边缘触发和条件触发

本文探讨了边缘触发和条件触发的区别,重点在于它们发生事件的时间点。条件触发在输入缓冲有数据时持续通知事件,而边缘触发则在输入缓冲受到数据时仅注册一次事件。边缘触发允许将接收数据与处理数据的时间分离,适用于优化系统性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 两者区别:在于发生事件的时间点,条件触发:只要输入缓冲有数据就一直通知该事件;边缘触发:输入缓冲受到数据时仅注册一次事件。

epoll默认以条件触发方式工作,select也是以条件触发模式工作的。


2. 条件触发事件特性:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
#include <sys/socket.h>  
#include <sys/epoll.h>
  
#define BUF_SIZE    4
#define EPOLL_SIZE	50
  
void error_handling(char *message);  
  
int main(int argc, char *argv[]){  
    int serv_sock,clnt_sock;  
    char buf[BUF_SIZE];  
    struct sockaddr_in serv_adr;  
    struct sockaddr_in clnt_adr;  
    socklen_t adr_sz;  
    int str_len,i;
    struct epoll_event *ep_events;
    struct epoll_event event;
    int epfd,event_cnt;  

    if(argc != 2){  
        printf("Usage : %s <port>\n",argv[0]);  
        exit(1);  
    }  

    serv_sock = socket(PF_INET,SOCK_STREAM,0);  
    if(serv_sock == -1){  
        error_handling("socket error");  
    }  
  
    memset(&serv_adr,0,sizeof(serv_adr));  
    serv_adr.sin_family=AF_INET;  
    serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);  
    serv_adr.sin_port=htons(atoi(argv[1]));  
  
    if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)) == -1){  
        error_handling("bind() error");  
    }  
  
    if(listen(serv_sock,5) == -1){  
        error_handling("listen() error");  
    }  
    
    epfd = epoll_create(EPOLL_SIZE);
    ep_events = malloc(sizeof(struct epoll_event)*EPOLL_SIZE);

    event.events = EPOLLIN;
    event.data.fd = serv_sock;
    epoll_ctl(epfd,EPOLL_CTL_ADD,serv_sock,&event);

    while(1){ 
	event_cnt = epoll_wait(epfd,ep_events,EPOLL_SIZE,-1);

	if(event_cnt  == -1){
	    puts("epoll wait error");
	    break;
	}

	puts("return epoll wait");
	for(i=0;i<event_cnt;i++){
	    if(ep_events[i].data.fd == serv_sock){
		adr_sz = sizeof(clnt_adr);
        	clnt_sock = accept(serv_sock,(struct sockaddr *)&clnt_adr,&adr_sz);  
		event.events = EPOLLIN;
		event.data.fd = clnt_sock;
		epoll_ctl(epfd,EPOLL_CTL_ADD,clnt_sock,&event);
		printf("connected client : %d \n", clnt_sock);
	    }else{	//read message
		str_len = read(ep_events[i].data.fd,buf,BUF_SIZE);

		if(str_len == 0){	//close
		    epoll_ctl(epfd,EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);
		    close(ep_events[i].data.fd);
		    printf("closed client %d \n",ep_events[i].data.fd);
		}else{
			write(ep_events[i].data.fd,buf,str_len);	//echo


		}	    
	    }
	}
    }  
    close(serv_sock);  
    close(epfd);
    return 0;  
}  
  
void error_handling(char *message){  
  
    fputs(message,stderr);  
    fputs("\n",stderr);  
    exit(1);  
}
执行结果:多次触发事件

alex@alex-virtual-machine:/extra/tcpip/17$ ./a.out 9190
return epoll wait
connected client : 5
return epoll wait
return epoll wait
return epoll wait
return epoll wait
return epoll wait
return epoll wait
return epoll wait
return epoll wait
return epoll wait
3. 边缘触发

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
#include <sys/socket.h>  
#include <sys/epoll.h>
#include <errno.h>
#include <fcntl.h>
  
#define BUF_SIZE    4
#define EPOLL_SIZE	50
  
void error_handling(char *message);  
void setnonblockingmode(int fd);
  
int main(int argc, char *argv[]){  
    int serv_sock,clnt_sock;  
    char buf[BUF_SIZE];  
    struct sockaddr_in serv_adr;  
    struct sockaddr_in clnt_adr;  
    socklen_t adr_sz;  
    int str_len,i;
    struct epoll_event *ep_events;
    struct epoll_event event;
    int epfd,event_cnt;  

    if(argc != 2){  
        printf("Usage : %s <port>\n",argv[0]);  
        exit(1);  
    }  

    serv_sock = socket(PF_INET,SOCK_STREAM,0);  
    if(serv_sock == -1){  
        error_handling("socket error");  
    }  
  
    memset(&serv_adr,0,sizeof(serv_adr));  
    serv_adr.sin_family=AF_INET;  
    serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);  
    serv_adr.sin_port=htons(atoi(argv[1]));  
  
    if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)) == -1){  
        error_handling("bind() error");  
    }  
  
    if(listen(serv_sock,5) == -1){  
        error_handling("listen() error");  
    }  
    
    epfd = epoll_create(EPOLL_SIZE);
    ep_events = malloc(sizeof(struct epoll_event)*EPOLL_SIZE);

    event.events = EPOLLIN;
    event.data.fd = serv_sock;
    epoll_ctl(epfd,EPOLL_CTL_ADD,serv_sock,&event);

    while(1){ 
	event_cnt = epoll_wait(epfd,ep_events,EPOLL_SIZE,-1);

	if(event_cnt  == -1){
	    puts("epoll wait error");
	    break;
	}

	puts("return epoll wait");
	for(i=0;i<event_cnt;i++){
	    if(ep_events[i].data.fd == serv_sock){
		adr_sz = sizeof(clnt_adr);
        	clnt_sock = accept(serv_sock,(struct sockaddr *)&clnt_adr,&adr_sz);  
		setnonblockingmode(clnt_sock);
		event.events = EPOLLIN | EPOLLET;	//设置成边缘触发
		event.data.fd = clnt_sock;
		epoll_ctl(epfd,EPOLL_CTL_ADD,clnt_sock,&event);
		printf("connected client : %d \n", clnt_sock);
	    }else{	//read message
		while(1){
		    str_len = read(ep_events[i].data.fd,buf,BUF_SIZE);

		    if(str_len == 0){	//close
		    	epoll_ctl(epfd,EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);
		    	close(ep_events[i].data.fd);
		    	printf("closed client %d \n",ep_events[i].data.fd);
		    	break;
		    }else if(str_len < 0){
			if(errno == EAGAIN){	//无数据可读
			    break;
			}
		    }else{
			write(ep_events[i].data.fd,buf,str_len);	//echo
		    }
		}	    
	    }
	}
    }  
    close(serv_sock);  
    close(epfd);
    return 0;  
}  
  
void error_handling(char *message){  
  
    fputs(message,stderr);  
    fputs("\n",stderr);  
    exit(1);  
}

void setnonblockingmode(int fd){
    int flag = fcntl(fd,F_GETFL,0);	//设置成非阻塞形式
    fcntl(fd,F_SETFL,flag | O_NONBLOCK);
}
执行结果:触发一次

alex@alex-virtual-machine:/extra/tcpip/17$ ./a.out 9191
return epoll wait
connected client : 5
return epoll wait
5. 条件触发与边缘触发比较:应该从服务器端实现模型角度考虑。

边缘触发能够做到接收数据与处理数据的时间点分离。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值