2010-05-06 35 views
5

Chuẩn POSIX có cho phép có tên là khối bộ nhớ dùng chung để chứa biến mutex và biến điều kiện không?Biến điều kiện trong bộ nhớ dùng chung - đây có phải là mã POSIX-conformant không?

Chúng tôi đã cố gắng sử dụng biến mutex và điều kiện để đồng bộ hóa quyền truy cập vào bộ nhớ được chia sẻ có tên theo hai quy trình trên LynuxWorks LynxOS-SE system (tuân thủ POSIX).

Một khối bộ nhớ chia sẻ được gọi là "/sync" và chứa biến mutex và điều kiện, biến còn lại là "/data" và chứa dữ liệu thực tế mà chúng tôi đang đồng bộ hóa quyền truy cập.

Chúng tôi đang chứng kiến ​​những thất bại từ pthread_cond_signal() nếu cả hai quá trình không thực hiện các cuộc gọi trong mmap()chính xác theo thứ tự, hoặc nếu một quá trình mmaps trong một số phần khác của bộ nhớ chia sẻ trước khi nó mmaps bộ nhớ "/sync".

mã ví dụ này là về như ngắn như tôi có thể làm cho nó:

#include <sys/types.h> 
#include <sys/stat.h> 
#include <sys/mman.h> 
#include <sys/file.h> 
#include <stdlib.h> 
#include <pthread.h> 
#include <errno.h> 
#include <iostream> 
#include <string> 
using namespace std; 

static const string shm_name_sync("/sync"); 
static const string shm_name_data("/data"); 

struct shared_memory_sync 
{ 
    pthread_mutex_t mutex; 
    pthread_cond_t condition; 
}; 

struct shared_memory_data 
{ 
    int a; 
    int b; 
}; 


//Create 2 shared memory objects 
// - sync contains 2 shared synchronisation objects (mutex and condition) 
// - data not important 
void create() 
{ 
    // Create and map 'sync' shared memory 
    int fd_sync = shm_open(shm_name_sync.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); 
    ftruncate(fd_sync, sizeof(shared_memory_sync)); 
    void* addr_sync = mmap(0, sizeof(shared_memory_sync), PROT_READ|PROT_WRITE, MAP_SHARED, fd_sync, 0); 
    shared_memory_sync* p_sync = static_cast<shared_memory_sync*> (addr_sync); 

    // init the cond and mutex 
    pthread_condattr_t cond_attr; 
    pthread_condattr_init(&cond_attr); 
    pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); 
    pthread_cond_init(&(p_sync->condition), &cond_attr); 
    pthread_condattr_destroy(&cond_attr); 

    pthread_mutexattr_t m_attr; 
    pthread_mutexattr_init(&m_attr); 
    pthread_mutexattr_setpshared(&m_attr, PTHREAD_PROCESS_SHARED); 
    pthread_mutex_init(&(p_sync->mutex), &m_attr); 
    pthread_mutexattr_destroy(&m_attr); 

    // Create the 'data' shared memory 
    int fd_data = shm_open(shm_name_data.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); 
    ftruncate(fd_data, sizeof(shared_memory_data)); 

    void* addr_data = mmap(0, sizeof(shared_memory_data), PROT_READ|PROT_WRITE, MAP_SHARED, fd_data, 0); 
    shared_memory_data* p_data = static_cast<shared_memory_data*> (addr_data); 

    // Run the second process while it sleeps here. 
    sleep(10); 

    int res = pthread_cond_signal(&(p_sync->condition)); 
    assert(res==0); // <--- !!!THIS ASSERT WILL FAIL ON LYNXOS!!! 

    munmap(addr_sync, sizeof(shared_memory_sync)); 
    shm_unlink(shm_name_sync.c_str()); 
    munmap(addr_data, sizeof(shared_memory_data)); 
    shm_unlink(shm_name_data.c_str()); 
} 

//Open the same 2 shared memory objects but in reverse order 
// - data 
// - sync 
void open() 
{ 
    sleep(2); 
    int fd_data = shm_open(shm_name_data.c_str(), O_RDWR, S_IRUSR|S_IWUSR); 
    void* addr_data = mmap(0, sizeof(shared_memory_data), PROT_READ|PROT_WRITE, MAP_SHARED, fd_data, 0); 
    shared_memory_data* p_data = static_cast<shared_memory_data*> (addr_data); 

    int fd_sync = shm_open(shm_name_sync.c_str(), O_RDWR, S_IRUSR|S_IWUSR); 
    void* addr_sync = mmap(0, sizeof(shared_memory_sync), PROT_READ|PROT_WRITE, MAP_SHARED, fd_sync, 0); 
    shared_memory_sync* p_sync = static_cast<shared_memory_sync*> (addr_sync); 

    // Wait on the condvar 
    pthread_mutex_lock(&(p_sync->mutex)); 
    pthread_cond_wait(&(p_sync->condition), &(p_sync->mutex)); 
    pthread_mutex_unlock(&(p_sync->mutex)); 

    munmap(addr_sync, sizeof(shared_memory_sync)); 
    munmap(addr_data, sizeof(shared_memory_data)); 
} 

int main(int argc, char** argv) 
{ 
    if(argc>1) 
    { 
     open(); 
    } 
    else 
    { 
     create(); 
    } 

    return (0); 
} 

Chạy chương trình này không có args, sau đó một bản sao với args, và là người đầu tiên sẽ thất bại tại assert kiểm tra pthread_cond_signal(). Nhưng thay đổi thứ tự của các open() chức năng để mmap() các "/sync "bộ nhớ trước khi "/data" và nó sẽ tất cả làm việc tốt.

Điều này có vẻ giống như một lỗi lớn trong LynxOS với tôi, nhưng LynuxWorks tuyên bố rằng việc sử dụng mutex và biến điều kiện trong vòng bộ nhớ được chia sẻ theo cách này không thuộc phạm vi điều chỉnh của tiêu chuẩn POSIX, do đó, họ không quan tâm đến tiêu chuẩn POSIX, vì vậy họ không quan tâm đến các tiêu chuẩn POSIX, vì vậy họ không quan tâm đến việc tuân theo quy định của POSIX, vì vậy họ không quan tâm đến việc tuân thủ POSIX?

Chỉnh sửa: chúng tôi biết rằng PTHREAD_PROCESS_SHARED là POSIX và được LynxOS hỗ trợ. Khu vực tranh chấp là liệu mutexes và semaphores có thể được sử dụng trong bộ nhớ chia sẻ được đặt tên (như chúng ta đã làm) hay không.

+1

Tôi tự hỏi làm thế nào họ tưởng tượng chính xác việc chia sẻ cùng một biến bằng cách sử dụng PTHREAD_PROCESS_SHARED giữa hai quy trình (rõ ràng ngụ ý * một số cơ chế chia sẻ biến giữa các quá trình được cho là tồn tại.) Và AFAIK không có tiêu chuẩn cấm đặt mutexes và semaphores ở bất cứ nơi nào bạn muốn , vì vậy "không được bảo hiểm" có nghĩa là "nên hành xử như bình thường." –

Trả lời

2

tôi có thể dễ dàng nhìn thấy như thế nào PTHREAD_PROCESS_SHARED thể được khôn lanh để thực hiện trên hệ điều hành cấp (ví dụ như hệ điều hành MacOS không, trừ rwlocks nó có vẻ). Nhưng chỉ cần đọc tiêu chuẩn, bạn dường như có một trường hợp.

Để hoàn tất, bạn có thể muốn xác nhận trên sysconf(_SC_THREAD_PROCESS_SHARED) và giá trị trả về của hàm * _setpshared() gọi — có thể có một "bất ngờ" khác đang chờ bạn (nhưng tôi có thể thấy từ các nhận xét mà bạn đã kiểm tra thực sự được hỗ trợ).

@JesperE: bạn có thể muốn tham khảo API docs at the OpenGroup thay vì tài liệu HP.

+1

Cảm ơn @ vs: Vâng, tôi đã cắt bớt các xác nhận và xử lý lỗi khác để ngăn chặn mã vì lợi ích ngắn gọn, nhưng hãy yên tâm rằng tất cả các cuộc gọi khác nhau đều trả về thành công cho đến khi 'pthread_cond_signal()' chỉ ra. 'pthread _ * _ setpshared()' chắc chắn được hỗ trợ và được đề cập rõ ràng trong tài liệu đào tạo LynxOS (nhưng chúng chỉ cung cấp các ví dụ trong đó một tiến trình tạo bộ nhớ chia sẻ rồi fork() s, thay vì hai tiến trình sử dụng bộ nhớ chia sẻ). – GrahamS

+0

Không có câu trả lời thực tế nào được cung cấp, vì vậy tôi sẽ trao cho bạn tiền thưởng như bạn là người duy nhất đã xử lý liệu đây có phải là tuân thủ POSIX hay không. – GrahamS

4

Chức năng pthread_mutexattr_setpshared có thể được sử dụng để cho phép một mutex pthread trong bộ nhớ chia sẻ được truy cập bởi bất kỳ chuỗi nào có quyền truy cập vào bộ nhớ đó, ngay cả chủ đề trong các quy trình khác nhau. Theo số this link, pthread_mutex_setpshared phù hợp với POSIX P1003.1c. (Cùng một điều đi cho biến điều kiện, xem pthread_condattr_setpshared.)

câu hỏi liên quan: pthread condition variables on Linux, odd behaviour

+0

Cảm ơn @JesperE, tôi hiểu rằng 'pthread_mutex_setpshared' và' PTHREAD_PROCESS_SHARED' là POSIX. Tôi không nghĩ LynuxWorks phủ nhận điều đó. Tôi nghĩ rằng tranh chấp là nhiều hơn về cách chúng tôi đang tạo ra bộ nhớ chia sẻ rằng mutex và condvar là trong ví dụ. mỗi quá trình truy cập thông qua bộ nhớ được chia sẻ có tên, thay vì chỉ tạo ra nó trong một quá trình và sau đó giả mạo để tạo ra một bộ nhớ khác. – GrahamS

+0

Xin lỗi, tôi đọc câu hỏi một chút cẩu thả. Trang người đàn ông nói rằng "tùy chọn này cho phép một mutex được vận hành bởi bất kỳ chuỗi nào có quyền truy cập vào bộ nhớ nơi phân bổ mutex." Nó âm thanh với tôi rằng làm thế nào bộ nhớ được chia sẻ là lên đến người sử dụng, và bất kỳ cuộc gọi khác để mmap() không nên ảnh hưởng đến ngữ nghĩa biến mutex/điều kiện. Tại sao việc chia sẻ vùng dữ liệu ảnh hưởng đến mutex? LynuxWorks tuyên bố tiêu chuẩn nói gì? Có phải họ đang đề cập đến bất kỳ nơi nào trong tiêu chuẩn, hoặc họ chỉ cần vẫy tay? – JesperE

+0

Yeah LynuxWorks có vẻ như đang được handwaving và nói rằng nếu nó không được xác định rõ ràng trong POSIX thì họ không hỗ trợ nó. Tôi đồng ý với bạn: POSIX cho phép mutex và condvars xuất hiện trong bộ nhớ chia sẻ (mà LynxOS hỗ trợ) - nhưng tôi không thấy gì trong POSIX giới hạn cách chia sẻ bộ nhớ được chia sẻ bởi các quá trình chia sẻ nó. – GrahamS

1

Có thể có một số con trỏ trong pthread_cond_t (không chia sẻ), vì vậy bạn phải đặt nó vào cùng một địa chỉ trong cả hai luồng/quy trình. Với cùng một lệnh mmaps bạn có thể nhận được một địa chỉ bình đẳng cho cả hai quá trình.

Trong glibc, con trỏ trong cond_t là chuỗi mô tả chuỗi, thuộc sở hữu mutex/cond.

Bạn có thể kiểm soát địa chỉ với tham số đầu tiên không NULL thành mmap.

+0

Vâng, một con trỏ đến mutex cơ bản là kết luận của chúng tôi, nhưng vì chúng tôi đã chỉ ra rằng biến điều kiện sẽ được chia sẻ giữa các quá trình, nó có vẻ giống như một lỗ hổng triển khai nếu bạn cũng cần chỉ định địa chỉ mmap (chỉ Cách đáng tin cậy duy nhất để sử dụng gợi ý địa chỉ mmap là sử dụng địa chỉ được trả về bởi mmap() trong quá trình đầu tiên làm gợi ý cho quá trình khác, tất nhiên yêu cầu liên lạc giữa các quá trình! :) – GrahamS

+0

@GrahamS, quá trình chia sẻ cờ trên cond_t sẽ không thay đổi nội bộ thực tế của cấu trúc. Và nếu trong thực hiện chúng ta có một số con trỏ bên trong nó, sẽ được sử dụng (hmm ... ví dụ như con trỏ tới chính cond_t?), Ngay cả trong môi trường chia sẻ quy trình sẽ có yêu cầu của các địa chỉ bằng nhau. độ tin cậy của địa chỉ mmap cố định phụ thuộc vào nền tảng. Bạn biết nền tảng hoặc có một số cấu hình (IPC giả dựa trên tệp), bạn có thể chọn một số địa chỉ mmap. – osgx

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