IO模型
1 阻塞IO
1.效率高,当没有IO事件时,任务被挂起,不占用CPU资源
2.多个阻塞IO放在一起,会导致相互受到影响
示例程序:
1. 头文件
#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
2. makefile
all:client server
client:client.c
gcc $^ -o $@
server:server.c
gcc $^ -o $@
.PHONY:
clean:
rm client server
3. client.c
#include "head.h"
int main(int argc, char const *argv[])
{
int ret = 0;
int sockfd = 0;
int cnt = 0;
ssize_t nsize = 0;
char tmpbuff[4096] = {0};
struct sockaddr_in seraddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
perror("fail to socket");
return -1;
}
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(50000);
seraddr.sin_addr.s_addr = inet_addr("192.168.0.161");
ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
if (-1 == ret)
{
perror("fail to connect");
return -1;
}
while (1)
{
sprintf(tmpbuff, "hello world ---- %d", cnt);
cnt++;
nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);
if (-1 == nsize)
{
perror("fail to send");
return -1;
}
memset(tmpbuff, 0, sizeof(tmpbuff));
nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);
if (-1 == nsize)
{
perror("fail to recv");
return -1;
}
else if (0 == nsize)
{
printf("服务端断开连接!\n");
break;
}
printf("RECV:%s\n", tmpbuff);
sleep(1);
}
close(sockfd);
return 0;
}
4. server.c
#include "head.h"
int main(int argc, char const *argv[])
{
int ret = 0;
int confd = 0;
int sockfd = 0;
ssize_t nsize = 0;
char tmpbuff[4096] = {0};
struct sockaddr_in seraddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
perror("fail to socket");
return -1;
}
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(50000);
seraddr.sin_addr.s_addr = inet_addr("192.168.0.161");
ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
if (-1 == ret)
{
perror("fail to bind");
return -1;
}
ret = listen(sockfd, 10);
if (-1 == ret)
{
perror("fail to listen");
return -1;
}
while (1)
{
confd = accept(sockfd, NULL, NULL);
if (-1 == confd)
{
perror("fail to accept");
return -1;
}
while (1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
if (-1 == nsize)
{
perror("fail to recv");
return -1;
}
else if (0 == nsize)
{
printf("客户端退出!\n");
break;
}
printf("RECV:%s\n", tmpbuff);
sprintf(tmpbuff, "%s ---- echo", tmpbuff);
nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
if (-1 == nsize)
{
perror("fail to send");
return -1;
}
}
close(confd);
}
close(sockfd);
return 0;
}
2 非阻塞IO——(fcntl、NONBLOCK)
1.效率低,当没有IO事件时,CPU一直轮询判断是否有IO事件发生
2.可以解决多个阻塞导致程序编写顺序性的问题
示例程序:
1. 头文件
#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
2. makefile
all:send recv
send:send.c
gcc $^ -o $@
recv:recv.c
gcc $^ -o $@
.PHONY:
clean:
rm send recv
3. client.c
#include "head.h"
int main(int argc, char const *argv[])
{
int fd = 0;
char tmpbuff[4096] = {0};
mkfifo("/tmp/myfifo", 0777);
fd = open("/tmp/myfifo", O_WRONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
while (1)
{
gets(tmpbuff);
write(fd, tmpbuff, strlen(tmpbuff));
}
close(fd);
return 0;
}
4. server.c
#include "head.h"
int main(int argc, char const *argv[])
{
int fd = 0;
int flags = 0;
ssize_t nret = 0;
char *pret = NULL;
char stdbuff[4096] = {0};
char fifobuff[4096] = {0};
mkfifo("/tmp/myfifo", 0777);
fd = open("/tmp/myfifo", O_RDONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
flags = fcntl(fd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
flags = fcntl(0, F_GETFL);
flags |= O_NONBLOCK;
fcntl(0, F_SETFL, flags);
while (1)
{
memset(fifobuff, 0, sizeof(fifobuff));
nret = read(fd, fifobuff, sizeof(fifobuff));
if (nret > 0)
{
printf("FIFO:%s\n", fifobuff);
}
memset(stdbuff, 0, sizeof(stdbuff));
pret = gets(stdbuff);
if (pret != NULL)
{
printf("STDIN:%s\n", stdbuff);
}
}
close(fd);
return 0;
}
3 异步IO——(信号)
1.效率高,当有IO事件发生时,内核会向应用层发送SIGIO信号,此时可以操作IO事件
2.只能操作一个文件描述符,多个文件描述符无法区分
示例程序:
1. 头文件
#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
2. makefile
all:send recv
send:send.c
gcc $^ -o $@
recv:recv.c
gcc $^ -o $@
.PHONY:
clean:
rm send recv
3. client.c
#include "head.h"
int main(int argc, char const *argv[])
{
int fd = 0;
char tmpbuff[4096] = {0};
mkfifo("/tmp/myfifo", 0777);
fd = open("/tmp/myfifo", O_WRONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
while (1)
{
gets(tmpbuff);
write(fd, tmpbuff, strlen(tmpbuff));
}
close(fd);
return 0;
}
4. server.c
#include "head.h"
int fd = 0;
void handler(int signo)
{
char fifobuff[4096] = {0};
memset(fifobuff, 0, sizeof(fifobuff));
read(fd, fifobuff, sizeof(fifobuff));
printf("FIFO:%s\n", fifobuff);
return;
}
int main(int argc, char const *argv[])
{
int flags = 0;
ssize_t nret = 0;
char *pret = NULL;
char stdbuff[4096] = {0};
signal(SIGIO, handler);
mkfifo("/tmp/myfifo", 0777);
fd = open("/tmp/myfifo", O_RDONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
flags = fcntl(fd, F_GETFL);
flags |= O_ASYNC;//设置异步信号中断
fcntl(fd, F_SETFL, flags);
fcntl(fd, F_SETOWN, getpid());
while (1)
{
memset(stdbuff, 0, sizeof(stdbuff));
gets(stdbuff);
printf("STDIN:%s\n", stdbuff);
}
close(fd);
return 0;
}
4 多路复用IO——(高并发:多路IO(边沿触发)+线程)
4.1 select——谁产生事件了,谁留在文件描述符集合中,没产生的踢出去(用户层)
(1) select
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
功能:
监听IO事件
参数:
nfds:最大文件描述符个数+1
readfds:读文件描述符集合
writefds:写文件描述符集合
exceptfds:其余文件描述符集合
timeout:最大允许阻塞等待的时间
NULL:永远阻塞等待
返回值:
成功返回产生事件的个数
失败返回-1
等待时间到达没有产生的事件返回0
(2)void FD_CLR(int fd, fd_set *set);
将fd文件描述符从set集合中清除
(3)int FD_ISSET(int fd, fd_set *set);
判断fd是否仍在set集合中
(4)void FD_SET(int fd, fd_set *set);
将fd加入文件描述符集合set中
(5)void FD_ZERO(fd_set *set);
将文件描述符集合清0
示例程序:
1. 头文件
#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
2. makefile
all:send recv
send:send.c
gcc $^ -o $@
recv:recv.c
gcc $^ -o $@
.PHONY:
clean:
rm send recv
3. client.c
#include "head.h"
int main(int argc, char const *argv[])
{
int fd = 0;
char tmpbuff[4096] = {0};
mkfifo("/tmp/myfifo", 0777);
fd = open("/tmp/myfifo", O_WRONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
while (1)
{
gets(tmpbuff);
write(fd, tmpbuff, strlen(tmpbuff));
}
close(fd);
return 0;
}
4. server.c
#include "head.h"
int main(int argc, char const *argv[])
{
int fd = 0;
int flags = 0;
ssize_t nret = 0;
char *pret = NULL;
char stdbuff[4096] = {0};
char fifobuff[4096] = {0};
fd_set rdfds;
fd_set tmpfds;
int maxfd = 0;
int nready = 0;
mkfifo("/tmp/myfifo", 0777);
fd = open("/tmp/myfifo", O_RDONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
FD_ZERO(&rdfds);
FD_SET(fd, &rdfds);
FD_SET(0, &rdfds);
maxfd = fd;
tmpfds = rdfds;
while (1)
{
tmpfds = rdfds;
nready = select(maxfd+1, &tmpfds, NULL, NULL, NULL);
if (-1 == nready)
{
perror("fail to nready");
return -1;
}
if (FD_ISSET(fd, &tmpfds))
{
memset(fifobuff, 0, sizeof(fifobuff));
read(fd, fifobuff, sizeof(fifobuff));
printf("FIFO:%s\n", fifobuff);
}
if (FD_ISSET(0, &tmpfds))
{
memset(stdbuff, 0, sizeof(stdbuff));
gets(stdbuff);
printf("STDIN:%s\n", stdbuff);
}
}
return 0;
}
4.3 epoll——(内核层)
(1)epoll_create
int epoll_create(int size);
功能:
创建一个用来监听事件的内核事件表
参数:
size:事件表中事件个数
返回值:
成功返回文件描述符
失败返回-1
(2)epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:
管理内核事件表
参数:
epfd:事件表文件描述符
op:
EPOLL_CTL_ADD:添加
EPOLL_CTL_DEL:删除
fd:文件描述符
event:事件信息
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 */
};
EPOLLIN 读事件
EPOLLOUT 写事件
EPOLLET 边沿触发(默认为水平触发)
返回值:
成功返回0
失败返回-1
(3)epoll_wait
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
功能:
等待事件发生
参数:
epfd:事件表文件描述符
events:存放产生事件的数组
maxevents:最多存放事件的个数
timeout:超时时间
返回值:
成功返回产生事件的个数
失败返回-1
时间到达没有事件产生返回0
示例程序:
1. 头文件
#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#endif
2. makefile
all:send recv
send:send.c
gcc $^ -o $@
recv:recv.c
gcc $^ -o $@
.PHONY:
clean:
rm send recv
3. recv.c
#include "head.h"
int main(int argc, char const *argv[])
{
int fd = 0;
int flags = 0;
ssize_t nret = 0;
char *pret = NULL;
char stdbuff[4096] = {0};
char fifobuff[4096] = {0};
int epfd = 0;
int ret = 0;
int nready = 0;
struct epoll_event env;
struct epoll_event retenv[2];
int i = 0;
mkfifo("/tmp/myfifo", 0777);
fd = open("/tmp/myfifo", O_RDONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
epfd = epoll_create(2);
if (-1 == epfd)
{
perror("fail to epoll_create");
return -1;
}
env.events = EPOLLIN;
env.data.fd = fd;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &env);
if (-1 == ret)
{
perror("fail to epoll_ctl");
return -1;
}
env.events = EPOLLIN;
env.data.fd = 0;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &env);
if (-1 == ret)
{
perror("fail to epoll_ctl");
return -1;
}
while (1)
{
nready = epoll_wait(epfd, retenv, 2, -1);
if (-1 == nready)
{
perror("fail to epoll_wait");
return -1;
}
for (i = 0; i < nready; i++)
{
if (retenv[i].data.fd == fd)
{
memset(fifobuff, 0, sizeof(fifobuff));
read(fd, fifobuff, sizeof(fifobuff));
printf("FIFO:%s\n", fifobuff);
}
else if (retenv[i].data.fd == 0)
{
memset(stdbuff, 0, sizeof(stdbuff));
gets(stdbuff);
printf("STDIN:%s\n", stdbuff);
}
}
}
close(epfd);
return 0;
}
4. send.c
#include "head.h"
int main(int argc, char const *argv[])
{
int fd = 0;
char tmpbuff[4096] = {0};
mkfifo("/tmp/myfifo", 0777);
fd = open("/tmp/myfifo", O_WRONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
while (1)
{
gets(tmpbuff);
write(fd, tmpbuff, strlen(tmpbuff));
}
close(fd);
return 0;
}