2010-01-13 28 views
39

Tôi có nhiều ứng dụng được biên dịch bằng g ++, chạy trong Ubuntu. Tôi đang sử dụng các semaphores có tên để phối hợp giữa các quá trình khác nhau.Làm cách nào để khôi phục một semaphore khi quá trình giảm xuống không bị treo?

Tất cả hoạt động tốt trừ trong tình hình sau: Nếu một trong những quá trình gọi sem_wait() hoặc sem_timedwait() để giảm các semaphore và sau đó bị treo hoặc bị giết -9 trước khi nó được một cơ hội để gọi sem_post(), sau đó từ thời điểm đó trên , semaphore được đặt tên là "không sử dụng được".

Bởi "không sử dụng được", ý tôi là số đếm semaphore giờ là 0, và quá trình cần tăng lên trở lại 1 đã chết hoặc bị giết.

Tôi không thể tìm thấy API sem_*() có thể cho tôi biết quy trình giảm dần cuối cùng đã bị lỗi.

Tôi có thiếu API ở đâu đó không?

Sau đây là cách tôi mở semaphore tên:

sem_t *sem = sem_open("/testing", 
    O_CREAT  | // create the semaphore if it does not already exist 
    O_CLOEXEC , // close on execute 
    S_IRWXU  | // permissions: user 
    S_IRWXG  | // permissions: group 
    S_IRWXO  , // permissions: other 
    1   ); // initial value of the semaphore 

Dưới đây là cách tôi giảm nó:

struct timespec timeout = { 0, 0 }; 
clock_gettime(CLOCK_REALTIME, &timeout); 
timeout.tv_sec += 5; 

if (sem_timedwait(sem, &timeout)) 
{ 
    throw "timeout while waiting for semaphore"; 
} 

Trả lời

32

Hóa ra không có cách nào để phục hồi đáng tin cậy semaphore. Chắc chắn, bất kỳ ai cũng có thể post_sem() vào semaphore được đặt tên để tăng số lần truy cập trước đó, nhưng làm cách nào để biết khi nào cần khôi phục? API được cung cấp quá hạn chế và không thể hiện bằng bất kỳ cách nào khi điều này xảy ra.

Hãy coi chừng các công cụ ipc cũng khả dụng - các công cụ phổ biến ipcmk, ipcrmipcs chỉ dành cho các ẩn dụ SysV lỗi thời. Họ đặc biệt không làm việc với các semaphores POSIX mới. Nhưng có vẻ như có những thứ khác có thể được sử dụng để khóa những thứ mà hệ điều hành tự động phát hành khi một ứng dụng chết theo cách không thể bị bắt gặp trong bộ xử lý tín hiệu. Hai ví dụ: một ổ cắm nghe được gắn với một cổng cụ thể hoặc một khóa trên một tệp cụ thể.

Tôi đã quyết định khóa trên tệp là giải pháp tôi cần. Vì vậy, thay vì một sem_wait()sem_post() cuộc gọi, tôi đang sử dụng:

lockf(fd, F_LOCK, 0) 

lockf(fd, F_ULOCK, 0) 

Khi thoát khỏi ứng dụng trong bất kỳ cách nào, các tập tin được tự động đóng mà cũng ra mắt khóa tập tin. Các ứng dụng khách khác chờ đợi "semaphore" sau đó được tự do tiến hành như mong đợi.

Cảm ơn sự giúp đỡ của các bạn.

+1

+1, cuối cùng cũng làm điều tương tự, các ẩn dụ vô dụng trong các tình huống như vậy –

+0

Ai đó đã gửi email cho tôi để hỏi thêm chi tiết. Tôi đã viết một bài đăng blog nhỏ gần 3 năm trước khi tôi gặp phải vấn đề này. Thêm chi tiết về cách tôi giải quyết nó với khóa tập tin có sẵn ở đây: http://charette.no-ip.com:81/programming/2010-01-13_PosixSemaphores/index.html –

+0

Điều tương tự có thể đạt được bằng cách mở một đóng một tập tin? Tôi tìm thấy điều này trên trang người đàn ông cho mở(): "Khi mở một tập tin, một khóa với đàn (2) ngữ nghĩa có thể thu được bằng cách thiết lập O_SHLOCK cho một khóa chia sẻ, hoặc O_EXLOCK cho một khóa độc quyền." –

2

Bạn sẽ có thể tìm thấy nó từ vỏ sử dụng lsof. Sau đó, có thể bạn có thể xóa nó?

Cập nhật

Ah vâng ... man -k semaphore để giải cứu.

Có vẻ như bạn có thể sử dụng ipcrm để loại bỏ một semaphore. Có vẻ như bạn không phải là người đầu tiên gặp vấn đề này.

+1

Vâng, tôi biết về ipcrm, nhưng không hiệu quả. Nếu tôi biết semaphore đã bị mất, tôi có thể dễ dàng sem_post() để "lấy lại". Vấn đề dường như không có sự kiện nào được kích hoạt để chỉ ra rằng ứng dụng đã giảm dần nó đã bị giết. –

+1

Ngoài ra, chỉ cần chú ý trên trang người đàn ông mà ipcrm chỉ hoạt động trên các semaphores SysV cũ, không phải là các semaphores POSIX. Tương tự với ipcs. –

1

Nếu quá trình này bị KẾT THÚC thì sẽ không có bất kỳ cách trực tiếp nào để xác định rằng nó đã biến mất.

Bạn có thể vận hành một số loại kiểm tra tính toàn vẹn định kỳ trên tất cả các ẩn dụ bạn có - sử dụng semctl (cmd = GETPID) để tìm PID cho quá trình cuối cùng chạm vào mỗi semaphore ở trạng thái bạn mô tả. vẫn còn xung quanh. Nếu không, hãy dọn dẹp.

+0

Điều gì đó dọc theo những dòng này là những gì tôi đang tìm kiếm, nhưng tất nhiên đối với các semaphores POSIX bạn sẽ tìm thấy trong #include . Từ những gì tôi có thể nói, phong cách semctl() của các cuộc gọi là cụ thể cho các semaphores SysV cũ từ . –

2

Bạn sẽ cần phải kiểm tra kỹ nhưng tôi tin rằng sem_post có thể được gọi từ trình xử lý tín hiệu. Nếu bạn có thể bắt được một số tình huống đang làm giảm quá trình, điều này có thể hữu ích.

Không giống như một mutex, bất kỳ quá trình hoặc chuỗi nào (có quyền) có thể đăng lên semaphore. Bạn có thể viết một tiện ích đơn giản để thiết lập lại nó. Có lẽ bạn biết khi hệ thống của bạn đã bế tắc. Bạn có thể mang nó xuống và chạy chương trình tiện ích.

Ngoài ra, semaphone thường được liệt kê trong/dev/shm và bạn có thể xóa nó.

Các ẩn dụ SysV phù hợp hơn với kịch bản này. Bạn có thể chỉ định SEM_UNDO, trong đó hệ thống sẽ trả lại các thay đổi đối với semaphore được thực hiện bởi một quá trình nếu nó chết. Họ cũng có khả năng cho bạn biết id quá trình cuối cùng để thay đổi semaphore.

+1

Một số tín hiệu như kill -9 bypasses handers tín hiệu, đó là tình huống mà tôi đã gặp phải. Tôi có một handler tín hiệu cho những người tôi có thể bắt, và trong một destructor cho một đối tượng dựa trên phạm vi tôi gọi sem_post() như ngăn xếp thư giãn. Nhưng vài tín hiệu không thể kéo dài đó là những gì tôi hy vọng sẽ giải quyết. –

+1

Tôi nghĩ một câu hỏi công bằng là hỏi ai là người dùng và tại sao họ lại giết chết ứng dụng theo cách đó?Bạn có thể thử các tuyến đường SysV hoặc thậm chí khóa tập tin, mà nên trở lại khi quá trình chết. – Duck

+0

Thực ra, đó là những gì tôi quyết định làm đêm qua. Vì các tệp đã được mở() và lockf() được tự động phát hành khi các ứng dụng bị giết -9, phương thức "giao tiếp" này thực sự hoạt động đáng tin cậy hơn so với các semaphores xem xét những gì tôi cần phối hợp. –

4

Đây là vấn đề điển hình khi quản lý các semaphores. Một số chương trình sử dụng một quy trình duy nhất để quản lý việc khởi tạo/xóa bỏ semaphore. Thông thường quá trình này chỉ thực hiện điều này và không có gì khác.Các ứng dụng khác của bạn có thể đợi cho đến khi semaphore có sẵn. Tôi đã nhìn thấy điều này được thực hiện với các loại SYSV API, nhưng không phải với POSIX. Tương tự như những gì 'Duck' được đề cập, sử dụng cờ SEM_UNDO trong cuộc gọi semop() của bạn.


Nhưng, với thông tin bạn đã cung cấp, tôi khuyên bạn không nên sử dụng các ẩn dụ. Đặc biệt là nếu quá trình của bạn có nguy cơ bị giết hoặc bị rơi. Hãy thử sử dụng một cái gì đó mà hệ điều hành sẽ dọn dẹp tự động cho bạn.

-1

Chỉ cần thực hiện sem_unlink() ngay sau sem_open(). Linux sẽ xóa sau khi tất cả các quy trình đã đóng tài nguyên, bao gồm các đóng nội bộ.

+1

Điều đó sẽ không làm cho semaphore được đặt tên bị xóa? Tôi không muốn xóa nó, tôi muốn đăng nó khi một trong các ứng dụng bị rơi hoặc bị giết. (Nhiều ứng dụng được sử dụng ở đây, tất cả đều hoạt động với cùng một tập hợp các semaphores được đặt tên để phối hợp một số công việc nội bộ.) –

+2

Điều này sẽ không hoạt động: nếu một quá trình khác bị chặn trên semaphore, và quá trình có khóa bị treo, sau đó quá trình bị chặn sẽ giữ cho semaphore mở và do đó semaphore sẽ không bao giờ bị phá hủy. –

5

Sử dụng tệp khóa thay vì semaphore, giống như giải pháp của @ Stéphane nhưng không có cuộc gọi đổ xô. Bạn chỉ có thể mở tệp bằng khóa độc quyền:

//call to open() will block until it can obtain an exclusive lock on the file. 
errno = 0; 
int fd = open("/tmp/.lockfile", 
    O_CREAT | //create the file if it's not present. 
    O_WRONLY | //only need write access for the internal locking semantics. 
    O_EXLOCK, //use an exclusive lock when opening the file. 
    S_IRUSR | S_IWUSR); //permissions on the file, 600 here. 

if (fd == -1) { 
    perror("open() failed"); 
    exit(EXIT_FAILURE); 
} 

printf("Entered critical section.\n); 
//Do "critical" stuff here. 

//exit the critical section 
errno = 0; 
if (close(fd) == -1) { 
    perror("close() failed"); 
    exit(EXIT_FAILURE); 
} 

printf("Exited critical section.\n"); 
+1

Mã tốt, với 1 sửa đổi: Bạn nên tạo tệp khóa trước khi tranh chấp bắt đầu, và, tất nhiên, giữ cho nó được liên kết. Nếu không, trong các thử nghiệm của tôi trong Mac OS X 10.10 DP5, mở() có thể thành công cho hai quy trình ngang hàng có ý định ban đầu tạo tệp, nếu trong vòng vài phần nghìn giây. Sự cố xảy ra với mã của Stéphane hoặc Raffi. Sau đó tôi đã thử nghiệm ứng suất. Kết quả: Mã của Raffi hoạt động hoàn hảo, mã của Stéphane không hoàn toàn. Tôi không học tại sao. Nếu bạn quan tâm, hãy xem https://github.com/jerrykrinock/ClassesObjC/blob/master/SSYSemaphore.h và .m. –

+0

@JerryKrinock Nhưng, không 'mở()' tạo tệp khóa nếu nó không hiện diện (khi được đưa ra O_CREAT) cờ? –

+1

Tôi sẽ bắn từ hông, bởi vì tôi không có 30 phút mà nó sẽ làm cho đúng cách làm mới sự hiểu biết của tôi về điều này có sẵn ngay bây giờ. Tôi nghĩ câu trả lời là, yes, open() với O_CREAT sẽ tạo tệp nếu cần, nhưng nếu hai tiến trình thực hiện mở() trong vài phần nghìn giây, kết quả là không thể đoán trước. Do đó đề nghị của tôi để tạo tập tin khóa trước khi nó quan trọng; tốt, tôi sẽ thêm, trừ khi nó là OK cho tranh chấp đầu tiên là một throwaway. –

Các vấn đề liên quan