부분 읽기 시 Unix 스트림 보조 데이터는 어떻게 되나요?

부분 읽기 시 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가 이렇게 합니까?)를 모두 얻습니다. msg2의 일부를 얻으면 즉시 보조 데이터를 얻고 다음 읽기를 위해 저장해야 합니까? 메시지가 실제로 데이터로 무엇을 해야 하는지 알려준다는 것을 알고 계십니까? msg1에서 20바이트를 해제한 다음 recvmsg를 다시 호출하면 msg3과 msg4를 모두 전달합니까? msg3과 msg4의 보조 데이터가 제어 메시지 구조에 연결되어 있습니까?

이것을 실험적으로 알아내기 위해 테스트 프로그램을 작성할 수 있지만, 나는 다음을 찾고 있습니다.문서스트리밍 컨텍스트에서 보조 데이터가 작동하는 방식에 대해 설명합니다. 이상한 점은 공식적인 정보를 찾을 수 없다는 것입니다.


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

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

리눅스 3.2.59, 3.17.6

Linux는 recvmsg 호출 중에 이전 보조 페이로드를 전달할 필요가 없는 한 보조 베어러 메시지의 일부를 다른 메시지 끝에 추가하는 것으로 보입니다. 메시지의 보조 데이터가 전송되면 다음 보조 데이터 메시지를 시작하는 대신 간단한 읽기를 반환합니다. 따라서 위의 예에서 내가 얻는 판독값은 다음과 같습니다.

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는 Linux보다 더 많은 정렬을 제공하며 즉시 짧은 읽기를 수행할 수 있습니다.앞으로보조 데이터로 메시지 시작. 그러나 2차 베어러 메시지 끝에 2차가 아닌 베어러 메시지를 추가하는 것은 가능합니다. 따라서 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]

할 것:

이전 버전의 Linux, iOS, Solaris 등에서 어떻게 발생하는지, 그리고 어떻게 발생하는지 여전히 궁금합니다.할 수 있다미래에는 이런 일이 일어날 것으로 예상됩니다.

답변1

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

--POSIX.1-2017

나머지 질문에 대해서는 상황이 조금 더 까다로워집니다.

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

프로토콜을 통해 데이터가 소켓으로 전송되면 데이터 세그먼트가 대기열에 배치됩니다. 일반 데이터 세그먼트는 전송 시 대기열 끝에 배치됩니다. 새 세그먼트가 이전 세그먼트와 동일한 유형의 데이터를 포함하고 보조 데이터를 포함하지 않고 이전 세그먼트가 레코드를 종료하지 않는 경우 세그먼트는 논리적으로 단일 세그먼트로 병합됩니다.

수신 작업은 여러 세그먼트에서 데이터나 보조 데이터를 반환해서는 안 됩니다.

따라서 최신 BSD 소켓은 이 발췌문과 정확히 일치합니다. 이는 놀라운 일이 아닙니다 :-).

POSIX 표준은 UNIX 이후, 그리고 BSD와 System V 등이 분리된 이후에 작성되었습니다. 주요 목표 중 하나는 기존 동작 범위를 이해하고 기존 기능의 추가 단편화를 방지하는 것입니다.

Linux 구현에서는 BSD 코드를 참조하지 않습니다. 여기서는 다르게 행동하는 것 같습니다.

  1. 내가 올바르게 들었다면 Linux도 새 세그먼트가 나타날 때 "세그먼트"를 병합하는 것처럼 들립니다.하다보조 데이터가 포함되어 있지만 이전 단락에는 포함되어 있지 않습니다.

  2. "Recvmsg 호출 중에 이전 보조 페이로드가 전달될 필요가 없는 한 Linux는 보조 전달자 메시지의 일부를 다른 메시지 끝에 추가합니다"라는 요점이 표준에 의해 완전히 설명되지 않는 것 같습니다. 한 가지 가능한 설명은 경쟁 조건과 관련이 있습니다. "스니펫"의 일부를 읽으면 보조 데이터를 받게 됩니다. 아마도 Linux는 이를 세그먼트의 나머지 부분이 더 이상 보조 데이터를 포함하는 것으로 간주하지 않는다는 의미로 해석할 것입니다. 따라서 새 세그먼트가 수신되면 표준에 따라 또는 위의 diff 1에 따라 병합됩니다.

이식성이 극대화된 프로그램을 작성하려면 이 영역을 완전히 피해야 합니다. 보조 데이터로 작업할 때 다음을 사용하는 것이 더 일반적입니다.데이터그램소켓. 기술적으로 대부분 POSIX와 같은 것을 제공하기를 열망하는 모든 이상한 플랫폼에서 작업하고 싶다면 귀하의 질문은 어둡고 테스트되지 않은 구석으로 모험을 떠나는 것처럼 보입니다.


Linux는 여전히 몇 가지 중요한 원칙을 따르고 있다고 말할 수 있습니다.

  1. "보조 데이터는 세그먼트의 첫 번째 일반 데이터 옥텟과 함께 대기열에 있는 것처럼 수신됩니다."
  2. 말씀하신 것처럼 보조 데이터는 결코 "연결"되지 않습니다.

하지만 나는 리눅스의 행동이 특별하다고 생각하지 않는다.효과가있다, BSD 동작과 비교할 때. 설명하신 절차에는 Linux 관련 해결 방법을 추가해야 하는 것으로 보입니다. 리눅스가 왜 이런 일을 기대하는지 모르겠습니다.

Linux 커널 코드를 작성할 때 합리적으로 보일 수 있지만 어떤 프로그램에서도 테스트되거나 사용된 적이 없습니다.

또는 일부 프로그램 코드에 의해 실행될 수도 있습니다.최대이 하위 집합에서 작동하지만 원칙적으로 극단적인 경우 "버그" 또는 경쟁 조건이 있을 수 있습니다.

Linux의 동작과 의도된 용도를 이해할 수 없다면 Linux의 "어둡고 테스트되지 않은 구석"이라고 생각할 수 있다고 생각합니다.

관련 정보