2010-01-07 12 views
11

Tôi muốn theo dõi việc sử dụng mallocs và giải phóng trong một ứng dụng bằng cách sử dụng móc nối và móc miễn phí.sử dụng các móc trượt glibc malloc theo chủ đề an toàn

Đây là tài liệu hướng dẫn http://www.gnu.org/s/libc/manual/html_node/Hooks-for-Malloc.html

Từ trang Ví dụ, bạn có thể thấy rằng my_malloc_hook thoáng qua chuyển mạch malloc treo tắt (hoặc móc trước đó trong chuỗi) trước khi tái gọi malloc.

Đây là vấn đề khi giám sát các ứng dụng đa luồng (xem phần cuối câu hỏi để được giải thích).

Ví dụ khác về việc sử dụng móc malloc mà tôi đã tìm thấy trên internet có cùng vấn đề.

Có cách nào để viết lại chức năng này để hoạt động chính xác trong ứng dụng đa luồng không?

Ví dụ, có hàm libc nội bộ mà móc malloc có thể gọi để hoàn tất việc phân bổ, mà không cần phải hủy kích hoạt móc của tôi.

Tôi không thể xem mã nguồn libc do chính sách pháp lý của công ty, vì vậy câu trả lời có thể hiển nhiên.

Thông số kỹ thuật thiết kế của tôi cho biết tôi không thể thay thế malloc bằng thiết kế malloc khác.

Tôi có thể giả định rằng không có móc nào khác đang được phát.


CẬP NHẬT

Kể từ khi móc malloc được tạm thời loại bỏ trong khi phục vụ các malloc, thread khác có thể gọi malloc và KHÔNG nhận được móc. Nó đã được gợi ý rằng malloc có một khóa lớn xung quanh nó ngăn chặn điều này xảy ra, nhưng nó không được ghi lại, và thực tế là tôi gọi đệ trình một cách hiệu quả malloc cho thấy bất kỳ khóa phải tồn tại sau móc, hoặc thông minh vui vẻ :

caller -> 
    malloc -> 
    malloc-hook (disables hook) -> 
     malloc -> # possible hazard starts here 
     malloc_internals 
     malloc <- 
    malloc-hook (enables hook) <- 
    malloc 
caller 
+0

Nếu một trong số chúng tôi xem xét nguồn libc và cung cấp cho bạn thông tin dựa trên đó, bạn sẽ ở cùng vị trí, hợp pháp. –

+0

Tại sao bạn không * nhìn * tại mã nguồn libc? – Will

+0

Vì tôi có thể gây ô nhiễm mã độc quyền của chúng tôi với mã GPL. Đơn giản chỉ cần được nói rằng một chức năng cụ thể sẽ làm những gì tôi muốn không có vấn đề đó. –

Trả lời

8

CẬP NHẬT

Bạn đang right để không tin tưởng __malloc_hooks; Tôi đã liếc nhìn mã, và họ đang - đáng kinh ngạc điên cuồng - không phải là chủ đề an toàn.

Gọi trực tiếp chuỗi được kế thừa, thay vì khôi phục và nhập lại malloc, dường như bị lệch khỏi tài liệu bạn trích dẫn quá nhiều để cảm thấy thoải mái khi đề xuất.

Từ http://manpages.sgvulcan.com/malloc_hook.3.php:

biến Hook không thread-safe vì vậy họ đang bị phản đối ngay bây giờ. Thay vào đó, các lập trình viên nên ưu tiên gọi các hàm liên quan bằng cách xác định và xuất các hàm như "malloc" và "free".

Cách thích hợp để tiêm các chức năng gỡ lỗi malloc/realloc/free là cung cấp thư viện của riêng bạn xuất các phiên bản 'gỡ rối' của các chức năng này, và sau đó tự bảo vệ các chức năng thực. Liên kết C được thực hiện theo thứ tự rõ ràng, vì vậy nếu hai thư viện cung cấp cùng một hàm, thì hàm được chỉ định đầu tiên được sử dụng. Bạn cũng có thể tiêm malloc của bạn tại thời gian tải trên Unix bằng cách sử dụng các cơ chế LD_PRELOAD.

http://linux.die.net/man/3/efence mô tả Hàng rào điện, chi tiết cả hai phương pháp này.

Bạn có thể sử dụng khóa của riêng mình nếu trong các chức năng gỡ lỗi này nếu cần.

+0

Câu hỏi có thể là: Khóa có được lấy trước khi móc được gọi hay không xảy ra bên trong malloc()? Tôi đoán các móc sẽ vô ích nếu không có khóa xảy ra bên ngoài nhưng tôi tự hỏi làm thế nào các cuộc gọi đệ quy hoạt động, sau đó. –

+0

Gọi đệ quy có thể hoạt động với các khóa đệ quy - khi chuỗi có khóa, nó được phép lấy nó nhiều lần. – Novelocrat

+0

Vâng, điều đó có thể đúng, nhưng trừ khi tôi biết nó là sự thật tôi không thể sử dụng nó, vì nó có thể phá vỡ. Ngoài ra tôi không thể nói nếu một triển khai malloc trong tương lai có thể cho phép nhiều khu malloc với ổ khóa riêng biệt để nâng cao hiệu suất đa luồng. –

2

Vì tất cả các cuộc gọi đến malloc() sẽ đi qua móc của bạn, bạn có thể đồng bộ hóa trên một semaphore (chờ cho đến khi nó là miễn phí, khóa nó, sắp xếp các móc và giải phóng các semaphore).

[EDIT] IANAL nhưng ... Nếu bạn có thể sử dụng glibc trong mã của bạn, sau đó bạn có thể nhìn vào đoạn code (vì nó LGPL, bất cứ ai sử dụng nó phải được phép có một bản sao của nguồn). Vì vậy, tôi không chắc chắn bạn đã hiểu được tình trạng pháp lý một cách chính xác hoặc có thể bạn không được phép sử dụng glibc bởi công ty của bạn.

[EDIT2] Sau khi suy nghĩ một số, tôi đoán rằng phần này của đường dẫn cuộc gọi phải được bảo vệ bởi một khóa của một số loại mà glibc tạo ra cho bạn. Nếu không, sử dụng móc trong mã đa luồng sẽ không bao giờ hoạt động đáng tin cậy và tôi chắc chắn rằng các tài liệu sẽ đề cập đến điều này. Vì malloc() phải là chỉ an toàn, các móc cũng phải như vậy.

Nếu bạn vẫn lo lắng, tôi đề nghị viết một chương trình thử nghiệm nhỏ với hai luồng phân bổ và giải phóng bộ nhớ trong một vòng lặp. Tăng số lượt truy cập trong móc. Sau một triệu vòng, bộ đếm phải chính xác là hai triệu. Nếu điều này giữ, thì móc được bảo vệ bởi khóa malloc().

[EDIT3] Nếu thử nghiệm không thành công, vì tình hình pháp lý của bạn, bạn không thể triển khai màn hình. Nói với sếp của bạn và để anh ta đưa ra quyết định về nó.

[EDIT4] Googling bật lên nhận xét này từ một báo cáo lỗi:

Các móc không được thread-safe. Giai đoạn. Bạn đang cố sửa chữa cái gì?

Đây là một phần của cuộc thảo luận từ tháng 3 năm 2009 về lỗi trong libc/malloc/malloc.c có chứa bản sửa lỗi. Vì vậy, có thể một phiên bản glibc sau ngày này hoạt động nhưng có vẻ như không phải là sự đảm bảo. Nó cũng có vẻ phụ thuộc vào phiên bản GCC của bạn.

+0

Tôi không được phép xem mã GPL của công ty mình. Đó là quy tắc. –

+0

Vì mã móc phải loại bỏ mã móc trước khi gọi lại malloc, một chuỗi thứ hai gọi malloc trong khi tôi đã tháo móc của tôi sẽ không sử dụng móc. –

+0

@ Alex Tôi đoán điều đó có nghĩa là bạn không được phép xem hoặc sử dụng mã GPL? –

3

Tôi gặp vấn đề tương tự. Tôi đã giải quyết nó với ví dụ đó. Nếu chúng ta không định nghĩa THREAD_SAFE, chúng ta có ví dụ được đưa ra bởi người đàn ông, và chúng ta có một lỗi phân đoạn. Nếu chúng tôi xác định THREAD_SAFE, chúng tôi không có lỗi phân đoạn.

#include <malloc.h> 
#include <pthread.h> 

#define THREAD_SAFE 
#undef THREAD_SAFE 

/** rqmalloc_hook_ */ 

static void* (*malloc_call)(size_t,const void*); 

static void* rqmalloc_hook_(size_t taille,const void* appel) 
{ 
void* memoire; 

__malloc_hook=malloc_call; 
memoire=malloc(taille);  
#ifndef THREAD_SAFE 
malloc_call=__malloc_hook; 
#endif 
__malloc_hook=rqmalloc_hook_; 
return memoire; 
} 

/** rqfree_hook_ */ 

static void (*free_call)(void*,const void*); 

static void rqfree_hook_(void* memoire,const void* appel) 
{ 
__free_hook=free_call; 
free(memoire);    
#ifndef THREAD_SAFE 
free_call=__free_hook;  
#endif 
__free_hook=rqfree_hook_; 
} 

/** rqrealloc_hook_ */ 

static void* (*realloc_call)(void*,size_t,const void*); 

static void* rqrealloc_hook_(void* memoire,size_t taille,const void* appel) 
{ 
__realloc_hook=realloc_call;  
memoire=realloc(memoire,taille); 
#ifndef THREAD_SAFE 
realloc_call=__realloc_hook;  
#endif 
__realloc_hook=rqrealloc_hook_; 
return memoire; 
} 

/** memory_init */ 

void memory_init(void) 
{ 
    malloc_call = __malloc_hook; 
    __malloc_hook = rqmalloc_hook_; 

    free_call = __free_hook; 
    __free_hook = rqfree_hook_; 

    realloc_call = __realloc_hook; 
    __realloc_hook = rqrealloc_hook_; 
} 

/** f1/f2 */ 

void* f1(void* param) 
{ 
void* m; 
while (1) {m=malloc(100); free(m);} 
} 

void* f2(void* param) 
{ 
void* m; 
while (1) {m=malloc(100); free(m);} 
} 

/** main */ 
int main(int argc, char *argv[]) 
{ 
memory_init(); 
pthread_t t1,t2; 

pthread_create(&t1,NULL,f1,NULL); 
pthread_create(&t1,NULL,f2,NULL); 
sleep(60); 
return(0); 
} 
0

Không có cách nào để sử dụng móc neooc theo cách an toàn chỉ trong khi đệ quy vào malloc. Giao diện được thiết kế kém, có lẽ ngoài sửa chữa.

Thậm chí nếu bạn đặt một mấu chốt trong mã móc, vấn đề là các cuộc gọi vào malloc không thấy các khóa đó cho đến khi chúng đi qua cơ chế móc và để vượt qua cơ chế móc, chúng xem xét các biến toàn cầu (các con trỏ móc) mà không cần có mutex của bạn. Khi bạn đang lưu, thay đổi và khôi phục các con trỏ này trong một chuỗi, các cuộc gọi cấp phát trong một chuỗi khác bị ảnh hưởng bởi chúng.

Vấn đề thiết kế chính là các móc là con trỏ rỗng theo mặc định. Nếu giao diện đơn giản cung cấp các móc mặc định không null là trình phân bổ thích hợp (cấp phát cấp dưới không gọi bất kỳ móc nào), thì sẽ đơn giản và an toàn để thêm móc: bạn có thể lưu các móc trước đó, và trong móc mới, recurse vào malloc bằng cách gọi các móc giữ, mà không có fiddling với bất kỳ con trỏ toàn cầu (khác với thời gian cài đặt móc, có thể được thực hiện trước khi bất kỳ chủ đề bắt đầu lên).

Ngoài ra, glibc có thể cung cấp giao diện nội bộ malloc không gọi móc.

Thiết kế sane khác sẽ là sử dụng bộ nhớ cục bộ cho các móc. Việc ghi đè và khôi phục một móc sẽ được thực hiện trong một luồng, mà không làm xáo trộn các móc được thấy bởi một luồng khác.

Vì nó là viết tắt, những gì bạn có thể làm để sử dụng móc neo glocc malloc một cách an toàn là tránh đệ quy vào malloc. Không thay đổi con trỏ móc bên trong các callbacks hook, và chỉ cần gọi cấp phát của riêng bạn.