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


18

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

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, 솔라리스에서 발생하는 방법을 알고하는 것입니다 수있는 미래에 일어날 것으로 예상된다.


스트림과 패킷을 혼동하지 마십시오. 스트림에서 데이터가 전송 된 것과 동일한 청크로 전달 될 것이라는 보장은 없습니다.이를 위해서는 스트림 기반이 아닌 패킷 기반 프로토콜이 필요합니다.
ctrl-alt-delor

그것이 바로 내가이 질문을하는 이유입니다.
M Conrad

순서는 유지되어야합니다. 그것이 스트림이하는 일입니다. 블로킹 읽기가 0을 반환하면 스트림의 끝입니다. 다른 숫자를 반환하면 더 많은 숫자가있을 수 있으므로 적어도 하나 이상의 읽기를 수행해야합니다. message1, message2 등은 없습니다. 메시지 구분 기호가 전송되지 않습니다. 필요한 경우이를 프로토콜에 추가해야합니다.
ctrl-alt-delor

1
특히 텍스트 스트림 프로토콜이 있으며 텍스트 줄로 파일 설명자를 전달하는 명령을 추가하고 있습니다. 코드를 올바르게 작성하려면 메시지의 텍스트와 관련하여이 보조 데이터가 수신되는 순서를 알아야합니다.
M 콘래드

1
@ MConrad : POSIX.1g 사양의 사본을 얻으려고합니다. 명시 적으로 작성되지 않은 경우 구현 관련 동작을 기대할 수 있습니다.
Laszlo Valko

답변:


1

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

- POSIX.1-2017

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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


심층적 인 검토에 감사드립니다! 여기서 테이크 아웃은 두 개의 버퍼 (각각 데이터 부분과 보조 부분) 로이를 안전하게 처리 할 수 ​​있다고 생각합니다. 첫 번째 읽기에서 파일 설명자를 수신했지만 메시지에 속하지 않지만 다른 메시지가 시작되면 다음 읽기에도 보조 데이터가 포함되어 있으면 첫 번째 보조 페이로드를 소유하는 데이터 메시지의 끝을 확실히 찾을 수 있습니다. 그 두 번째 읽기에서. 앞뒤로 번갈아 가면서 항상 첫 번째 바이트의 위치에 따라 페이로드와 메시지를 일치시킬 수 있어야합니다.
M 콘래드
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.