-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsim.c
277 lines (244 loc) · 8.71 KB
/
sim.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/* Simulator for the protocols in chapter 3 of
* "Computer Networks, 3rd ed. by Andrew S. Tanenbaum.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include "common.h"
#define DEADLOCK (3 * timeout_interval) /* defines what a deadlock is */
#define MAX_PROTOCOL 6 /* highest protocol being simulated */
#define MANY 256 /* big enough to clear pipe at the end */
bigint tick = 0; /* the current time, measured in events */
bigint last_tick; /* when to stop the simulation */
int exited[2]; /* set if exited (for each worker) */
int hanging[2]; /* # times a process has done nothing */
struct sigaction act, oact;
/* Prototypes. */
void main(int argc, char *argv[]);
int parse_args(int argc, char *argv[]);
void set_up_pipes(void);
void fork_off_workers(void);
void terminate(char *s);
void sender2(void);
void receiver2(void);
void sender3(void);
void receiver3(void);
void protocol4(void);
void protocol5(void);
void protocol6(void);
void main(int argc, char *argv[])
{
/* The simulator has three processes: main, M0, and M1, all of which run
* independently. Set them all up first. Once set up, main maintains the
* clock (tick), and picks a process to run. Then it writes a 32-bit word
* to that process to tell it to run. The process sends back an answer
* when it is done. Main then picks another process, and the cycle repeats.
*/
int process = 0; /* whose turn is it */
int rfd, wfd; /* file descriptor for talking to workers */
bigint word; /* message from worker */
act.sa_handler = SIG_IGN;
setvbuf(stdout, (char *) 0, _IONBF, (size_t) 0); /* disable buffering*/
if (parse_args(argc, argv) < 0) exit(1); /* check args; store in mem */
set_up_pipes(); /* create five pipes */
fork_off_workers(); /* fork off the worker processes */
/* Main simulation loop. */
while (tick <last_tick) {
process = rand() & 1; /* pick process to run: 0 or 1 */
tick = tick + DELTA;
rfd = (process == 0 ? r4 : r6);
if (read(rfd, &word, TICK_SIZE) != TICK_SIZE) terminate("");
if (word == OK) hanging[process] = 0;
if (word == NOTHING) hanging[process] += DELTA;
if (hanging[0] >= DEADLOCK && hanging[1] >= DEADLOCK)
terminate("A deadlock has been detected");
/* Write the time to the selected process to tell it to run. */
wfd = (process == 0 ? w3 : w5);
if (write(wfd, &tick, TICK_SIZE) != TICK_SIZE)
terminate("Main could not write to worker");
}
/* Simulation run has finished. */
terminate("End of simulation");
}
int parse_args(int argc, char *argv[])
{
/* Inspect args on the command line and save them. */
if (argc != 7) {
printf("Usage: sim protocol events timeout loss cksum debug\n");
return(-1);
}
protocol = atoi(argv[1]);
if (protocol < 2 || protocol > MAX_PROTOCOL) {
printf("Protocol %d is not valid.\n", protocol);
return(-1);
}
/* Each event uses DELTA ticks to make it possible for each timeout to
* occur at a different tick. For example, with DELTA = 10, ticks will
* occur at 0, 10, 20, etc. This makes it possible for a timeout in
* protocol 5 to schedule multiple timeouts for the future, all at unique
* times, e.g. 1070, 1071, 1072, 1073, etc. This property is needed to
* make sure timers go off in the order they were set. As a consequence,
* internally, the variable tick is bumped by DELTA on each event. Thus
* asking for a simulation run of 1000 events will give 1000 events, but
* they internally they will be called 0 to 10,000.
*/
last_tick = DELTA * atol(argv[2]); /* each event uses DELTA ticks */
if ((long) last_tick < 0) {
printf("Number of simulation events must be positive\n");
return(-1);
}
/* Convert from external units to internal units so the user does not see
* the internal units at all.
*/
timeout_interval = DELTA * atoi(argv[3]);
if ((long)timeout_interval < 0 || (protocol > 2 && timeout_interval == 0) ){
printf("Timeout interval must be positive\n");
return(-1);
}
/* Packet loss takes place at the sender. Packets selected for being lost
* are not put on the wire at all. Internally, pkt_loss and garbled are
* from 0 to 990 so they can be compared to 10 bit random numbers. The
* inaccuracy here is about 2.4% because 1000 != 1024. In effect, it is
* not possible to say that all packets are lost. The most that can be
* be lost is 990/1024.
*/
pkt_loss = atoi(argv[4]); /* percent of sends that chuck pkt out */
if (pkt_loss < 0 || pkt_loss > 99) {
printf("Packet loss rate must be between 0 and 99\n");
return(-1);
}
pkt_loss = 10 * pkt_loss; /* for our purposes, 1000 == 1024 */
/* This arg tells what fraction of arriving packets are garbled. Thus if
* pkt_loss is 50 and garbled is 50, half of all packets (actually,
* 500/1024 of all packets) will not be sent at all, and of the ones that
* are sent, 500/1024 will arrive garbled.
*/
garbled = atoi(argv[5]);
if (garbled < 0 || garbled > 99) {
printf("Packet cksum rate must be between 0 and 99\n", garbled);
return(-1);
}
garbled = 10 * garbled; /* for our purposes, 1000 == 1024 */
/* Turn tracing options on or off. The bits are defined in worker.c. */
debug_flags = atoi(argv[6]);
if (debug_flags < 0) {
printf("Debug flags may not be negative\n", debug_flags);
return(-1);
}
printf("\n\nProtocol %d. Events: %u Parameters: %u %d %u\n", protocol,
last_tick/DELTA, timeout_interval/DELTA, pkt_loss/10, garbled/10,
debug_flags);
return(0); /* no errors in command line parameters */
}
void set_up_pipes(void)
{
/* Create six pipes so main, M0 and M1 can communicate pairwise. */
int fd[2];
pipe(fd); r1 = fd[0]; w1 = fd[1]; /* M0 to M1 for frames */
pipe(fd); r2 = fd[0]; w2 = fd[1]; /* M1 to M0 for frames */
pipe(fd); r3 = fd[0]; w3 = fd[1]; /* main to M0 for go-ahead */
pipe(fd); r4 = fd[0]; w4 = fd[1]; /* M0 to main to signal readiness */
pipe(fd); r5 = fd[0]; w5 = fd[1]; /* main to M1 for go-ahead */
pipe(fd); r6 = fd[0]; w6 = fd[1]; /* M1 to main to signal readiness */
}
void fork_off_workers(void)
{
/* Fork off the two workers, M0 and M1. */
if (fork() != 0) {
/* This is the Parent. It will become main, but first fork off M1. */
if (fork() != 0) {
/* This is main. */
sigaction(SIGPIPE, &act, &oact);
setvbuf(stdout, (char *)0, _IONBF, (size_t)0);/*don't buffer*/
close(r1);
close(w1);
close(r2);
close(w2);
close(r3);
close(w4);
close(r5);
close(w6);
return;
} else {
/* This is the code for M1. Run protocol. */
sigaction(SIGPIPE, &act, &oact);
setvbuf(stdout, (char *)0, _IONBF, (size_t)0);/*don't buffer*/
close(w1);
close(r2);
close(r3);
close(w3);
close(r4);
close(w4);
close(w5);
close(r6);
id = 1; /* M1 gets id 1 */
mrfd = r5; /* fd for reading time from main */
mwfd = w6; /* fd for writing reply to main */
prfd = r1; /* fd for reading frames from worker 0 */
switch(protocol) {
case 2: receiver2(); break;
case 3: receiver3(); break;
case 4: protocol4(); break;
case 5: protocol5(); break;
case 6: protocol6(); break;
}
terminate("Impossible. Protocol terminated");
}
} else {
/* This is the code for M0. Run protocol. */
sigaction(SIGPIPE, &act, &oact);
setvbuf(stdout, (char *)0, _IONBF, (size_t)0);/*don't buffer*/
close(r1);
close(w2);
close(w3);
close(r4);
close(r5);
close(w5);
close(r6);
id = 0; /* M0 gets id 0 */
mrfd = r3; /* fd for reading time from main */
mwfd = w4; /* fd for writing reply to main */
prfd = r2; /* fd for reading frames from worker 1 */
switch(protocol) {
case 2: sender2(); break;
case 3: sender3(); break;
case 4: protocol4(); break;
case 5: protocol5(); break;
case 6: protocol6(); break;
}
terminate("Impossible. protocol terminated");
}
}
void terminate(char *s)
{
/* End the simulation run by sending each worker a 32-bit zero command. */
int n, k1, k2, res1[MANY], res2[MANY], eff, acc, sent;
for (n = 0; n < MANY; n++) {res1[n] = 0; res2[n] = 0;}
write(w3, &zero, TICK_SIZE);
write(w5, &zero, TICK_SIZE);
sleep(2);
/* Clean out the pipe. The zero word indicates start of statistics. */
n = read(r4, res1, MANY*sizeof(int));
k1 = 0;
while (res1[k1] != 0) k1++;
k1++; /* res1[k1] = accepted, res1[k1+1] = sent */
/* Clean out the other pipe and look for statistics. */
n = read(r6, res2, MANY*sizeof(int));
k2 = 0;
while (res1[k2] != 0) k2++;
k2++; /* res1[k2] = accepted, res1[k2+1] = sent */
if (strlen(s) > 0) {
acc = res1[k1] + res2[k2];
sent = res1[k1+1] + res2[k2+1];
if (sent > 0) {
eff = (100 * acc)/sent;
printf("\nEfficiency (payloads accepted/data pkts sent) = %d%c\n", eff, '%');
}
printf("%s. Time=%u\n",s, tick/DELTA);
}
exit(1);
}