내가 찾은이 Q&A공유 메모리를 사용하여 프로세스 간에 공유 라이브러리를 공유할 수 있다고 말합니다. 그러나 공유할 수 있는 코드 유형을 상당히 엄격하게 제한하지 않고는 프로세스 간에 코드를 공유하는 것이 불가능해 보입니다. 나는 정의 본문 내의 전역 또는 정적 변수 값에 따라 출력이 달라지는 재진입이 불가능한 C 함수가 있는 라이브러리를 고려하고 있습니다. 이와 같이.
int really_really_nonreentrant(void x)
{
static int i = 0;
i++;
return i;
}
이와 같은 함수가 있는 라이브러리는 이를 사용하는 각 프로세스에 대해 별도의 증분 시퀀스를 반환하므로 확실히 코드처럼 보일 것입니다.아니요프로세스 간에 공유됩니다. real_really_nonreentrant()는 재진입 함수와 분리되어 있나요? 아니면 주로 다른 함수와 함께 유지되고 static int i만 분리되어 있나요? 아니면 이 함수가 재진입이 불가능하기 때문에 전체 라이브러리가 공유 메모리에서 제외됩니까?
궁극적으로 라이브러리가 공유 메모리에 할당되도록 하려면 얼마나 많은 작업이 필요합니까?
답변1
정말 짧은 대답은 Linux 컴파일러가 코드를 여러 부분으로 분할하고 그 중 적어도 하나는 순수한 코드이므로 메모리가 여러 프로세스의 주소 공간에 매핑될 수 있다는 것입니다. 모든 전역 변수는 각 프로세스가 자체 복사본을 갖도록 매핑됩니다.
, 또는 을 사용하여 readelf
이것을 볼 수 objdump
있지만 readelf
더 명확한 그림을 제공한다고 생각합니다.
이것은 출력의 일부입니다 readelf -e /usr/lib/libc.so.6
. 이는 아마도 거의 모든 프로세스에 매핑되는 C 라이브러리입니다. 출력의 관련 부분 readelf
(이 모든 것이 흥미롭긴 하지만)은 프로그램 헤더입니다.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00000034 0x00000034 0x00140 0x00140 R E 0x4
INTERP 0x164668 0x00164668 0x00164668 0x00017 0x00017 R 0x4
[Requesting program interpreter: /usr/lib/ld-linux.so.2]
LOAD 0x000000 0x00000000 0x00000000 0x1adfc4 0x1adfc4 R E 0x1000
LOAD 0x1ae220 0x001af220 0x001af220 0x02c94 0x057c4 RW 0x1000
DYNAMIC 0x1afd90 0x001b0d90 0x001b0d90 0x000f8 0x000f8 RW 0x4
NOTE 0x000174 0x00000174 0x00000174 0x00044 0x00044 R 0x4
TLS 0x1ae220 0x001af220 0x001af220 0x00008 0x00048 R 0x4
GNU_EH_FRAME 0x164680 0x00164680 0x00164680 0x06124 0x06124 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10
GNU_RELRO 0x1ae220 0x001af220 0x001af220 0x01de0 0x01de0 R 0x1
두 개의 LOAD 줄은 메모리에 직접 매핑되는 파일의 유일한 부분입니다. 첫 번째 LOAD 헤더는 R 및 E 권한(읽기 및 실행)을 사용하여 블록을 /usr/lib/libc.so.6
메모리에 매핑합니다. 이것이 코드입니다. 하드웨어 기능은 프로그램이 해당 메모리에 쓰는 것을 방지하므로 모든 프로그램이 실제 물리적 메모리의 동일한 페이지를 공유할 수 있습니다. 커널은 동일한 물리적 메모리를 모든 프로세스에 매핑하도록 하드웨어를 설정할 수 있습니다.
두 번째 LOAD 헤더는 RW(읽기 및 쓰기)로 표시됩니다. C 라이브러리에서 사용하는 전역 변수의 일부입니다. 각 프로세스는 실제 메모리에 자체 복사본을 갖고 해당 프로세스의 주소 공간에 매핑되며 읽기 및 쓰기를 허용하도록 설정된 하드웨어 권한을 가집니다. 이 섹션은 공유되지 않습니다.
파일 시스템을 사용하여 실행 중인 프로세스 내에서 이러한 메모리 맵을 볼 수 있습니다 /proc
. 설명하기 좋은 명령은 다음과 같습니다. cat /proc/self/maps
. 프로세스가 소유한 모든 메모리 매핑 cat
과 커널이 이를 가져오는 파일을 나열합니다.
함수에 다른 프로세스에 매핑된 메모리가 할당되도록 하기 위해 수행해야 하는 작업의 양은 거의 전적으로 컴파일러에 제공하는 플래그에 따라 달라집니다. ".so" 공유 라이브러리에 대한 코드는 "위치 독립적으로" 컴파일됩니다. 위치 독립적 코드는 절대 주소에서 로드하거나 쓴 후 점프하는 대신 현재 명령어에 대한 오프셋을 사용하여 변수의 메모리 위치를 참조하고 현재 명령어를 기준으로 한 위치로 점프하거나 분기하는 등의 작업을 수행합니다. 절대 주소로. 즉, "RE" LOAD 블록 /usr/lib/libc.so
과 "RW" 블록은 각 프로세스에서 동일한 거리만큼 떨어져 있는 주소에만 로드하면 됩니다. 예제 코드에서 static
변수는 이를 참조하는 코드를 제외하고 항상 페이지 크기의 배수 이상이며 LOAD ELF 헤더가 제공되는 방식으로 인해 항상 프로세스의 주소 공간에 로드됩니다.
"공유 메모리"라는 용어와 관련하여 "System V 프로세스 간 통신 시스템"과 관련된 사용자 수준 공유 메모리 시스템이 있다는 점에 유의하십시오. 이는 여러 프로세스가 메모리 블록을 매우 명시적으로 공유하는 방법입니다. 이를 설정하고 올바르게 설정하는 것은 매우 복잡하고 모호합니다. 여기서 말하는 공유 메모리는 어떤 사용자 프로세스에서도 거의 완전히 보이지 않습니다. 샘플 코드가 여러 프로세스 간에 공유되는 위치 독립적 코드로 실행되거나 유일한 복사본인 경우 샘플 코드는 차이점을 알 수 없습니다.