[t:/]$ 지식_

go 슬라이스 해킹 주의사항

2020/06/23

해킹이라 썼는데 훼이크임. 그냥 있어보여서.

go 쪼렙입니다. 며칠 안 됐어요.

여튼, go에서 복사와 자동 힙 할당을 피하고자 포인터 연산처럼 다룰려면 꽤 복잡한데 다음과 같이 쓴다.

    header := (*reflect.SliceHeader)(unsafe.Pointer(&my_data[0].pos1))
    header.Len = 1024   
    header.Cap = 1024 

    unix.Msync(*(*[]byte)(unsafe.Pointer(header)), unix.MS_SYNC)

my_data 구조체의 특정위치를 슬라이스 헤더 형태에 넣고, 이것을 원하는 형태로 디리퍼런싱 하면 되는데.. 영 껄끄럽다.

여튼 이때 주의사항은 header가 다음과 같이 구성되어 있다는 것이다.

주소 8바이트  
길이 8바이트  
용량 8바이트  

아키에 따라서 다를지도 모른다. 나도 몰러. 헥사로 봤을 때 자료형을 안내하는 메타는 보이지 않았다.. 아마도 정적타입 언어라서 컴파일 타임때 전부 지지고 볶고 할 듯..

위에서 사용한 my_data 구조체가 다음과 같다고 치자.

type data_file struct {
    pos1 int
    d [MAX_ITEM]data
}

이러면 pos1 자리를 header로 잡는 순간 Len, Cap이 d를 덮어쓴다. 하아..

따라서 더미공간을 남겨둬야 한다. 나는 mmap 빠돌이니까 4킬로 단위의 더미를 넣었다. go에도 이런 짓을 할지는 몰랐다.

type data_file struct {
    pos1 int
    dummy1 [4096 - INT_SIZE]byte
    d [MAX_ITEM]data
}

더미에 header의 Len, Cap을 쓰는데 남이사다. 나는 다시 쓸 일 없다. 가만.. 어? 이걸 가지고 프로토버프 느낌이 나면서 슬라이스 직때려넣기 동적 타이핑이 되겠는데?? (나중에 연구해야지..)

실제로는 msync로 mmap page 캐시를 강제 디스크 동기화하려고 써봤다.

    header = (*reflect.SliceHeader)(unsafe.Pointer(&my_data[0].pos2))
    header.Len = 4096 - 8
    header.Cap = 4096 - 8

    unix.Msync(*(*[]byte)(unsafe.Pointer(header)), unix.MS_SYNC)    

아마도 go에서는 더 go스러운 우아한 패턴이 있을 것 같은데 쪼렙이라 모른다. 책을 하나 샀는데 고 프로그래밍 뭐더라.. 책이 집에 있다. 아마도 거기에도 안 나올 듯. 쪼렙책 산 듯..









[t:/] is not "technology - root". dawnsea, rss