通过经典的client-server模型演示一下epoll监听pipe的用法。模型如下图。
首先创建用于通信的命名管道,三个producer-client管道,一个client-server管道。
int cp[N_THREAD]; //client-producer-pipe描述符
//创建并打开client-producer-pipe
for(int i = 0; i < N_THREAD; ++i)
{
char path[128] = {0};
sprintf(path, "%s%d", PRODUCER_CLIENT, i);
mkfifo(path, 0666);
cp[i] = open(path, O_RDWR);
}
//创建并打开client-server-pipe
mkfifo(CLIENT_SERVER, 0666);
int cs = open(CLIENT_SERVER, O_WRONLY);
创建producer子线程。
void* producer(void *arg)
{
int id = *((int*)arg);
char buf[DATA_SIZE] = {0};
while(1){
sleep(rand() % 3 + 1);
int data = rand() % 1000;
sprintf(buf, "%d", data);
write(cp[id], buf, sizeof(buf));
printf("id:%d data:%d\n", id, data);
memset(buf, 0, sizeof(buf));
}
}
//创建生产者子线程
pthread_t tid[N_THREAD];
int id[N_THREAD]; //线程标识
for(int i = 0; i < N_THREAD; ++i)
{
id[i] = i;
pthread_create(&tid[i], NULL, producer, id + i);
}
创建epoll,注册监听事件。注册监听事件分为三部,设置监听事件标识符,设置事件触发条件,注册设置好的事件。
//创建epoll
int epfd = epoll_create(N_THREAD + 1);
struct epoll_event event[N_THREAD + 1];
for(int i = 0; i < N_THREAD; ++i)
{
event[i].data.fd = cp[i];
event[i].events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, cp[i], event + i);
}
event[N_THREAD].data.fd = cs;
event[N_THREAD].events = EPOLLOUT;
epoll_ctl(epfd, EPOLL_CTL_ADD, cs, event + N_THREAD);
监听epoll。epoll_wait监听到事件相关信息会记录到wait_event中,可根据监听到的标识符选择不同的应对方式,返回值为监听到的事件个数。
//监听epoll
struct epoll_event wait_event[N_THREAD + 1];
char buf[DATA_SIZE] = {0};
while(1){
int n = epoll_wait(epfd, wait_event, N_THREAD + 1, -1);
for(int i = 0; i < n; ++i)
{
if(wait_event[i].data.fd == cs)
{
if(!isEmpty(&buffer))
{
int data;
pop(&buffer, &data);
memset(buf, 0, sizeof(buf));
sprintf(buf, "%d", data);
write(cs, buf, sizeof(buf));
}
}
else
{
memset(buf, 0, sizeof(buf));
read(wait_event[i].data.fd, buf, sizeof(buf));
if(!isFull(&buffer)){
int data;
sscanf(buf, "%d", &data);
push(&buffer, data);
}
}
}
}
server部分与client部分类似。下面放出完整代码。
//client.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <sys/syscall.h>
#define PRODUCER_CLIENT "./producer_client"
#define CLIENT_SERVER "./client_server"
#define N_THREAD 3 //线程数
#define DATA_SIZE 5 //数据大小
#define BUFFER_SIZE 20//缓冲区大小
//定义缓冲区
typedef struct Queue{
int rear;
int front;
int elem[BUFFER_SIZE];
}Queue;
void initQueue(Queue*);
int push(Queue*, int);
int pop(Queue*, int*);
int isEmpty(Queue*);
int isFull(Queue*);
void* producer(void*);
int cp[N_THREAD]; //client-producer-pipe描述符
int main(int argc, char *argv[])
{
Queue buffer;
initQueue(&buffer);
//创建并打开client-producer-pipe
for(int i = 0; i < N_THREAD; ++i)
{
char path[128] = {0};
sprintf(path, "%s%d", PRODUCER_CLIENT, i);
mkfifo(path, 0666);
cp[i] = open(path, O_RDWR);
}
//创建并打开client-server-pipe
mkfifo(CLIENT_SERVER, 0666);
int cs = open(CLIENT_SERVER, O_WRONLY);
//创建生产者子线程
pthread_t tid[N_THREAD];
int id[N_THREAD]; //线程标识
for(int i = 0; i < N_THREAD; ++i)
{
id[i] = i;
pthread_create(&tid[i], NULL, producer, id + i);
}
//创建epoll
int epfd = epoll_create(N_THREAD + 1);
struct epoll_event event[N_THREAD + 1];
for(int i = 0; i < N_THREAD; ++i)
{
event[i].data.fd = cp[i];
event[i].events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, cp[i], event + i);
}
event[N_THREAD].data.fd = cs;
event[N_THREAD].events = EPOLLOUT;
epoll_ctl(epfd, EPOLL_CTL_ADD, cs, event + N_THREAD);
//监听epoll
struct epoll_event wait_event[N_THREAD + 1];
char buf[DATA_SIZE] = {0};
while(1){
int n = epoll_wait(epfd, wait_event, N_THREAD + 1, -1);
for(int i = 0; i < n; ++i)
{
if(wait_event[i].data.fd == cs)
{
if(!isEmpty(&buffer))
{
int data;
pop(&buffer, &data);
memset(buf, 0, sizeof(buf));
sprintf(buf, "%d", data);
write(cs, buf, sizeof(buf));
}
}
else
{
memset(buf, 0, sizeof(buf));
read(wait_event[i].data.fd, buf, sizeof(buf));
if(!isFull(&buffer)){
int data;
sscanf(buf, "%d", &data);
push(&buffer, data);
}
}
}
}
for(int i = 0; i < N_THREAD; ++i)
{
pthread_join(tid[i], NULL);
}
for(int i = 0; i < N_THREAD; ++i)
{
close(cp[i]);
}
close(cs);
return 0;
}
void* producer(void *arg)
{
int id = *((int*)arg);
char buf[DATA_SIZE] = {0};
while(1){
sleep(rand() % 3 + 1);
int data = rand() % 1000;
sprintf(buf, "%d", data);
write(cp[id], buf, sizeof(buf));
printf("id:%d data:%d\n", id, data);
memset(buf, 0, sizeof(buf));
}
}
void initQueue(Queue* q)
{
memset(q, 0, sizeof(Queue));
}
int push(Queue* q, int data)
{
if(isFull(q))
return 0;
q->elem[q->rear] = data;
q->rear = (q->rear + 1) % BUFFER_SIZE;
return 1;
}
int pop(Queue* q, int* data)
{
if(isEmpty(q))
return 0;
*data = q->elem[q->front];
q->front = (q->front + 1) % BUFFER_SIZE;
return 1;
}
int isEmpty(Queue* q)
{
return q->rear == q->front;
}
int isFull(Queue* q)
{
return (q->rear + 1) % BUFFER_SIZE == q->front;
}
//server.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <sys/syscall.h>
#define SERVER_CONSUMER "./server_consumer"
#define CLIENT_SERVER "./client_server"
#define N_THREAD 3 //线程数
#define DATA_SIZE 5 //数据大小
#define BUFFER_SIZE 20 //缓冲区大小
//定义缓冲区
typedef struct Queue{
int rear;
int front;
int elem[BUFFER_SIZE];
}Queue;
void initQueue(Queue*);
int push(Queue*, int);
int pop(Queue*, int*);
int isEmpty(Queue*);
int isFull(Queue*);
void* consumer(void*);
int fd[N_THREAD]; //server-consumer-pipe描述符
int main(int argc, char *argv[])
{
Queue buffer;
initQueue(&buffer);
//创建并打开server-consumer-pipe
for(int i = 0; i < N_THREAD; ++i)
{
char path[128] = {0};
sprintf(path, "%s%d", SERVER_CONSUMER, i);
mkfifo(path, 0666);
fd[i] = open(path, O_RDWR);
}
//打开client-server-pipe(由client创建)
int cs = open(CLIENT_SERVER, O_RDONLY);
//创建消费者子线程
pthread_t tid[N_THREAD];
int id[N_THREAD]; //线程标识
for(int i = 0; i < N_THREAD; ++i)
{
id[i] = i;
pthread_create(&tid[i], NULL, consumer, id + i);
}
//创建epoll
int epfd = epoll_create(N_THREAD + 1);
struct epoll_event event[N_THREAD + 1];
for(int i = 0; i < N_THREAD; ++i)
{
event[i].data.fd = fd[i];
event[i].events = EPOLLOUT;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd[i], event + i);
}
event[N_THREAD].data.fd = cs;
event[N_THREAD].events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, cs, event + N_THREAD);
//监听epoll
struct epoll_event wait_event[N_THREAD + 1];
while(1){
int n = epoll_wait(epfd, wait_event, N_THREAD + 1, -1);
char buf[DATA_SIZE] = {0};
for(int i = 0; i < n; ++i)
{
if(wait_event[i].data.fd == cs)
{
memset(buf, 0, sizeof(buf));
read(cs, buf, sizeof(buf));
if(!isFull(&buffer)){
int data;
sscanf(buf, "%d", &data);
push(&buffer, data);
}
}
else
{
if(!isEmpty(&buffer))
{
int data;
pop(&buffer, &data);
memset(buf, 0, sizeof(buf));
sprintf(buf, "%d", data);
write(wait_event[i].data.fd, buf, sizeof(buf));
}
}
}
}
for(int i = 0; i < N_THREAD; ++i)
{
pthread_join(tid[i], NULL);
}
for(int i = 0; i < N_THREAD; ++i)
{
close(fd[i]);
}
return 0;
}
void* consumer(void *arg)
{
int id = *((int*)arg);
char buf[DATA_SIZE] = {0};
while(1){
memset(buf, 0, sizeof(buf));
read(fd[id], buf, sizeof(buf));
sleep(rand() % 3 + 1);
int data;
sscanf(buf, "%d", &data);
printf("id:%d data:%d\n", id, data);
}
}
void initQueue(Queue* q)
{
memset(q, 0, sizeof(Queue));
}
int push(Queue* q, int data)
{
if(isFull(q))
return 0;
q->elem[q->rear] = data;
q->rear = (q->rear + 1) % BUFFER_SIZE;
return 1;
}
int pop(Queue* q, int* data)
{
if(isEmpty(q))
return 0;
*data = q->elem[q->front];
q->front = (q->front + 1) % BUFFER_SIZE;
return 1;
}
int isEmpty(Queue* q)
{
return q->rear == q->front;
}
int isFull(Queue* q)
{
return (q->rear + 1) % BUFFER_SIZE == q->front;
}