c++ - / proc / 파일을 파싱하는 것이 안전합니까?


/proc/net/tcp/ 를 분석하고 싶지만 안전합니까?

/proc/ 에서 파일을 열고 두려워하지 않고 다른 프로세스 (또는 OS 자체)가 동시에 변경하게하려면 어떻게해야합니까?


Answers



일반적으로, 아니오. (따라서 대부분의 답변이 잘못되었습니다.) 원하는 속성에 따라 안전 할 수도 있습니다. /proc 에서 파일의 일관성에 대해 너무 많이 생각하면 코드에 버그가 생기기 쉽습니다. 예를 들어 /proc/mounts 가 일관된 스냅 샷이라고 가정했을 때 발생하는이 버그를 참조하십시오.

예 :

  • /proc/uptime 은 다른 답변에서 언급 한 것처럼 완전히 원자 적입니다 .하지만 Linux 2.6.30 이후에만 2 년 미만입니다. 그래서이 작고 사소한 파일조차도 그때까지 경쟁 상태에 처해 있었고 여전히 대부분의 엔터프라이즈 커널에 있습니다. 현재 소스를 보려면 fs/proc/uptime.c 를, 원자 적으로 만든 커밋을 참조하십시오. 2.6.30 이전의 커널에서는 파일을 open 약간 read 다음 나중에 다시 read 다시 read 첫 번째 조각과 일치하지 않게됩니다. (방금 설명해 봤지만 재미있게 놀아보세요.)

  • /proc/mounts단일 read 시스템 호출 내에서 원자 적입니다. 따라서 전체 파일을 한꺼번에 read 시스템의 마운트 지점에 대한 일관된 단일 스냅 샷을 얻을 수 있습니다. 그러나 여러 개의 read 시스템 호출을 사용하는 경우 - 파일이 크다면 일반 I / O 라이브러리를 사용하고이 문제에 특별한주의를 기울이지 않은 경우 정확히 발생합니다. 경쟁 조건. 일관성있는 스냅 샷을 얻지 못할뿐만 아니라, 시작하기 전에 존재했지만 멈추지 않은 마운트 포인트는 사용자가 보는 것에 누락 될 수 있습니다. 하나의 read() 대해 원자 m_start() 을 확인하려면 fs/namespace.c m_start() 보고 m_stop() 까지 유지되는 마운트 포인트의 목록을 보호하는 세마포어를 가져온다. read() 가 완료됩니다. 무엇이 잘못 될지 알아 보려면 blithely read /proc/mounts 읽는 고품질의 소프트웨어 에서 작년의 버그 (위에 링크 된 동일한 버그)를 참조하십시오.

  • /proc/net/tcp 는 실제로 묻고있는 것이지만, 그보다 덜 일관성이 있습니다. 테이블의 각 행 내에서만 원자가됩니다 . 이것을 보시려면 net/ipv4/tcp_ipv4.c established_get_next() 와 같은 파일의 established_get_next() 보고 각 항목에서 차례대로 꺼내는 잠금을보십시오. 행마다 일관성이 부족하다는 것을 입증하기 위해 재현 코드가 없지만 일관성을 유지할 수있는 잠금 장치가 없습니다. 네트워크가 종종 시스템의 매우 바쁜 부분이기 때문에이 진단 도구에서 일관된 뷰를 제시하는 것이 오버 헤드가되지 않습니다.

각 행에 /proc/net/tcp atomic를 유지하는 다른 부분은 seq_read() 에서 버퍼링하는 seq_read() fs/seq_file.c 에서 읽을 수 있습니다. 이렇게하면 한 행의 일부를 read() 후에는 전체 행의 텍스트가 버퍼에 유지되어 다음 read() 가 새 행을 시작하기 전에 나머지 행을 가져 오게됩니다. /proc/mounts 에서 여러 개의 read() 호출을 수행하더라도 각 행을 원자 적으로 유지하기 위해 동일한 메커니즘이 사용되며, 새로운 커널에서 /proc/uptime 이 원자 적으로 유지되는 데 사용되는 메커니즘이기도합니다. 이 메커니즘은 커널이 메모리 사용에 대해주의를 기울이기 때문에 전체 파일을 버퍼링하지 않습니다 .

/proc 있는 대부분의 파일은 적어도 /proc/net/tcp 와 같이 일관성이 있습니다. 각 행은 그들이 제공하는 정보에 관계없이 동일한 seq_file 추상화를 사용하기 때문에 각 행에 일관성있는 그림 하나를 제공합니다. 그러나 /proc/uptime 예제에서 볼 수 있듯이 일부 파일은 2009 년처럼 seq_file 을 사용하기 위해 아직 마이그레이션 중입니다. 더 오래된 메커니즘을 사용하고 심지어 그 정도의 원 자성을 가지고 있지 않은 것들이 여전히있을 것입니다. 이러한 경고는 거의 문서화되지 않습니다. 주어진 파일의 유일한 보장은 소스를 읽는 것입니다.

/proc/net/tcp , 그것을 읽고 두려움없이 각 행을 구문 분석 할 수 있습니다. 그러나 한 번에 여러 줄에서 결론을 내리려는 경우주의하십시오. 다른 프로세스와 커널 이를 읽는 동안 커널 변경하고 있으므로 버그가 발생했을 수 있습니다.




/proc 파일은 userspace에 일반 파일로 표시되지만 실제 파일이 아니라 userspace ( open , read , close )에서 표준 파일 작업을 지원하는 엔티티입니다. 이것은 커널에 의해 변경되는 디스크상의 일반 파일을 갖는 것과는 매우 다른 점에 유의하십시오.

커널이 수행하는 모든 작업은 sprintf 와 같은 함수를 사용하여 내부 상태를 자체 메모리에 인쇄하고 read(2) 시스템 호출을 실행할 때마다 메모리가 사용자 공간에 복사됩니다.

커널은 일반 파일과는 완전히 다른 방식으로 이러한 호출을 처리하므로 open(2) 읽을 때 데이터의 전체 스냅 샷을 준비 할 수 있고 커널은 동시 호출이 일관되고 원자 적이다. 나는 그걸 어디서든지 읽지는 못했지만 실제로는 그렇지 않다는 것을 의미하지는 않습니다.

내 조언은 특정 유닉스 풍미에서 proc 파일의 구현을 살펴 보는 것이다. 이것은 실제로 표준에 의해 규율되지 않는 구현 문제 (출력의 형식 및 내용)입니다.

가장 간단한 예는 Linux에서 uptime proc 파일을 구현하는 것입니다. single_open 제공된 콜백 함수에서 전체 버퍼가 생성되는 방식에 single_open .




/ proc은 가상 파일 시스템입니다. 사실, 커널 내부를 쉽게 볼 수 있습니다. 확실히 읽는 것이 안전합니다 (그것이 여기에있는 이유입니다). 그러나이 가상 파일의 내부는 새로운 버전의 커널로 발전 할 수 있으므로 장기적으로는 위험합니다.

편집하다

리눅스 커널 문서 , 1.4 장 있는 proc 문서에서 더 많은 정보를 얻을 수있다. 네트워킹 정보가 어떻게 시간에 따라 진화하는지 알 수 없다. 나는 그것이 열렸을 때 얼어 붙었다 고 생각했지만 명확한 답을 가질 수는 없다.

EDIT2

Sco doc 에 따르면 (Linux가 아니지만, * nix의 모든 맛은 그런 것처럼 보입니다)

프로세스 상태와 결과적으로 / proc 파일의 내용이 순식간에 변경 될 수 있지만 / proc 파일의 단일 읽기 (2)는 상태의 "정상"표현을 반환하도록 보장됩니다. 즉, 읽기는 프로세스 상태의 원자 스냅 샷. 실행중인 프로세스의 / proc 파일에 연속적으로 적용되는 읽기에는 이러한 보장이 적용되지 않습니다. 또한 원 자성은 as (주소 공간) 파일에 적용된 입출력에 대해 특별히 보장되지 않습니다. 프로세스의 주소 공간 내용은 해당 프로세스의 LWP 또는 시스템의 다른 프로세스에 의해 동시에 수정 될 수 있습니다.




Linux 커널의 procfs API는 읽기가 일관성있는 데이터를 반환하도록하는 인터페이스를 제공합니다. __proc_file_read 있는 주석을 읽으십시오. 항목 1)은이 인터페이스를 설명합니다.

물론,이 인터페이스를 올바르게 사용하여 반환 된 데이터가 일관성이 있는지 확인하는 것이 특정 proc 파일의 구현에 달려 있습니다. 그래서, 질문에 답하기 위해 : 아니오, 커널은 읽기 동안 proc 파일의 일관성을 보장하지 않지만 일관성을 제공하기 위해 파일의 구현을위한 수단을 제공합니다.




임베디드 ARM 타겟에서 드라이버 개발을하고 있기 때문에 리눅스 2.6.27.8 용 소스가 있습니다.

... 934 행의 ... linux-2.6.27.8-lpc32xx/net/ipv4/raw.c 파일에는 다음과 같은 내용이 들어 있습니다.

    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));

어느 출력

[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
...

in function raw_sock_seq_show() 이것은 procfs 처리 함수의 계층의 일부이다. 텍스트는 /proc/net/tcp 파일에 대한 read() 요청이있을 때까지 생성되지 않습니다. procfs 읽기는 정보를 업데이트하는 것보다 훨씬 일반적이지 않기 때문에 적절한 메커니즘입니다.

일부 드라이버 (예 : 광산)는 단일 sprintf() 사용하여 proc_read 함수를 구현합니다. 코어 드라이버 구현에서의 추가 복잡성은 단일 읽기 동안 중간 커널 공간 버퍼에 맞지 않는 잠재적으로 매우 긴 출력을 처리하는 것입니다.

필자는 64K 읽기 버퍼를 사용하는 프로그램에서 proc_read가 데이터를 반환하도록 내 시스템에서 3072 바이트의 커널 공간 버퍼를 테스트했습니다. 많은 텍스트를 반환하는 것보다 포인터를 가져 오는 다중 호출이 필요합니다. 하나 이상의 I / O가 필요할 때 반환 된 데이터를 일관성있게 유지하는 올바른 방법을 모릅니다. 물론 /proc/net/tcp 각 항목은 일관성이 있습니다. 선이 서로 다른 시간에 스냅 샷이 될 가능성이 있습니다.




알려지지 않은 버그가 없으므로, /proc 에는 손상된 데이터 또는 오래된 데이터와 새 데이터가 혼합되어 읽히는 경쟁 조건이 없습니다. 이 의미에서, 그것은 안전합니다. 그러나 /proc 에서 읽은 많은 데이터가 생성되는 즉시 잠재적으로 구식이되고 경쟁자가 읽거나 처리하는 시점까지는 여전히 경쟁 조건이 있습니다. 예를 들어 프로세스는 언제든지 죽을 수 있으며 새로운 프로세스에는 동일한 PID가 할당 될 수 있습니다. 경합 조건없이 사용할 수있는 유일한 프로세스 ID는 자신의 하위 프로세스입니다. ' 네트워크 정보 (열린 포트 등)에 대해서도 동일하지만 /proc 에있는 대부분의 정보. 자신의 프로세스 및 잠재적으로 하위 프로세스에 관한 데이터를 제외하고 /proc 모든 데이터에 의존하는 것은 위험하고 위험한 방법이라고 생각합니다. 물론 정보 / 로깅 / 등을 위해 /proc 다른 정보를 사용자 / 관리자에게 제시하는 것이 여전히 유용 할 수 있습니다. 목적.




/ proc 파일에서 읽을 때 커널은 사전에 해당 proc 파일의 "읽기"기능으로 등록 된 기능을 호출합니다. fs / proc / generic.c의 __proc_file_read 함수를 참조하십시오.

따라서 proc 읽기의 안전성은 커널이 읽기 요청을 만족시키기 위해 호출하는 기능만큼이나 안전합니다. 이 함수가 버퍼에있는 모든 데이터를 제대로 잠그고 사용자에게 반환하면 해당 함수를 사용하여 읽는 것이 안전합니다. / proc / net / tcp에 대한 읽기 요청을 만족시키기 위해 사용되는 것과 같은 proc 파일은 잠시 동안 있었고 꼼꼼한 검토를 거쳤기 때문에 여러분이 요청할 수있는만큼 안전합니다. 사실, 많은 일반적인 리눅스 유틸리티는 proc 파일 시스템에서 읽는 것과 다른 방식으로 출력을 포맷팅하는 것에 의존합니다. (내 머리 꼭대기에서, 나는 'ps'와 'netstat'이 이것을한다고 생각한다).

언제나 그렇듯이, 당신은 내 말을들을 필요가 없습니다. 당신은 당신의 두려움을 진정시키기위한 근원을 볼 수 있습니다. proc_net_tcp.txt의 다음 문서는 / proc / net / tcp에 대한 "읽기"기능의 위치를 ​​알려주므로 해당 proc 파일에서 읽을 때 실행되는 실제 코드를보고 아무 것도 없다는 것을 확인할 수 있습니다 잠금 위험.

이 문서는 / proc / net / tcp와 / proc / net / tcp6 인터페이스에 대해 설명합니다.
이 인터페이스는 tcp_diag를 위해 더 이상 사용되지 않습니다. 이 / proc 인터페이스는 현재 활성화 된 TCP 연결에 대한 정보를 제공하며 net / ipv4 / tcp_ipv4.c의 tcp4_seq_show () 및 net / ipv6 / tcp_ipv6.c의 tcp6_seq_show ()로 각각 구현됩니다.