[unix] 부분 읽기에서 유닉스 스트림 보조 데이터는 어떻게됩니까?

그래서 유닉스 스트림 보조 데이터에 대한 많은 정보를 읽었지만 모든 문서에서 누락 된 부분은 부분 읽기가있을 때 일어나는 일입니다.

24 바이트 버퍼에 다음 메시지를 수신한다고 가정하십시오.

msg1 [20 byes]   (no ancillary data)
msg2 [7 bytes]   (2 file descriptors)
msg3 [7 bytes]   (1 file descriptor)
msg4 [10 bytes]  (no ancillary data)
msg5 [7 bytes]   (5 file descriptors)

recvmsg에 대한 첫 번째 호출, 나는 모든 msg1 (그리고 msg2의 일부? OS가 그렇게 할 것인가?)을 얻는다. 메시지가 실제로 데이터와 관련하여 무엇을 말하고 있는지 알 때? msg1에서 20 바이트를 비운 다음 recvmsg를 다시 호출하면 msg3과 msg4가 동시에 전달됩니까? msg3 및 msg4의 보조 데이터가 제어 메시지 구조체에 연결됩니까?

실험적 으로이 문제를 찾기 위해 테스트 프로그램을 작성할 수는 있지만 스트리밍 컨텍스트에서 보조 데이터가 어떻게 작동하는지에 대한 설명서 를 찾고 있습니다. 내가 그것에 대해 공식적인 것을 찾을 수 없다는 것이 이상하게 보인다.


이 테스트 프로그램에서 얻은 실험 결과를 여기에 추가하겠습니다.

https://github.com/nrdvana/daemonproxy/blob/master/src/ancillary_test.c

리눅스 3.2.59, 3.17.6

recvmsg를 호출하는 동안 이전의 보조 페이로드를 전달할 필요가없는 한 Linux는 보조 베어링 메시지의 일부를 다른 메시지의 끝에 추가하는 것으로 보입니다. 한 메시지의 보조 데이터가 전달되면 다음 보조 데이터 메시지를 시작하지 않고 짧은 읽기를 반환합니다. 따라서 위의 예에서 내가 읽는 수치는 다음과 같습니다.

recv1: [24 bytes] (msg1 + partial msg2 with msg2's 2 file descriptors)
recv2: [10 bytes] (remainder of msg2 + msg3 with msg3's 1 file descriptor)
recv3: [17 bytes] (msg4 + msg5 with msg5's 5 file descriptors)
recv4: [0 bytes]

BSD 4.4, 10.0

BSD가 리눅스보다 더 많은 정렬을 제공하고, 즉시 짧은 읽기를 제공 하기 전에 보조 데이터와 메시지의 시작입니다. 그러나 비 보조 메시지가 있으면 비 보조 메시지의 끝에 행복하게 추가됩니다. 따라서 BSD의 경우 버퍼가 보조 베어링 메시지보다 큰 경우 거의 패킷과 유사한 동작이 나타납니다. 내가 얻는 읽기는 다음과 같습니다

recv1: [20 bytes] (msg1)
recv2: [7 bytes]  (msg2, with msg2's 2 file descriptors)
recv3: [17 bytes] (msg3, and msg4, with msg3's 1 file descriptor)
recv4: [7 bytes]  (msg5 with 5 file descriptors)
recv5: [0 bytes]

할 것:

여전히 같은이 어떻게 등 오래된 리눅스, 아이폰 OS, 솔라리스에서 발생하는 방법을 알고하는 것입니다 수있는 미래에 일어날 것으로 예상된다.



답변

보조 데이터는 세그먼트의 첫 번째 일반 데이터 옥텟과 함께 대기열에있는 것처럼 수신됩니다 (있는 경우).

POSIX.1-2017

귀하의 질문의 나머지 부분에는 문제가 약간 있습니다.

…이 섹션의 목적 상 데이터 그램은 레코드를 종료하고 특수한 유형의 보조 데이터로 소스 주소를 포함하는 데이터 세그먼트로 간주됩니다.

프로토콜에 의해 데이터가 소켓에 전달 될 때 데이터 세그먼트가 큐에 배치됩니다. 일반 데이터 세그먼트는 전달 될 때 큐의 끝에 배치됩니다. 새로운 세그먼트가 이전 세그먼트와 동일한 유형의 데이터를 포함하고 보조 데이터를 포함하지 않고, 이전 세그먼트가 레코드를 종료하지 않으면 세그먼트는 논리적으로 단일 세그먼트로 병합됩니다.

수신 작업은 둘 이상의 세그먼트에서 데이터 또는 보조 데이터를 반환하지 않아야합니다.

따라서 현대 BSD 소켓은이 추출과 정확히 일치합니다. 이것은 놀라운 일이 아닙니다 :-).

POSIX 표준은 UNIX 이후, BSD와 System V와 같은 분할 이후에 작성되었습니다. 주요 목표 중 하나는 기존의 동작 범위를 이해하고 기존 기능에서 더 많은 분할을 방지하는 것입니다.

리눅스는 BSD 코드를 참조하지 않고 구현되었습니다. 여기서 다르게 동작하는 것 같습니다.

  1. 내가 당신을 올바르게 읽으면, 새로운 세그먼트에 보조 데이터 포함되지만 이전 세그먼트는 포함하지 않을 때 Linux가 추가로 “세그먼트”를 병합하는 것처럼 들립니다 .

  2. “Linux가이 recvmsg 호출 중에 이전 보조 페이로드를 전달할 필요가없는 한, Linux는 보조 베어링 메시지의 일부를 다른 메시지의 끝에 추가 할 것”이라고 전적으로 표준에서 설명하지 않는 것 같습니다. 가능한 설명 중 하나는 경쟁 조건과 관련이 있습니다. “세그먼트”의 일부를 읽으면 보조 데이터가 수신됩니다. 아마도 리눅스는 이것을 세그먼트의 나머지 부분이 더 이상 보조 데이터를 포함하는 것으로 간주하지 않는다는 것을 의미하는 것으로 해석했습니다! 따라서 새 세그먼트가 수신되면 표준에 따라 또는 위의 차이 1에 따라 병합됩니다.

최대한 이식 가능한 프로그램을 작성하려면이 영역을 모두 피해야합니다. 보조 데이터를 사용할 때는 데이터 그램 소켓 을 사용하는 것이 훨씬 일반적 입니다. 기술적으로 POSIX와 같은 것을 제공하고자하는 모든 이상한 플랫폼에서 작업하고 싶다면, 당신의 질문은 어둡고 테스트되지 않은 곳으로 향하는 것 같습니다.


리눅스가 여전히 몇 가지 중요한 원칙을 따른다고 주장 할 수 있습니다.

  1. “보조 데이터는 세그먼트의 첫 번째 일반 데이터 옥텟과 함께 큐잉 된 것처럼 수신됩니다.”
  2. 보조 데이터는 입력 한대로 “연결”되지 않습니다.

그러나 Linux 동작이 BSD 동작과 비교할 때 특히 유용하다고 확신하지 않습니다 . 설명하는 프로그램이 Linux 관련 해결 방법을 추가 해야하는 것처럼 보입니다. 그리고 나는 리눅스가 왜 그렇게 기대하는지에 대한 정당성을 모른다.

a

Linux 커널 코드를 작성할 때 현명 해 보였지만 어떤 프로그램도 테스트하거나 연습하지 않았습니다.

또는 이 하위 세트에서 주로 작동 하는 일부 프로그램 코드에 의해 실행될 수 있지만 원칙적으로 에지 “버그”또는 경쟁 조건을 가질 수 있습니다.

만약 당신이 리눅스의 행동과 의도 된 사용법을 이해할 수 없다면, 나는 이것을 리눅스에서 “테스트되지 않은 어두운 곳”으로 취급한다고 주장한다.


답변