[英]Linux epoll syscall, waiting though data available
使用簡單的父子程序測試Linux syscall epoll 。
預期行為
當孩子每秒寫一個no時,父母應該從管道讀取它,並每秒向stdout寫一個no。
實際行為
父級一直等到孩子寫完所有的no,然后從管道讀取所有數據並寫入stdout。 通過對父級執行strace進行驗證。 它在epoll_wait中阻塞。
父級
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <error.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define NAMED_FIFO "aFifo"
static void set_nonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
perror("fcntl()");
return;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("fcntl()");
}
}
void errExit(char *msg) {
perror(msg);
exit(-1);
}
void printArgs(char **argv,char **env) {
for(int i=0;argv[i];i++)
printf("argv[%d]=%s\n",i,argv[i]);
for(int i=0;env[i];i++)
printf("env[%d]=%s\n",i,env[i]);
}
void PrintNos(short int max,char *name) {
int fifo_fd,rVal;
int bSize=2;
char buffer[bSize];
fifo_fd = open(NAMED_FIFO,O_RDONLY);
if(fifo_fd<0)
errExit("open");
for(short int i=0;i<max;i++) {
rVal = read(fifo_fd,buffer,bSize);
if(rVal != bSize)
errExit("read");
printf("%03d\n",i);
}
}
int main(int argc, char *argv[],char *env[]) {
//int pipe_fds_child_stdin[2] ;
int pipe_fds_child_stdout[2] ;
pid_t child_id ;
//if( pipe(pipe_fds_child_stdin) < 0 )
// errExit("pipe");
if( pipe(pipe_fds_child_stdout) < 0 )
errExit("pipe");
child_id = fork();
if( child_id > 0 ) {
const int MAX_POLL_FDS = 2;
const int BUF_SIZE = 4;
size_t readSize;
char buf[BUF_SIZE];
int status;
int epoll_fd;
int nfds ;
struct epoll_event e_e, e_events[MAX_POLL_FDS];
memset(e_events,'\0',sizeof(e_events));
memset(&e_e,'\0',sizeof(e_e));
//close(pipe_fds_child_stdin[0]);
close(pipe_fds_child_stdout[1]);
epoll_fd = epoll_create1(0);
if(epoll_fd < 0)
errExit("epoll_create1");
e_e.data.fd = pipe_fds_child_stdout[0];
e_e.events = EPOLLIN;
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipe_fds_child_stdout[0], &e_e) < 0)
errExit("epoll_ctl");
while(1) {
nfds = epoll_wait(epoll_fd, e_events,MAX_POLL_FDS,-1);
if( nfds < 0)
errExit("epoll_wait");
for(int i=0;i<nfds;i++) {
if( e_events[i].data.fd == pipe_fds_child_stdout[0]) {
if( e_events[i].events & EPOLLIN) {
readSize = read(pipe_fds_child_stdout[0],buf,BUF_SIZE);
if( readSize == BUF_SIZE ) {
write(STDOUT_FILENO,buf,BUF_SIZE);
} else if(readSize == 0) { // eof
errExit("readSize=0");
} else {
errExit("read");
}
} else if( e_events[i].events & EPOLLHUP) {
printf("got EPOLLHUP on pipefd\n");
wait(&status);
exit(0);
} else {
errExit("Unexpected event flag returned by epoll_wait on waited fd");
}
} else {
errExit("epoll_wait returned non-awaited fd");
}
}
}
} else if( child_id == 0 ) {
close(0);
close(1);
//close(pipe_fds_child_stdin[1]);
close(pipe_fds_child_stdout[0]);
//dup2(pipe_fds_child_stdin[0],0);
dup2(pipe_fds_child_stdout[1],1);
execvpe(argv[1],&(argv[1]),env);
//PrintNos(100,"P");
//errExit("execvp");
} else {
errExit("fork");
}
}
兒童
import sys
import time
import os
#f=open("aFifo",'r')
for x in range(10):
#try:
# val = f.read(2)
#except Exception as e:
# raise
time.sleep(1)
print(f'{x:03d}')
這是由於python緩沖,可以通過將-u選項傳遞給python來禁用它。
經過大量搜索和研究,了解到這是由於管道緩沖區引起的。 盡管客戶端進行寫操作,但它在管道緩沖區中。 僅在管道緩沖區已滿后,內核才會在該描述符上發送就緒事件。 最小值為pagesize,內核不允許將其設置為該值以下。 但是可以增加。 通過將epoll更改為poll / select,可以做到這一點。 更改為輪詢/選擇后,行為是相同的。 盡管管道中有可用數據,但仍阻塞。
import fcntl
import os
F_SETPIPE_SZ=1031
fds = os.pipe()
for i in range(5):
print(fcntl.fcntl(fds[0],F_SETPIPE_SZ,64))
$ python3.7 pipePageSize.py
4096
4096
這是修改后的客戶端。 服務器也進行了適當的更改。
import time
pageSize=1024*8
for x in range(100):
time.sleep(0.5)
print(f'{x:-{pageSize}d}')
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.