Có an toàn để phân tích a / Proc / file không?


152

Tôi muốn phân tích /proc/net/tcp/, nhưng nó có an toàn không?

Làm thế nào tôi nên mở và đọc các tệp từ /proc/và không sợ, rằng một số quy trình khác (hoặc chính hệ điều hành) sẽ thay đổi nó cùng một lúc?


29
+1. Đó là một chết tiệt câu hỏi hay. Tôi chỉ ước mình có câu trả lời, nhưng tôi mong muốn được tìm hiểu vì tôi đã làm điều đó khá nhiều trước đây.
paxdiablo

1
Tôi khá chắc chắn rằng chỉ cần đọc nó sẽ cung cấp cho bạn một danh sách các kết nối, cộng với UID sở hữu từng kết nối, giống như khi bạn mở nó. Tuy nhiên, tôi không thể tìm thấy tài liệu đó, vì vậy hãy đưa ra nhận xét này ngay bây giờ.
Tim Post

3
Câu trả lời đơn giản rõ ràng là có, vì nó không phải là một tập tin - đọc nó phải luôn an toàn. Câu trả lời có thể không nhất quán lần sau khi bạn đọc nó, nhưng nó sẽ an toàn.
Rory Alsop

Đây là lý do tại sao bạn nên sử dụng sysctl thay thế. (tòa nhà cũng ít hơn)
Người tốt

@ GoodPerson - làm thế nào điều này có thể sysctl giúp tôi phân tích một /proc/net/tcp/tập tin, ví dụ?
Kiril Kirov

Câu trả lời:


111

Nói chung, không. (Vì vậy, hầu hết các câu trả lời ở đây đều sai.) Nó thể an toàn, tùy thuộc vào tài sản bạn muốn. Nhưng thật dễ dàng để kết thúc với các lỗi trong mã của bạn nếu bạn giả định quá nhiều về tính nhất quán của một tệp /proc. Ví dụ, xem lỗi này xuất phát từ giả định rằng đó /proc/mountslà một ảnh chụp nhanh nhất quán .

Ví dụ:

  • /proc/uptimehoàn toàn nguyên tử , như ai đó đã đề cập trong một câu trả lời khác - nhưng chỉ kể từ Linux 2.6.30 , chưa đầy hai năm. Vì vậy, ngay cả tập tin nhỏ, tầm thường này đã phải chịu một điều kiện chủng tộc cho đến lúc đó, và vẫn còn trong hầu hết các hạt nhân doanh nghiệp. Xem fs/proc/uptime.ccho nguồn hiện tại, hoặc cam kết làm cho nó nguyên tử . Trên kernel trước 2.6.30, bạn có thể opentập tin, readmột chút về nó, sau đó nếu sau này bạn quay lại và readmột lần nữa, phần bạn nhận được sẽ không nhất quán với phần đầu tiên. (Tôi chỉ chứng minh điều này - hãy tự thử nó cho vui.)

  • /proc/mountsnguyên tử trong một readcuộc gọi hệ thống duy nhất . Vì vậy, nếu bạn toàn bộ tập tin cùng một lúc, bạn sẽ có được một ảnh chụp nhanh nhất quán về các điểm gắn kết trên hệ thống. Tuy nhiên, nếu bạn sử dụng một số cuộc gọi hệ thống - và nếu tệp lớn, đây chính xác là điều sẽ xảy ra nếu bạn sử dụng thư viện I / O bình thường và không chú ý đặc biệt đến vấn đề này - bạn sẽ phải chịu một cuộc đua tình trạng. Không chỉ bạn sẽ không có được một ảnh chụp nhanh nhất quán, mà các điểm gắn kết có mặt trước khi bạn bắt đầu và không bao giờ ngừng hiện diện có thể bị mất trong những gì bạn nhìn thấy. Để thấy rằng nó là nguyên tử cho một , nhìn vào trong và nhìn thấy nó lấy một semaphore rằng lính gác danh sách các điểm lắp, mà nó giữ cho đến khi , được gọi là khiđã xong. Để xem những gì có thể đi sai, xem lỗi này từ năm ngoáireadreadread()m_start()fs/namespace.cm_stop()read() (cùng một lỗi tôi đã liên kết ở trên) trong phần mềm chất lượng cao mà hoàn toàn đọc được /proc/mounts.

  • /proc/net/tcp, đó là một trong những gì bạn thực sự hỏi về, thậm chí còn ít nhất quán hơn thế. Đó là nguyên tử chỉ trong mỗi hàng của bảng . Để thấy điều này, hãy nhìn listening_get_next()vàonet/ipv4/tcp_ipv4.cestablished_get_next()ngay dưới trong cùng một tập tin, và xem các khóa họ đưa ra trên mỗi mục lần lượt. Tôi không có mã repro tiện dụng để chứng minh sự thiếu nhất quán từ hàng này sang hàng khác, nhưng không có khóa nào ở đó (hoặc bất cứ điều gì khác) sẽ làm cho nó nhất quán. Điều này có ý nghĩa nếu bạn nghĩ về nó - mạng thường là một phần siêu bận rộn của hệ thống, do đó, không đáng để chi phí để trình bày một quan điểm nhất quán trong công cụ chẩn đoán này.

Các mảnh khác mà giữ /proc/net/tcpnguyên tử trong mỗi hàng là đệm trong seq_read(), mà bạn có thể đọc trongfs/seq_file.c . Điều này đảm bảo rằng một khi bạn read()là một phần của một hàng, văn bản của toàn bộ hàng được giữ trong một bộ đệm để phần tiếp theo read()sẽ lấy phần còn lại của hàng đó trước khi bắt đầu một hàng mới. Cơ chế tương tự được sử dụng /proc/mountsđể giữ cho mỗi hàng nguyên tử ngay cả khi bạn thực hiện nhiềuread() cuộc gọi và đó cũng là cơ chế mà /proc/uptimetrong các hạt nhân mới hơn sử dụng để duy trì nguyên tử. Cơ chế đó không đệm toàn bộ tập tin, vì kernel thận trọng về việc sử dụng bộ nhớ.

Hầu hết các tệp trong /procsẽ ít nhất là nhất quán /proc/net/tcp, với mỗi hàng một hình ảnh nhất quán của một mục trong bất kỳ thông tin nào chúng cung cấp, vì hầu hết chúng đều sử dụng giống nhauseq_file trừu tượng hóa. Tuy nhiên, /proc/uptimenhư ví dụ minh họa, một số tệp vẫn đang được di chuyển để sử dụng seq_filegần đây như năm 2009; Tôi cá là vẫn còn một số sử dụng các cơ chế cũ hơn và thậm chí không có mức độ nguyên tử đó. Những cảnh báo này hiếm khi được ghi nhận. Đối với một tệp nhất định, đảm bảo duy nhất của bạn là đọc nguồn.

Trong trường hợp /proc/net/tcp, bạn có thể đọc nó và phân tích từng dòng mà không sợ hãi. Nhưng nếu bạn cố gắng rút ra bất kỳ kết luận nào từ nhiều dòng cùng một lúc - hãy cẩn thận, các quy trình khác và kernel đang thay đổi nó trong khi bạn đọc nó và có thể bạn đang tạo ra một lỗi.


1
Nguyên tử readdir thì sao? thích đọc / Proc / tự / fd? nó có an toàn không?
thất bại

Không phải là nó trả lời câu hỏi nhưng thêm về làm thế nào để kiểm tra thời gian hoạt động, bạn có thể sử dụng clock_gettime(2)với CLOCK_MONOTONIC(mặc dù có thể có một công nghệ Tôi không biết ở đây nhưng cá nhân tôi đã chỉ nhìn thấy nó với kể từ thời điểm khởi động). Đối với Linux, bạn cũng có tùy chọn sysinfo(2).
Pryftan

44

Mặc dù các tập tin trong /procxuất hiện dưới dạng file thường xuyên trong không gian người dùng, họ không thực sự file mà là đơn vị hỗ trợ các hoạt động tập tin tiêu chuẩn từ userspace ( open, read, close). Lưu ý rằng điều này khá khác so với việc có một tệp thông thường trên đĩa đang được thay đổi bởi kernel.

Tất cả các nhân thực hiện việc in trạng thái bên trong của nó vào bộ nhớ của chính nó bằng cách sử dụng sprintfchức năng giống như và bộ nhớ đó được sao chép vào không gian người dùng bất cứ khi nào bạn thực hiện một read(2)cuộc gọi hệ thống.

Hạt nhân xử lý các cuộc gọi này theo một cách hoàn toàn khác so với các tệp thông thường, điều đó có nghĩa là toàn bộ ảnh chụp nhanh của dữ liệu bạn sẽ đọc có thể sẵn sàng tại thời điểm bạn thực hiện open(2), trong khi hạt nhân đảm bảo rằng các cuộc gọi đồng thời là nhất quán và nguyên tử. Tôi đã không đọc nó ở bất cứ đâu, nhưng nó không thực sự có ý nghĩa khác.

Lời khuyên của tôi là hãy xem việc triển khai tệp Proc trong hương vị Unix cụ thể của bạn. Đây thực sự là một vấn đề triển khai (như định dạng và nội dung của đầu ra) không bị chi phối bởi một tiêu chuẩn.

Ví dụ đơn giản nhất sẽ là việc triển khai uptimetệp Proc trong Linux. Lưu ý cách toàn bộ bộ đệm được tạo ra trong chức năng gọi lại được cung cấp cho single_open.


3
@Ignacio: Tôi chỉ hướng OP theo hướng này bởi vì tôi có ấn tượng rằng anh ấy nghĩ rằng proccác tệp là các tệp thông thường được mở để ghi bởi kernel.
Blagovest Buyukliev

4
Lời khuyên của bạn để xem việc thực hiện các tập tin cụ thể là tốt. Thật không may, dự đoán rằng tất cả các snapshot tại open()là sai đối với nhiều tệp và đặc biệt là /proc/net/tcp, điều mà OP quan tâm. Điều này có ý nghĩa nếu bạn nghĩ về chi phí cung cấp các ngữ nghĩa đó - bạn sẽ phải làm một cái gì đó như khóa các cấu trúc dữ liệu nội bộ ghi lại tất cả các kết nối TCP, mà trên một hệ thống bận rộn là một thảm họa ngay cả khi bạn chỉ giữ nó lâu đủ để quét qua và định dạng dữ liệu vào bộ đệm. Xem câu trả lời của tôi để biết chi tiết về những gì thực sự xảy ra.
Greg Giá

16

/ Proc là một hệ thống tệp ảo: trên thực tế, nó chỉ cung cấp một cái nhìn thuận tiện cho các phần bên trong kernel. Nó chắc chắn an toàn để đọc nó (đó là lý do tại sao nó ở đây) nhưng về lâu dài sẽ có rủi ro, vì nội bộ của các tệp ảo này có thể phát triển với phiên bản kernel mới hơn.

BIÊN TẬP

Thông tin thêm có sẵn trong tài liệu Proc trong tài liệu nhân Linux , chương 1.4 Kết nối mạng Tôi không thể tìm thấy nếu thông tin phát triển thông tin theo thời gian như thế nào. Tôi nghĩ rằng nó đã bị đóng băng khi mở, nhưng không thể có câu trả lời chắc chắn.

EDIT2

Theo Sco doc (không phải linux, nhưng tôi khá chắc chắn tất cả các hương vị của * nix đều hoạt động như vậy)

Mặc dù trạng thái xử lý và do đó, nội dung của các tệp / Proc có thể thay đổi từ tức thời sang tức thời, một lần đọc (2) của tệp / Proc được đảm bảo trả về trạng thái đại diện của `` sane '', nghĩa là, lần đọc sẽ là một ảnh chụp nguyên tử của trạng thái của quá trình. Không có đảm bảo nào áp dụng cho các lần đọc liên tiếp được áp dụng cho tệp / Proc cho một quy trình đang chạy. Ngoài ra, tính nguyên tử đặc biệt không được đảm bảo cho bất kỳ I / O nào được áp dụng cho tệp dưới dạng (không gian địa chỉ); nội dung của không gian địa chỉ của bất kỳ quy trình nào có thể được sửa đổi đồng thời bởi một LWP của quy trình đó hoặc bất kỳ quy trình nào khác trong hệ thống.


3
"Tôi nghĩ" ? Thật tuyệt khi có câu trả lời dứt khoát :)
static_rtti

Với việc thực hiện / Proc trong kernel, điều này cũng đúng với linux. Nếu bạn đọc một tệp Procfs trong một cuộc gọi đọc, thì nó nhất quán - tất nhiên giả sử rằng tệp Proc bạn đọc đã được triển khai chính xác kernelside.
Erik

8
Tôi không nghĩ rằng bạn có thể tìm ra một nguồn thông tin tồi tệ hơn SCO và cố gắng xử lý procnhư thể nó có hành vi tương tự giữa các hạt nhân khác nhau (hoặc thậm chí giả sử nó tồn tại - nó không phải trong một hệ thống Unix ) sẽ giúp bạn có một thế giới bị tổn thương.
Nicholas Knight

1
@Nicholas: tốt, không thể tìm thấy một số câu trả lời dứt khoát trong tài liệu kernel, vui lòng chỉ ra nếu bạn biết.
Bruce

2
Thật thú vị khi các tài liệu SCO nói rằng. Thật không may, điều này không phải lúc nào cũng đúng trong Linux, và đặc biệt nó không đúng /proc/net/tcp, đó là mối quan tâm chính của OP. Thay vào đó, chỉ mỗi hàng riêng lẻ trong đầu ra là nguyên tử. Xem câu trả lời của tôi để biết chi tiết.
Greg Giá

14

API Procfs trong nhân Linux cung cấp một giao diện để đảm bảo rằng việc đọc trả về dữ liệu nhất quán. Đọc các bình luận trong __proc_file_read. Mục 1) trong khối nhận xét lớn giải thích giao diện này.

Điều đó đang được nói, tất nhiên tùy thuộc vào việc thực hiện một tệp Proc cụ thể để sử dụng giao diện này một cách chính xác để đảm bảo dữ liệu trả về của nó là nhất quán. Vì vậy, để trả lời câu hỏi của bạn: không, hạt nhân không đảm bảo tính nhất quán của các tệp Proc trong quá trình đọc nhưng nó cung cấp phương tiện cho việc triển khai các tệp đó để cung cấp tính nhất quán.


4
Thật không may, nhiều tập tin /proctrong thực tế không cung cấp tính nhất quán. Xem câu trả lời của tôi để biết chi tiết.
Greg Giá

3
Ngoài ra, __proc_file_read()không được ủng hộ seq_file. Xem bình luận nghe có vẻ khá bực tức (của Linus) ngay phía trên bình luận khối dài.
Greg Giá

6

Tôi có sẵn nguồn cho Linux 2.6.27.8 vì hiện tại tôi đang phát triển trình điều khiển cho mục tiêu ARM nhúng.

Tệp ... linux-2.6.27.8-lpc32xx/net/ipv4/raw.cở dòng 934 chứa, ví dụ

    seq_printf(seq, "%4d: %08X:%04X %08X:%04X"
            " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d\n",
            i, src, srcp, dest, destp, sp->sk_state,
            atomic_read(&sp->sk_wmem_alloc),
            atomic_read(&sp->sk_rmem_alloc),
            0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp),
            atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops));

đầu ra nào

[wally@zenetfedora ~]$ cat /proc/net/tcp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     
   0: 017AA8C0:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 15160 1 f552de00 299
   1: 00000000:C775 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 13237 1 f552ca00 299
...

trong chức năng raw_sock_seq_show()là một phần của hệ thống phân cấp các chức năng xử lý Procfs . Văn bản không được tạo cho đến khi một read()yêu cầu được tạo ra từ /proc/net/tcptệp, một cơ chế hợp lý vì các lần đọc của Procfs chắc chắn ít phổ biến hơn nhiều so với việc cập nhật thông tin.

Một số trình điều khiển (chẳng hạn như của tôi) triển khai chức năng Proc_read bằng một sprintf() . Sự phức tạp thêm trong việc triển khai trình điều khiển lõi là xử lý đầu ra rất dài có thể không phù hợp với bộ đệm không gian nhân trung gian trong một lần đọc.

Tôi đã thử nghiệm rằng với một chương trình sử dụng bộ đệm đọc 64K nhưng kết quả là bộ đệm không gian kernel có 3072 byte trong hệ thống của tôi để Proc_read trả về dữ liệu. Nhiều cuộc gọi với con trỏ tiến là cần thiết để nhận được nhiều hơn số văn bản được trả về. Tôi không biết cách nào đúng để làm cho dữ liệu được trả về nhất quán khi cần nhiều hơn một i / o. Chắc chắn mỗi mục trong /proc/net/tcplà tự nhất quán. Có một số khả năng các dòng cạnh nhau được chụp nhanh tại các thời điểm khác nhau.


Thực sự xin lỗi, tôi đã không nhận được nó nhiều. Vì vậy, ý bạn là, nếu tôi sử dụng ifstream, nó sẽ không an toàn, nhưng nếu tôi sử dụng readnó sẽ an toàn? Hoặc ifstreamsử dụng nội bộ read? Và bạn đề nghị gì?
Kiril Kirov

@Kiril: Xin lỗi vì sự nhầm lẫn. Đây là một lời giải thích về cách dữ liệu /proc/net/tcpđược định dạng và hoàn toàn độc lập với cách mọi người đọc nó.
wallyk

1
Vâng! Và dự đoán của bạn là chính xác rằng các dòng khác nhau (trong /proc/net/tcp) không đến từ cùng một ảnh chụp nhanh. Xem câu trả lời của tôi cho một số lời giải thích.
Greg Giá

3

Thiếu các lỗi không xác định, không có điều kiện chủng tộc /procnào dẫn đến việc đọc dữ liệu bị hỏng hoặc trộn lẫn dữ liệu cũ và mới. Theo nghĩa này, nó an toàn. Tuy nhiên, vẫn còn điều kiện cuộc đua là phần lớn dữ liệu bạn đọc /proccó khả năng bị lỗi thời ngay khi được tạo và thậm chí ngay cả khi bạn đọc / xử lý dữ liệu. Ví dụ, các quy trình có thể chết bất cứ lúc nào và một quy trình mới có thể được gán cùng một pid; id quá trình duy nhất bạn có thể sử dụng mà không cần điều kiện chủng tộc là quy trình con của chính bạn '. Tương tự với thông tin mạng (cổng mở, v.v.) và thực sự hầu hết thông tin trong /proc. Tôi sẽ coi đó là thực hành xấu và nguy hiểm khi dựa vào bất kỳ dữ liệu nào trong/proclà chính xác, ngoại trừ dữ liệu về quy trình của riêng bạn và có khả năng là quy trình con của nó. Tất nhiên nó vẫn có thể hữu ích để trình bày thông tin khác từ /procngười dùng / quản trị viên để cung cấp thông tin / đăng nhập / vv. mục đích.


Tôi đang làm điều này để có được và sử dụng một số thông tin cho quy trình của riêng tôi (đối với PID của tôi, sử dụng getpid()). Vì vậy, nó phải được an toàn.
Kiril Kirov

1
Vâng, tôi sẽ xem xét rằng hoàn toàn an toàn.
R .. GitHub DỪNG GIÚP ICE

Tôi không đồng ý rằng các quy trình con sẽ có hành vi tốt hơn bất kỳ quy trình nào khác. Về /procgiao diện, tất cả đều có điểm yếu và điểm mạnh giống nhau. Dù sao, OP hỏi về thông tin liên quan đến trình điều khiển thiết bị, không phải các quy trình.
wallyk

1
Nếu pid Nlà quá trình con của bạn, bạn có thể đảm bảo rằng pid Nvẫn đề cập đến quá trình tương tự (có thể chấm dứt) cho đến khi bạn gọi waithàm -f Family trên nó. Điều này đảm bảo rằng không có chủng tộc.
R .. GitHub DỪNG GIÚP ICE

Điều gì với trận lụt -1 và không có lời giải thích?
R .. GitHub DỪNG GIÚP ICE

2

Khi bạn đọc từ tệp / Proc, kernel đang gọi một hàm đã được đăng ký trước là hàm "đọc" cho tệp Proc đó. Xem __proc_file_readhàm trong fs / Proc / generic.c.

Do đó, sự an toàn của Proc đọc chỉ an toàn như chức năng mà kernel gọi để đáp ứng yêu cầu đọc. Nếu chức năng đó khóa chính xác tất cả dữ liệu mà nó chạm vào và trả về cho bạn trong bộ đệm, thì việc đọc bằng chức năng đó là hoàn toàn an toàn. Vì các tệp Proc như tệp được sử dụng để đáp ứng các yêu cầu đọc tới / Proc / net / tcp đã xuất hiện được một thời gian và đã trải qua quá trình đánh giá nghiêm ngặt, chúng an toàn như bạn có thể yêu cầu. Trong thực tế, nhiều tiện ích Linux phổ biến dựa vào việc đọc từ hệ thống tập tin Proc và định dạng đầu ra theo một cách khác. (Ra khỏi đỉnh đầu, tôi nghĩ rằng 'ps' và 'netstat' làm điều này).

Như mọi khi, bạn không cần phải tin lời tôi; bạn có thể nhìn vào nguồn để làm dịu nỗi sợ hãi của bạn. Tài liệu sau đây từ Proc_net_tcp.txt cho bạn biết chức năng "đọc" cho / Proc / net / tcp sống ở đâu, vì vậy bạn có thể xem mã thực tế được chạy khi bạn đọc từ tệp Proc đó và tự xác minh rằng không có khóa nguy hiểm.

Tài liệu này mô tả các giao diện / Proc / net / tcp và / Proc / net / tcp6.
Lưu ý rằng các giao diện này không được hỗ trợ cho tcp_diag. Các giao diện / Proc này cung cấp thông tin về các kết nối TCP hiện đang hoạt động và được triển khai bởi tcp4_seq_show () trong net / ipv4 / tcp_ipv4.c và tcp6_seq_show () trong net / ipv6 / tcp_ipv6.c, tương ứng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.