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);
}
}
#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);
}
}
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);
}
}
#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);
}
建议性锁需要合作进程配合,不然起不到锁的作用。
强制性锁,通过关闭文件的组执行权限,并且打开组设置设置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);
}
流提供用户进程和内核之间传递消息的一个全双工通道。
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
#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;
}
}
}
}
从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
提供了分散区域集中写, 分散区域集中读的功能,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);
}