2013-04-11 37 views
10

Hãy xem xét các chương trình C tầm thường sau,Cách chính xác để xây dựng một thư viện C đa nền tảng, an toàn và chủ đề là gì?

#include <errno.h> 
int 
main(int argc, char* argv[]) { 
    return errno; 
} 

Khi biên soạn trên Solaris, hành vi của mã này là phụ thuộc vào sự hiện diện của -D_REENTRANT.

solaris$ cc -E test.c | grep return 
    return errno; 
solaris$ cc -D_REENTRANT -E test.c | grep return 
    return (* (___errno ())); 

với phiên bản thứ hai là an toàn chỉ. Nếu chúng ta biên dịch cùng mã trên Linux, chúng tôi nhận được cùng một độc lập hành vi của -D_REENTRANT

linux$ gcc -E test.c | grep return 
    return (*__errno_location()); 
linux$ gcc -D_REENTRANT -E test.c | grep return 
    return (*__errno_location()); 

Solaris' cc có quyền lựa chọn -mt, trong đó hàm ý -D_REENTRANT, cũng như gcc 's -pthread. Tuy nhiên, đối với một thư viện, việc chỉ rõ các tùy chọn đa luồng này có vẻ kém, vì nó tiêm một sự phụ thuộc không cần thiết vào thời gian chạy luồng. Tuy nhiên, nếu thư viện cần phải có chủ đề an toàn (bao gồm cả errno), thì ngữ nghĩa chủ đề an toàn là cần thiết ở cả thời gian biên dịch của thư viện và mã bắt nguồn. Trên Linux, điều này là dễ dàng, bởi vì errno luôn là thread-local, nhưng điều đó không được bảo đảm trên các hệ thống khác như vừa được chứng minh.

Kết quả đó trong câu hỏi: thư viện an toàn chủ đề được biên soạn và phân phối đúng cách bằng tiêu đề như thế nào? Một tùy chọn sẽ là #define _REENTRANT trong tiêu đề chính, nhưng điều này sẽ gây ra sự cố nếu #include <errno.h> xảy ra trước khi bao gồm tiêu đề thư viện. Một tùy chọn khác là biên dịch thư viện với -D_REENTRANT và có tiêu đề chính #error nếu _REENTRANT không được xác định.

Cách chính xác/tốt nhất để tạo thư viện an toàn cho chủ đề và đảm bảo thư viện hoạt động chính xác với mã được liên kết với nó?

Trả lời

4

Tôi không có quyền truy cập vào bất kỳ máy Solaris nào vào lúc này, vì vậy tôi không thể kiểm tra điều này. Nhưng điều gì sẽ xảy ra khi bạn đặt #define _POSIX_C_SOURCE 200112L làm dòng đầu tiên trong số test.c (trước khi bao gồm <errno.h>)? Nếu Solaris của bạn tuân thủ POSIX, thì điều đó sẽ làm cho errno mở rộng sang phiên bản an toàn chỉ. Điều này là do POSIX định nghĩa errno như sau:

Đối với mỗi chủ đề của một quá trình, giá trị của errno sẽ không bị ảnh hưởng bởi chức năng cuộc gọi hoặc các bài tập để errno bởi chủ đề khác.

Theo đó, điều này là di động đối với bất kỳ hệ thống tuân thủ POSIX nào. Trong thực tế, nếu bạn muốn viết mã ứng dụng tuân thủ POSIX, thì bạn nên luôn luôn xác định _POSIX_C_SOURCE cho giá trị thích hợp cho phiên bản POSIX tối thiểu mà bạn đang nhắm mục tiêu. Định nghĩa phải ở đầu mỗi tệp nguồn trước khi bao gồm bất kỳ tiêu đề nào. Từ phiên bản tiêu chuẩn năm 2001:

Ứng dụng POSIX tuân thủ đúng là một ứng dụng chỉ yêu cầu các cơ sở được mô tả trong IEEE Std 1003.1-2001. Ứng dụng như vậy:

...

8.Đối với ngôn ngữ lập trình C, phải xác định _POSIX_C_SOURCE là 200112L trước khi bất kỳ tiêu đề nào được bao gồm

+0

'cc -D_POSIX_C_SOURCE = 200112L -E test.c | grep return -> return (* (___errno())); ' Thật vậy, hoạt động như mong đợi; bạn cũng đề xuất xác định điều này trong tiêu đề của thư viện tương thích POSIX? –

+0

@AlexanderChernyakhovsky: Không, nó không bao giờ cần phải được xác định trong bất kỳ tập tin tiêu đề. Nó phải luôn luôn được định nghĩa ở đầu tệp nguồn. Đó là cách duy nhất để chắc chắn rằng nó được xác định trước khi đưa vào bất kỳ tiêu đề nào. Bạn cũng có thể xác định nó trên dòng lệnh, giống như bạn đã làm trong ví dụ của bạn. Đó là chấp nhận được. Tôi thích nhìn thấy nó trong tập tin nguồn, nhưng đó là một phần chỉ là một vấn đề của hương vị. –

+0

Thú vị. Có vẻ như bạn đang ủng hộ việc kiểm tra '# define' trong tiêu đề thư viện của mình, bởi vì tôi vẫn không muốn một nhà phát triển cuối cùng kết thúc nhận sai errno. –

0

Nếu thư viện của bạn sử dụng autoconf, bạn có thể muốn sử dụng macro AC_USE_SYSTEM_EXTENSIONS. Macro đó đặt một số định nghĩa mục tiêu cụ thể cho phép các ngữ nghĩa mở rộng của POSIX +. Tôi không có hệ thống Solaris để kiểm tra vào lúc này, nhưng tôi tin rằng _POSIX_PTHREAD_SEMANTICS nên kích hoạt errno an toàn chỉ. Ít nhất nó cũng cho phép các hàm POSIX _r() thay vì các biến thể POSIX-draft _r() mà Solaris cung cấp theo mặc định.

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