-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathepoll-listen-fork.c
136 lines (122 loc) · 3.2 KB
/
epoll-listen-fork.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
* Create a listening socket, wait for connections using epoll with level- or
* edge-triggering, and fork a child for each epoll event that sleeps for a
* specified time then exits.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
void handle_error(const char *msg)
{
perror(msg);
exit(1);
}
void usage()
{
fprintf(stderr, "Usage: epoll-listen-fork [unix|inet] [path|port] [et|lt] sleep\n");
exit(1);
}
int main(int argc, const char * const argv[])
{
if (argc != 5) {
usage();
}
argv++;
const char *protocol = *argv++;
const char *port = *argv++;
const char *epoll_type = *argv++;
const int delay = atoi(*argv++);
struct sockaddr_un saddr_un;
struct sockaddr_in saddr_in;
struct sockaddr *saddr;
socklen_t addrlen;
if (strcmp(protocol, "unix") == 0) {
unlink(port);
memset(&saddr_un, 0, sizeof(saddr_un));
saddr_un.sun_family = AF_UNIX;
strncpy(saddr_un.sun_path, port, sizeof(saddr_un.sun_path)-1);
saddr = (struct sockaddr *) &saddr_un;
addrlen = sizeof(saddr_un);
}
else if (strcmp(protocol, "inet") == 0) {
memset(&saddr_in, 0, sizeof(saddr_in));
saddr_in.sin_family = AF_INET;
saddr_in.sin_port = htons(atoi(port));
if (inet_aton("0.0.0.0", &saddr_in.sin_addr) < 0) {
handle_error("inet_aton");
}
saddr = (struct sockaddr *) &saddr_in;
addrlen = sizeof(saddr_in);
}
else {
usage();
}
int epoll_flags;
if (strcmp(epoll_type, "lt") == 0) {
epoll_flags = EPOLLIN;
}
else if (strcmp(epoll_type, "et") == 0) {
epoll_flags = EPOLLIN | EPOLLET;
}
else {
usage();
}
// Ignore SIGCHLD.
signal(SIGCHLD, SIG_IGN);
// Create a listening socket.
int listener = socket(saddr->sa_family, SOCK_STREAM, 0);
if (listener < 0) {
handle_error("socket");
}
if (bind(listener, (struct sockaddr *) saddr, addrlen) < 0) {
handle_error("bind");
}
if (listen(listener, 1024) < 0) {
handle_error("listen");
}
// Set up epoll with edge triggering on listener.
struct epoll_event events[16];
const int epollfd = epoll_create(1024);
struct epoll_event event = { epoll_flags, 0 };
event.data.fd = listener;
epoll_ctl(epollfd, EPOLL_CTL_ADD, listener, &event);
int timeout = 1000;
int ret, total = 0;
int start = time(0);
while ((ret = epoll_wait(epollfd, events, sizeof(events) / sizeof(*events), timeout)) >= 0) {
int now = time(0);
int i;
if (ret > 0) {
printf("%03d: parent: %d event(s)\n", now-start, ret);
}
for (i = 0; i < ret; i++) {
printf("%03d: parent: forking child %d\n", now-start, ++total);
switch (fork()) {
case -1:
handle_error("fork");
break;
case 0:
// in the child, total is the child number
now = time(0);
printf("%03d: child %d (pid %d): started\n", now-start, total, getpid());
int sock = accept(listener, NULL, 0);
if (sock < 0) {
handle_error("accept");
}
now = time(0);
printf("%03d: child %d: accepted\n", now-start, total);
sleep(delay);
now = time(0);
printf("%03d: child %d: exiting\n", now-start, total);
exit(0);
}
}
}
return 0;
}