Giao thức này là một giao thức đơn giản nhƣng về khía cạnh thời gian là phức tạp với các hoạt động đúng đắn của giao thức phụ thuộc vào khoảng thời gian [16].
Bài toán: Giao thức bao gồm 4 thành phần: thành phần gửi (Sender), thành phần nhận (Receiver), Kênh dữ liệu (kênh K), kênh truyền thông báo (L). Bài toán đặt ra cần thiết lập thời gian cho thành phần gửi để đảm bảo bên nhận đủ các bản tin từ bên gửi.
Đặc tả: Giao thức này đƣợc áp dụng trong giao thức tầng data-link một chiều trên các kênh truyền không đảm bảo có thể gián đoạn hay mất mát dữ liệu. Trong giao thức này có bốn thành phần trong thực hiên: bên gửi, bên nhận, kênh dữ liệu K và kênh thông báo L. Bên gửi nhận dữ liệu từ tầng trên và gửi dữ liệu, gán nhãn với một số đƣợc chuyển giữa o và 1 qua kênh K. Sau đó đợi nhận một thông báo từ kênh L. Nếu sau một khoảng thời gian không nhận đƣợc thông báo thì bên gửi hết thời gian và gửi lại dữ liệu cũ. Nếu dữ liệu nhận đƣợc không mất mát và đƣợc gán nhãn với số thứ tự mong muốn kênh nhận sẽ chuyền dữ liệu lên tầng trên và gửi một thông báo đến bên gửi qua kênh L.
Hình 5.1: Các thực thể của giao thức gửi và nhận song song
Theo quan sát ta thấy khoảng thời gian time-out của bên gửi nên lớn hơn tổng độ trễ trên các kênh và thời gian xử lý thông tin của tiến trình bên nhận. Nếu thiết lập thời gian chờ cho tiến trình gửi quá bé có thể là nguyên nhân gây mất mát khung dữ liệu theo kịch bản: Tiến trình gửi gửi một frame và timesout quá sớm. Điều này gây nên gửi lại frame dữ liệu đã đƣợc gửi trƣớc đó. Tiến trình nhận nhận đƣợc hai frame và gửi hai thông báo. Khi đó tiến trình gửi nhận đƣợc thông báo cho frame đầu tiên nó
nghĩ rằng thông báo của frame gửi thứ hai. Sau khi nhận đƣợc thông báo từ tiến trình nhận, tiến trình gửi sẽ gửi frame kế tiếp và frame này bị mất trên kênh truyền dữ liệu. Trong khi đó thông báo thứ hai đến và tiến trình gửi nhầm lẫn thông báo này là của frame vừa gửi vì vậy sẽ không gửi lại frame này.
Mô hình hóa giao thức với real-time Spin. Đầu tiên ta lần lƣợt định nghĩa độ trễ trên các kênh tƣơng ứng K, L, R. Hằng To là khoảng thời gian sender timeout sau khi gửi một tin đến bên nhận mà không nhận đƣợc thông báo thì sẽ tiếp tục gửi tin kế tiếp. Ta cũng khai báo các biến toàn cụ đồng hồ tƣơng ứng với các tiến trình bên gửi, bên nhận, kênh K, kênh L. #define dK 3 #define dL 3 #define dR 1 #define To 9 #define MAX 8 clock sc, rc, kc, lc;
Với mỗi thực thể của giao thức có một timer, chẳng hạn tiến trình gửi (sc) , tiến trình nhận (rc), kênh K (kc) và kênh L (lc). Mặc dù các timer đƣợc sử dụng cục bộ trong mô hình nhƣng đƣợc khai báo ở mức toàn cục để có thể đƣợc truy cập. Các định nghĩa của kênh StoK, KtoR, RtoL và LtoS có dung lƣợng kênh bằng 0 nghĩa là các kênh truyền đồng bộ. StoK, KtoR, RtoL và LtoS đƣợc sử dụng nhƣ các kênh phụ trợ, bởi các kênh không đƣợc mô hình hóa nhƣ proctype là các tiến trình riêng rẽ. Các kênh StoK và KtoR là đầu vào và ra của kênh K, RtoL và LtoS là đầu vào và ra của kênh L.
mtype = {data, ack, err}
chan StoK = [0] of {mtype, byte, bit}; chan KtoR = [0] of {mtype, byte, bit}; chan RtoL = [0] of {mtype, bit};
chan LtoS = [0] of {mtype, bit};
Trong mô hình để đơn giản tiến trình gửi tạo một tin với nội dung mt = (mt+1)%MAX. Tin cũng kèm theo một số thứ tự đƣợc gửi đến kênh K. Sự kiện này đƣợc gãn nhãn bởi việc thiết lập thời gian sc. Giao thức này với các tin bao gồm hai trƣờng – trƣờng kiểu byte và một trƣờng kiểu bit tƣơng ứng với nội dung tin và bit chuyển đổi. Bên gửi bao gồm 3 nhãn R_h thiết lập nôi dung tin, nhãn S_f thiết lập lại đồng hồ và bắt đầu gửi dữ liệu. Nhãn W_s với 3 lựa chọn dựa trên các ràng buộc nếu đồng hồ đạt đến ngƣỡng timeout thì sẽ tiếp tục gửi lại bản tin cũ. Nếu nhận đƣợc thông báo xác nhận từ kênh L thì sẽ thiết lập lại bit chuyển đổi và tiếp tục gửi tin mới.
Hàm reset nhằm khởi tạo lại đồng hồ sc. Ngƣợc lại nhận đƣợc thông báo lỗi sẽ in thông báo lỗi và nhảy đến trạng thái để gửi tiếp bản tin.
Trong trƣờng hợp đầu một lựa chọn không xác định giữa một thông báo nhận đƣợc bị gián đoạn và chấp nhận một thông báo đúng.
proctype Sender(chan in, out) {
byte mt; /* message data */ bit sn=0; /* sequence number*/ R_h: mt = (mt+1)%MAX;
S_f: reset{sc} out!data(mt,sn); W_s: do
:: when {sc < To} reset{sc} in?ack(1)-> /*delay before sending new message*/ atomic {
when{sc==1} reset{sc}
sn=1-sn;
goto R_h
};/*ack received*/
/* the last reset is not necessery */ :: atomic{
when {sc < To} reset{sc} in?err(0); printf("MSC: ACKerr\n"); goto S_f }; /*ack error*/ :: when{sc==To} reset{sc} (1==1); goto S_f; /*timeout*/ od; }
Tiến trình Kênh K nhằm giả lập việc nhận thông tin từ sender và lựa chọn không xác định chuyển tin đên receiver hay gửi lỗi đến receiver.
proctype ChannelK(chan in, out) {
byte mr; bit sn; ready_K:
reset{kc} in?data(mr,sn); if
:: when {kc==dK} reset{kc} printf("MSC: OK\n"); when {kc==0} reset{kc} out!data(mr,sn);
:: when {kc==dK} reset{kc} /*printf("MSC: ERR\n"); when {kc==0} reset{kc}*/ out!err(mr,sn);
:: when {kc==dK} reset{kc} skip; fi; /* end_K: */ atomic { when {kc==dK} (2==2); goto ready_K } }
Tƣơng tự tiến trình kênh L gửi tin với chỉ một bit – thông báo nhận đƣợc hay thông báo lỗi. Hai tiến trình kênh K và L có cùng cấu trúc – sau độ trễ kênh không xác định chọn việc truyền tin hay mất tin. Lệnh skip là luôn luôn có thể thực hiện. Nó đƣợc sử dụng với thứ tự bằng nhau giữa hai lựa chọn.
proctype ChannelL(chan in, out) {
ready_L:
reset {lc} in?ack(1); if
:: when {lc==dL} reset {lc} out!ack(1); :: when {lc==dL} reset {lc} out!err(0); :: when {lc==dL} reset {lc} skip;
fi; /* end_L: */ atomic { when {lc==dL} (3==3); goto ready_L } }
Tiến trình nhận bắt đầu bằng viêc đợi một tin gửi đến bởi câu in?data(mr,rsn) thông qua việc nhận trên kênh KtoR. Lệnh nhận có thể thực ngay khi có thể và trong cùng khoảng thời gian (slice) khi tiến trình gửi tƣơng ứng sẵn sàng gửi. Sau nhãn S_h lệnh assert đƣợc sử dụng để đảm bảo tin nhận đƣợc là tin đúng. Độ trễ xử lý tin đƣợc mô hình bởi lệnh delay trên timer của tiến trình nhận. Tin nhận và số trình tự mong muốn đƣợc cập nhật. Việc thông báo nhận thành công đƣợc xác nhận bằng cách gửi một tin qua kênh L (trong Promela kênh C) và sau độ trễ tiến trình nhận đợi tiếp cho một tin mới.
proctype Receiver(chan in, out) {
byte mr; , me=1;
bit rsn, esn=0; /*received and expected sequence numbers*/
W_f: if
::reset{rc} in?data(mr,rsn)-> if
:: when{rc==0} reset{rc} rsn==esn -> atomic {when{rc==0} reset{rc} (4==4); goto S_h}
:: when{rc==0} reset{rc} rsn==1-esn -> atomic {when{rc==0} reset{rc}
(5==5); goto S_a} fi;
:: reset{rc} in?err(mr,rsn) ->
when{rc==0}reset{rc} printf("MSC: MSGerr\n"); atomic {when{rc==0} (6==6); goto W_f}
fi;
S_h: when{rc==dR} reset{rc} assert(mr == me); esn = 1-esn; me = (me+1)%MAX};
S_a: when {rc==0} reset{rc} out!ack(1);
atomic {when {rc==1} (7==7); goto W_f} }
Tất cả các tiến trình đƣợc bắt đầu từ một tiến trình đặc biệt đƣợc gọi là init với lệnh run. Các lệnh run đƣợc thực hiện trong atomic nhằm đảm bảo các tiến trình đƣợc thực hiện đồng bộ.
init {
atomic{
run Sender(LtoS, StoK); run ChannelK(StoK, KtoR); run ChannelL(RtoL, LtoS); run Receiver(KtoR, RtoL); };
}
Kiểm chứng: Trong mô hình đƣợc thực hiện nhƣ trong RtSpin. Spin có thể tìm ra và hiển thị kịch bản mất một tin. Tiến trình làm việc hợp lý nếu To > dK+dL+dR. tuannt@HN-N90713-07 ~/RTSpin/examples
$ rtspin -a par.pml
tuannt@HN-N90713-07 ~/RTSpin/examples $ make tpan
gcc -DNOREDUCE -DXUSAFE -DSAFETY -
I/home/tuannt/RTSpin/examples -I/home/tuannt/RTSpin/Timers -c -o pan.o pan.c
tuannt@HN-N90713-07 ~/RTSpin/examples $ ./tpan
error: max search depth too small (Spin Version 2.9.0 -- 14 July 1996) Warning: Search not completed
Full statespace search for:
never-claim +
assertion violations + (if within scope of claim) cycle checks - (disabled by -DSAFETY) invalid endstates - (disabled by never-claim) State-vector 40 byte, depth reached 9999, errors: 0
537493 states, stored 351527 states, matched
889020 transitions (= stored+matched) 270071 atomic steps
hash conflicts: 340710 (resolved) (max size 2^18 states)
1.84559e+07 memory usage (bytes) Clock Regions Hashed : 95188
Collisions : 95121
tuannt@HN-N90713-07 ~/RTSpin/examples
Với RtSpin số trạng thái là rất lớn. Ta cũng thử nghiệm tƣơng tự với DtSpin cho cùng kết quả nhƣng không gian trạng thái ít hơn rất nhiều.
spin -a ParTime.pml
gcc-4 -DMEMLIM=1024 -O2 -DXUSAFE -DSAFETY -DNOCLAIM -w -o pan pan.c ./pan -m10000 -E
Pid: 1380
+ Partial Order Reduction Full statespace search for:
never claim - (not selected) assertion violations +
cycle checks - (disabled by -DSAFETY) invalid end states - (disabled by -E flag) State-vector 80 byte, depth reached 640, errors: 0 1202 states, stored
148 states, matched
1350 transitions (= stored+matched) 1377 atomic steps
hash conflicts: 0 (resolved) Stats on memory usage (in Megabytes):
0.105 equivalent memory usage for states (stored*(State-vector + overhead))
0.283 actual memory usage for states 64.000 memory used for hash table (-w24) 0.343 memory used for DFS stack (-m10000) 64.539 total actual memory usage