Các câu lệnh gửi và nhận của kênh được đặt trong hàng đợi FIFO. Sơ đồ trên hình 4.5 chỉ ra rằng kênh đã xuất hiện với hai thông điệp đã sẵn sàng để gửi tới kênh. Câu lệnh gửi được thực hiện vì có một chỗ trong kênh cho các thông điệp, kênh không bị đầy (not full); và thực thi câu lệnh được đặt trong thông điệp ở cuối hàng đợi. Câu lệnh nhận có thể thực hiện được bởi vì có các thông điệp khác trong kênh, khi kênh không rỗng (not empty); thì thực hiện câu lệnh, gỡ bỏ đi thông điệp ở đầu hàng đợi và gán giá trị của nó cho các biến trong lệnh nhận.
Kênh là một phần của các trạng thái tính toán. Các câu lệnh gửi và nhận là các thực thi nguyên tử.
Khi kênh bị đầy, thì có thể xảy ra hiện tượng mất các thông điệp thay vì là khóa kênh không sử dụng.
Để kiểm tra một kênh là đầy hay rỗng ta dùng các hàm tiền định nghĩa để kiểm tra: full và empty, và hàm ngược của chúng là nfull và nempty.
Để kiểm tra số thông điệp trong một kênh ch thì ta dùng hàm len(ch), hàm này trả về kết quả là số thông điệp trong kênh ch.
d. So sánh kênh gặp và kênh đệm
Kênh gặp và kênh đệm đều có những ưu nhược điểm riêng, tùy vào hoàn cảnh sử dụng mà ta dùng kiểu tương ứng. Ta so sánh kênh gặp và kênh đệm theo một số tiêu chí.
Về sự hiệu quả, kênh gặp hiệu quả hơn. Không có biến kết hợp với kênh gặp, nên nó không làm tăng không gian trạng thái. Kênh đệm thì làm tăng không gian trạng thái vì mỗi hoán vị của thông điệp làm tăng dung lượng kênh và các thông điệp cũng có nhiều trường trong đó.
Về mặt cảm quan, thì kênh đệm chỉ tiện lợi hơn bởi vì nó có thể cài đặt cùng với các lệnh lặp và một quá trình thêm vào để lưu nội dung kênh. Kênh đệm cũng hỗ trợ việc mô hình hóa trực tiếp các hệ thống không đồng bộ nơi mà xử lý việc truyền dữ liệu mà không bị khóa.
Khi dùng kênh đệm thì cần lưu ý đến dung lượng của kênh và không gian trạng thái phải đủ nhỏ để phép kiểm chứng khả thi.
4.1.4. Một số hàm đặc biệt
Promela có cung cấp cho người dùng một số hàm, phép toán đặc biệt để hỗ trợ cho việc mô hình hóa và kiểm chứng được dễ dàng hơn. Đó là các hàm assert,
timeout, d_step và atomic.
a. Assert
Assert là hàm dùng để xác nhận tính đúng dắn của các đặc tả. Một đánh giá
assert có thể nằm giữa hai câu lệnh bất kỳ của chương trình và bộ kiểm chứng sẽ đánh giá nó như một phần của không gian tìm kiếm. Nếu trong quá trình tìm kiếm, nó tìm thấy một tính toán gây ra giả thiết sai, làm chương trình bị sai, hoặc xác nhận không biểu diễn đúng chính xác một thuộc tính mà đã được coi là của chương trình.
Cấu trúc của hàm assert:
int max, a = 5, b = 3;
assert (a > b) max = a;
printf (“Max of 2 numbers: %d\n”, max)
Chỉ khi hàm assert cho biểu thức trong dấu ngoặc trả về giá trị true thì các câu lệnh sau đó mới được thực hiện, nếu nó trả về giá trị false thì chương trình sẽ ngừng lại và đưa ra thông báo lỗi. Như ở ví dụ trên, vì a thực sự lớn hơn b (5 > 3) nên hàm assert trả về giá trị true, và lệnh gán giá trị max = a được thực hiện và in ra giá trị lớn hơn trong 2 số a và b.
b. Timeout
Trong Spin, trạng thái hết giờ (timeout) có thể được phát hiện ra. Biến tiền định nghĩa Boolean timeout đúng khi và chỉ khi không có các lệnh có thể thực thi được
trong bất kỳ quá trình nào. Timeoutgiúp chương trình thoát ra khỏi các vòng lặp vô hạn, khi mà các tiến trình vẫn đang active nhưng không được thực hiện nữa.
Bằng việc phát hiện ra timeout, chúng ta có thể sắp xếp để in ra một thông điệp khi mà một chuỗi bị từ chối. Ví dụ: ở đây, ta thêm một quá trình Watchdog với 1 câu lệnh in ra.
active proctype Watchdog() {
timeout -> printf(“Rejected … \n”) }
Biểu thức timeout sẽ không được thực hiện cho đến khi quá trình chính bị khóa; sau đó nó sẽ được thực thi và lệnh printf sẽ luôn được thực hiện.
c. d_step và atomic
Mỗi câu lệnh của Promela đều được thực thi nguyên tử. Nhưng việc mô hình các câu lệnh nguyên tử phức tạp hơn là một câu lệnh đơn. Thực thi một chuỗi câu lệnh một cách nguyên tử có nghĩa là khi các câu lệnh này bắt đầu được thực hiện thì nó sẽ được thực hiện từ đầu đến cuối mà không bị các quá trình khác xen vào. Có hai cách để tạo ra các chuỗi nguyên tử của các câu lệnh là d_step (là cách viết rút gọn của deterministic step – bước quyết định) và atomic.
atomic { !wantQ;
wantP = true; }
Hai câu lệnh !wantQ và wantP = true được khai báo là nằm trong một thực thi nguyên tử atomic. Vậy nên 2 câu lệnh này sẽ được thực hiện tuần tự và không bị các quá trình khác xen vào cho đến khi kết thúc một atomic.
Ưu điểm của d_step là hàm này đặc biệt hiệu quả với các câu lệnh của chuỗi được thực thi hoặc kiểm tra như một bước đơn trong một tổng thể xác định đầy đủ. Mặc dù vậy d_step có ba nhược điểm sau:
Ngoại trừ câu lệnh đầu tiên trong chuỗi, thì các câu lệnh đều không bị khóa. Việc nhảy (jump) trong chuỗi hoặc nhảy ra khỏi chuỗi bởi lệnh goto và
break là không hợp lệ.
Tính không xác định luôn được giải quyết bằng cái chọn giá trị đúng đầu tiên trong một lệnh được bảo vệ (guarded command).
Một chuỗi d_step luôn luôn được thực thi như là một lệnh đơn lẻ, trong khi
atomic thường được dùng để khởi tạo mỗi chuỗi các tiến trình, và các tiến trình trong đó sẽ không được thực hiện cho đến tất cả các tiến trình được khởi tạo xong.
4.1.5. Biểu diễn các thuộc tính thời gian tuyến tính
a. Biểu diễn và kiểm chứng thuộc tính an toàn
Thuộc tính an toàn được biểu diễn trong LTL như sau: Với A là một công thức LTL và có là một tính toán. , đọc là luôn luôn A là đúng trong trạng thái si khi và chỉ khi A đúng với tất cả trạng thái sj trong với j >= i [3].
Nếu đúng trong một trạng thái s, thì A cũng phải đúng trong s. Công thức []A được gọi là thuộc tính an toàn bởi vì nó đặc tả rằng một tính toán là an toàn nếu không có gì “xấu” xảy ra, tương đương với là những điều xảy ra đều “tốt”.
Ví dụ về một chương trình xử lý tương tranh.
active proctype P() { do :: wantP = true; !wantQ; critical++; critical--; wantP = false; od
} /*Tương tự với tiến trình Q */
Trong Promela ta có một số cách để biểu diễn thuộc tính an toàn. Ví dụ như:
#define mutex (critical <= 1)
Biểu thức trên tương đương với công thức LTL: []mutex
Chúng ta cũng có thể định nghĩa hai biến csp và csq có kiểu Boolean, và gán giá trị cho các biến này khi một tiến trình tiến vào đoạn găng:
active proctype P(){
do
:: wantP = true; !wantQ;
csp = true; csp = false; wantP = false;
od } /* Với tiến trình Q cũng code tương tự*/ Công thức LTL mà chúng ta phải chứng minh bây giờ là: []!(csp && csq)
Cách thứ ba để biểu diễn thuộc tính này là không sử dụng các biến ghost (ghost variables).
Thuộc tính an toàn có thể được kiểm chứng bằng việc lựa chọn tùy chọn kiểm chứng tính an toàn trong iSPIN. Ngoài ra ta cũng có thể kiểm chứng bằng dòng lệnh như sau:
spin –a –f “[]mutex” > safety.ltl
b. Biểu diễn thuộc tính hoạt động được
A là một công thức LTL và là một tính toán thì A, đọc là tồn tại A là đúng trong trạng thái si khi và chỉ khi A đúng với sj bất kỳ trong thỏa mãn j i [3].
Toán tử này là một toán tử phản thân, vì vậy nếu A là đúng trong một trạng thái s, thì ta có A. Công thức A được gọi là thuộc tính hoạt động được bởi vì nó đặc tả cho mệnh đề điều “tốt” tồn tại trong tính toán.
Thông thường thì các đặc tả tính đúng đắn có chứa các thuộc tính hoạt động được bởi vì một thuộc tính an toàn đã được thỏa mãn bởi một chương trình rỗng – chương trình không làm gì cả. Ví dụ, một giải pháp cho vấn đề đoạn găng là không có tiến trình nào cố gắng tiến vào đoạn găng thì sẽ thỏa mãn thuộc tính đúng đắn của tương tranh và sự vắng mặt của khóa chết.
Thuộc tính hoạt động được sẽ được kiểm chứng trong iSPIN bằng cách chọn các tùy chọn “acceptance cycles” và “weak fairness” cũng phải được đặc tả khi chương trình được kiểm chứng. Ngoài ra, nó có thể được chứng minh bằng dòng lệnh với tuỳ chọn –a (acceptance) và –f (weak fairness):
spin –a –f “!<>csp” forth-liveness.pml gcc –o pan pan.c
pan –a -f
c. Biểu diễn tính công bằng
Một tính toán là công bằng yếu (weakly fair) khi và chỉ khi điều kiện sau đúng: nếu một câu lệnh luôn luôn là thực thi được, thì nó tồn tại khả năng thực thi như một phần của phép tính toán [3].
Thuộc tính này được chứng minh trong iSPIN bằng cách đánh dấu vào ô tùy chọn “Weakly fairness” hoặc là kiểm chứng bằng dòng lệnh khi dùng thêm tham số -f.
d. Tính đối ngẫu
Phép toán và là đối ngẫu theo một các tương tự như phép đối ngẫu trong biểu thức deMorgan:
Tương tự, ta cũng có những phép biến đổi toán tử thời gian đơn thân thành các công thức khác tương đương
4.2. Kiểm chứng bằng công cụ SPIN
4.2.1. Công cụ SPIN
Công cụ kiểm chứng SPIN được phát triển tại phòng thí nghiệm Bell Labs từ những năm 1980 của thế kỷ trước, và ngày nay nó vẫn được sử dụng rộng rãi. Công cụ tiếp tục được phát triển trong nhiều năm, thu hút nhiều nhóm người dùng trong cả lĩnh vực học thuật và công nghiệp. Năm 2002, SPIN được Hiệp hội máy tính Mỹ (Association for Computing Manchinery- ACM) trao Phần thưởng Hệ thống phần mềm (Software System Award) cho những cống hiến của nó với nền khoa học [6].
Hiện nay, công cụ SPIN đã phát triển đến phiên bản 6.2.7 được cập nhật vào ngày 02/03/2014 cùng với giao diện iSPIN phiên bản 1.1.1 cập nhật vào ngày 22/02/2014. Phiên bản iSPIN cho phép người dùng sử dụng SPIN với giao diện đồ họa trên hệ điều hành Windows.
Công cụ iSPIN cung cấp cho chúng ta các chức năng chính để kiểm chứng một chương trình Promela. Đó là các chức năng về kiểm tra cú pháp của chương trình, kiểm tra sự dư thừa trong chương trình, thiết lập bảng biểu tương ứng, vẽ mô hình automat của chương trinh; mô phỏng, mô phỏng ngẫu nhiên, mô phỏng tương tác, mô phỏng theo hướng dẫn – dùng để chạy lại các phản ví dụ và định vị lỗi; kiểm chứng một chương trình, sinh một bộ kiểm chứng, biên dịch bộ kiểm chứng, thực thi việc kiểm chứng và một số tùy chọn khác.
Hình 4.6 mô tả giao diện chính cùng các chứ cnăng của công cụ iSPIN. Trong đó có các chức năng là Edit/ View, Simulate/ Replay, Verification, Swarm Run, Help, Save Session, Restore Session và Quit. Ở màn hình Edit/ View có các chức năng thông thường như là Open – mở một tệp tin Promela plm, ReOpen – mở lại một tệp tin, Save – lưu tệp tin, Save as – lưu tệp tin với tên khác, Find – tìm kiếm trong tệp tin đang mở. iSPIN cũng cung cấp cho người dùng chức năng Syntax Check – kiểm tra cú pháp của tệp tin, Redundant Check – kiểm tra tính dư thừa trong các tệp tin Promela, Symbol Table – hiển thị bảng các biểu tượng được dùng, Automata View – chức năng hiển thị automata của chương trình (chức năng này chỉ sử dụng được khi cài thêm phần mềm Graphviz).