Linux에서 특수 파일의 개념을 이해하려고 노력하고 있습니다. 그러나 /dev
내가 아는 한, C 몇 줄로 기능을 구현할 수 있는데 특별한 파일을 갖는 것은 어리석은 것 같습니다.
또한 거의 동일한 방식으로 사용할 수 있습니다. 즉, null
리디렉션하는 대신 파이프 /dev/null
로 파일로 저장하는 특별한 이유가 있습니까? 파일로 저장하면 동일한 파일에 액세스하는 너무 많은 프로그램과 같은 다른 많은 문제가 발생하지 않습니까?
답변1
캐릭터별 장치를 사용함으로써 얻을 수 있는 성능상의 장점 외에도 가장 큰 장점은모듈식. /dev/null은 쉘 파이프뿐만 아니라 파일이 예상되는 거의 모든 컨텍스트에서 사용할 수 있습니다. 파일을 명령줄 인수로 받아들이는 프로그램을 생각해 보세요.
# 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
이러한 경우 프로그램을 소스나 싱크로 사용하는 것은 매우 번거로울 수 있습니다. 쉘 파이프의 경우에도 stdout 및 stderr은 독립적으로 파일로 리디렉션될 수 있으며 이는 실행 파일을 싱크로 사용하기 어렵습니다.
# Suppress errors, but print output.
$ grep foo * 2>/dev/null
답변2
공평하게 말하면 이것은일반 파일그 자체입니다.캐릭터 특수 장치:
$ file /dev/null
/dev/null: character special (3/2)
파일이나 프로그램이 아닌 장치로 실행된다는 사실은 표준 입력/출력/오류를 포함한 모든 파일 설명자에 첨부할 수 있기 때문에 입력 또는 출력의 리디렉션이 더 간단하다는 것을 의미합니다.
답변3
나는 의심한다왜Unix(및 후속 Linux)를 형성한 비전/설계 및 그에 따른 이점과 많은 관련이 있습니다.
추가 프로세스를 시작하지 않음으로써 무시할 수 없는 성능상의 이점이 있다는 것은 의심할 여지가 없지만, 그 이상의 것이 있다고 생각합니다. 초기 Unix에는 "모든 것이 파일입니다"라는 비유가 있었습니다. 우아한 장점은 쉘 스크립트 관점이 아닌 시스템 관점에서 비롯됩니다.
null
명령줄 프로그램과 /dev/null
장치 노드가 있다고 가정합니다 . 쉘 스크립트 관점에서 볼 때, foo | null
프로그램은 실제로효과가있다그리고편리한, foo >/dev/null
입력하는 데 시간이 조금 더 걸리고 이상해 보입니다.
하지만 여기에는 두 가지 연습이 있습니다.
null
기존 Unix 도구와 - easy :을 사용하여/dev/null
이 프로그램을 구현해 보겠습니다cat >/dev/null
. 완벽한./dev/null
그것을 구현할 수 있나요null
?
당신 말이 맞습니다. 입력 C 코드를 버리는 것만으로는 충분하지 않으므로 아직 명확하지 않을 수 있습니다.왜이 작업에 사용할 수 있는 더미 파일이 있으면 유용합니다.
고려 사항: 거의 모든 프로그래밍 언어이미 필요함파일, 파일 설명자 및 파일 경로는 처음부터 Unix의 "모든 것이 파일입니다" 패러다임의 일부였기 때문에 작업합니다.
당신이 가진 것이 표준 출력에 쓰는 프로그램뿐이라면, 그 프로그램은 모든 쓰기를 삼키는 가상 파일로 리디렉션하는지, 아니면 모든 쓰기를 삼키는 프로그램으로 파이프를 리디렉션하는지 상관하지 않습니다.
이제 프로그램이 데이터를 읽거나 쓰기 위해 파일 경로를 사용하고(대부분의 프로그램이 수행함) 해당 프로그램에 "빈 입력" 또는 "이 출력 삭제" 기능을 추가하려는 경우에는 /dev/null
이 기능이 필요하지 않습니다.
그 우아함은 모든 관련 프로그램의 코드 복잡성을 줄이는 데 있습니다. 시스템이 실제 "파일 이름"이 있는 "파일"로 제공할 수 있는 모든 일반적이지만 특별한 사용 사례에 대해 코드는 라인 옵션 및 사용자 정의 코드 경로 추가를 방지합니다. 처리할 사용자 정의 명령입니다.
좋은 소프트웨어 엔지니어링은 문제의 특정 요소를 추상화하기 위해 좋은 또는 "자연스러운" 은유를 찾는 데 의존하는 경우가 많습니다.생각하기 쉬워진다하지만유연성을 유지하다, 동일한 하위 수준 문제에 대한 솔루션을 지속적으로 다시 구현하는 데 시간과 노력을 들이지 않고도 기본적으로 동일한 범위의 상위 수준 문제를 해결할 수 있습니다.
"모든 것이 파일이다"는 리소스 액세스에 대한 비유인 것 같습니다. open
계층적 네임스페이스에서 지정된 경로를 호출하고, 개체(파일 설명자)에 대한 참조를 가져오고, read
파일 설명자에서 등의 작업을 수행할 수 있습니다. write
stdin/stdout/stderr도 미리 열려 있는 파일 설명자입니다. 파이프는 파일과 파일 설명자일 뿐이며, 파일 리디렉션을 사용하면 이러한 모든 조각을 하나로 묶을 수 있습니다.
Unix가 성공하는 이유 중 하나는 이러한 추상적인 개념이 함께 잘 작동하고 /dev/null
전체의 일부로 가장 잘 이해되기 때문입니다.
/dev/null
PS 많은 후속 시스템에서 구현된 이 비유의 보다 유연하고 강력한 일반화를 향한 첫 번째 단계로서 "모든 것이 파일입니다"라는 Unix 버전을 살펴볼 가치가 있습니다 .
예를 들어 유닉스에서는 특별한 파일과 유사한 객체를 /dev/null
커널 자체에서 구현해야 했지만 파일/폴더 형태로 기능을 노출할 만큼 유용하다는 것이 입증되었으며 그 이후로 프로그램에 다음과 같은 기능을 제공하기 위해 여러 시스템이 만들어졌습니다. 이를 수행하는 방법입니다.
첫 번째는 일부 Unix 개발자가 개발한 Plan 9 운영 체제입니다. 나중에 GNU Hurd는 "번역기"와 비슷한 작업을 수행했습니다. 한편, Linux는 마침내 FUSE를 갖게 되었습니다(현재 다른 주류 시스템으로도 확장되었습니다).
답변4
"모든 것이 파일입니다"라는 이유로 대부분의 다른 답변이 기반으로 하는 사용 편의성 외에도 @user5626466에서 언급한 성능 문제도 있습니다.
이를 실제로 보여주기 위해 다음과 같은 간단한 프로그램을 만들겠습니다 nullread.c
.
#include <unistd.h>
char buf[1024*1024];
int main() {
while (read(0, buf, sizeof(buf)) > 0);
}
그리고 그것을 컴파일gcc -O2 -Wall -W nullread.c -o nullread
(참고: 파이프에서는 lseek(2)를 사용할 수 없으므로 파이프를 비우는 유일한 방법은 파이프가 빌 때까지 데이터를 읽는 것입니다.)
% 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
그리고 표준 /dev/null
파일 리디렉션을 사용하면 더 나은 속도를 얻을 수 있습니다(언급된 사실로 인해 컨텍스트 전환이 적고 커널이 데이터를 복사하는 대신 데이터를 무시하는 등).
% 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
(이것은 주석이어야 하지만 너무 커서 전혀 읽을 수 없습니다.)