高级IO(非阻塞IO,有限状态机编程、多路转接、其他读写函数、存储映射IO)

非阻塞IO

数据中继
在这里插入图片描述
第一种方法:rL-wR-rr-wl(读左边的写到右边,然后读右边的写到左边)
如果用的是阻塞的方式实现:读左边的设备,左边的设备上一直都没有数据出现,那么就会阻塞在读左这样一个动作上,假设右边
的设备一直有其他的数据到来,(如果这个时候先读右再去写左,就可以正常运行)由于一直在读左,所以后续的操作,读右写左
就没有办法实现。所以就可能出现右边设备数据都满了,而左边还一直在读左的动作上,一直阻塞在左边。这个总体是一个
任务

第二种方式:分成两个任务
rL-wR
rR-wL
类似这种模型,用第一种方式是不可以的,用第二种方式可以勉强考虑,尤其是在使用阻塞方式实现的时候,就更不好使用了

所以换成非阻塞去实现是可以的,企图读左,没有数据,那么就企图去读右,没有数据,再去读左,在没有就去读右,那一块可以操作,就先去执行哪一块操作

复杂流程:自然流程不是结构化的
简单流程:程序的自然流程是结构化的

自然流程:最直接最直观的一个思路

例如:把大象装进冰箱需要几步?
open-》put-》close(打开冰箱-》装进冰箱-》关掉冰箱)这就是一个自然流程而且是结构化的

在这里插入图片描述

多半的网络协议都是非结构化的

有限状态机编程的思想
在这里插入图片描述
有限状态机的实现

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"

#define BUFSIZE 1024
enum
{
    STATE_R=1,
    STATE_W,
    STATE_Ex,
    STATE_T
};

struct fsm_st
{
    int state;
    int sfd;
    int dfd;
    char buf[BUFSIZE];
    int len;
    int pos;
    char *errstr;


};

static void fsm_driver(struct fsm_st *fsm)
{    int ret;
    switch(fsm->state)
    {
        case STATE_R:
            fsm->len=read(fsm->sfd,fsm->buf,BUFSIZE);
            if(fsm->len==0)
                fsm->state=STATE_T;
            else if(fsm->len<0)
            {
                if(errno==EAGAIN)
                    fsm->state=STATE_R;
                else
                {
                    fsm->errstr="read()";
                    fsm->state=STATE_Ex;
                }
            }
            else
            {

                fsm->pos=0;
                fsm->state=STATE_W;
            }
            break;
        case STATE_W:
            ret=write(fsm->dfd,fsm->buf+fsm->pos,fsm->len);
            if(ret<0)
            {
                if(errno==EAGAIN)
                    fsm->state=STATE_W;
                else
                {
                    fsm->errstr="write()";
                    fsm->state=STATE_Ex;
                }
            }
            else
            {
                fsm->pos+=ret;
                fsm->len-=ret;
                if(fsm->len==0)
                    fsm->state=STATE_R;
                else
                    fsm->state=STATE_W;

            }
            break;
        case STATE_Ex:
            perror(fsm->errstr);
            fsm->state=STATE_T;
            break;
        case STATE_T:
            /*
            do sth
            */
            break;
        default:
            abort();
            /*do sth*/
            break;

    }


}

static void relay(int fd1,int fd2)
{
    int fd1_save,fd2_save;
    struct fsm_st    fsm12,fsm21;
    fd1_save=fcntl(fd1,F_GETFL);
    fcntl(fd1,F_SETFL,fd1_save|O_NONBLOCK);
    
    fd2_save=fcntl(fd2,F_GETFL);
    fcntl(fd2,F_SETFL,fd2_save|O_NONBLOCK);

    fsm12.state=STATE_R;
    fsm12.sfd=fd1;
    fsm12.dfd=fd2;

    fsm21.state=STATE_R;
    fsm21.sfd=fd2;
    fsm21.dfd=fd1;

    while(fsm12.state!=STATE_T||fsm21.state!=STATE_T)
    {
        fsm_driver(&fsm12);
        fsm_driver(&fsm21);

    }
    fcntl(fd1,F_SETFL,fd1_save);
    fcntl(fd2,F_SETFL,fd2_save);

}


int main()
{

    int fd1,fd2;
    fd1=open(TTY1,O_RDWR);
    if(fd1<0)
    {
        perror("open()");
        exit(1);
    }
    write(fd1,"TTY1\n",5);    


    fd2=open(TTY2,O_RDWR|O_NONBLOCK);
    if(fd2<0)
        {
                perror("open()");
                exit(1);
        }
    write(fd2,"TTY2\n",5);

    relay(fd1,fd2);
    

    close(fd2);
    close(fd1);
    exit(0);

}

root用户运行。切换到非界面模式。就可以实现两个设备的内容相互转换

中继引擎实例实现:
main.c文件内容:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "relayer.h"
#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"

#define TTY3 "/dev/tty10"
#define TTY4 "/dev/tty9"

int main()
{

    int fd1,fd2;
    int fd3,fd4;
    int job2;
    int job1;
    fd1=open(TTY1,O_RDWR);
    if(fd1<0)
    {
        perror("open()");
        exit(1);
    }
    write(fd1,"TTY1\n",5);    


    fd2=open(TTY2,O_RDWR|O_NONBLOCK);
    if(fd2<0)
        {
                perror("open()");
                exit(1);
        }
    write(fd2,"TTY2\n",5);

    job1=rel_addjob(fd1,fd2);
    if(job1<0)
    {
        fprintf(stderr,"rel_addjob():%s\n",strerror(-job1));
        exit(1);

    }

    fd3=open(TTY3,O_RDWR);
    if(fd3<0)
    {
        perror("open()");
        exit(1);
    }
    write(fd3,"TTY3\n",5);

    fd4=open(TTY4,O_RDWR);
    if(fd4<0)
    {
        perror("open()");
        exit(1);
    }
    write(fd4,"TTY4\n",5);

    
    job2=rel_addjob(fd1,fd4);
    if(job2<0)
    {
        fprintf(stderr,"rel_addjob():%s\n",strerror(-job2));
        exit(1);
    }
    
    while(1)
        pause();
    
    close(fd2);
    close(fd1);
    close(fd3);
    close(fd4);
    exit(0);

}

relayer.c文件内容:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include "relayer.h"
#define BUFSIZE 10000
static struct rel_job_st *rel_job[REL_JOBMAX];
static pthread_mutex_t mut_rel_job=PTHREAD_MUTEX_INITIALIZER;
static pthread_once_t init_once=PTHREAD_ONCE_INIT;


enum
{
    STATE_R=1,
    STATE_W,
    STATE_Ex,
    STATE_T
};

struct rel_fsm_st
{
    int state;
    int sfd;
    int dfd;
    char buf[BUFSIZE];
    int len;
    int pos;
    char *errstr;
    int64_t count;

};


struct rel_job_st
{
    int fd1;
    int fd2;
    int job_state;
    struct rel_fsm_st fsm12,fsm21;
    int fd1_save;
    int fd2_save;

};

static void fsm_driver(struct rel_fsm_st *fsm)
{    int ret;
    switch(fsm->state)
    {
        case STATE_R:
            fsm->len=read(fsm->sfd,fsm->buf,BUFSIZE);
            if(fsm->len==0)
                fsm->state=STATE_T;
            else if(fsm->len<0)
            {
                if(errno==EAGAIN)
                    fsm->state=STATE_R;
                else
                {
                    fsm->errstr="read()";
                    fsm->state=STATE_Ex;
                }
            }
            else
            {

                fsm->pos=0;
                fsm->state=STATE_W;
            }
            break;
        case STATE_W:
            ret=write(fsm->dfd,fsm->buf+fsm->pos,fsm->len);
            if(ret<0)
            {
                if(errno==EAGAIN)
                    fsm->state=STATE_W;
                else
                {
                    fsm->errstr="write()";
                    fsm->state=STATE_Ex;
                }
            }
            else
            {
                fsm->pos+=ret;
                fsm->len-=ret;
                if(fsm->len==0)
                    fsm->state=STATE_R;
                else
                    fsm->state=STATE_W;

            }
            break;
        case STATE_Ex:
            perror(fsm->errstr);
            fsm->state=STATE_T;
            break;
        case STATE_T:
            /*
            do sth
            */
            break;
        default:
            abort();
            /*do sth*/
            break;

    }


}




static void *thr_relayer(void *p)
{
    int i;
    while(1)
    {
    pthread_mutex_lock(&mut_rel_job);
    for(i=0;i<REL_JOBMAX;i++)
    {
        if(rel_job[i]!=NULL)
        {
            if(rel_job[i]->job_state==STATE_RUNNING)
            {
            
                fsm_driver(&rel_job[i]->fsm12);
                fsm_driver(&rel_job[i]->fsm21);
                if(rel_job[i]->fsm12.state==STATE_T&&rel_job[i]->fsm21.state==STATE_T)
                {
                    rel_job[i]->job_state=STATE_OVER;
                }
                
            }

            
        }

    }
    pthread_mutex_unlock(&mut_rel_job);
    }

}
//module_unlag
static void module_load(void)
{    int err;
    pthread_t tid_relayer;
    err=pthread_create(&tid_relayer,NULL,thr_relayer,NULL);
    if(err)
    {
        fprintf(stderr,"pthread_create():%s\n",strerror(err));
        exit(1);
    }

    

}

static int get_free_pos_unlock()
{
    int i;
    for(i=0;i<REL_JOBMAX;i++)
    {
        if(rel_job[i]==NULL)
            return i;
        
    }
    return -1;
}




int rel_addjob(int fd1,int fd2)
{

    struct rel_job_st *me;
    int pos;
    pthread_once(&init_once,module_load);
    
    me=malloc(sizeof(*me));
    if(me==NULL)
    {
        return -ENOMEM;
    }    
    me->fd1=fd1;
    me->fd2=fd2;
    me->job_state=STATE_RUNNING;
    
    me->fd1_save=fcntl(me->fd1,F_GETFL);
    fcntl(me->fd1,F_SETFL,me->fd1_save|O_NONBLOCK);
    me->fd2_save=fcntl(me->fd2,F_GETFL);
    fcntl(me->fd2,F_SETFL,me->fd2_save|O_NONBLOCK);
    
    
    
    me->fsm12.sfd=me->fd1;
    me->fsm12.dfd=me->fd2;
    me->fsm12.state=STATE_R;

    me->fsm21.sfd=me->fd2;
    me->fsm21.dfd=me->fd1;
    me->fsm21.state=STATE_R;
    
    pthread_mutex_lock(&mut_rel_job);
    pos=get_free_pos_unlock();
    if(pos<0)
    {
        pthread_mutex_unlock(&mut_rel_job);
        fcntl(me->fd1,F_SETFL,me->fd1_save);
        fcntl(me->fd2,F_SETFL,me->fd2_save);
        free(me);
        return -ENOSPC;
    }

    rel_job[pos]=me;
        

    pthread_mutex_unlock(&mut_rel_job);
    return pos;
}

/*
int rel_canceljob(int id);

int rel_waitjob(int id,struct rel_stat_st *);

int rel_statjob(int id,struct rel_stat_st *);
*/

relayer.h文件内容:

#ifndef RELAYER_H__
#define RELAYER_H__

#define REL_JOBMAX 10000

enum
{
    STATE_RUNNING=1,
    STATE_CANCELED,
    STATE_OVER

};

struct rel_stat_st
{
    int state;
    int fd1;
    int fd2;
    int64_t count12,count21;
//    struct timerval start,end;
};

int rel_addjob(int fd1,int fd2);
/*
return >=0 cheng gong fan hui dang qian ren wu id
    ==-EINVAL fail can shu fei fa
    ==-ENOSPA fail ren wu shu zu man le
    ==-ENOMEM fail nei cun fen pei you wu

*/

int rel_canceljob(int id);
/*
return  ==0 sucessful zhi ding ren wu qu xiao
    ==-EINVAL fail can shu fei fa
    ==-EBUSY fail chong fu qu xiao,ren wu zao jiu bei qu xiao
    

*/

int rel_waitjob(int id,struct rel_stat_st *);
/*
return  ==0 sucessful zhi ding ren wu yi jing zhong zhi bing fan hui zhuang tai
    ==-EINVAL fail can shu fei fa
    
*/

int rel_statjob(int id,struct rel_stat_st);
/*
return  ==0 sucessful zhi ding re wu yi jing fan hui
    ==-EINVAL failcan shu fei fa
    
*/



#endif

makefile文件内容:

CFLAGS+=-pthread
LDFLAGS+=-pthread


all:relayer

relayer:relayer.o main.o
    gcc $^ -o $@ $(CFLAGS) $(LDFLAGS)


clean:
    rm - rf* .o relayer

IO多路转接

监视文件描述符的行为,当当前文件描述符的行为,发生了我感兴趣的行为的时候,才会去做后续的操作。
比如说数据中继,两个设备来做数据交换,一直都是在忙推,一直不停的探测,看有没有可读或是可写。如果加上多路转接,当某一个文件描述符发生了我感兴趣的动作的时候,才会去做后续工作。
用相应的机制来做:
首先一定要布置监视现场,然后通过当前的监视结果来确定推哪一个状态机。
IO多路转接可以监视文件描述符,并且可以监视多个
相关函数:
select();
poll();
epoll();
三个函数都是用来实现IO多路转接

    select();//移植性很好,是一个古老的函数,是以事件为单位组织文件描述符
   /* According to POSIX.1-2001, POSIX.1-2008 */
   #include <sys/select.h>

   /* According to earlier standards */
   #include <sys/time.h>
   #include <sys/types.h>
   #include <unistd.h>

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

   void FD_CLR(int fd, fd_set *set);//从一个集合当中删掉一个特定的一个文件描述符
   int  FD_ISSET(int fd, fd_set *set);//判断一个文件描述符是否存在在一个集合当中
   void FD_SET(int fd, fd_set *set);//把一个特定的文件描述符放到一个集合当中
   void FD_ZERO(fd_set *set);//清空一个文件描述符集合
select函数参数解析:
        int nfds:
        指的是文件描述符的个数,但是不是数文件描述符的个数,写的是所监视文件描述符当中最大的那个再加一。比如,现在监视的文件描述符是3、5、7、9那么这个位置就写10
        fd_set *readfds:
指的是一个读集,你所关心的,可以发生读的状态的文件描述符
        fd_set *writefds:
指的是一个写集,你所关心的,可以发生写的状态的文件描述符
        fd_set *exceptfds:
所关心的异常的文件描述符
        struct timeval *timeout:
超时设置

如果不设置超时设置,那么就会一直等,等到感兴趣的事件发生
返回值:
       On success, select() and pselect() return the number of  file  descrip‐
       tors  contained  in  the  three  returned descriptor sets(that is, the
       total number of bits that are  set  in  readfds,  writefds,  exceptfds)
       which  may  be  zero if the timeout expires before anything interesting
       happens.  On error, -1 is returned, and errno is set  to  indicate  the
       error;  the  file  descriptor  sets are unmodified, and timeout becomes
       undefined.
  The timeout
       The time structures involved are defined in <sys/time.h> and look like

           struct timeval {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */
           };

       and

           struct timespec {
               long    tv_sec;         /* seconds */
               long    tv_nsec;        /* nanoseconds */
           };

实例:

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"

#define BUFSIZE 1024
enum
{
    STATE_R=1,
    STATE_W,
    STATE_Ex,
    STATE_T
};

struct fsm_st
{
    int state;
    int sfd;
    int dfd;
    char buf[BUFSIZE];
    int len;
    int pos;
    char *errstr;


};

static void fsm_driver(struct fsm_st *fsm)
{    int ret;
    switch(fsm->state)
    {
        case STATE_R:
            fsm->len=read(fsm->sfd,fsm->buf,BUFSIZE);
            if(fsm->len==0)
                fsm->state=STATE_T;
            else if(fsm->len<0)
            {
                if(errno==EAGAIN)
                    fsm->state=STATE_R;
                else
                {
                    fsm->errstr="read()";
                    fsm->state=STATE_Ex;
                }
            }
            else
            {

                fsm->pos=0;
                fsm->state=STATE_W;
            }
            break;
        case STATE_W:
            ret=write(fsm->dfd,fsm->buf+fsm->pos,fsm->len);
            if(ret<0)
            {
                if(errno==EAGAIN)
                    fsm->state=STATE_W;
                else
                {
                    fsm->errstr="write()";
                    fsm->state=STATE_Ex;
                }
            }
            else
            {
                fsm->pos+=ret;
                fsm->len-=ret;
                if(fsm->len==0)
                    fsm->state=STATE_R;
                else
                    fsm->state=STATE_W;

            }
            break;
        case STATE_Ex:
            perror(fsm->errstr);
            fsm->state=STATE_T;
            break;
        case STATE_T:
            /*
            do sth
            */
            break;
        default:
            abort();
            /*do sth*/
            break;

    }


}


static int max(int a,int b)
{
    if(a>b)
        return a;
    return b;


}

static void relay(int fd1,int fd2)
{
    int fd1_save,fd2_save;
    struct fsm_st    fsm12,fsm21;
    fd_set rset,wset;
    
    fd1_save=fcntl(fd1,F_GETFL);
    fcntl(fd1,F_SETFL,fd1_save|O_NONBLOCK);
    
    fd2_save=fcntl(fd2,F_GETFL);
    fcntl(fd2,F_SETFL,fd2_save|O_NONBLOCK);

    fsm12.state=STATE_R;
    fsm12.sfd=fd1;
    fsm12.dfd=fd2;

    fsm21.state=STATE_R;
    fsm21.sfd=fd2;
    fsm21.dfd=fd1;

    while(fsm12.state!=STATE_T||fsm21.state!=STATE_T)
    {
        
        //布置监视任务
        FD_ZERO(&rset);
        FD_ZERO(&wset);
        if(fsm12.state==STATE_R)
        {
            FD_SET(fsm12.sfd,&rset);
        }
        if(fsm12.state==STATE_W)
        {
            FD_SET(fsm12.dfd,&wset);
        }
                if(fsm21.state==STATE_R)
                {
                        FD_SET(fsm21.sfd,&rset);
                }
                if(fsm21.state==STATE_W)
                {
                        FD_SET(fsm21.dfd,&wset);
                }

        //监视

        if(select(max(fd1,fd2)+1,&rset,&wset,NULL,NULL)<0)
        {
            if(errno==EINTR)
                continue;
            perror("select()");
            exit(1);
        }
        //查看监视结果
        if(FD_ISSET(fd1,&rset) || FD_ISSET(fd2,&wset))
                    fsm_driver(&fsm12);
        if(FD_ISSET(fd2,&rset) || FD_ISSET(fd1,&wset))
                    fsm_driver(&fsm21);
        

    }
    fcntl(fd1,F_SETFL,fd1_save);
    fcntl(fd2,F_SETFL,fd2_save);

}


int main()
{

    int fd1,fd2;
    fd1=open(TTY1,O_RDWR);
    if(fd1<0)
    {
        perror("open()");
        exit(1);
    }
    write(fd1,"TTY1\n",5);    


    fd2=open(TTY2,O_RDWR|O_NONBLOCK);
    if(fd2<0)
        {
                perror("open()");
                exit(1);
        }
    write(fd2,"TTY2\n",5);

    relay(fd1,fd2);
    

    close(fd2);
    close(fd1);
    exit(0);

}

上面的程序只关注了读和写的状态
从Ex态和T态应该是无条件推动的
加上一个线:
enum
{
STATE_R=1,
STATE_W,
STATE_AUTO,
STATE_Ex,
STATE_T
};
如果状态机的状态大于3就会推状态机

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"

#define BUFSIZE 1024
enum
{
    STATE_R=1,
    STATE_W,
    STATE_AUTO,
    STATE_Ex,
    STATE_T
};

struct fsm_st
{
    int state;
    int sfd;
    int dfd;
    char buf[BUFSIZE];
    int len;
    int pos;
    char *errstr;


};

static void fsm_driver(struct fsm_st *fsm)
{    int ret;
    switch(fsm->state)
    {
        case STATE_R:
            fsm->len=read(fsm->sfd,fsm->buf,BUFSIZE);
            if(fsm->len==0)
                fsm->state=STATE_T;
            else if(fsm->len<0)
            {
                if(errno==EAGAIN)
                    fsm->state=STATE_R;
                else
                {
                    fsm->errstr="read()";
                    fsm->state=STATE_Ex;
                }
            }
            else
            {

                fsm->pos=0;
                fsm->state=STATE_W;
            }
            break;
        case STATE_W:
            ret=write(fsm->dfd,fsm->buf+fsm->pos,fsm->len);
            if(ret<0)
            {
                if(errno==EAGAIN)
                    fsm->state=STATE_W;
                else
                {
                    fsm->errstr="write()";
                    fsm->state=STATE_Ex;
                }
            }
            else
            {
                fsm->pos+=ret;
                fsm->len-=ret;
                if(fsm->len==0)
                    fsm->state=STATE_R;
                else
                    fsm->state=STATE_W;

            }
            break;
        case STATE_Ex:
            perror(fsm->errstr);
            fsm->state=STATE_T;
            break;
        case STATE_T:
            /*
            do sth
            */
            break;
        default:
            abort();
            /*do sth*/
            break;

    }


}


static int max(int a,int b)
{
    if(a>b)
        return a;
    return b;


}

static void relay(int fd1,int fd2)
{
    int fd1_save,fd2_save;
    struct fsm_st    fsm12,fsm21;
    fd_set rset,wset;
    
    fd1_save=fcntl(fd1,F_GETFL);
    fcntl(fd1,F_SETFL,fd1_save|O_NONBLOCK);
    
    fd2_save=fcntl(fd2,F_GETFL);
    fcntl(fd2,F_SETFL,fd2_save|O_NONBLOCK);

    fsm12.state=STATE_R;
    fsm12.sfd=fd1;
    fsm12.dfd=fd2;

    fsm21.state=STATE_R;
    fsm21.sfd=fd2;
    fsm21.dfd=fd1;

    while(fsm12.state!=STATE_T||fsm21.state!=STATE_T)
    {
        
        //布置监视任务
        FD_ZERO(&rset);
        FD_ZERO(&wset);
        if(fsm12.state==STATE_R)
        {
            FD_SET(fsm12.sfd,&rset);
        }
        if(fsm12.state==STATE_W)
        {
            FD_SET(fsm12.dfd,&wset);
        }
                if(fsm21.state==STATE_R)
                {
                        FD_SET(fsm21.sfd,&rset);
                }
                if(fsm21.state==STATE_W)
                {
                        FD_SET(fsm21.dfd,&wset);
                }

        //监视
        if(fsm12.state<STATE_AUTO||fsm21.state<STATE_AUTO)
        {
            if(select(max(fd1,fd2)+1,&rset,&wset,NULL,NULL)<0)
            {
                if(errno==EINTR)
                    continue;
                perror("select()");
                exit(1);
            }
        }
        //查看监视结果
        if(FD_ISSET(fd1,&rset) || FD_ISSET(fd2,&wset)||fsm12.state>STATE_AUTO)
                    fsm_driver(&fsm12);
        if(FD_ISSET(fd2,&rset) || FD_ISSET(fd1,&wset)||fsm21.state>STATE_AUTO)
                    fsm_driver(&fsm21);
        

    }
    fcntl(fd1,F_SETFL,fd1_save);
    fcntl(fd2,F_SETFL,fd2_save);

}


int main()
{

    int fd1,fd2;
    fd1=open(TTY1,O_RDWR);
    if(fd1<0)
    {
        perror("open()");
        exit(1);
    }
    write(fd1,"TTY1\n",5);    


    fd2=open(TTY2,O_RDWR|O_NONBLOCK);
    if(fd2<0)
        {
                perror("open()");
                exit(1);
        }
    write(fd2,"TTY2\n",5);

    relay(fd1,fd2);
    

    close(fd2);
    close(fd1);
    exit(0);

}

select缺陷:
监视现场和监视结果用的是同一个空间
对于文件描述符的最大值有一个限制
监视的事件过于单一

       poll();//是以文件描述符为单位来组织事件,也是可以移植的,用户自己维护
       poll - wait for some event on a file descriptor//以文件描述符为单位等待事件发生

       #include <poll.h>
       int poll(struct pollfd *fds, nfds_t nfds, int timeout);
 struct pollfd {
           int   fd;         /* file descriptor */
           short events;     /* requested events */
           short revents;    /* returned events */
       };
   The  bits that may be set/returned in events and revents are defined in
   <poll.h>:位图

          POLLIN There is data to read.

          POLLPRI
                 There is urgent data to read (e.g., out-of-band  data  on
                 TCP socket; pseudoterminal master in packet mode has seen
                 state change in slave).

          POLLOUT
                 Writing is now possible, though a write larger  that  the
                 available  space  in  a  socket  or pipe will still block
                 (unless O_NONBLOCK is set).

          POLLRDHUP (since Linux 2.6.17)
                 Stream socket peer closed connection, or shut down  writ‐
                 ing  half  of  connection.   The _GNU_SOURCE feature test
                 macro must be defined (before including any header files)
                 in order to obtain this definition.

          POLLERR
                 Error  condition  (only  returned  in revents; ignored in
                 events).
poll参数解析:

        struct pollfd *fds:
                结构体数组的启示位置
        nfds_t nfds:
                数组的长度
       int timeout://以毫秒为单位
                 超时设置

0表示非阻塞
-1表示阻塞
时间数值就是超时设置
返回值:
       On success, a positive number is returned; this is the number of struc‐
       tures which have nonzero revents fields (in other words, those descrip‐
       tors  with events or errors reported).  A value of 0 indicates that the
       call timed out and no file descriptors were ready.   On  error,  -1  is
       returned, and errno is set appropriately.

实例:

#include <stdio.h>
#include <poll.h>
#include <sys/time.h>
#include <errno.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"

#define BUFSIZE 1024
enum
{
    STATE_R=1,
    STATE_W,
    STATE_AUTO,
    STATE_Ex,
    STATE_T
};

struct fsm_st
{
    int state;
    int sfd;
    int dfd;
    char buf[BUFSIZE];
    int len;
    int pos;
    char *errstr;


};

static void fsm_driver(struct fsm_st *fsm)
{    int ret;
    switch(fsm->state)
    {
        case STATE_R:
            fsm->len=read(fsm->sfd,fsm->buf,BUFSIZE);
            if(fsm->len==0)
                fsm->state=STATE_T;
            else if(fsm->len<0)
            {
                if(errno==EAGAIN)
                    fsm->state=STATE_R;
                else
                {
                    fsm->errstr="read()";
                    fsm->state=STATE_Ex;
                }
            }
            else
            {

                fsm->pos=0;
                fsm->state=STATE_W;
            }
            break;
        case STATE_W:
            ret=write(fsm->dfd,fsm->buf+fsm->pos,fsm->len);
            if(ret<0)
            {
                if(errno==EAGAIN)
                    fsm->state=STATE_W;
                else
                {
                    fsm->errstr="write()";
                    fsm->state=STATE_Ex;
                }
            }
            else
            {
                fsm->pos+=ret;
                fsm->len-=ret;
                if(fsm->len==0)
                    fsm->state=STATE_R;
                else
                    fsm->state=STATE_W;

            }
            break;
        case STATE_Ex:
            perror(fsm->errstr);
            fsm->state=STATE_T;
            break;
        case STATE_T:
            /*
            do sth
            */
            break;
        default:
            abort();
            /*do sth*/
            break;

    }


}


static int max(int a,int b)
{
    if(a>b)
        return a;
    return b;


}

static void relay(int fd1,int fd2)
{
    int fd1_save,fd2_save;
    struct fsm_st    fsm12,fsm21;
    struct pollfd pfd[2];
    
    fd1_save=fcntl(fd1,F_GETFL);
    fcntl(fd1,F_SETFL,fd1_save|O_NONBLOCK);
    
    fd2_save=fcntl(fd2,F_GETFL);
    fcntl(fd2,F_SETFL,fd2_save|O_NONBLOCK);

    fsm12.state=STATE_R;
    fsm12.sfd=fd1;
    fsm12.dfd=fd2;

    fsm21.state=STATE_R;
    fsm21.sfd=fd2;
    fsm21.dfd=fd1;
    pfd[0].fd=fd1;
    pfd[1].fd=fd2;



    while(fsm12.state!=STATE_T||fsm21.state!=STATE_T)
    {
        
        //布置监视任务

        
        pfd[0].events=0;
        if(fsm12.state==STATE_R)
            pfd[0].events|=POLLIN;
        if(fsm21.state==STATE_W)
            pfd[0].events|=POLLOUT;
        
        pfd[1].events=0;

        if(fsm12.state==STATE_W)
            pfd[1].events|=POLLOUT;
                if(fsm21.state==STATE_R)
            pfd[1].events|=POLLIN;
        

        //监视
        if(fsm12.state<STATE_AUTO||fsm21.state<STATE_AUTO)
        {
            while(poll(pfd,2,-1)<0)
            {
                if(errno==EINTR)
                    continue;
                perror("poll()");
                exit(1);
            }
        }
        //查看监视结果
        if(pfd[0].revents & POLLIN ||pfd[1].revents & POLLOUT ||fsm12.state>STATE_AUTO)
                    fsm_driver(&fsm12);
        if(pfd[1].revents & POLLIN ||pfd[0].revents & POLLOUT ||fsm21.state>STATE_AUTO)
                    fsm_driver(&fsm21);
        

    }
    fcntl(fd1,F_SETFL,fd1_save);
    fcntl(fd2,F_SETFL,fd2_save);

}


int main()
{

    int fd1,fd2;
    fd1=open(TTY1,O_RDWR);
    if(fd1<0)
    {
        perror("open()");
        exit(1);
    }
    write(fd1,"TTY1\n",5);    


    fd2=open(TTY2,O_RDWR|O_NONBLOCK);
    if(fd2<0)
        {
                perror("open()");
                exit(1);
        }
    write(fd2,"TTY2\n",5);

    relay(fd1,fd2);
    

    close(fd2);
    close(fd1);
    exit(0);

}
    epoll();//linux从poll的基础上,自己做的,移植不行。简化了用户描述

   #include <sys/epoll.h>
DESCRIPTION
       The  epoll  API performs a similar task to poll(2): monitoring multiple
       file descriptors to see if I/O is possible on any of them.   The  epoll
       API can be used either as an edge-triggered or a level-triggered inter‐
       face and scales well to large numbers of watched file descriptors.  The
       following  system  calls  are  provided  to  create and manage an epoll
       instance:

       *  epoll_create(2)  creates  an  epoll  instance  and  returns  a  file
          descriptor  referring to that instance.  (The more recent epoll_cre‐
          ate1(2) extends the functionality of epoll_create(2).)
第一步创建一个epoll实例用epoll——create

       *  Interest in particular  file  descriptors  is  then  registered  via
          epoll_ctl(2).   The  set of file descriptors currently registered on
          an epoll instance is sometimes called an epoll set.
第二步epoll实例的设置和控制用epoll_ctl
       *  epoll_wait(2) waits for I/O events, blocking the calling  thread  if
          no events are currently available.
最后取监视结果用epoll_wait

相关函数:

       epoll_create - open an epoll file descriptor
       #include <sys/epoll.h>
       int epoll_create(int size);
       int epoll_create1(int flags);
size:值,可以写随意的一个数值
RETURN VALUE
       On  success,  these  system calls return a nonnegative file descriptor.
       On error, -1 is returned, and errno is set to indicate the error.
返回的是一个文件描述符
epoll_ctl

       #include <sys/epoll.h>
       int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

对epfd实例中的fd文件描述符进行op(添加,删除,修改)行为,最后的参数是事件,op操作是针对fd这个描述符的哪个事件

op:想做什么?
Valid values for the op argument are:

       EPOLL_CTL_ADD
              Register  the  target  file  descriptor fd on the epoll instance
              referred to by the file descriptor epfd and associate the  event
              event with the internal file linked to fd.

       EPOLL_CTL_MOD
              Change  the event event associated with the target file descrip‐
              tor fd.

       EPOLL_CTL_DEL
              Remove (deregister) the target file descriptor fd from the epoll
              instance  referred  to by epfd.  The event is ignored and can be
              NULL (but see BUGS below).
   The event argument describes the object linked to the  file  descriptor
   fd.  The struct epoll_event is defined as:

       typedef union epoll_data {
           void        *ptr;
           int          fd;
           uint32_t     u32;
           uint64_t     u64;
       } epoll_data_t;

       struct epoll_event {
           uint32_t     events;      /* Epoll events */
           epoll_data_t data;        /* User data variable */
       };
    The  events member is a bit mask composed using the following available
       event types:

       EPOLLIN
              The associated file is available for read(2) operations.

       EPOLLOUT
              The associated file is available for write(2) operations.

……(具体自己man手册查看)
epoll_wait://往外取现在发生的事件
       epoll_wait,  epoll_pwait  -  wait  for  an  I/O  event on an epoll file
       descriptor


       #include <sys/epoll.h>
       int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);

 int maxevents个事件放到struct epoll_event *events结构体当中

实例:

#include <stdio.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <errno.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"
#define BUFSIZE 1024

enum
{
    STATE_R=1,
    STATE_W,
    STATE_AUTO,
    STATE_Ex,
    STATE_T
};

struct fsm_st
{
    int state;
    int sfd;
    int dfd;
    char buf[BUFSIZE];
    int len;
    int pos;
    char *errstr;


};

static void fsm_driver(struct fsm_st *fsm)
{    int ret;
    switch(fsm->state)
    {
        case STATE_R:
            fsm->len=read(fsm->sfd,fsm->buf,BUFSIZE);
            if(fsm->len==0)
                fsm->state=STATE_T;
            else if(fsm->len<0)
            {
                if(errno==EAGAIN)
                    fsm->state=STATE_R;
                else
                {
                    fsm->errstr="read()";
                    fsm->state=STATE_Ex;
                }
            }
            else
            {

                fsm->pos=0;
                fsm->state=STATE_W;
            }
            break;
        case STATE_W:
            ret=write(fsm->dfd,fsm->buf+fsm->pos,fsm->len);
            if(ret<0)
            {
                if(errno==EAGAIN)
                    fsm->state=STATE_W;
                else
                {
                    fsm->errstr="write()";
                    fsm->state=STATE_Ex;
                }
            }
            else
            {
                fsm->pos+=ret;
                fsm->len-=ret;
                if(fsm->len==0)
                    fsm->state=STATE_R;
                else
                    fsm->state=STATE_W;

            }
            break;
        case STATE_Ex:
            perror(fsm->errstr);
            fsm->state=STATE_T;
            break;
        case STATE_T:
            /*
            do sth
            */
            break;
        default:
            abort();
            /*do sth*/
            break;

    }


}


static int max(int a,int b)
{
    if(a>b)
        return a;
    return b;


}

static void relay(int fd1,int fd2)
{
    int fd1_save,fd2_save;
    struct fsm_st    fsm12,fsm21;
    struct epoll_event ev;
    int epfd;

    fd1_save=fcntl(fd1,F_GETFL);
    fcntl(fd1,F_SETFL,fd1_save|O_NONBLOCK);
    
    fd2_save=fcntl(fd2,F_GETFL);
    fcntl(fd2,F_SETFL,fd2_save|O_NONBLOCK);

    fsm12.state=STATE_R;
    fsm12.sfd=fd1;
    fsm12.dfd=fd2;

    fsm21.state=STATE_R;
    fsm21.sfd=fd2;
    fsm21.dfd=fd1;
       
    epfd=epoll_create(10);
    if(epfd<0)
    {
        perror("epoll_create()");
        exit(1);
    }

    ev.events=0;
    ev.data.fd=fd1;
    epoll_ctl(epfd,EPOLL_CTL_ADD,fd1,&ev);

    ev.events=0;
    ev.data.fd=fd2;
    epoll_ctl(epfd,EPOLL_CTL_ADD,fd2,&ev);
    

    while(fsm12.state!=STATE_T||fsm21.state!=STATE_T)
    {
        
        //布置监视任务
        ev.data.fd=fd1;
        ev.events=0;

        if(fsm12.state==STATE_R)
            ev.events |= EPOLLIN;
        if(fsm21.state==STATE_W)
            ev.events |=EPOLLOUT;

        epoll_ctl(epfd,EPOLL_CTL_MOD,fd1,&ev);
        
        ev.data.fd=fd2;
        ev.events=0;
        if(fsm12.state==STATE_W)
            ev.events|=EPOLLOUT;
                if(fsm21.state==STATE_R)
            ev.events|=EPOLLIN;
         epoll_ctl(epfd,EPOLL_CTL_MOD,fd2,&ev);

        

        //监视
        if(fsm12.state<STATE_AUTO||fsm21.state<STATE_AUTO)
        {
            while(epoll_wait(epfd,&ev,1,-1)<0)
            {
                if(errno==EINTR)
                    continue;
                perror("poll_wait()");
                exit(1);
            }
        }

        //查看监视结果
        if(ev.data.fd==fd1 && ev.events &EPOLLIN \
        ||ev.data.fd==fd2 && ev.events & EPOLLOUT\
        ||fsm12.state>STATE_AUTO)
                    fsm_driver(&fsm12);
        if(ev.data.fd==fd2 && ev.events & EPOLLIN \
         ||ev.data.fd==fd1 && ev.events & EPOLLOUT \
         ||fsm21.state>STATE_AUTO)
                    fsm_driver(&fsm21);
        

    }
    fcntl(fd1,F_SETFL,fd1_save);
    fcntl(fd2,F_SETFL,fd2_save);
    
    close(epfd);
}


int main()
{

    int fd1,fd2;
    fd1=open(TTY1,O_RDWR);
    if(fd1<0)
    {
        perror("open()");
        exit(1);
    }
    write(fd1,"TTY1\n",5);    


    fd2=open(TTY2,O_RDWR|O_NONBLOCK);
    if(fd2<0)
        {
                perror("open()");
                exit(1);
        }
    write(fd2,"TTY2\n",5);

    relay(fd1,fd2);
    

    close(fd2);
    close(fd1);
    exit(0);

}

其他读写函数

    readv();
    writev();//read or write data into multiple buffers
操作对象都不是一块完整的空间
   #include <sys/uio.h>
   ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
   ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
       struct iovec {
           void  *iov_base;    /* Starting address */
           size_t iov_len;     /* Number of bytes to transfer */
       };//以base为起始地址的空间有len这么大

比如说现在要写一个文件,但是要写的文件的数据来源并不是一块完整的空间(多少个字节,完整的空间)可能是这块空间我要拿十个字节写到文件中去,在另外一个地址有一百个字节写到文件中等等。

多个碎片的小地址要写道同一个地址里去。

存储映射IO

存储映射:把某一块内存或者是某一个文件的存储内容映射到当前的进程空间中来,看到的就是在当前进程空间当中访问一段char型内容,就如同访问内存一样

mmap();

       #include <sys/mman.h>
       void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);
       int munmap(void *addr, size_t length);//解除映射

在这里插入图片描述

参数解析:
void *addr:起始位置,可以写NULL,意味着主动完成一个映射,找到一个可用的空间
size_t length:多长的内容映射进来
int prot:当前如果完成了内存映射,那么当前这块是什么样的属性(就是对映射过来的空间做什么样的操作)
int flags:特殊要求,是一个位图
int fd:文件描述符
off_t offset:偏移量
把一个打开的文件fd,映射length长度
从fd文件的offset偏移量开始,要映射length个字节到addr地址来
   The prot argument describes the desired memory protection of  the  map‐
   ping  (and  must  not  conflict with the open mode of the file).  It is
   either PROT_NONE or the bitwise OR of one  or  more  of  the  following
   flags:

   PROT_EXEC  Pages may be executed.

   PROT_READ  Pages may be read.

   PROT_WRITE Pages may be written.

   PROT_NONE  Pages may not be accessed.
    The  flags argument determines whether updates to the mapping are visi‐
       ble to other processes mapping the same region, and whether updates are
       carried through to the underlying file.  This behavior is determined by
       including exactly one of the following values in flags:(必须选下面其中一个)

       MAP_SHARED
              Share this mapping.  Updates to the mapping are visible to other
              processes  that  map  this  file, and are carried through to the
              underlying file.  (To precisely control when updates are carried
              through to the underlying file requires the use of msync(2).)
表示对这块空间的修改别人都是可以看到的
       MAP_PRIVATE
              Create  a private copy-on-write mapping.  Updates to the mapping
              are not visible to other processes mapping the  same  file,  and
              are  not carried through to the underlying file.  It is unspeci‐
              fied whether changes made to the file after the mmap() call  are
              visible in the mapped region.

表示对这块空间的修改只有自己可以看到,不会去更改那块内存本身的数据



其他要注意的:
       MAP_ANONYMOUS//匿名映射
              The mapping is not backed by any file; its contents are initial‐
              ized to zero.  The fd and offset arguments are ignored; however,
              some implementations require fd to be -1  if  MAP_ANONYMOUS  (or
              MAP_ANON)  is specified, and portable applications should ensure
              this.  The use of MAP_ANONYMOUS in conjunction  with  MAP_SHARED
              is supported on Linux only since kernel 2.4.//不依赖于任何文件,并且这块空间会被初始化为0
RETURN VALUE
       On success, mmap() returns a pointer to the mapped area.  On error, the
       value MAP_FAILED (that is, (void *) -1) is returned, and errno  is  set
       to indicate the cause of the error.

       On  success,  munmap() returns 0.  On failure, it returns -1, and errno
       is set to indicate the cause of the error (probably to EINVAL).

实例:
计算一个文件当中有多少个a

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/mman.h>


int main(int argc,char *argv[])
{
    int fd;
    struct stat statres;
    char *str;
    int count=0;
    int i;
    if(argc < 2)
    {
        fprintf(stderr,"Usage......\n");
        exit(1);
    }

    fd=open(argv[1],O_RDONLY);
    if(fd<0)
    {
        perror("open()");
        exit(1);
    }
    
    if(fstat(fd,&statres)<0)
    {
        perror("fstat()");
        exit(1);
    }
    
    str=mmap(NULL,statres.st_size,PROT_READ,MAP_SHARED,fd,0);
    if(str==MAP_FAILED)
    {
        perror("mmap()");
        exit(1);
    }
        
    close(fd);

    for(i=0;i<statres.st_size;i++)
    {
        if(str[i]=='a')
            count++;

    }

    printf("%d\n",count);
    munmap(str,statres.st_size);



    exit(0);
}

实现共享内存:
在这里插入图片描述

子进程写,父进程写

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define MEMSIZE 1024

int main()
{
    char *ptr;
    pid_t pid;

    ptr=mmap(NULL,MEMSIZE,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
    if(ptr==MAP_FAILED)
    {    
        perror("mmap()");
        exit(1);
    }
    
    
    pid=fork();
    if(pid<0)
    {
        perror("fork()");
        munmap(ptr,MEMSIZE);
        exit(1);
    }

    if(pid==0)    //child write
    {
        strcpy(ptr,"hello!");
        munmap(ptr,MEMSIZE);
        exit(0);    
    }
    else        //parent read
    {
        wait(NULL);
        puts(ptr);
        munmap(ptr,MEMSIZE);
        exit(0);
    }
    exit(0);

}

运行结果:
在这里插入图片描述

可以知道这算是另一种申请空间的方式,mmap和munmap相当于malloc和free
5、文件锁

实现方式
fcntl()
lockf()
flock()

这里使用lockf实现文件锁

       #include <unistd.h>
       int lockf(int fd, int cmd, off_t len);
第三个参数如果为0,那么就表示文件有多长就锁多长
cmd选项:
       Valid operations are given below:

       F_LOCK Set  an exclusive lock on the specified section of the file.  If
              (part of) this section is already locked, the call blocks  until
              the previous lock is released.  If this section overlaps an ear‐
              lier locked section, both are merged.  File locks  are  released
              as  soon  as  the  process  holding  the  locks closes some file
              descriptor for the file.  A child process does not inherit these
              locks.

       F_TLOCK
              Same  as  F_LOCK  but the call never blocks and returns an error
              instead if the file is already locked.

       F_ULOCK
              Unlock the indicated section of the  file.   This  may  cause  a
              locked section to be split into two locked sections.

       F_TEST Test  the lock: return 0 if the specified section is unlocked or
              locked by this process; return -1, set errno to  EAGAIN  (EACCES
              on some other systems), if another process holds a lo

实例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define PROCNUM 20
#define FNAME "/tmp/out"
#define LINESIZE 1024


static void func_add(void)
{
        FILE *fp;
    int fd;
        char linebuf[LINESIZE];
        fp=fopen(FNAME,"r+");
        if(fp==NULL)
        {
        perror("fopen()");
        exit(1);
        }
    fd=fileno(fp);
    lockf(fd,F_LOCK,0);
        fgets(linebuf,LINESIZE,fp);
        fseek(fp,0,SEEK_SET);
    sleep(1);
        fprintf(fp,"%d\n",atoi(linebuf)+1);
        fflush(fp);
    lockf(fd,F_ULOCK,0);
    
    fclose(fp);
    return ;

}

int main()
{
      
        int i;
    pid_t pid;
        for(i=0;i<PROCNUM;i++)
        {
                   pid=fork();       
        if(pid<0)
        {
            perror("fork()");
            exit(1);
        }
        if(pid==0)
        {
            func_add();
            exit(0);
        }
        
          
        }

        for(i=0;i<PROCNUM;i++)
                wait(NULL);

        exit(0);
    
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值