歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

IO的多路復用和信號驅動

Linux為多路復用IO提供了較多的接口,有select(),pselect(),poll()的方式,繼承自BSD和System V 兩大派系。

  select模型比較簡單,“輪詢”檢測fd_set的狀態,然後再采取相應的措施。

  信號驅動模型有必要仔細研究一下,一般有如下步驟:

  1. 設置安裝函數,信號是驅動信號是SIGIO(最好使用sigaction的方式,方便設置flag為SA_RESTART,因為client中讀取終端的syscall可能會被中斷,有必要重啟。當然,使用signal()的方式然後再對errno進行判斷是否為ETNTR,自行重啟也是一種方法。但是signal()的可移植性問題,我強烈不建議使用)
  2. 設置fd的屬主。F_SETOWN,要接受信號的進程,fcntl().
  3. 設置fd的異步標志。小細節,用'|'添加,要把之前的狀態保留,也就是先F_GETFL再F_SETFL。(注意:在打開文件open()時設置標識O_ASYNC無實質效果)  

    On Linux, specifying the O_ASYNC
    flag when calling open() has no effect. To enable signal-driven I/O, we must
    instead set this flag using the fcntl() F_SETFL operation (Section 5.3).————《the linux programming interface》 4.3.1

  4. sigaction()安裝

  具體進下文client例程。

  寫了一個聊天程序的demo,把這兩種技術都使用了。服務端采取多路復用的IO方式,代替多進(線)程的模型,客服端采取的是信號驅動,如下:

容易產生bug的地方都寫注釋裡邊了。

serv.c

#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <signal.h>

void endServ(int sig)
{
    printf("Server ended!\n");
    exit(-1);
}

int main()
{
    // signal
    struct sigaction act;
    sigemptyset(&act.sa_mask);
    act.sa_handler = endServ;
    act.sa_flags = 0;
    sigaction(SIGINT,&act,0);
    printf("This server is started,enter CTRL+C to end. \n");

    int servfd = socket(AF_INET,SOCK_STREAM,0);
    if(servfd == -1) {
        printf("something wrong with socket():%m\n");
        exit(-1);
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);
    inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);

    if (bind(servfd,(struct sockaddr*)&addr,sizeof(addr)) == -1) {
        printf("Some thing wrong with bind():%m\n");
        exit(-1);
    }
    printf("Bind success!\n");

    listen(servfd,20);
   
    int numbers=0;//how many clients has accepted
    fd_set fs;
    FD_ZERO(&fs);
    int client_fd[100];
    int i;
    for(i=0;i<100;++i)
    {
        client_fd[i] = -1;
    }

    int maxfd=0;
    char buf[1024];
    for(;;)
    {
        maxfd =0;
        FD_ZERO(&fs);
        FD_SET(servfd,&fs);
        maxfd = maxfd>servfd?maxfd:servfd;
        for(i=0;i<numbers;++i)
        {
            if(client_fd[i] != -1) {
                FD_SET(client_fd[i],&fs);
                maxfd = maxfd>client_fd[i]?maxfd:client_fd[i];
            }
        }

        int res = select(maxfd+1,&fs,0,0,0);
        if(res == -1) {
            printf("Something wrong with select():%m\n");
            exit(-1);
        }

        if(FD_ISSET(servfd,&fs) && numbers < 100) {
            printf("New client!\n");
            client_fd[numbers] = accept(servfd,0,0);
            numbers++;
        }
       
        for(i=0;i<numbers;++i)
        {
            bzero(buf,sizeof(buf));
            //judge if client_fd[i] equal -1 is necessary
            //if a client quited,next time the program will
            //have a segment default
            //also it should be in the front.
            if(client_fd[i] != -1 && FD_ISSET(client_fd[i],&fs))
            {
                res = recv(client_fd[i],buf,sizeof(buf),0);
                if(res == 0) {
                    printf("A client quit\n");
                    close(client_fd[i]);
                    FD_CLR(client_fd[i],&fs);
                    client_fd[i] = -1;
                }
                else if(res == -1) {
                    printf("Something wrong with net.\n");
                    exit(-1);
                }
                else {
                    int j;
                    for(j=0;j<numbers;++j)
                    {
                        if(client_fd[j] != -1)
                            send(client_fd[j],buf,sizeof(buf),0);
                    }
                }
            }
        }
    }
}

client:

#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

static int fd;

void show(int sig)
{
    char buf[1024] = {0};
    int n = read(fd,buf,sizeof(buf));
    buf[n] = 0;
    write(1,"MSG:",strlen("MSG:"));
    write(1,buf,strlen(buf));
}

int main()
{
    struct sigaction act;
    sigemptyset(&act.sa_mask);
    act.sa_handler = show;
    //This is necessary,in last loop read() counld be interrupt;
    act.sa_flags = SA_RESTART;
    sigaction(SIGIO,&act,0);

    fd = socket(AF_INET,SOCK_STREAM,0);
    if(fd == -1) {
        printf("Cannot get socketfd!:%m\n");
        exit(-1);
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);
    inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);

    if(connect(fd,(struct sockaddr*)&addr,sizeof(addr)) != -1)
        printf("connect success!\n");
    else
        exit(-1);

    int flag = fcntl(fd,F_GETFL);
    flag |= O_ASYNC;
    fcntl(fd,F_SETFL,flag);
    fcntl(fd,F_SETOWN,getpid());

    char buffer[1024]={0};
    for(;;)
    {
        int n = read(0,buffer,sizeof(buffer));
        if(n==-1)
            break;
        send(fd,buffer,n,0);
    }

    write(1,"Closed.",strlen("Closed."));
}

Copyright © Linux教程網 All Rights Reserved