1. 非阻塞IO。
阻塞IO有如下情況
。 數據不存在, 則讀操作永遠阻塞。典型的爲 管道操作。
。 數據不能被立即接受,寫這樣的文件會被阻塞。
。 打開文件會被阻塞。(典型爲調制解調器。只寫方式打開FIFO,要等待一個讀進程)
。 對已經加上強制性鎖的文件進行讀寫。
。 ioctl 操作
。 某些進程間通信函數。 比如semophore的p,v 操作。
管道阻塞的demo,子進程等待5s後再寫,父進程阻塞5秒讀數據。
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
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 <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
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 <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
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 <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
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 <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
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 <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#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 <stdio.h>
#include <string.h>
#include <error.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#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<CONN_LIMIT;fd++)
{
if(!FD_ISSET(fd,&testfds))
continue;
if(fd==listenfd)
{
if((connfd = accept(listenfd,(struct sockaddr*)NULL,NULL))==-1)
{
printf("accept socket error.reason: %s, errno=%d", strerror(errno),errno);
continue;
}
else
{
FD_SET(connfd, &readfds);
printf("adding client on fd %d/n",connfd);
}
continue;
}
n = recv(fd,buff,MAXLINE,0);
buff[n]='\0';
printf("recv msg from client:%s\n",buff);
close(fd);
FD_CLR(fd, &readfds);
}
}
close(listenfd);
}
7. poll 的例子。
#include <stropts.h>
#include <poll.h>
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 <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <poll.h>
#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; i<MAX_CONNECTION ; i++)
{
fds[i].fd = -1;
}
int timeout=3000;
int sockMax = 0;
int ret;
while(1)
{
ret = poll(fds,sockMax+1,timeout);
if(ret<0)
{
printf("select error\n");
break;
}
else if(ret==0)
{
printf("timeout,continue\n");
continue;
}
if(fds[0].revents & POLLIN)
{
connfd = accept(listenfd,NULL,NULL);
if(connfd==-1)
{
printf("accept socket error.reason:%s,errno:%d",strerror(errno),errno);
continue;
}
for(i=1;i<MAX_CONNECTION ;i++)
{
if(fds[i].fd<0)
{
fds[i].fd =connfd;
break;
}
}
if(i==MAX_CONNECTION )
{
printf("connections overflow\n");
return -1;
}
fds[i].events = POLLIN;
if(i>sockMax)
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 <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <fcntl.h>
#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<nfds;n++)
{
int interestingFd = events[n].data.fd;
if(listenfd==interestingFd)
{
//accept
acceptAndAddFd(listenfd,epollfd);
continue;
}
//handle fd
handlefd(interestingFd,epollfd );
}
}
}
8. readv 和writev
提供了分散區域集中寫, 分散區域集中讀的功能,demo.
#include <sys/uio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
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);
}