高级I/O
一、主要函数:select,poll,epoll,readv,writev
二、
1 、select,poll,epoll 的作用就是监视多个I/O. 如果不希望被某些信号打断可用pselect,ppoll.
2 、select
每次用select 之前都要构造一下。当监视读一个写端关闭的进程时,select 总是能返回。
3 、poll
POLLIN 读,POLLOUT 写
struct pollfd fds[2];
nfds = 2;
fds[0].fd = 0;
fds[0].events = POLLIN;
fds[1].fd = fd[0];
fds[1].events = POLLIN;
ret = poll(fds, 2, -1);//-1 表示永远等待
......
如果第三个参数用了timeout ,那么当超时的时候返回0.
每执行一次poll ,fds 都要被拷到内核处理,所以select 和poll 效率低。
4 、epoll
第一步:epoll_create();
epfd=epoll_create(1), 创建一个event poll, 返回一个指向这个池的文件描述符,它里面的参数只要大于0 就行,如果为-1 的话就会死掉。
第二步:epoll_ctl();
epoll_ctl(epfd,EPOLL_CTL_ADD,fd[i][0],&event); 用 EPOLL_CTL_ADD 添加关心的事件到池中
第三步:epoll_wait();
struct epoll_event event[2];
while(1)
{
epoll_wait(epfd,event,2,-1);// 最后一个参数用-1 表示永远等待
....// 如果返回来的不只2 两个,通过while 循环下一次过来再取
}
5 、readv,writev 分散读,聚集写
ssize_t readv(int fd, const struct iovec *iov, int iovcnt) ;
第二个参数iov 和第三个参数iovcnt 组成一个结构体数组iov[iovcnt];
int main(void)
{
int fd, i;
char *ptr[3] = {
struct iovec iov[3];
for (i = 0; i < 3; i++) {
iov[i].iov_base = ptr[i];
iov[i].iov_len = 6;
}
writev(1, iov, 3);
exit(0);
}
6 、SIGIO 信号 F_SETOWN, O_ASYNC
1)fcntl(fd,F_SETOWN,getpid())
2)fcntl(fd,F_SETFL,ret|O_ASYNC) 其中ret=fcntl(fd,F_GETFL)
3)sigaction(SIGIO,&act,NULL)
三、例程
1 、/*select.c*//*监视多个I/O*/
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void con_fds(fd_set *set, int fd)
{
FD_ZERO(set);
FD_SET(0, set);
FD_SET(fd, set);
}
int main(void)
{
int ret, nfds, cnt;
char buf[88];
fd_set readfds;
int fd[2];
pid_t pid;
ret = pipe(fd);
if (ret == -1) {
perror("pipe");
exit(1);
}
pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
}
if (pid == 0) {
close(fd[0]);
srand(time(NULL));
sleep(rand()%5 + 5);
write(fd[1], "Hello!", 6);
//while(1)
sleep(1);
close(fd[1]);
exit(0);
}
close(fd[1]);
nfds = fd[0]+1;
while (1) {
con_fds(&readfds, fd[0]);
ret = select(nfds, &readfds, NULL,
NULL, NULL);
if (ret > 0) {
printf("ret=%d/n", ret);
if (FD_ISSET(0, &readfds)) {
cnt = read(0, buf, 88);
buf[cnt] = '/0';
printf("%s/n", buf);
}
if (FD_ISSET(fd[0], &readfds)) {
cnt = read(fd[0], buf, 88);
buf[cnt] = '/0';
printf("%s/n", buf);
}
}
if (ret == -1) {
perror("select");
exit(1);
}
}
}
/*1、本程序有两个关心的fd: 0和fd[0],监视这两个I/O,哪个有数据就了都要读
2、select:在每次调用select之前要构造,con_fds,构造所关心的fd。
3、声明一个描述符集之后,必须用FD_ZERO清除其所有们,然后在其中设置我们关心的各个位。
4、select的第一个参数是所关心的fd中"最大的描述符加1".一般我们不关心后三个参数,
所以都置为NULL
*/
2 、
/*poll.c*/
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
int main(void)
{
int ret, nfds, cnt;
char buf[88];
int fd[2];
pid_t pid;
struct pollfd fds[2];
ret = pipe(fd);
if (ret == -1) {
perror("pipe");
exit(1);
}
pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
}
if (pid == 0) {
close(fd[0]);
srand(time(NULL));
sleep(rand()%5 + 5);
write(fd[1], "Hello!", 6);
while(1)
sleep(1);
close(fd[1]);
exit(0);
}
close(fd[1]);
nfds = 2;//2是所关心描述符的个数
fds[0].fd = 0;
fds[0].events = POLLIN;//关心输入,也就是关心"写"
fds[1].fd = fd[0];
fds[1].events = POLLIN;
while (1) {
ret = poll(fds, 2, -1);
if (ret > 0) {
printf("ret=%d/n", ret);
if (fds[0].revents & POLLIN) {
cnt = read(fds[0].fd,
buf, 88);
buf[cnt] = '/0';
printf("%s/n", buf);
}
if (fds[1].revents & POLLIN) {
cnt = read(fds[1].fd,
buf, 88);
buf[cnt] = '/0';
printf("%s/n", buf);
}
}
if (ret == -1) {
perror("poll");
exit(1);
}
}
}
/*1、poll比select略胜一点,不用在每次调用之前都构造一次
2、 struct pollfd fds[2];
3、if (fds[0].revents & POLLIN)
*/
3 、
/*epoll.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
#include <string.h>
#include <poll.h>
#include <sys/epoll.h>
#define PIPENUM 5
int fd[PIPENUM][2];
int creat_pipe(void)//创建5个管道
{
int i, ret;
for (i = 0; i < PIPENUM; i++) {
ret = pipe(fd[i]);
if (ret == -1) {
perror("pipe");
return -1;
}
}
return 0;
}
int con_readfds(void)//构造事件池
{
int i, epfd;
struct epoll_event event;
epfd = epoll_create(1);
if (epfd == -1) {
perror("epoll_create");
return epfd;
}
for (i = 0; i < PIPENUM; i++) {
event.events = EPOLLIN;
event.data.fd = fd[i][0];
epoll_ctl(epfd, EPOLL_CTL_ADD, fd[i][0],
&event);
}
return epfd;
}
void father_close_fds(void)
{
int i;
for (i = 0; i < PIPENUM; i++) {
close(fd[i][1]);
}
}
void child_close_fds(int index)
{
int i;
for (i = 0; i < PIPENUM; i++) {
close(fd[i][0]);
if (i != index) {
close(fd[i][1]);
}
}
}
void child_work(int i)
{
char buf[100];
child_close_fds(i);
srand(getpid());
bzero(buf, 100);
sprintf(buf, "I am pid %d!", getpid());
while (1) {
sleep(rand() % (PIPENUM) + 6);
write(fd[i][1], buf, strlen(buf));
}
}
static void father_actual_work(struct epoll_event *event)
{
int i, ret;
char buf[100];
for (i = 0; i < 2; i++) {
if (event[i].events & EPOLLIN) {
ret = read(event[i].data.fd, buf, 100);
buf[ret] = '/0';
printf("%s/n", buf);
}
}
}
void father_work()
{
int nfds, ret, epfd;
struct epoll_event event[2];
father_close_fds();
epfd = con_readfds();
while (1) {
ret = epoll_wait(epfd, event, 2, -1);
if (ret > 0) {
father_actual_work(event);
}
if (ret == -1) {
perror("select");
return;
}
}
}
int main(void)
{
int ret, i;
pid_t pid;
ret = creat_pipe();
if (ret == -1)
exit(1);
for (i = 0; i < PIPENUM; i++) {
pid = fork();
if (pid == 0) {
child_work(i);
exit(0);
}
}
father_work();
exit(0);
}
/*1、epoll_create(1)创建event poll;
2、epoll_ctl(epfd, EPOLL_CTL_ADD, fd[i][0],&event);*/
4 、
/*select and pipe*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
#include <string.h>
#define PIPENUM 5
int fd[PIPENUM][2];
fd_set readfds;
int creat_pipe(void) // 产生5 个管道
{
int i, ret;
for (i = 0; i < PIPENUM; i++) {
ret = pipe(fd[i]);
if (ret == -1) {
perror("pipe");
return -1;
}
}
return 0;
}
void con_readfds(void )// 构造
{
int i;
FD_ZERO(&readfds);
for (i = 0; i < PIPENUM; i++) {
FD_SET(fd[i][0], &readfds); // 关心的是fd[i][0]
}
return;
}
int get_max_fds(void )// 取最大描述符加1
{
int i, max = 0;
for (i = 0; i < PIPENUM; i++) {
if (max < fd[i][0])
max = fd[i][0];
}
return max+1;
}
void father_close_fds(void)
// 父进程关闭5 个管道的写端
{
int i;
for (i = 0; i < PIPENUM; i++) {
close(fd[i][1]);
}
}
void child_close_fds(int index)
// 子进程关闭除自己以外的写端
{
int i;
for (i = 0; i < PIPENUM; i++) {
close(fd[i][0]);
if (i != index) {
close(fd[i][1]);
}
}
}
void child_work(int i)
{
char buf[100];
child_close_fds(i);
srand(getpid());
bzero(buf, 100);
sprintf(buf, "I am pid %d!", getpid());
while (1) {
sleep(rand() % (PIPENUM) + 6);
write(fd[i][1], buf, strlen(buf));
}
}
static void father_actual_work(void)
{
int i, ret;
char buf[100];
for (i = 0; i < PIPENUM; i++) {
if (FD_ISSET(fd[i][0], &readfds)) { // 不能写成fd[i][1]!
ret = read(fd[i][0], buf, 100);
buf[ret] = '/0';
printf("%s/n", buf);
}
}
}
void father_work()
{
int nfds, ret;
father_close_fds();
nfds = get_max_fds();
while (1) {
con_readfds(); // 每次用select 之前都要构造一下,设置你关心的描述符
ret = select(nfds, &readfds, NULL,
NULL, NULL);
if (ret > 0) {
father_actual_work();
}
if (ret == -1) {
perror("select");
return;
}
}
}
int main(void)
{
int ret, i;
pid_t pid;
ret = creat_pipe();
if (ret == -1)
exit(1);
for (i = 0; i < PIPENUM; i++) {
pid = fork();
if (pid == 0) {
child_work(i);
exit(0);
}
}
father_work();
exit(0);
}
/*select和pipe,监视5个管道,子进程写,父进程读*/
5 、
/*poll and pipe*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
#include <string.h>
#include <poll.h>
#define PIPENUM 5
int fd[PIPENUM][2];
struct pollfd fds[PIPENUM];
int creat_pipe(void)
{
int i, ret;
for (i = 0; i < PIPENUM; i++) {
ret = pipe(fd[i]);
if (ret == -1) {
perror("pipe");
return -1;
}
}
return 0;
}
void con_readfds(void)
{
int i;
for (i = 0; i < PIPENUM; i++) {
fds[i].fd = fd[i][0];
fds[i].events = POLLIN;
}
return;
}
void father_close_fds(void)
{
int i;
for (i = 0; i < PIPENUM; i++) {
close(fd[i][1]);
}
}
void child_close_fds(int index)
{
int i;
for (i = 0; i < PIPENUM; i++) {
close(fd[i][0]);
if (i != index) {
close(fd[i][1]);
}
}
}
void child_work(int i)
{
char buf[100];
child_close_fds(i);
srand(getpid());
bzero(buf, 100);
sprintf(buf, "I am pid %d!", getpid());
while (1) {
sleep(rand() % (PIPENUM) + 6);
write(fd[i][1], buf, strlen(buf));
}
}
static void father_actual_work(void)
{
int i, ret;
char buf[100];
for (i = 0; i < PIPENUM; i++) {
if (fds[i].revents & POLLIN) {
ret = read(fds[i].fd, buf, 100);
buf[ret] = '/0';
printf("%s/n", buf);
}
}
}
void father_work()
{
int nfds, ret;
father_close_fds();
con_readfds();
while (1) {
ret = poll(fds, PIPENUM, -1);
if (ret > 0) {
father_actual_work();
}
if (ret == -1) {
perror("select");
return;
}
}
}
int main(void)
{
int ret, i;
pid_t pid;
ret = creat_pipe();
if (ret == -1)
exit(1);
for (i = 0; i < PIPENUM; i++) {
pid = fork();
if (pid == 0) {
child_work(i);
exit(0);
}
}
father_work();
exit(0);
}
/*poll 和pipe ,监视5 个管道, 子进程写,父进程读*/
6 、
/*writev.c*/
#include <sys/uio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd, i;
char *ptr[3] = {
"china:",
"korea:",
"japan1"
};
struct iovec iov[3];
for (i = 0; i < 3; i++) {
iov[i].iov_base = ptr[i];
iov[i].iov_len = 6;
}
writev(1, iov, 3);
printf("/n");
exit(0);
}
7 、
/*readv.c*/
#include <sys/uio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd, i;
struct iovec iov[3];
for (i = 0; i < 3; i++) {
iov[i].iov_base = malloc(32);
iov[i].iov_len = 32;
}
fd = open("/etc/passwd", O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
readv(fd, iov, 3);
for (i = 0; i < 3; i++) {
printf("%s/n", (char *)iov[i].iov_base);
}
close(fd);
exit(0);
}
8 、
/*sigio.c*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
int fd[2];
void async_read(int s)
{
char buf[32];
int ret = read(fd[0], buf, 32);
write(1, buf, ret);
printf("/n");
}
int main(void)
{
struct sigaction act;
int ret;
act.sa_handler = async_read;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
sigaction(SIGIO,&act, NULL);
ret = pipe(fd);
if (ret == -1) {
perror("pipe");
exit(1);
}
fcntl(fd[0], F_SETOWN, getpid());
ret = fcntl(fd[0], F_GETFL);
fcntl(fd[0], F_SETFL, O_ASYNC);//???fcntl(fd[0], F_SETFL,ret | O_ASYNC)
while (1) {
sleep(3+rand()%3);
write(fd[1], "Hello", 5);
}
}
/*
SIGIO信号:这个信号可以通知某种事件已发生
为了接收SIGIO信号,需执行下列几步:
1、sigaction(SIGIO,&act, NULL);为SIGIO信号建立信号处理程序
2、fcntl(fd[0], F_SETOWN, getpid());以F_SETOWN调用fcntl来设置进程ID和进程组ID,它们将接收对于该描述符的信号。
3、fcntl(fd[0], F_SETFL,ret | O_ASYNC);以 F_SETFL调用fcntl设置O_ASYNC文件状态标志,使在该描述符上可以进行异步I/O.
*/