Tại sao / dev / null một tập tin? Tại sao chức năng của nó không được thực hiện như một chương trình đơn giản?


114

Tôi đang cố gắng để hiểu khái niệm về các tệp đặc biệt trên Linux. Tuy nhiên, việc có một tệp đặc biệt /devcó vẻ ngớ ngẩn khi chức năng của nó có thể được thực hiện bởi một số dòng trong C theo hiểu biết của tôi.

Ngoài ra, bạn có thể sử dụng nó theo cách tương tự, nghĩa là chuyển vào nullthay vì chuyển hướng vào /dev/null. Có một lý do cụ thể để có nó như là một tập tin? Không làm cho nó thành một tệp gây ra nhiều vấn đề khác như quá nhiều chương trình truy cập vào cùng một tệp?


20
Ngẫu nhiên, phần lớn chi phí này cũng cat foo | barlà lý do tại sao tồi tệ hơn nhiều (ở quy mô) hơn bar <foo. catlà một chương trình tầm thường, nhưng ngay cả một chương trình tầm thường cũng tạo ra chi phí (một số trong số đó cụ thể theo ngữ nghĩa của FIFO - vì các chương trình không thể seek()bên trong FIFO, ví dụ, một chương trình có thể được thực hiện hiệu quả với việc tìm kiếm có thể thực hiện các hoạt động tốn kém hơn nhiều khi được cung cấp một đường ống dẫn, với một thiết bị ký tự như /dev/nullnó có thể giả mạo các hoạt động đó hoặc với một tệp thực sự, nó có thể thực hiện chúng, nhưng một FIFO không cho phép bất kỳ loại xử lý theo ngữ cảnh nào).
Charles Duffy

13
grep blablubb file.txt 2>/dev/null && dosomethingkhông thể làm việc với null là một chương trình hoặc một chức năng.
rexkogitans

16
Bạn có thể thấy thật sáng sủa (hoặc ít nhất là mở rộng tâm trí) để đọc về hệ điều hành Plan 9 để xem tầm nhìn "mọi thứ là một tập tin" đang diễn ra ở đâu - sẽ dễ dàng hơn khi thấy sức mạnh của việc có sẵn tài nguyên dưới dạng tệp các đường dẫn một khi bạn thấy một hệ thống hoàn toàn chấp nhận khái niệm (chứ không phải chủ yếu / một phần, giống như Linux / Unix hiện đại).
mtraceur

25
Cũng như không ai chỉ ra rằng trình điều khiển thiết bị chạy trong không gian kernel một chương trình có "một số dòng C" , cho đến nay chưa có câu trả lời nào thực sự giải quyết được giả thuyết "có quá nhiều chương trình truy cập vào cùng một tệp" trong câu hỏi
JdeBP

12
Re "chức năng của nó có thể được thực hiện bởi một số dòng trong C": Bạn sẽ không tin, nhưng nó được thực hiện bởi một số dòng trong C! Ví dụ: phần thân của readhàm /dev/nullbao gồm "return 0" (có nghĩa là nó không làm gì cả và, tôi cho rằng, kết quả là EOF): (Từ github.com/torvalds/linux/blob/master/ trình điều khiển / char / mem.c ) ssize_t read_null(struct file *file, char __user *buf, size_t count, loff_t *ppos) { return 0; }(Ồ, tôi chỉ thấy rằng @JdeBP đã đưa ra quan điểm đó. Dù sao, đây là hình minh họa :-).
Peter A. Schneider

Câu trả lời:


153

Ngoài các lợi ích về hiệu suất của việc sử dụng một thiết bị đặc biệt cho nhân vật, lợi ích chính là tính mô đun . / dev / null có thể được sử dụng trong hầu hết mọi ngữ cảnh nơi tệp được mong đợi, không chỉ trong các đường ống vỏ. Hãy xem xét các chương trình chấp nhận các tệp như các tham số dòng lệnh.

# We don't care about log output.
$ frobify --log-file=/dev/null

# We are not interested in the compiled binary, just seeing if there are errors.
$ gcc foo.c -o /dev/null  || echo "foo.c does not compile!".

# Easy way to force an empty list of exceptions.
$ start_firewall --exception_list=/dev/null

Đây là tất cả các trường hợp sử dụng một chương trình làm nguồn hoặc chìm sẽ cực kỳ cồng kềnh. Ngay cả trong trường hợp đường ống vỏ, thiết bị xuất chuẩn và thiết bị xuất chuẩn có thể được chuyển hướng đến các tệp một cách độc lập, một điều khó thực hiện với các tệp thực thi là phần chìm:

# Suppress errors, but print output.
$ grep foo * 2>/dev/null

14
Ngoài ra, bạn không sử dụng /dev/nullchỉ trong các lệnh của shell. Bạn có thể sử dụng nó trong các tham số khác được cung cấp cho một phần mềm --- ví dụ như trong các tệp cấu hình. --- Fort phần mềm này rất thuận tiện. Nó không cần phải tạo sự khác biệt giữa /dev/nullvà một tập tin thông thường.
pabouk

Tôi không chắc là tôi hiểu phần về các chuyển hướng riêng biệt để thực hiện các lệnh thực thi bị chìm. Trong C, bạn chỉ cần thực hiện một pipe, forkexecvegiống như bất kỳ đường ống quy trình nào khác, chỉ với các thay đổi đối với các dup2cuộc gọi thiết lập các kết nối, phải không? Đó là hầu hết các shell không cung cấp các cách đẹp nhất để làm điều đó, nhưng có lẽ nếu chúng ta không có quá nhiều mẫu thiết bị như tập tin và hầu hết mọi thứ trong /dev/procđược coi là các tệp thực thi, các shell sẽ được thiết kế theo các cách để làm điều đó dễ dàng như chúng ta chuyển hướng bây giờ.
aschepler

6
@aschepler Không khó để chuyển hướng sang thực thi chìm. Đó là việc viết các ứng dụng có thể ghi / đọc từ cả hai tệp null null sẽ phức tạp hơn nếu null null không phải là một tệp. Trừ khi bạn đang nói về một thế giới trong đó thay vì mọi thứ là một tập tin, mọi thứ đều có thể thực thi được? Đó sẽ là một mô hình rất khác so với những gì bạn có trong * nix OS.
Khối

1
@aschepler Bạn quên mất wait4! Bạn đã đúng, chắc chắn có thể chuyển stdout và stderr sang các chương trình khác nhau bằng cách sử dụng POSIX apis và có thể phát minh ra một cú pháp shell thông minh để chuyển hướng stdout và stderr sang các lệnh khác nhau. Tuy nhiên, hiện tại tôi không biết về bất kỳ shell nào như vậy và điểm lớn hơn là / dev / null nằm gọn trong công cụ hiện có (phần lớn hoạt động với các tệp) và / bin / null sẽ không. Chúng tôi cũng có thể tưởng tượng một số API IO giúp gcc dễ dàng (an toàn!) Xuất ra một chương trình như một tệp, nhưng đó không phải là tình huống chúng tôi gặp phải.
ioctl

2
@ioctl liên quan đến đạn pháo; cả zsh và bash ít nhất sẽ cho phép bạn làm những việc như thế grep localhost /dev/ /etc/hosts 2> >(sed 's/^/STDERR:/' > errfile ) > >(sed 's/^/STDOUT:/' > outfile), kết quả là được xử lý riêng errfileoutfile
Matija Nalis

62

Nói một cách công bằng, nó không phải là một tập tin thông thường ; đó là một thiết bị đặc biệt của nhân vật :

$ file /dev/null
/dev/null: character special (3/2)

Nó hoạt động như một thiết bị chứ không phải là một tệp hoặc chương trình có nghĩa là hoạt động đơn giản hơn để chuyển hướng đầu vào hoặc đầu ra từ nó, vì nó có thể được gắn vào bất kỳ mô tả tệp nào, bao gồm cả đầu vào / đầu ra / lỗi tiêu chuẩn.


24
cat file | nullsẽ có rất nhiều chi phí, trước tiên là trong việc thiết lập một đường ống, sinh ra một quy trình, thực hiện "null" trong quy trình mới, v.v. Ngoài ra, nullbản thân nó sẽ sử dụng khá nhiều CPU trong một vòng lặp đọc byte vào bộ đệm sau đó chỉ cần loại bỏ ... Việc thực hiện /dev/nulltrong kernel chỉ hiệu quả hơn theo cách đó. Ngoài ra, nếu bạn muốn vượt qua /dev/nullnhư một đối số, thay vì chuyển hướng thì sao? (Bạn có thể sử dụng <(...)trong bash, nhưng điều đó thậm chí còn nặng tay hơn!)
filbranden

4
Nếu bạn phải chuyển sang một chương trình có tên nullThay vì sử dụng chuyển hướng đến /dev/null, liệu có một cách đơn giản, rõ ràng để bảo trình chạy chương trình trong khi gửi stderr của nó thành null?
Đánh dấu Plotnick

5
Đây là một thiết lập thực sự tốn kém cho một cuộc biểu tình trên cao. Tôi sẽ đề nghị sử dụng /dev/zerothay thế.
chrylis -on đình công-

20
Những ví dụ đó là sai. dd of=-ghi vào một tệp được gọi -, chỉ cần bỏ qua of=để ghi vào thiết bị xuất chuẩn vì đó là nơi ddghi theo mặc định. Đường ống để falsekhông hoạt động vì falsekhông đọc stdin của nó, vì vậy ddsẽ bị giết bằng SIGPIPE. Đối với một lệnh loại bỏ đầu vào của nó bạn có thể sử dụng ... cat > /dev/null. Ngoài ra, so sánh những người có lẽ không liên quan như cổ chai có lẽ sẽ là thế hệ số ngẫu nhiên ở đây.
Stéphane Chazelas

8
Các phiên bản AST của ddvv thậm chí không bận tâm đến việc viết một tòa nhà khi họ phát hiện đích là / dev / null.
Đánh dấu Plotnick

54

Tôi nghi ngờ lý do tại sao có nhiều liên quan đến tầm nhìn / thiết kế hình thành Unix (và do đó là Linux), và những lợi thế bắt nguồn từ nó.

Không nghi ngờ gì có một lợi ích hiệu suất không đáng kể để không tạo ra một quy trình bổ sung, nhưng tôi nghĩ có nhiều hơn thế: Unix ban đầu có một phép ẩn dụ "mọi thứ là một tập tin", có một lợi thế không rõ ràng nhưng thanh lịch nếu bạn nhìn vào nó từ góc độ hệ thống, thay vì quan điểm kịch bản shell.

Giả sử bạn có nullchương trình dòng lệnh và /dev/nullnút thiết bị. Từ góc độ shell-script, foo | nullchương trình thực sự hữu íchtiện lợi , và foo >/dev/nullmất một chút thời gian để gõ và có vẻ kỳ lạ.

Nhưng đây là hai bài tập:

  1. Hãy thực hiện chương trình nullbằng các công cụ Unix hiện có và /dev/null- dễ dàng : cat >/dev/null. Làm xong.

  2. Bạn có thể thực hiện /dev/nulltrong điều khoản null?

Bạn hoàn toàn đúng khi mã C chỉ loại bỏ đầu vào là không đáng kể, vì vậy có thể chưa rõ lý do tại sao có sẵn một tệp ảo cho tác vụ.

Hãy xem xét: hầu hết mọi ngôn ngữ lập trình đã cần để làm việc với các tệp, mô tả tệp và đường dẫn tệp, bởi vì chúng là một phần của mô hình "mọi thứ là một tệp" của Unix ngay từ đầu.

Nếu tất cả những gì bạn có là các chương trình ghi vào thiết bị xuất chuẩn, thì chương trình sẽ không quan tâm nếu bạn chuyển hướng chúng thành một tệp ảo nuốt tất cả ghi hoặc một đường dẫn vào một chương trình nuốt tất cả ghi.

Bây giờ nếu bạn có các chương trình lấy đường dẫn tệp để đọc hoặc ghi dữ liệu (hầu hết các chương trình đều làm như vậy) - và bạn muốn thêm chức năng "đầu vào trống" hoặc "loại bỏ đầu ra này" cho các chương trình đó - tốt, /dev/nullmiễn phí.

Lưu ý rằng tính thanh lịch của nó là làm giảm độ phức tạp mã của tất cả các chương trình có liên quan - đối với mỗi usecase phổ biến nhưng đặc biệt mà hệ thống của bạn có thể cung cấp dưới dạng "tệp" với "tên tệp" thực tế, mã của bạn có thể tránh thêm lệnh tùy chỉnh tùy chọn -line và đường dẫn mã tùy chỉnh để xử lý.

Kỹ thuật phần mềm tốt thường phụ thuộc vào việc tìm các ẩn dụ tốt hoặc "tự nhiên" để trừu tượng hóa một số yếu tố của vấn đề theo cách dễ suy nghĩ hơn nhưng vẫn linh hoạt , để bạn có thể giải quyết cơ bản cùng một loạt các vấn đề cấp cao hơn mà không cần phải dành thời gian và năng lượng tinh thần để thực hiện lại các giải pháp cho các vấn đề cấp thấp hơn liên tục.

"Tất cả mọi thứ là một tập tin" có vẻ là một phép ẩn dụ đó để truy cập vào các nguồn tài nguyên: Bạn gọi opencủa một con đường được đưa ra trong một không gian tên heirarchical, nhận được một tài liệu tham khảo (mô tả tập tin) cho các đối tượng, và bạn có thể readwrite, vv trên file descriptor. Stdin / stdout / stderr của bạn cũng là các mô tả tập tin vừa được mở trước cho bạn. Các đường ống của bạn chỉ là các tập tin và mô tả tập tin, và chuyển hướng tập tin cho phép bạn dán tất cả các phần này lại với nhau.

Unix đã thành công nhiều như nó đã làm một phần vì các trừu tượng này hoạt động tốt như thế nào và /dev/nullđược hiểu rõ nhất như là một phần của toàn bộ.


PS Thật đáng để xem phiên bản Unix của "mọi thứ là một tập tin" và những thứ như /dev/nullbước đầu tiên hướng tới sự khái quát hóa linh hoạt và mạnh mẽ hơn của phép ẩn dụ đã được triển khai trong nhiều hệ thống tiếp theo.

Ví dụ, trong các đối tượng giống như tệp đặc biệt của Unix /dev/nullphải được triển khai trong chính kernel, nhưng hóa ra nó đủ hữu ích để hiển thị chức năng ở dạng tệp / thư mục mà từ đó nhiều hệ thống đã được tạo ra để cung cấp một cách cho các chương trình Để làm việc đó.

Một trong những cái đầu tiên là hệ điều hành Plan 9, được tạo ra bởi một số người đã tạo ra Unix. Sau đó, GNU Hurd đã làm một cái gì đó tương tự với "dịch giả" của nó. Trong khi đó, Linux cuối cùng cũng đã nhận được FUSE (hiện đã lan sang các hệ thống chính thống khác).


8
@PeterCordes điểm của câu trả lời là bắt đầu từ một vị trí không hiểu thiết kế. Nếu mọi người đã hiểu thiết kế, câu hỏi này sẽ không tồn tại.
OrangeDog

1
@mtraceur: Gắn tệp hình ảnh mà không có quyền root? cho thấy một số bằng chứng cho thấy FUSE có thể không cần root, nhưng tôi không chắc.
Peter Cordes

1
@PeterCordes RE: "có vẻ kỳ lạ": Đây không phải là lời xin lỗi cho thiết kế, chỉ là sự thừa nhận về cách nó có vẻ như nếu bạn không nghĩ đến việc triển khai hệ thống theo nó và chưa có khoảnh khắc eureka về hệ thống lợi thế thiết kế trên toàn thế giới. Tôi đã cố gắng làm rõ điều đó bằng cách mở câu đó bằng "từ quan điểm kịch bản shell" và ám chỉ sự tương phản giữa điều đó so với quan điểm hệ thống một vài câu trước đó. Suy nghĩ thêm "có vẻ kỳ lạ" là tốt hơn, vì vậy tôi sẽ điều chỉnh nó thành như vậy. Tôi hoan nghênh các đề xuất từ ​​ngữ để làm cho nó rõ ràng hơn mà không làm cho nó quá dài dòng.
mtraceur

2
Điều đầu tiên tôi được nói với tư cách là một kỹ sư trẻ liên quan đến Unix là "Mọi thứ là một tệp" và tôi thề rằng bạn có thể nghe thấy thủ đô. Và việc nắm bắt ý tưởng đó sớm khiến Unix / Linux có vẻ dễ hiểu hơn rất nhiều. Linux thừa hưởng hầu hết triết lý thiết kế đó. Tôi vui vì ai đó đã đề cập đến nó.
StephenG

2
@PeterCordes, DOS "giải quyết" vấn đề gõ bằng cách làm cho tên tệp ma thuật NULxuất hiện trong mọi thư mục, tức là tất cả những gì bạn phải gõ là > NUL.
Cristian Ciupitu

15

Tôi nghĩ /dev/nulllà một thiết bị nhân vật (hoạt động như một tệp thông thường) thay vì một chương trình vì lý do hiệu suất .

Nếu nó là một chương trình, nó sẽ yêu cầu tải, bắt đầu, lập lịch, chạy và sau đó dừng và dỡ chương trình. Chương trình C đơn giản mà bạn đang mô tả tất nhiên sẽ không tiêu tốn nhiều nguồn tài nguyên, nhưng tôi nghĩ nó tạo ra sự khác biệt đáng kể khi xem xét một số lượng lớn (hàng triệu) hành động chuyển hướng / vận hành vì các hoạt động quản lý quy trình tốn kém trên quy mô lớn như chúng liên quan đến chuyển mạch ngữ cảnh.

Một giả định khác: Đường ống vào một chương trình yêu cầu bộ nhớ được phân bổ bởi chương trình nhận (ngay cả khi nó bị loại bỏ trực tiếp sau đó). Vì vậy, nếu bạn chuyển sang công cụ bạn có mức tiêu thụ bộ nhớ gấp đôi, một lần trên chương trình gửi và một lần nữa trên chương trình nhận.


10
Không chỉ là chi phí thiết lập, mà mỗi lần ghi vào một đường ống đều yêu cầu sao chép bộ nhớ và chuyển đổi ngữ cảnh sang chương trình đọc. (Hoặc ít nhất là một công tắc ngữ cảnh khi bộ đệm ống đầy. Và người đọc phải thực hiện một bản sao khác khi nó readlà dữ liệu). Điều này không đáng kể trên PDP-11 lõi đơn nơi Unix được thiết kế! Băng thông bộ nhớ / sao chép ngày nay rẻ hơn nhiều so với trước đây. Một writecuộc gọi hệ thống tới FD mở /dev/nullcó thể quay lại ngay mà không cần đọc bất kỳ dữ liệu nào từ bộ đệm.
Peter Cordes

@PeterCordes, lưu ý của tôi là tiếp tuyến, nhưng có thể, nghịch lý là, bộ nhớ ghi ngày nay đắt hơn bao giờ hết. CPU 8 lõi có khả năng thực hiện 16 thao tác số nguyên trong một thời gian, trong khi ghi bộ nhớ đầu cuối sẽ hoàn thành trong ví dụ 16 đồng hồ (CPU 4GHz, RAM 250 MHz). Đó là yếu tố của 256. RAM cho CPU hiện đại giống như RL02 cho CPU PDP-11, gần giống như một đơn vị lưu trữ ngoại vi! :) Không đơn giản, tự nhiên, nhưng mọi thứ nhấn vào bộ đệm sẽ bị ghi ra và việc ghi vô dụng sẽ làm mất đi các tính toán khác của không gian bộ đệm quan trọng.
kkm

@kkm: Có, lãng phí khoảng 2x 128kiB bộ nhớ cache L3 trên bộ đệm ống trong kernel và bộ đệm đọc trong nullchương trình sẽ hút, nhưng hầu hết các CPU đa lõi không chạy với tất cả các lõi luôn bận rộn, vì vậy Thời gian CPU để chạy nullchương trình chủ yếu là miễn phí. Trên một hệ thống với tất cả các lõi được chốt, đường ống vô dụng là một vấn đề lớn hơn. Nhưng không, bộ đệm "nóng" có thể được viết lại nhiều lần mà không bị xóa vào RAM, vì vậy chúng tôi chủ yếu chỉ cạnh tranh băng thông L3, không phải bộ đệm. Không tuyệt vời, đặc biệt là trên hệ thống SMT (siêu phân luồng) trong đó các lõi logic khác trên cùng một vật lý đang cạnh tranh ...
Peter Cordes

.... Nhưng tính toán bộ nhớ của bạn rất thiếu sót. Các CPU hiện đại có rất nhiều bộ nhớ song song, do đó, mặc dù độ trễ đối với DRAM là khoảng 200-400 chu kỳ xung nhịp lõi và L3> 40, băng thông là ~ 8 byte / xung nhịp. (Đáng ngạc nhiên, băng thông một luồng đến L3 hoặc DRAM kém hơn trên Xeon nhiều lõi với bộ nhớ bốn kênh so với máy tính để bàn bốn lõi, bởi vì nó bị giới hạn bởi sự đồng thời tối đa của các yêu cầu mà một lõi có thể duy trì trong chuyến bay. Băng thông = max_concurrency / độ trễ: Tại sao Skylake lại tốt hơn Broadwell-E cho thông lượng bộ nhớ đơn luồng? )
Peter Cordes

... Xem thêm 7-cpu.com/cpu/Haswell.html để biết số Haswell so sánh lõi tứ so với lõi 18. Dù sao, có CPU hiện đại có thể nhận được một lượng công việc lố bịch trên mỗi đồng hồ, nếu chúng không bị kẹt chờ bộ nhớ. Số của bạn dường như chỉ có 2 thao tác ALU trên mỗi đồng hồ, như có thể là Pentium từ năm 1993 hoặc ARM cấp thấp hiện đại. Một Ryzen hoặc Haswell có khả năng thực hiện 4 số nguyên ALU ops + 2 ops bộ nhớ cho mỗi lõi trên mỗi đồng hồ, hoặc nhiều hơn nữa với SIMD. ví dụ: Skylake-AVX512 có thông lượng 2 trên mỗi đồng hồ trên vpaddd zmm: 16 phần tử 32 bit cho mỗi lệnh.
Peter Cordes

7

Ngoài "mọi thứ là một tập tin" và do đó dễ sử dụng ở mọi nơi mà hầu hết các câu trả lời khác đều dựa vào, cũng có vấn đề về hiệu suất như @ user5626466 đề cập.

Để hiển thị trong thực tế, chúng tôi sẽ tạo ra chương trình đơn giản gọi là nullread.c:

#include <unistd.h>
char buf[1024*1024];
int main() {
        while (read(0, buf, sizeof(buf)) > 0);
}

và biên dịch nó với gcc -O2 -Wall -W nullread.c -o nullread

(Lưu ý: chúng ta không thể sử dụng lseek (2) trên các đường ống, vì vậy cách duy nhất để thoát ống là đọc từ đó cho đến khi nó rỗng).

% time dd if=/dev/zero bs=1M count=5000 |  ./nullread
5242880000 bytes (5,2 GB, 4,9 GiB) copied, 9,33127 s, 562 MB/s
dd if=/dev/zero bs=1M count=5000  0,06s user 5,66s system 61% cpu 9,340 total
./nullread  0,02s user 3,90s system 41% cpu 9,337 total

trong khi với /dev/nullchuyển hướng tệp tiêu chuẩn, chúng tôi có tốc độ tốt hơn nhiều (do các sự kiện được đề cập: chuyển đổi ngữ cảnh ít hơn, kernel chỉ bỏ qua dữ liệu thay vì sao chép nó, v.v.):

% time dd if=/dev/zero bs=1M count=5000 > /dev/null
5242880000 bytes (5,2 GB, 4,9 GiB) copied, 1,08947 s, 4,8 GB/s
dd if=/dev/zero bs=1M count=5000 > /dev/null  0,01s user 1,08s system 99% cpu 1,094 total

(đây phải là một nhận xét ở đó, nhưng quá lớn cho điều đó và sẽ hoàn toàn không thể đọc được)


Bạn đã kiểm tra phần cứng nào? 4,8 GB / giây là khá thấp so với 23 GB / giây tôi nhận được trên Skylake i7-6700k (DDR4-2666, nhưng bộ đệm nên nóng trong bộ đệm L3. Vì vậy, một phần chi phí tốt là các cuộc gọi hệ thống đắt tiền với Spectre + Giảm thiểu Meltdown được kích hoạt. Điều đó gây tổn hại gấp đôi cho đường ống, bởi vì bộ đệm đường ống nhỏ hơn 1M, do đó, các cuộc gọi hệ thống ghi / đọc nhiều hơn. Tuy nhiên, sự khác biệt gần gấp 10 lần là tồi tệ hơn tôi mong đợi, mặc dù trên hệ thống Skylake của tôi là 23GB / giây so với 3,3GB / giây , chạy x86-64 Linux 4.15.8-1-ARCH, vì vậy đó là hệ số của 6.8. Wow, các cuộc gọi hệ thống hiện rất đắt )
Peter Cordes

1
@PeterCordes 3GB / s với bộ đệm ống 64k gợi ý 2x 103124 tòa nhà mỗi giây ... và số lượng chuyển đổi ngữ cảnh đó, heh. Trên một cpu máy chủ, với 200000 tòa nhà mỗi giây, bạn có thể mong đợi ~ 8% chi phí từ PTI, vì có rất ít bộ làm việc. (Biểu đồ mà tôi tham khảo không bao gồm tối ưu hóa PCID, nhưng có lẽ điều đó không quá quan trọng đối với các bộ công việc nhỏ). Vì vậy, tôi không chắc chắn PTI có tác động lớn ở đó? brendangregg.com/blog/2018-02-09/...
sourcejedi

1
Thật thú vị, vì vậy, đó là Silvermont với 2 MB bộ đệm L2 , do đó, ddbộ đệm + nhận bộ đệm của bạn không phù hợp; có lẽ bạn đang xử lý băng thông bộ nhớ thay vì băng thông bộ đệm cấp độ cuối. Bạn có thể nhận được băng thông tốt hơn với bộ đệm 512k hoặc thậm chí bộ đệm 64k. (Theo stracetrên máy tính để bàn của tôi, writereadđang quay trở lại 1048576, vì vậy tôi nghĩ rằng đó có nghĩa là chúng ta chỉ trả tiền cho người dùng <->. Chi phí hạt nhân của TLB huỷ bỏ hiệu lực + ngành dự đoán tuôn ra một lần mỗi MiB, không mỗi 64k, @sourcejedi Đó là Spectre giảm thiểu mà có chi phí nhiều nhất, tôi nghĩ)
Peter Cordes

1
@sourcejedi: Với tính năng giảm thiểu Spectre được kích hoạt, chi phí syscalltrả lại ngay lập tức ENOSYSlà ~ 1800 chu kỳ trên Skylake với giảm thiểu Spectre được kích hoạt, hầu hết đều wrmsrlà vô hiệu hóa BPU, theo thử nghiệm của @ BeeOnRope . Khi giảm thiểu bị vô hiệu hóa, người dùng-> kernel-> thời gian chuyến đi khứ hồi của người dùng là ~ 160 chu kỳ. Nhưng nếu bạn đang chạm vào nhiều bộ nhớ, giảm thiểu Meltdown cũng rất đáng kể. Hugepages sẽ giúp (các mục TLB ít hơn cần được tải lại).
Peter Cordes

1
@PeterCordes Trên hệ thống unix lõi đơn, chúng tôi chắc chắn sẽ thấy 1 công tắc ngữ cảnh trên 64K, hoặc bất kỳ bộ đệm ống nào của bạn, và điều đó sẽ làm tổn thương ... thực sự tôi cũng thấy [cùng một số công tắc ngữ cảnh với 2 lõi cpu] ( unix.stackexchange.com/questions/439260/ - ); nó cũng phải được tính một chu kỳ ngủ / thức cho mỗi 64k dưới dạng chuyển đổi ngữ cảnh (thành một "quá trình nhàn rỗi" danh nghĩa). Giữ các quy trình đường ống trên cùng một cpu thực sự hoạt động nhanh hơn gấp đôi.
sourcejedi

6

Câu hỏi của bạn được đặt ra như thể một cái gì đó sẽ đạt được có lẽ đơn giản bằng cách sử dụng một chương trình null thay cho một tập tin. Có lẽ chúng ta có thể thoát khỏi khái niệm "tập tin ma thuật" và thay vào đó chỉ là "những ống thông thường".

Nhưng xem xét, một đường ống cũng là một tập tin . Chúng thường không được đặt tên và do đó chỉ có thể được thao tác thông qua các mô tả tệp của chúng.

Hãy xem xét ví dụ hơi khó hiểu này:

$ echo -e 'foo\nbar\nbaz' | grep foo
foo

Sử dụng thay thế quy trình của Bash, chúng ta có thể thực hiện điều tương tự thông qua cách xoay vòng hơn:

$ grep foo <(echo -e 'foo\nbar\nbaz')
foo

Thay thế grepcho echovà chúng ta có thể nhìn thấy dưới nắp:

$ echo foo <(echo -e 'foo\nbar\nbaz')
foo /dev/fd/63

Cấu <(...)trúc chỉ được thay thế bằng một tên tệp và grep nghĩ rằng nó đang mở bất kỳ tệp cũ nào, nó chỉ được đặt tên /dev/fd/63. Ở đây, /dev/fdlà một thư mục ma thuật tạo ra các ống có tên cho mỗi mô tả tệp được sở hữu bởi tệp truy cập vào nó.

Chúng tôi có thể làm cho nó ít phép thuật hơn mkfifođể tạo ra một ống có tên xuất hiện lsvà mọi thứ, giống như một tệp thông thường:

$ mkfifo foofifo
$ ls -l foofifo 
prw-rw-r-- 1 indigo indigo 0 Apr 19 22:01 foofifo
$ grep foo foofifo

Ở nơi khác:

$ echo -e 'foo\nbar\nbaz' > foofifo

và kìa, grep sẽ xuất ra foo.

Tôi nghĩ rằng một khi bạn nhận ra các đường ống và các tệp thông thường và các tệp đặc biệt như / dev / null đều chỉ là các tệp, thì rõ ràng việc triển khai một chương trình null phức tạp hơn . Hạt nhân phải xử lý ghi vào một tệp, nhưng trong trường hợp / dev / null, nó chỉ có thể thả ghi trên sàn, trong khi với một đường ống, nó phải thực sự chuyển các byte sang một chương trình khác, sau đó phải thực sự đọc chúng


@Lyle Vâng? Vậy thì tại sao echo in /dev/fd/63?
Phil Frost

Hừm. Điểm tốt. Chà, điều này được thực hiện bằng vỏ sò, nên có lẽ vỏ của bạn khác với vỏ Bourne cũ mà tôi đã lớn lên cùng.
Lyle

Một điểm khác biệt là echo không đọc từ stdin, trong khi grep thì có, nhưng tôi không thể nghĩ làm thế nào shell sẽ biết điều đó trước khi nó thực thi chúng.
Lyle

1
Và strace làm cho điều này rõ ràng hơn: đối với tôi. bạn có nó chính xác, với bash. Cấu trúc '<(...)' đang làm một cái gì đó khá khác với <tên tệp. Hừm. Tôi đã học được điều gì đó.
Lyle

0

Tôi sẽ lập luận rằng có một vấn đề an ninh ngoài các mô hình và hiệu suất lịch sử. Giới hạn số lượng chương trình với thông tin thực thi đặc quyền, dù đơn giản đến đâu, là nguyên lý cơ bản của bảo mật hệ thống. Một sự thay thế /dev/nullchắc chắn sẽ được yêu cầu để có những đặc quyền như vậy do sử dụng bởi các dịch vụ hệ thống. Các khung bảo mật hiện đại thực hiện một công việc tuyệt vời ngăn chặn các khai thác, nhưng chúng không thể đánh lừa được. Một thiết bị điều khiển kernel được truy cập dưới dạng tệp khó khai thác hơn nhiều.


Điều này nghe có vẻ vô nghĩa. Viết trình điều khiển kernel không có lỗi không dễ hơn viết chương trình không có lỗi đọc + loại bỏ stdin của nó. Nó không cần phải được thiết lập hay bất cứ điều gì, vì vậy đối với cả hai /dev/nullhoặc một chương trình loại bỏ đầu vào được đề xuất, vectơ tấn công sẽ giống nhau: lấy một tập lệnh hoặc chương trình chạy bằng root để làm điều gì đó kỳ lạ (như thử lseekvào /dev/nullhoặc mở nó nhiều lần từ cùng một quá trình, hoặc IDK cái gì. Hoặc gọi /bin/nullvới một môi trường kỳ lạ, hoặc bất cứ điều gì).
Peter Cordes

0

Như những người khác đã chỉ ra, /dev/null một chương trình được tạo thành từ một số dòng mã. Chỉ là những dòng mã này là một phần của kernel.

Để làm cho nó rõ ràng hơn, đây là triển khai Linux: một thiết bị ký tự gọi các chức năng khi đọc hoặc ghi vào. Viết cho /dev/nullcác cuộc gọi write_null , trong khi đọc các cuộc gọi read_null , đã đăng ký tại đây .

Nghĩa đen là một dòng mã: các hàm này không làm gì cả. Bạn chỉ cần nhiều dòng mã hơn ngón tay trên bàn tay nếu bạn đếm các chức năng khác ngoài đọc và viết.


Có lẽ tôi nên nói nó chính xác hơn. Tôi có nghĩa là tại sao thực hiện nó như một thiết bị char thay vì một chương trình. Nó sẽ là một vài dòng theo cách nào đó nhưng việc thực hiện chương trình sẽ được quyết định đơn giản hơn. Như các câu trả lời khác đã chỉ ra, có khá nhiều lợi ích cho việc này; hiệu quả và tính di động trưởng trong số đó.
Ankur S

Chắc chắn rồi. Tôi chỉ thêm câu trả lời này vì thấy việc triển khai thực tế rất thú vị (bản thân tôi đã phát hiện ra nó gần đây), nhưng lý do thực sự là những gì người khác chỉ ra thực sự.
Matthieu Moy

Tôi cũng vậy! Gần đây tôi đã bắt đầu học các thiết bị trong linux và câu trả lời khá nhiều thông tin
Ankur S

-2

Tôi hy vọng rằng bạn cũng biết về / dev / Chargeen / dev / zero và những người khác thích chúng bao gồm / dev / null.

LINUX / UNIX có một vài trong số này - được cung cấp để mọi người có thể sử dụng tốt WAG WRITTEN CODE FrAGMEnTS.

Chargeen được thiết kế để tạo ra một mẫu ký tự cụ thể và lặp lại - nó khá nhanh và sẽ đẩy các giới hạn của các thiết bị nối tiếp và nó sẽ giúp gỡ lỗi các giao thức nối tiếp đã được viết và thất bại một số thử nghiệm hoặc khác.

Zero được thiết kế để điền vào một tệp hiện có hoặc xuất ra rất nhiều zero

/ dev / null chỉ là một công cụ khác có cùng ý tưởng.

Tất cả các công cụ này trong bộ công cụ của bạn có nghĩa là bạn có một nửa cơ hội để làm cho một chương trình hiện có làm một cái gì đó độc đáo mà không cần quan tâm đến chúng (nhu cầu cụ thể của bạn) như các thiết bị hoặc thay thế tệp

Hãy thiết lập một cuộc thi để xem ai có thể tạo ra kết quả thú vị nhất chỉ với một vài thiết bị nhân vật trong phiên bản LINUX của bạn.

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.