IO 차단 과 비 차단 에 관 한 자질구레한 지식

1. 차단 에 관 한 개념
블록 이라는 개념 을 막는다.프로 세 스 가 막 힌 시스템 함 수 를 호출 할 때 이 프로 세 스 는 수면 (Sleep) 상태 에 있 습 니 다. 이 때 커 널 은 다른 프로 세 스 를 실행 합 니 다. 이 프로 세 스 가 기다 리 는 이벤트 가 발생 할 때 까지 (예 를 들 어 네트워크 에서 패 킷 을 받 거나 sleep 에서 지정 한 수면 시간 을 호출 할 때) 계속 실 행 될 수 있 습 니 다.수면 상태 와 상대 적 으로 실행 (Running) 상태 로 리 눅 스 커 널 에서 실행 상태 에 있 는 프로 세 스 는 두 가지 상황 으로 나 뉜 다.
스케줄 링 실행 중 입 니 다.CPU 는 이 프로 세 스 의 컨 텍스트 환경 에 있 습 니 다. 프로그램 카운터 (eip) 에는 이 프로 세 스 의 명령 주소 가 저장 되 어 있 습 니 다. 유 니 버 설 레지스터 에는 이 프로 세 스 연산 과정의 중간 결 과 를 저장 하고 있 습 니 다. 이 프로 세 스 의 명령 을 실행 하고 있 습 니 다. 이 프로 세 스 의 주소 공간 을 읽 고 있 습 니 다.
준비 상태.이 프로 세 스 는 어떤 이벤트 가 발생 할 때 까지 기다 릴 필요 가 없습니다. 언제든지 실행 할 수 있 지만 CPU 는 다른 프로 세 스 를 잠시 실행 하고 있 기 때문에 이 프로 세 스 는 준 비 된 대기 열 에서 커 널 스케줄 링 을 기다 리 고 있 습 니 다.시스템 에 여러 개의 준 비 된 프로 세 스 가 동시에 있 을 수 있 습 니 다. 그러면 누가 실행 할 것 입 니까?커 널 의 스 케 쥴 링 알고리즘 은 우선 순위 와 시간 영 화 를 기반 으로 하고 모든 프로 세 스 의 운행 상황 에 따라 우선 순위 와 시간 영 화 를 동적 으로 조정 하여 모든 프로 세 스 가 비교적 공정 하 게 기 회 를 얻 을 수 있 도록 하 는 동시에 사용자 체험 을 병행 하여 사용자 와 상호작용 하 는 프로 세 스 의 응답 이 너무 느 려 서 는 안 됩 니 다.
2, IO 모드 설정
일반적으로 하나의 socket 이 차단 모드 인지 비 차단 모드 인지 두 가지 방식 이 있 습 니 다.
방법 1. fcntl 설정;
flags = fcntl(sockfd, F_GETFL, 0); //파일 의 flags 값 을 가 져 옵 니 다.
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); //비 차단 모드 로 설정 하기;
flags = fcntl(sockfd,F_GETFL,0);
fcntl(sockfd,F_SETFL,flags&~O_NUNBLOCK); //차단 모드 로 설정 하기;
방법 2, recv, send 함수 의 마지막 flag 매개 변 수 는 MSG 로 설정 할 수 있 습 니 다.DONTWAIT
임시로 sockfd 를 비 차단 모드 로 설정 합 니 다. 원래 차단 이 든 비 차단 이 든.
recv(sockfd, buff, buff_size,MSG_DONTWAIT); //비 차단 모드 메시지 전송
send(scokfd, buff, buff_size, MSG_DONTWAIT); //비 차단 모드 메시지 수락
3, 읽 기 (read / recv / msgrcv):
차단 과 비 차단 의 차 이 는 데이터 가 도착 하지 않 았 을 때 바로 돌아 오 느 냐 하 는 것 이다.
read 든 recv 든 데 이 터 를 바 텀 버퍼 copy 에서 우리 가 지정 한 위치 로 만 책임 집 니 다.
막 힌 상태 에서:
1. 데이터 가 네트워크 버퍼 에서 계속 기다 리 는 것 을 발견 하지 못 하면,
2. 데이터 가 발견 되면 사용자 가 지정 한 버퍼 에 데 이 터 를 읽 습 니 다. 그러나 이때 읽 은 데이터 의 양 이 적 고 매개 변수 에서 지정 한 길이 보다 작 으 며 read 는 계속 기다 리 지 않 고 바로 돌아 갑 니 다.
read 의 원칙 은 데이터 가 지 정 된 길 이 를 초과 하지 않 을 때 얼마나 읽 는 지, 데이터 가 없 으 면 계속 기다 리 는 것 이다.따라서 일반적인 상황 에서 우 리 는 데 이 터 를 읽 을 때 순환 적 으로 읽 는 방식 으로 데 이 터 를 읽 어야 한다. 왜냐하면 한 번 의 read 가 끝나 면 우리 가 필요 로 하 는 길이 의 데 이 터 를 읽 을 수 없 기 때문이다. read 가 끝나 면 읽 은 데이터 의 길 이 를 판단 하고 다시 읽 어야 하 는 지 여 부 를 결정 해 야 한다.
차단 되 지 않 은 상태 에서:
1. 데이터 가 없 는 것 을 발견 하면 바로 돌아 갑 니 다.
2. 데이터 가 있 는 것 을 발견 하면 읽 은 만큼 처리 합 니 다.
따라서 읽 은 데이터 의 길 이 를 한 번 판단 하고 다시 읽 어야 할 지 여 부 를 결정 해 야 합 니 다.
4, 쓰기 (send / write / msgsnd):
쓰기 의 본질 도 전송 작업 을 하 는 것 이 아니 라 사용자 상태의 데 이 터 를 시스템 밑바닥 으로 복사 한 다음 에 시스템 에서 전송 작업 을 한다. send, write 는 성공 적 으로 되 돌 아 왔 다. 데 이 터 는 copy 의 도대체 층 버퍼 가 되 었 음 을 나타 내 는 것 이지 데이터 가 이미 보 냈 다 는 것 을 나타 내 는 것 이 아니 라 사각형 포트 가 데 이 터 를 연결 했다 는 것 을 나타 내 는 것 이 아니다.
막 힌 상태 에서 write 는 데 이 터 를 다 보 냅 니 다.(단, 중 단 될 수 있 습 니 다)
막 힌 상황 에서 write 가 끝 날 때 까지 모든 데 이 터 를 되 돌려 줍 니 다. 이 행 위 는 읽 기 동작 과 다 릅 니 다.
비 차단 쓰기 의 경우 쓸 수 있 는 만큼 쓰 는 전략 을 사용 합 니 다. 읽 기와 다른 점 은 읽 을 수 있 는 만큼 읽 는 것 이 네트워크 에서 보 내 는 쪽 에 데이터 가 전송 되 는 지 여 부 를 기준 으로 하지만 쓸 수 있 는 만큼 쓸 수 있 는 것 이 로 컬 네트워크 차단 상황 을 기준 으로 합 니 다. 네트워크 차단 이 심 할 때 네트워크 층 은 쓰기 에 충분 한 메모리 가 없습니다.이 럴 때 는 쓰기 에 성공 하지 못 하 는 경우 가 있 습 니 다. 막 힌 상황 에서 가능 한 한 (중 단 될 수 있 습 니 다) 데이터 가 모두 전 송 될 때 까지 기다 리 고 있 습 니 다. 막 히 지 않 는 상황 에 대해 서 는 한 번 에 쓰 는 만큼 계산 하고 중단 되 지 않 은 상황 에서 도 write 가 일부 상황 에 이 를 수 있 습 니 다.
Server:
#include <sys/socket.h>  
#include <stdio.h>  
#include <string.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include <errno.h>  
  
#define  BUFSIZE 128  
  
int main(int argc,char *argv[]){  
    int server_sockfd, client_sockfd;  
    int server_len, client_len;  
    struct sockaddr_in server_address;  
    struct sockaddr_in client_address;  
    int i,byte;  
    char char_send[BUFSIZE];  
  
    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);  
  
    bzero(&server_address, sizeof(server_address));  
    server_address.sin_family = AF_INET;  
    server_address.sin_port = htons(7838);  
    server_address.sin_addr.s_addr = INADDR_ANY;  
    server_len = sizeof(server_address);  
  
    if ((bind(server_sockfd, (struct sockaddr *)&server_address, server_len))== -1) {  
        perror("bind");  
        exit(EXIT_FAILURE);  
    }  
  
    listen(server_sockfd, 5);  
  
    printf("server waiting for connect
"); client_len = sizeof(client_address); client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address, (socklen_t *)&client_len); for(i=0;i<5;i++){ memset(char_send,'\0',BUFSIZE); printf("input message to send:"); fgets(char_send,BUFSIZE,stdin); if((byte=send(client_sockfd,char_send,strlen(char_send),0))==-1){ perror("send"); exit(EXIT_FAILURE); } memset(char_send,'\0',BUFSIZE); // byte = recv(client_sockfd, char_send, BUFSIZE,MSG_DONTWAIT); if(byte > 0){ printf("get %d message:%s", byte, char_send); byte=0; }else if(byte<0){ if(errno==EAGAIN){ errno=0; continue; }else{ perror("recv"); exit(EXIT_FAILURE); } } } shutdown(client_sockfd,2); shutdown(server_sockfd,2); }

Client:
#include <stdio.h>  
#include <string.h>  
#include <errno.h>  
#include <sys/socket.h>  
#include <resolv.h>  
#include <stdlib.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <unistd.h>  
#include <fcntl.h>  
  
#define MAXBUF 128  
  
int main(int argc, char **argv){  
    int sockfd, ret, i;  
    struct sockaddr_in dest, mine;  
    char buffer[MAXBUF + 1];  
  
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {  
        perror("Socket");  
        exit(EXIT_FAILURE);  
    }  
  
    bzero(&dest, sizeof(dest));  
    dest.sin_family = AF_INET;  
    dest.sin_port = htons(7838);  
    if(argc<2){  
        printf("Usage: %s <dest ip> <src ip>",argv[0]);  
        exit(1);  
    }  
    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0){  
        perror(argv[1]);  
        exit(1);  
    }  
  
    bzero(&mine, sizeof(mine));  
    mine.sin_family = AF_INET;  
    mine.sin_port = htons(7839);  
    if (inet_aton(argv[2], (struct in_addr *) &mine.sin_addr.s_addr) == 0){  
        perror(argv[2]);  
        exit(EXIT_FAILURE);  
    }  
    if (bind(sockfd, (struct sockaddr *) &mine, sizeof(struct sockaddr)) == -1){  
        perror(argv[3]);  
        exit(EXIT_FAILURE);  
    }  
    printf("will connect!
"); if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) { perror("Connect "); exit(EXIT_FAILURE); } // sock if(fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) { perror("fcntl"); exit(EXIT_FAILURE); } while(1){ bzero(buffer, MAXBUF + 1); // socket , ret = recv(sockfd, buffer, MAXBUF, 0); if(ret > 0){ printf("get %d message:%s", ret, buffer); ret=0; }else if(ret < 0) { if(errno == EAGAIN) { errno=0; continue; }else{ perror("recv"); exit(EXIT_FAILURE); } } memset( buffer,'\0',MAXBUF+1); printf("input message to send:"); fgets( buffer,MAXBUF,stdin); if((ret=send(sockfd,buffer,strlen(buffer),0))==-1){ perror("send"); exit(EXIT_FAILURE); } } close(sockfd); return 0; }

원본:
http://blog.csdn.net/heyutao007/article/details/6733029
http://blog.csdn.net/heyutao007/article/details/6732928
http://blog.csdn.net/fansongy/article/details/6898577

좋은 웹페이지 즐겨찾기