Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

아래의 pselect 에 대한 내용은 따로 정리한다.

pselect
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, const struct timespec *timeout,
          const sigset_t *sigmask);
  • pselect는 struct timespec 구조체 사용한다. timespec 구조체를 사용함으로써 나노초까지 세밀하게 조정할 수 있게 되었다.
    struct timespec {
        long    tv_sec;         /* seconds */
        long    tv_nsec;        /* nanoseconds */
    };
  • pselect 는 Linux(:12) 커널 2.6.16에 추가되었다. 이전에는 glibc에서 애뮬레이트한 함수가 제공되었으나 버그를 가지고 있었다.
  • sigmask를 사용해서 시그널을 블럭시킬 수 있다. select의 경우 수행되는 도중에 시그널에 의한 인터럽트가 발생하게 되면, race condition 혹은 무한 블록킹 상태에 놓일 수 있었다. pselect를 사용하면 이러한 문제를 제거할 수 있다.
<a href="/modules/moniwiki/wiki.php/manSearch?name=select">select</a>(2)

1장. select(2)

차례
1.1절. 사용법
1.2절. 설명
1.3절. 반환값
1.4절. 에러
1.5절. 예제

동기적 I/O(입출력) 다중화


1.1절. 사용법

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int n , fd_set *readfds, fd_set *writefds, fd_set *exceptfds, 
		struct timeval *timeout);

FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd, fd_set *set);
FD_ZERO(fd_set *set);
		


1.2절. 설명

select는 상태가 변경되는 파일기술자들의 숫자를 기다린다.

세개의 독립적인 파일지시자 집합들을 검사한다. readfds에는 읽어들인 데이타가 있는지가 검사되며, 정확히는 읽기가 봉쇄되지 않았는지를 검사한다. 봉쇄되지 않음을 검사함으로 (EOF)end-of-file가 발생하는 것도 검사하고 리턴시킬수 있다. writefds에 있는 것들은 쓰기가 봉쇄되어 있는지가 검사된다. exceptfds에 있는 것들은 예외가 있는지가 검사된다. 종료시, 이 집합들은 파일 기술자들이 실제로 변경된 상태가 무엇인지 가리키도록 수정된다.

select가 리턴되었을때 파일지시자의 집합을 검사하기 위해서 몇개의 매크로가 제공된다. FD_ZERO는 집합을 소거한다. FD_SET과 FD_CLR은 집합에서 주어진 기술자를 더하거나 뺀다. FD_ISSET는 기술자가 집합의 일부분인지 아닌지를 검사한다. FD_ISSET은 select가 리턴되었을때 사용할수 있다.

n는 세개의 집합중 가장 높은 파일지시자에다가 1을 더한다. 이유는 파일지시자가 0(표준입력)번부터 시작하기 때문이다. 가장 최근에 생성된 가장 큰 파일지시자의 번호가 4번이라면 4+1(0)=5 가 된다.

timeout은 select가 반환하기전에 블럭킹될수 있는 시간의 상위제한값이다. 0으로 지정되어 있으면 select는 즉시 반환된다. 만일 timeout이 NULL이면, select는 무한히 봉쇄될수 있다.


1.3절. 반환값

성공시, select는 파일지정자집합에 표함된 숫자를 반환하며, 리턴하기전에 타임아웃이 발생하면 0을 반환한다. 이러시 -1이 반환되며, errno는 적당한 값으로 설정된다.


1.4절. 에러

EBADF

유효하지 않은 파일 기술자가 집합중 하나에 들어있다.

EINTR

(블럭되지 않은)신호가 발생했다.

EINVAL

n이 음수이다.

ENOMEM

select가 내부 테이블들을 위한 메모리를 할당할 수 없다.


1.5절. 예제

#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define FD_MAX_SIZE 1024
int main(int argc, char **argv)
{
    int server_sockfd, client_sockfd, sockfd;
    int state, client_len;
    int pid;
    int i, maxi, maxfd;

    int client[FD_MAX_SIZE];

    FILE *fp;
    struct sockaddr_in clientaddr, serveraddr;

    struct timeval tv;
    fd_set readfds, otherfds, allfds;

    int current_cli_num;

    char buf[255];
    char line[255];

    if (argc != 2)
    {
        printf("Usage : ./zipcode [port]\n");
        printf("예    : ./zipcode 4444\n");
        exit(0);
    }

    memset(line, 0x00, 255);
    state = 0;

    current_cli_num = 3;

    // 주소 파일을 읽어들인다. 
    if ((fp = fopen("zipcode.txt", "r")) == NULL)
    {
        perror("file open error : ");
        exit(0);
    }

    if ((server_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket error : ");
        exit(0);
    }
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(atoi(argv[1]));

    state = bind (server_sockfd, (struct sockaddr *)&serveraddr,
                  sizeof(serveraddr));
    if (state == -1)
    {
        perror("bind error : ");
        exit(0);
    }

    state = listen(server_sockfd, 5);
    if (state == -1)
    {
        perror("listen error : ");
        exit(0);
    }


    client_sockfd = server_sockfd;

    maxi = -1;
    maxfd = server_sockfd;
    for (i = 0; i < FD_MAX_SIZE; i++)
    {
        client[i] = -1;
    }

    FD_ZERO(&readfds);
    FD_SET(server_sockfd, &readfds);

    while(1)
    {

        allfds = readfds;

        state = select(maxfd + 1 , &allfds, NULL,
                      NULL, NULL);

        if (FD_ISSET(server_sockfd, &allfds))
        {
            client_len = sizeof(clientaddr);
            client_sockfd = accept(server_sockfd,
                    (struct sockaddr *)&clientaddr, &client_len);
            for (i = 0; i < FD_MAX_SIZE; i++)
            {
                if (client[i] < 0)
                {
                    client[i] = client_sockfd;
                    printf("%d : %d\n", i, client_sockfd);
                    break;
                }
            }

            printf("accept [%d]\n", client_sockfd);
            if (i == FD_MAX_SIZE)
            {
                perror("too many clients\n");
            }

            FD_SET(client_sockfd,&readfds);
    
            if (client_sockfd > maxfd)
                maxfd = client_sockfd;

            if (i > maxi)
                maxi = i;

            if (--state <= 0)
                continue;
        }
    
        printf("maxi %d\n", maxi);
        for (i = 0; i <= maxi; i++)
        {
            if ((sockfd = client[i]) < 0)
            {
                continue;
            }

            printf("maxi %d\n", maxi);
            if (FD_ISSET(sockfd, &allfds))
            {
                printf("ok read\n");
                memset(buf, 0x00, 255);
                if (read(sockfd, buf, 255) <= 0)
                {
                    close(sockfd);
                    perror("Close sockfd : ");
                    FD_CLR(sockfd, &eadfds);
                    client[i] = -1;
                }
                else
                {
                    printf("[%d] %s\n", sockfd, buf);
                }

                if (--state <= 0)
                    break;
            }
        }
    }
}
		
우편번호 검색 서버프로그램이다. 이 코드에 대한 자세한 설명은 다중연결서버 만들기(2)문서를 참고하기 바란다.

참고문헌