두 개 이상의 프로세스가 동시에 동일한 프로세스를 읽고 쓸 수 있습니까 unix socket
?
몇 가지 테스트를 해봤습니다.
sock_test.sh
다음은 50개의 클라이언트를 생성하고 각각 5K 메시지를 동시에 작성하는 내 것입니다 .
#! /bin/bash --
SOC='/tmp/tst.socket'
test_fn() {
soc=$1
txt=$2
for x in {1..5000}; do
echo "${txt}" | socat - UNIX-CONNECT:"${soc}"
done
}
for x in {01..50}; do
test_fn "${SOC}" "Test_${x}" &
done
그런 다음 하나를 만들고 unix socket
해당 파일에 대한 모든 트래픽을 캡처합니다 sock_test.txt
.
# netcat -klU /tmp/tst.socket | tee ./sock_test.txt
마지막으로 테스트 스크립트( )를 실행 sock_test.sh
하고 화면에 표시되는 워커 50개를 모두 모니터링합니다. 마지막으로 모든 메시지가 목적지에 도달했는지 확인합니다.
# ./sock_test.sh
# sort ./sock_test.txt | uniq -c
놀랍게도 50명의 작업자 모두가 오류 없이 5K 메시지를 모두 성공적으로 보냈습니다.
결론을 내려야 할 것 같은데, unix sockets
동시에 써도 괜찮을까요?
내 동시성 수준이 너무 낮아 충돌을 볼 수 없나요?
내 테스트 방법에 문제가 있나요? 그렇다면 어떻게 제대로 테스트할 수 있을까요?
편집하다
이 질문에 대한 훌륭한 답변에 따라 더 친숙한 사람들을 위해 python
여기에 내 테스트 베드가 있습니다.
#! /usr/bin/python3 -u
# coding: utf-8
import socket
from concurrent import futures
pow_of_two = ['B','KB','MB','GB','TB']
bytes_dict = {x: 1024**pow_of_two.index(x) for x in pow_of_two}
SOC = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
SOC.connect('/tmp/tst.socket')
def write_buffer(
char: 'default is a' = 'a',
sock: 'default is /tmp/tst.socket' = SOC,
step: 'default is 8KB' = 8 * bytes_dict['KB'],
last: 'default is 2MB' = 2 * bytes_dict['MB']):
print('## Dumping to the socket: {0}'.format(sock))
while True:
in_memory = bytearray([ord(char) for x in range(step)])
msg = 'Dumping {0} bytes of {1}'
print(msg.format(step, char))
sock.sendall(bytes(str(step), 'utf8') + in_memory)
step += step
if last % step >= last:
break
def workers(concurrency=5):
chars = concurrency * ['a', 'b', 'c', 'd']
with futures.ThreadPoolExecutor() as executor:
for c in chars:
executor.submit(write_buffer, c)
def parser(chars, file='./sock_test.txt'):
with open(file=file, mode='rt', buffering=8192) as f:
digits = set(str(d) for d in range(0, 10))
def is_digit(d):
return d in digits
def printer(char, size, found, junk):
msg = 'Checking {}, Expected {:8s}, Found {:8s}, Junk {:8s}, Does Match: {}'
print(msg.format(char, size, str(found), str(junk), size == str(found)))
char, size, found, junk = '', '', 0, 0
prev = None
for x in f.read():
if is_digit(x):
if not is_digit(prev) and prev is not None:
printer(char, size, found, junk)
size = x
else:
size += x
else:
if is_digit(prev):
char, found, junk = x, 1, 0
else:
if x==char:
found += 1
else:
junk += 1
prev = x
else:
printer(char, size, found, junk)
if __name__ == "__main__":
workers()
parser(['a', 'b', 'c', 'd'])
그러면 출력에서 다음과 같은 줄을 볼 수 있습니다.
Checking b, Expected 131072 , Found 131072 , Junk 0 , Does Match: True
Checking d, Expected 262144 , Found 262144 , Junk 0 , Does Match: True
Checking b, Expected 524288 , Found 219258 , Junk 0 , Does Match: False
Checking d, Expected 524288 , Found 219258 , Junk 0 , Does Match: False
Checking c, Expected 8192 , Found 8192 , Junk 0 , Does Match: True
Checking c, Expected 16384 , Found 16384 , Junk 0 , Does Match: True
Checking c, Expected 32768 , Found 32768 , Junk 610060 , Does Match: True
Checking c, Expected 524288 , Found 524288 , Junk 0 , Does Match: True
Checking b, Expected 262144 , Found 262144 , Junk 0 , Does Match: True
b
어떤 경우에는 페이로드( , d
)가 불완전하지만 누락된 조각( c
)이 나중에 수신되는 것을 볼 수 있습니다 . 간단한 수학이 이를 증명합니다.
# Expected
b + d = 524288 + 524288 = 1048576
# Found b,d + extra fragment on the other check on c
b + d + c = 219258 + 219258 + 610060 = 1048576
따라서 동시 쓰기 unix sockets
는좋아요안좋다.
답변1
이것은 매우 짧은 테스트 라인입니다. 사용된 버퍼 크기보다 더 큰 것을 시도 netcat
하고 socat
여러 테스트 인스턴스에서 문자열을 여러 번 보내십시오. 다음은 sender
이를 수행하는 프로그램입니다.
#!/usr/bin/env expect
package require Tcl 8.5
set socket [lindex $argv 0]
set character [string index [lindex $argv 1] 0]
set length [lindex $argv 2]
set repeat [lindex $argv 3]
set fh [open "| socat - UNIX-CONNECT:$socket" w]
# avoid TCL buffering screwing with our results
chan configure $fh -buffering none
set teststr [string repeat $character $length]
while {$repeat > 0} {
puts -nonewline $fh $teststr
incr repeat -1
}
그런 다음 launcher
매우 긴 길이(9999)의 다른 테스트 문자를 사용하여 여러 번(25) 여러 번(100) 호출하여 버퍼 경계를 잘 깨뜨릴 수 있기를 바랍니다.
#!/bin/sh
# NOTE this is a very bad idea on a shared system
SOCKET=/tmp/blabla
for char in a b c d e f g h i j k l m n o p q r s t u v w x y; do
./sender -- "$SOCKET" "$char" 9999 100 &
done
wait
글쎄요, 저는 netcat
Centos nc
7만으로는 충분하지 않다고 생각합니다.
$ nc -klU /tmp/blabla > /tmp/out
그런 다음 다른 곳에 데이터를 공급합니다.
$ ./launcher
이제 우리는 /tmp/out
개행 문자가 없기 때문에 당혹스러울 것입니다. (어떤 것들은 개행을 기반으로 버퍼링되므로 개행이 테스트 결과에 영향을 줄 수 있습니다. 이 경우 setbuf(3)
줄 기반 버퍼링의 가능성을 확인하십시오.) 변경 문자의 이전 동일 문자 시퀀스의 길이를 계산합니다.
#include <stdio.h>
int main(int argc, char *argv[])
{
int current, previous;
unsigned long count = 1;
previous = getchar();
if (previous == EOF) return 1;
while ((current = getchar()) != EOF) {
if (current != previous) {
printf("%lu %c\n", count, previous);
count = 0;
previous = current;
}
count++;
}
printf("%lu %c\n", count, previous);
return 0;
}
아, 꼬마 C! 출력을 컴파일하고 구문 분석해 보겠습니다.
$ make parse
cc parse.c -o parse
$ ./parse < /tmp/out | head
49152 b
475136 a
57344 b
106496 a
49152 b
49152 a
38189 r
57344 b
57344 a
49152 b
$
어 오. 옳지 않은 것 같습니다. 9999 * 100
999,900개의 연속된 단일 문자여야 하지만 우리가 얻는 것은... 그게 아닙니다. a
아주 일찍 b
시작했는데 r
왠지 이미 초기 영상이 있었던 것 같습니다. 이것이 귀하의 근무 일정입니다. 즉, 출력이 손상되었습니다. 파일 끝 부분에 가까운 것은 어떻습니까?
$ ./parse < /tmp/out | tail
8192 l
8192 v
476 d
476 g
8192 l
8192 v
8192 l
8192 v
476 l
16860 v
$ echo $((9999 * 100 / 8192))
122
$ echo $((9999 * 100 - 8192 * 122))
476
$
이 시스템의 버퍼 크기는 8192인 것 같습니다. 그래도! 테스트 입력이 너무 짧아서 버퍼 길이를 초과하여 실행할 수 없으며 여러 클라이언트 쓰기가 괜찮다는 잘못된 인상을 줍니다. 클라이언트에서 들어오는 데이터의 양을 늘리면 혼합되어 손상된 출력이 표시됩니다.