2010-02-09 11 views
7

Tôi muốn phân bổ và khởi tạo một phần khá lớn của bộ nhớ tiếp giáp (~ 1GB), sau đó đánh dấu nó là chỉ đọc và ngã ba nhiều (nói vài chục) quy trình con sẽ sử dụng nó, mà không tạo bản sao của bộ nhớ (máy sẽ không có đủ bộ nhớ cho việc này).Đây có phải là cách an toàn để chia sẻ bộ nhớ chỉ đọc với các tiến trình con không?

Tôi có nghĩ rằng nếu tôi malloc bộ nhớ như bình thường, thì đánh dấu nó là chỉ đọc với mprotect(addr, size, PROT_READ) và sau đó fork, điều này sẽ cho phép trẻ xử lý an toàn bộ nhớ mà không làm sao chép? (Cung cấp cho tôi đảm bảo không có gì cố gắng ghi vào bộ nhớ được cấp phát sau cuộc gọi mprotect).

chỉnh sửa: Cảm ơn tất cả các câu trả lời.

Câu hỏi tiếp theo - Tôi đã lên kế hoạch sử dụng shmget, nhưng tôi nghĩ nó đã sử dụng mm và do đó sẽ bị giới hạn ở phân bổ nhỏ hơn (xem Restrictions section of this page). ví dụ: /proc/sys/kernel/shmmax là 32MB trên máy chủ Tôi đang sử dụng cái này. Nhưng tôi muốn 1GB bộ nhớ tiếp giáp. Tôi có sai về giới hạn này không?

Trả lời

7

man mprotect

Việc thực hiện sẽ yêu cầu addr là một bội số của kích thước trang là được trả về bởi sysconf().

Hành vi của chức năng này không được chỉ định nếu ánh xạ không được thiết lập bằng một cuộc gọi đến mmap().

  1. mprotect chỉ hoạt động trên trang, không tùy tiện byte dãy, vì vậy nói chung malloc là không phù hợp. posix_memalign có thể hữu ích, nhưng ...
  2. Mặc dù nó có thể hoạt động trên hệ thống của bạn tại thời điểm hiện tại, nhưng bạn không được khôngmprotect bất kỳ thứ gì bạn không tự mình làm mmap. Sử dụng mmap(0, pages*sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0) để thay thế.
+0

Ah cảm ơn, tôi rõ ràng là không đọc trang người bảo vệ mprotect đủ chặt chẽ. –

+0

@therefromhere 'MAP_SHARED | MAP_ANONYMOUS' tại đây hoạt động để chia sẻ từ cha mẹ trước khi chia tay cho bất kỳ trẻ em nào. Nếu bạn muốn chia sẻ giữa các quá trình tùy ý, hãy lấy ra 'MAP_ANONYMOUS' và gán cho nó một FD thực thay vì' -1'; FD này có thể đến từ 'mở' một tệp trên đĩa hoặc từ 'shm_open'. – ephemient

+0

Trang người quản lý linux cho mprotect không lưu ý "Trên Linux, luôn luôn là hợp pháp để gọi mprotect() trên bất kỳ địa chỉ nào trong không gian địa chỉ quy trình ..." http://linux.die.net/man/2/mprotect –

0

Hiểu biết của tôi là có, vì Linux sử dụng cơ chế sao chép trên ghi cho các trang bộ nhớ được chuyển đến một tiến trình con.

3

Bạn không đúng vì lý do nào hơn bất kỳ quy trình con nào có thể gọi mprotect() để xóa bảo vệ và bắt đầu viết ở đó. Nếu các trang chưa được sao chép, nó sẽ vi phạm các nguyên tắc của fork().

Ngay cả khi nó hoạt động theo cách sao chép trên ghi được sử dụng cho các quy trình chia hai, tôi không tính bất kỳ vị trí nào trong các tiêu chuẩn nói như vậy (POSIX không cho biết đó là bản sao-ghi-ghi, cho ví dụ).

Thay vì sử dụng hành vi không chuẩn, bạn có thể sử dụng các biện pháp tiêu chuẩn để chia sẻ bộ nhớ. Ví dụ: bộ nhớ chia sẻ POSIX với shm_open và hậu quả là mmap (như đã được chỉ ra trong nhận xét và được giải thích trong số post của mình theo số ephemient). Bộ mô tả tập tin sẽ được bảo tồn thông qua việc tìm kiếm.

+0

Cảm ơn. theo bản chỉnh sửa tôi đã lên kế hoạch sử dụng 'shmget' nhưng tôi nghĩ nó bị hạn chế về kích thước phân bổ mà nó có thể làm. –

+2

Tôi muốn nói 'shm_open' an toàn hơn: nó cung cấp cho bạn một tập tin xử lý' mmap', trái với 'shmget' cung cấp cho bạn một số định danh tùy ý để' shmat', và tôi đã giải thích tại sao bạn nên sử dụng 'mmap' trong câu trả lời của tôi. (Hai API, mặc dù tên tương tự của chúng, đến từ các di sản khác nhau và hoạt động rất khác nhau.) – ephemient

+0

@ephemient, yeah, cảm ơn bạn đã chỉ ra điều này. –

1

Bạn đang làm cho một giả định rằng hạt nhân sẽ làm copy-on-viết tối ưu hóa và không sao chép mprotect trang -ed. Tôi sẽ không tin vào nó mặc dù. malloc-bộ nhớ có tất cả các loại siêu dữ liệu nổi xung quanh nó - trang bảo vệ, v.v. và chỉ Ulrich Drepper biết những gì đang xảy ra bên trong libc :)

Có lẽ sẽ dễ dàng hơn và an toàn hơn để chuẩn bị dữ liệu trong tệp đĩa và họ mmap nó vào tất cả các quy trình hoặc chỉ cần đi tuyến POSIX shm_open bình thường.

+0

Cảm ơn. theo bản chỉnh sửa tôi đã lên kế hoạch sử dụng 'shmget' nhưng tôi nghĩ nó bị hạn chế về kích thước phân bổ mà nó có thể làm. –

0

Bạn có thể làm theo cách đó.

Cách khác là sử dụng mmap().

Một cách khác là sử dụng bộ nhớ chia sẻ POSIX (shm_open()); lựa chọn chính khác là bộ nhớ chia sẻ Hệ thống V (shmget(), shmat()). Một lợi thế của các hệ thống bộ nhớ chia sẻ chính thức là quá trình cha mẹ của bạn có thể tạo bộ nhớ và sau đó quá trình không liên quan có thể kết nối với nó - nếu điều đó có lợi.

+0

Cảm ơn. theo bản chỉnh sửa tôi đã lên kế hoạch sử dụng 'shmget' nhưng tôi nghĩ nó bị hạn chế về kích thước phân bổ mà nó có thể làm. –

+1

@therefromhere: bạn có thể cần phải điều chỉnh cấu hình hạt nhân (shmmax hoặc các kích thước có liên quan), nhưng IBM Informix Dynamic Server sử dụng shmget() để phân bổ khối nhiều gigabyte bộ nhớ dùng chung nếu bạn yêu cầu nó làm như vậy. –

2

Không cần đánh dấu chỉ đọc, chỉ cần cho con của bạn xử lý để rời khỏi nó một mình.

Nếu cả cha mẹ lẫn con đều không viết, nó sẽ vẫn được chia sẻ. Nếu bạn không bao giờ muốn thay đổi nó, đó là tốt.

Nếu bạn muốn viết thư cho nó, bạn sẽ muốn sử dụng mmap với MAP_SHARED.