[APUE]再读之高级IO

1. 非阻塞IO。

阻塞IO有如下情况

。 数据不存在,  则读操作永远阻塞。典型的为 管道操作。

。 数据不能被立即接受,写这样的文件会被阻塞。

。 打开文件会被阻塞。(典型为调制解调器。只写方式打开FIFO,要等待一个读进程)

。 对已经加上强制性锁的文件进行读写。

。 ioctl 操作

。 某些进程间通信函数。 比如semophore的p,v 操作。

管道阻塞的demo,子进程等待5s后再写,父进程阻塞5秒读数据。

#include 
#include 
#include 
#include 
int main(int argc,char* argv[])
{

    int fd[2];
    char* buf ="message from child\n";
    char rbuf[1024];
    if(pipe(fd)<0)
    {
        printf("create pipe error\n");
        exit(-1);
    }

    pid_t pid;
    pid = fork();
    if(pid<0)
    {
        fprintf(stderr,"fork error: %s\n", strerror(errno));
        exit(-1);
    }
    else if(pid==0)
    {
        close(fd[0]);
        sleep(5);
        int len = strlen(buf);
        if(write(fd[1],buf, strlen(buf))!= len)
        {
            fprintf(stderr,"write error: %s\n", strerror(errno));
            exit(-1);
        }
    }
    else
    {
        close(fd[1]);
        int n = read(fd[0],rbuf,1024);
        if(n==-1)
        {
            fprintf(stderr,"read error: %s\n", strerror(errno));
            exit(-1);
        }
        rbuf[n] = 0;
        printf(rbuf);
    }
}

fifo 阻塞的demo.父进程sleep 5 s. 子进程5秒后才有数据可读,之前一直阻塞。

#include 
#include 
#include 
#include 
#include 

int main(int argc, char* argv)
{
    char errorbuf[1024];
    memset(errorbuf,1024,0);
    if(mkfifo("/tmp/fifowaittest", S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)==-1 && errno!=EEXIST)
    {
        fprintf(stderr,"error %s\n", strerror(errno));
        exit(-1);
    }

    int pid =fork();
    if(pid<0)
    {
        snprintf(errorbuf,1024,"fork error, error is: %d, reason is %s\n", errno, strerror(errno));
        printf("%s",errorbuf);
    }
    else if (pid==0)
    {
        int fd;
        int n;
        char rdbuf[1024];
        fd = open("/tmp/fifowaittest",O_RDONLY);
        if((n=read(fd,rdbuf,1024))<0)
        {
            printf("read error. error is :%d,reason is %s\n", errno,strerror(errno));
            return -1;
        }
        rdbuf[n]=0;
        printf(rdbuf);
    }
    else
    {
        sleep(5);
        int fd;
        char wrbuf[]="message from parent\n";
        fd = open("/tmp/fifowaittest",O_WRONLY);
        if (write(fd,wrbuf,strlen(wrbuf))!=strlen(wrbuf))
        {
            printf("write error. error is :%d,reason is %s\n", errno,strerror(errno));
            return -1;
        }
        int status;
        if (wait(&status)==-1)
        {
            printf("wait error. error is :%d,reason is %s\n", errno,strerror(errno));
            return -1;
        }
        printf("exit status is %d\n",status);
    }

}

两种方法指定非阻塞。

。open 函数时指定O_NONBLOCK标志

。已经打开的用fcntl设置O_NONBLOCK标志。

非阻塞实例,当stdout为文件时,并没有错误,并一次性写完。当终端为stderr时,出现大量错误,并多次重写。

 

#include 
#include 
#include 
#include 
#include 
#include 

char buf[10000];

int setFileFlag(int fd, int setFlag)
{
    int flag =  fcntl(fd,F_GETFL,0);
    fcntl(fd, F_SETFL, flag |  setFlag);
    return 1;
}

int main()
{
    int  n,nwrite;
    n = read(STDIN_FILENO, buf, sizeof(buf));
    fprintf(stderr,"read %d bytes\n",n);
    setFileFlag(STDOUT_FILENO,O_NONBLOCK);
    char* ptr;
    for(ptr=buf;n>0;)
    {
        errno=0;
        nwrite= write(STDOUT_FILENO,ptr, n);
        fprintf(stderr,"\n n = %d, write %d bytes,errno is %d,reason is %s\n", n,nwrite,errno,strerror(errno));
        if (nwrite>0)
        {
            n= n- nwrite;
            ptr = ptr+nwrite;
        }
    }

}

2. 记录锁。

int fcntl(int fileds,int cmd,...)

cmd 为F_GETLK,F_SETLK或者F_SETLKW.第三个参数为指向flock结构的指针。
struck flock{
short l_type; /*F_RDLCK,F_WRLCK,or F_UNLCK*/
off_t l_start;
short l_whence;
off_t l_len;
pid_t l_pid;
}

l_start 和l_whence lseek一样。区域的长度,由l_len表示。

l_len为0,则锁尽可能大的长度。

l_start设置为0, i_whence设置为SEEK_SET,l_len为0, 则可锁所有文件。

读写锁:如果区域被上读锁,其他读锁依然可以加。但写锁不可以。上了写锁,则其他任何锁都不可以加。


加锁和测试锁的例子:

#include 
#include 
#include 
#include 

int lock_reg(int fd, int cmd, int type, off_t offset,int whence,off_t len)
{
    struct flock lock;
    lock.l_type = type;
    lock.l_start = offset;
    lock.l_whence = whence;
    lock.l_len = len;
    return (fcntl(fd,cmd,&lock));
}

pid_t lock_test(int fd,int type, off_t offset, int whence,off_t len)
{
    struct flock lock;
    lock.l_type = type;
    lock.l_start = offset;
    lock.l_whence = whence;
    lock.l_len= len;
    if(fcntl(fd,F_GETLK,&lock)<0)
        return -1;
    if(lock.l_type==F_UNLCK)
        return 0;
    return lock.l_pid;
}

int main()
{
    int fd = open("/tmp/log/violation.file.test.log",O_RDWR);
    pid_t pid = fork();
    if(pid<0)
    {
        printf("fork error");
        return -1;
    }
    else  if(pid>0)
    {
        lock_reg(fd,F_SETLK,F_WRLCK,0,SEEK_SET,0);
        printf("parent pid is %d\n",getpid());
        sleep(10);
    }
    else
    {
        sleep(5);
        pid_t p=lock_test(fd,F_WRLCK,0,SEEK_SET,0);
        printf("file locked by %d\n", p);
    }
}

锁与进程和文件两方面都相关。进程关闭后,锁将自动释放。dup 出一个fd,然后关闭dup 出来的fd,锁也将会被释放。

fork不能继承锁,exec可以继承锁。

关闭dup 出来后fd,锁释放的例子。

#include 
#include 
#include 
#include 

int lock_reg(int fd, int cmd, int type, off_t offset,int whence,off_t len)
{
    struct flock lock;
    lock.l_type = type;
    lock.l_start = offset;
    lock.l_whence = whence;
    lock.l_len = len;
    return (fcntl(fd,cmd,&lock));
}

pid_t lock_test(int fd,int type, off_t offset, int whence,off_t len)
{
    struct flock lock;
    lock.l_type = type;
    lock.l_start = offset;
    lock.l_whence = whence;
    lock.l_len= len;
    if(fcntl(fd,F_GETLK,&lock)<0)
        return -1;
    if(lock.l_type==F_UNLCK)
        return 0;
    return lock.l_pid;
}

int main()
{
    int fd = open("/tmp/log/violation.file.test.log",O_RDWR);
    pid_t pid = fork();  
    if(pid<0)  
    {  
        printf("fork error");  
        return -1;  
    }  
    else  if(pid>0)  
    {  
        lock_reg(fd,F_SETLK,F_WRLCK,0,SEEK_SET,0);  
        printf("child pid is %d\n",getpid());  
        sleep(5);  
        int fd2= dup(fd);
        close(fd2);
        sleep(10);  
        printf("parent end\n");  
    }  
    else  
    {  
        sleep(2);  
        pid_t p=lock_test(fd,F_WRLCK,0,SEEK_SET,0);  
        printf("file locked by %d\n", p);  
        sleep(6);  
        p=lock_test(fd,F_WRLCK,0,SEEK_SET,0);  
        printf("file locked by %d\n", p);  
    }  
}


3. 利用文件锁强制进程进行单例运行。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define PID_FILE "/var/run/single.pid"

int main()
{
    int fd =  open(PID_FILE,O_WRONLY|O_CREAT,0644); 
    if(fd<0)
    {
        printf("open pid file error\n");
        exit(-1);
    }

    struct flock lock;
    lock.l_type = F_WRLCK; 
    lock.l_start= 0; 
    lock.l_whence= SEEK_SET; 
    lock.l_len= 0; 
    int result = fcntl(fd,F_SETLK,&lock);
    if(result<0 && (errno==EACCES || errno==EAGAIN))
    {
        printf("We have running process,exit\n");
        exit(0);
    }

    if(ftruncate(fd,0)<0)
    {
        printf("truncate file error\n");
        exit(-1);
    }
    char buf[64];
    sprintf(buf, "%d\n",getpid());

    if(write(fd,buf,strlen(buf))!=strlen(buf))
    {
        printf("write error\n");
        exit(-1);
    }

    int val;
    if((val= fcntl(fd,F_GETFD,0))<0)
    {
        printf("fcntl error\n");
        exit(-1);
    }
    val = val | FD_CLOEXEC;
    if(fcntl(fd,F_SETFD,val)<0)
    {
        printf("fcntl set fd error\n");
        exit(-1);
    }
    

    //do something
    sleep(100);
    exit(0);



}


4. 建议性锁和强制性锁

建议性锁需要合作进程配合,不然起不到锁的作用。

强制性锁,通过关闭文件的组执行权限,并且打开组设置设置ID为来实现。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
    int fd;
    fd = open("tmplock",O_RDWR|O_CREAT | O_TRUNC ,0644);
    if(fd<0)
    {
        printf("open file error\n");
        return -1;
    }

    if(write(fd,"abcde",5)!=5)
    {
        printf("write error\n");
        return -1;
    }
    
    struct stat statbuf;
    if(fstat(fd,&statbuf)<0)
    {
        printf("fstat error\n");
        return -1;
    }

    if( fchmod(fd,(statbuf.st_mode & ~S_IXGRP)|S_ISGID)<0)
    {
        printf("fchmod error\n");
        return -1;
    }

    pid_t pid = fork();
    if(pid<0)
    {
        printf("fork procees error\n");
        return -1;
    }
    else if(pid>0)
    {
        struct flock lock;
        lock.l_start =0;
        lock.l_whence =SEEK_SET;
        lock.l_type = F_WRLCK;
        lock.l_len = 0;
        if(fcntl(fd,F_SETLK,&lock)<0)
        {
            printf("write lock error\n");
            exit(-1);
        }
        if (waitpid(pid,NULL,0)<0)
        {
            printf("wait pid error\n");
            exit(-1);
        }
        printf("parent end\n");
    }
    else
    {
        sleep(2);
        //set fd non blocking
        int flag=fcntl(fd,F_GETFL,0);
        flag = flag | O_NONBLOCK;
        fcntl(fd,F_SETFL,flag);
        //try set read lock
        struct flock lock;
        lock.l_start =0 ;
        lock.l_whence =SEEK_SET ;
        lock.l_type = F_RDLCK ;
        lock.l_len= 0;
        if(fcntl(fd,F_SETLK,&lock)!=-1)
        {
            printf("mandary lock do not work\n");
            exit(-1);
        }
        printf("mandary lock do work.errno =%d,error = %s\n",errno,strerror(errno));
        
        if(lseek(fd,0,SEEK_SET)<0)
        {
            printf("lseek error\n");
            exit(-1);
        }
        char buf[64];
        if(read(fd,buf,2)<0)
        {
            printf("read error.mandary lock work\n");
        }
        else
        {
            buf[2]=0;
            printf("mandary lock error,buf=%s\n",buf);
        }
        printf("child end\n");
    }
    exit(0);

}


5. 流

流提供用户进程和内核之间传递消息的一个全双工通道。


6. IO 多路复用 select函数

int select(int maxfd, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,struct timeval *tvptr)
tvptr 为null时候,表示永远等待。

一个select 接受socket的例子,只是demo作用,不能在产品中使用(因为还是千疮百孔的^_^)。编译运行后,调用wget http://127.0.0.1:6666/,服务器端就可以看到过来的http请求了。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define MAXLINE 4096
#define CONN_LIMIT 100
int main(int argc,char** argv)
{
    int listenfd,connfd;
    struct sockaddr_in servaddr;
    char buff[4096];
    int n;
    if((listenfd= socket(AF_INET,SOCK_STREAM,0))==-1)
    {
        printf("create socket error:%s, errno=%d\n", strerror(errno),errno);
        exit(-1);
    }
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr= htonl(INADDR_ANY);
    servaddr.sin_port= htons(8888);
    if((bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)))==-1)
    {
        printf("bind socket error:%s, errno=%d\n",strerror(errno),errno);
        exit(-1);
    }
    if (listen(listenfd,10)==-1)
    {
        printf("listen socket error:%s, errno=%d\n",strerror(errno),errno);
        exit(-1);
    }
    fd_set readfds,testfds;
    FD_ZERO(&readfds);
    FD_SET(listenfd,&readfds);
    int result;
    struct sockaddr_in client_address; 
    int nread;
    int fd;
    printf("===waiting for clients' request===\n");
    while(1)
    {
        testfds = readfds;
        result = select(CONN_LIMIT,&testfds,(fd_set*)0,(fd_set*)0,(struct timeval*)0);
        if(result<1)
        {
            printf("select error. reason is %s,errno = %d\n", strerror(errno),errno);
            exit(-1);
        }
        for(fd=0;fd


7. poll 的例子。

#include 
#include 
int poll(struct pollfd array[],unsigned long nfds,int timeout)
poll和select 不一样,poll锁构造一个pollfd数组,poofd定义了fd我们所要关心的条件。

struct pollfd

{

int fd;

short events;

short revents;

}


poll 的简单服务器demo:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MAX_CONNECTION 200
#define MAX_BUFF 4096
char buff[MAX_BUFF];

int main()
{
    int listenfd,connfd;
    struct sockaddr_in servaddr;
    if(  (listenfd=socket(AF_INET,SOCK_STREAM,0))==-1    )
    {
        printf("create socket error:%s,errno=%d\n",strerror(errno),errno);
        exit(-1);
    }
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family =AF_INET;
    servaddr.sin_addr.s_addr =htonl(INADDR_ANY);
    servaddr.sin_port = htons(8888);
    if( bind(listenfd,(struct sockaddr*)&servaddr, sizeof(servaddr))==-1   )
    {
        printf("bind socket error:%s,errno=%d\n",strerror(errno),errno);
        exit(-1);
    }
    if (listen(listenfd,10)==-1)
    {
        printf("listen socket error:%s,errno=%d\n",strerror(errno),errno);
        exit(-1);
    }

    struct pollfd fds[MAX_CONNECTION ];
    memset(fds,0,sizeof(fds));
    fds[0].fd = listenfd;
    fds[0].events = POLLIN;
    int i=0;
    for(i=1; isockMax)
               sockMax = i;
			continue;
        }
		for(i=1; i<=sockMax; i++)
		{
			if(fds[i].fd < 0)
                continue;
			if (fds[i].revents & (POLLIN | POLLERR))
			{
				int n = recv(fds[i].fd,buff,MAX_BUFF,0);
				buff[n]='\0';
				printf("recv msg from client:%s\n",buff);
				close(fds[i].fd);
				fds[i].fd = -1;
			}
		}
    }
    
}

8. epoll

从linux2.6.2后,引入了一个性能更为高效的epoll.

相对于select,有如下优势:

1. 无需轮询

2. epoll用共享内存的方式,避免了fd 的复制。

epoll的简单demo:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define MAX_CONNECTION 100
#define MAX_LINE 4096

int bindListenSocket(int port)
{
    int listenfd;
    if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)
    {
        printf("create socket error:%s,errno=%d\n", strerror(errno),errno);
        return -1;
    }
    struct sockaddr_in servaddr;
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family =AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);

    if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))==-1)
    {
        printf("bind socket error: %s,errno=%d\n",strerror(errno),errno);
        return -1;
    }
    if(listen(listenfd,10)==-1)
    {
        printf("listen socket error:%s,errno=%d\n",strerror(errno),errno);
        return -1;
    }

    return listenfd;

}

int setNoneBlock(int fd)
{
    int flag = fcntl(fd,F_GETFL,0);
    flag =flag| O_NONBLOCK;
    if(fcntl(fd,F_SETFL,flag)<0)
    {
        perror("fcntl to nonblock failed\n");
        return -1;
    }
    return 1;
}

int acceptAndAddFd(int listenfd,int epollfd)
{
    struct sockaddr_in remote_addr;
    int size = sizeof(struct sockaddr_in);
    int fd = accept(listenfd,(struct sockaddr*)&remote_addr,&size);
    if(fd==-1)
    {
        perror("accept socket error\n");
        return -1;
    }
    int ret = setNoneBlock(fd);
    if(ret==-1)
        return -1;
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = fd;
    if(epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev)==-1)
    {
        perror("epoll add error for client fd");
        return -1;
    }
    return 1;
}

int handlefd(int fd,int epollfd)
{
    char buf[MAX_LINE];
    int n = recv(fd,buf,MAX_LINE,0);
    buf[n] = '\0';
    printf("recv msg from client:%s\n", buf);
    const char str[] = "God bless you!\n";
    if (send(fd, str,  sizeof(str),  0) == -1)
    {
        perror("send error\n");
        return -1;
    }
    close(fd);
    return 1;

}

int main()
{
    int listenfd =bindListenSocket(8080);
    if (listenfd==-1)
        return -1;
    int nfds;
    struct epoll_event ev, events[MAX_CONNECTION];
    int epollfd = epoll_create(MAX_CONNECTION);
    if(epollfd ==-1)
    {
        perror("create epoll fd error.");
        exit(-1);
    }
    ev.events = EPOLLIN;
    ev.data.fd = listenfd;

    if(epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&ev)==-1)
    {
        perror("epoll add error.");
        exit(-1);
    }
    printf("serving...");
    int n =0;
    
    while(1)
    {
        nfds =epoll_wait(epollfd,events,MAX_CONNECTION,-1);
        if(nfds==-1)
        {
            perror("epoll error\n");
            exit(-1);
        }
        for(n=0;n

8. readv 和writev

提供了分散区域集中写, 分散区域集中读的功能,demo.

#include 
#include 
#include 
#include 
#include 
#include 

int main(void)
{
    struct iovec iov[2];
    char *buf1 = (char *)malloc(5);
    char *buf2 = (char *)malloc(1024);
    memset(buf1, 0, 5);
    memset(buf2, 0, 1024);
    iov[0].iov_base = buf1;
    iov[1].iov_base = buf2;
    iov[0].iov_len = 5;
    iov[1].iov_len = 1024;

    ssize_t nread, nwrite;
    nread = readv(STDIN_FILENO, iov, 2);
    if(nread == -1)
    {
        perror("readv error");
        return -1;
    }
    else
    {
        printf("readv:\n");
        printf("buf1 is: %s\t length is: %d\n",buf1, strlen(buf1));
        printf("buf2 is: %s\t length is: %d\n",buf2, strlen(buf2));
    }
    printf("writev:\n");
    nwrite = writev(STDOUT_FILENO, iov, 2);
    if(nwrite == -1)
    {
        perror("writev error");
        return -1;
    }
    free(buf1);
    free(buf2);
    exit(0);

}



你可能感兴趣的:([APUE]再读之高级IO)