-
Notifications
You must be signed in to change notification settings - Fork 7
attributes(7)
attributes - POSIX 안전성 개념
참고: 이 맨 페이지는 GNU C 라이브러리 매뉴얼의 "POSIX Safety Concepts" 절에서 가져온 내용을 바탕으로 한 것이다. 여기서 설명하는 주제들에 대한 더 자세한 내용을 그 매뉴얼에서 찾아 볼 수 있다.
많은 함수 매뉴얼 페이지에는 ATTRIBUTES 절이 있어서 여러 문맥에서 그 함수 호출의 안전성을 기술한다. 그 절에서는 함수에 다음과 같은 안전성 표시를 붙인다.
- MT-Safe
-
MT-Safe, 즉 스레드 안전 함수는 다른 스레드가 존재할 때에도 안전하게 호출할 수 있다. MT-Safe에서 MT는 다중 스레드(Multi Thread)를 나타낸다.
MT-Safe라고 해서 함수가 원자적인 것은 아니며 POSIX에서 사용자에게 내보이는 어떤 메모리 동기화 메커니즘을 사용한다는 의미도 아니다. 게다가 MT-Safe 함수들을 차례로 호출하는 것이 MT-Safe인 조합이 아닐 수도 있다. 예를 들어 한 스레드에서 MT-Safe 함수 두 개를 잇달아 호출하는 것이 두 함수 조합을 원자적으로 실행하는 것과 동등한 동작을 보장하지 않는데, 다른 스레드에서 동시에 이뤄지는 호출들이 파괴적으로 간섭할 수 있기 때문이다.
라이브러리 인터페이스를 넘어서 함수를 인라인 처리할 수도 있는 프로그램 전체 최적화로 인해 안전하지 않은 순서 바뀜이 드러날 수도 있으며, 그래서 GNU C 라이브러리 인터페이스를 넘어서 인라이닝을 수행하는 걸 권장하지 않는다. 프로그램 전체 최적화 하에서는 기록돼 있는 MT 안전성이 보장되지 않는다. 하지만 사용자에게 보이는 헤더에 정의돼 있는 함수들은 인라인 하기에 안전하도록 설계된 것이다.
- MT-Unsafe
- MT-Unsafe 함수는 다중 스레드 프로그램에서 호출하기에 안전하지 않다.
안전성 표시에 등장하는 다른 키워드들을 이어지는 절에서 정의한다.
특정 문맥에서 함수 호출을 안전하지 않게 만드는 일부 특성들에 대해선 함수 호출 자체를 삼가는 것 말고도 안전성 문제를 피할 수 있는 알려진 방법들이 있다. 이어지는 키워드들이 그런 특성을 표시하는데, 각 정의에는 키워드가 나타내는 안전성 문제를 없애기 위해 전체 프로그램에 어떤 제약을 가해야 하는지가 나와 있다. 함수를 안전하지 않게 하는 이유들을 모두 확인해서 적혀 있는 제약을 적용해 해결할 때에만 함수가 어떤 문맥에서 호출하기에 안전해진다.
- init
-
MT-Unsafe 특성으로 init 표시가 된 함수들은 첫 호출 때 MT-Unsafe인 초기화를 수행한다.
그런 함수를 단일 스레드 모드에서 적어도 한 번 호출하면 그 함수가 MT-Unsafe이게 하는 그 특정 원인이 없어진다. 그 함수에 대해 다른 이유가 남아 있지 않다면 다른 스레드 시작 후에 그 함수를 안전하게 호출할 수 있다.
- race
- MT 안전성과 관련해 race 표시가 된 함수들은 동시 실행으로 인해 데이터 경쟁이나 유사한 형태의 파괴적 간섭을 유발하는 방식으로 객체들에 동작한다. 일부 경우에서는 객체를 사용자가 함수로 전달하고, 다른 경우에서는 객체를 이용해 함수가 사용자에게 값을 반환하고, 또 다른 경우에서는 객체가 사용자에게 전혀 드러나지 않는다.
- const
-
MT 안전성과 관련해 const 표시가 된 함수들은 GNU C 라이브러리의 상당 부분에서 동기화 없이 접근하기 때문에 상수로 여기는 게 나은 내부 객체를 원자적이지 않게 변경한다. 내부 객체를 읽는 쪽과 쓰는 쪽 모두를 MT-Unsafe이게 하는 race와 달리 이 표시는 쓰는 쪽에만 적용된다. 쓰는 쪽을 호출하는 건 여전히 MT-Unsafe이지만 객체 변경 후에 객체의 상수성이 다시 돌아오므로 읽는 쪽을 (안전하지 않게 하는 다른 이유가 남아 있지 않다면) MT-Safe로 여길 수 있게 된다. 객체가 실질적으로 상수일 때 동기화가 없는 것은 문제가 되지 않는다.
const 표시 뒤에 식별자가 오는데 읽는 쪽에는 그 식별자만 안전성 표시로 나오게 된다. 쓰는 쪽을 호출하면서 이 안전성 문제를 피하고 싶은 프로그램에서는 식별자에 연계된 비재귀 읽기-쓰기 락을 사용할 수 있다. 식별자 붙은 const로 표시된 함수들에 대한 호출을 모두 쓰기 락으로 보호하고 식별자만 표시된 함수들에 대한 호출을 모두 읽기 락으로 보호하면 된다.
- sig
-
MT 안전성과 관련해 sig 표시가 된 함수들에서는 내부적인 용도로 임시로 시그널 핸들러를 설치할 수 있으며, 그래서 콜론 뒤에 표시된 시그널을 이용하는 경우 간섭이 있을 수 있다.
호출 동안에 그 시그널을 다른 곳에서 쓰지 않도록 하여 이 안전성 문제를 피할 수 있다. 같은 임시 시그널을 쓰는 모든 함수들의 호출 동안 비재귀 뮤텍스를 잡고, 호출 전에 그 시그널을 막았다가 이후에 핸들러를 재설정하기를 권장한다.
- term
-
MT 안전성과 관련해 term 표시가 된 함수들에서는 표준 방식에 따라 터미널 설정을 바꿀 수 있다. 즉 tcgetattr(3)을 호출하고 어떤 플래그를 변경한 다음 tcsetattr(3)을 호출할 수 있는데, 그러면 다른 스레드에 의한 변경 사항이 유실될 수 있는 가능성이 생긴다. 따라서 term으로 표시된 함수들은 MT-Unsafe이다.
그러므로 터미널을 쓰는 응용에서는 동시적이고 재진입 가능하게 터미널과 상호작용 하는 걸 피하는 게 바람직하다. 시그널 핸들러에서 그 함수들을 쓰지 않거나 쓸 수도 있을 시그널을 막고, 그 함수들을 호출하는 동안과 터미널과 상호작용 하는 동안에 락을 잡고 있으면 된다. 그 락을 race:tcattr(fd) 표시가 된 함수와의 상호 배제에도 쓰는 게 좋은데, 여기서 fd는 터미널 제어를 위한 파일 디스크립터이다. 단순함을 위해 호출자가 뮤텍스 한 개를 쓸 수도 있고 (여러 파일 디스크립터가 참조하더라도) 터미널별로 뮤텍스 한 개씩을 쓸 수도 있다.
함수 호출을 안전하지 않게 만드는 건 아니지만 특정 프로그램 유형에서 고려가 필요할 수도 있는 특성들을 나타내기 위한 키워드를 함수에 추가로 붙일 수 있다.
- locale
-
MT 안전성과 관련해 locale 표시가 된 함수들은 어떤 형태의 동기화도 없이 로캘 객체의 데이터를 읽는다. 로캘 변경과 동시에 locale 표시 함수를 호출하면 실행 중 활성인 로캘 어느 것에도 해당하지 않으면서 그게 예측 불가능하게 섞인 방식으로 동작할 수 있다.
하지만 이 함수들을 MT-Unsafe라고 표시하지는 않는다. 로캘 객체를 변경하는 함수들에 const:locale 표시를 하고 안전하지 않은 것으로 여기기 때문이다. 안전하지 않은 그 함수들은 여러 스레드가 돌거나 비동기 시그널이 활성화돼 있을 때 호출되지 않을 것이고, 따라서 그 맥락에서는 로캘이 실질적으로 상수라고 볼 수 있으며, 그래서 이 함수들이 안전해진다.
- env
-
MT 안전성과 관련해 env 표시가 된 함수들은 동시 변경 시 안전성을 보장하기 위한 어떤 안전 장치도 없이 getenv(3) 내지 유사 함수로 환경에 접근한다.
하지만 이 함수들을 MT-Unsafe라고 표시하지는 않는다. 환경을 변경하는 함수들에 모두 const:env 표시를 하고 안전하지 않은 것으로 여기기 때문이다. 안전하지 않은 그 함수들은 여러 스레드가 돌거나 비동기 시그널이 활성화돼 있을 때 호출되지 않을 것이고, 따라서 그 맥락에서는 환경이 실질적으로 상수라고 볼 수 있으며, 그래서 이 함수들이 안전해진다.
- hostid
- MT 안전성과 관련해 hostid 표시가 된 함수들은 머신의 "호스트 ID"를 담은 시스템 전역 자료 구조들을 읽는다. 이 자료 구조들은 일반적으로 원자적으로 변경할 수 없다. 보통은 "호스트 ID"가 바뀌지 않을 것이므로 그걸 읽는 함수(gethostid(3))는 안전한 것으로 여기고 변경하는 함수(sethostid(3))에 const:hostid 표시를 해서 호출 시 특별한 주의가 필요하다고 표시한다. 그 특별한 경우에서 특별한 주의는 (프로세스 내부 정도가 아니라) 시스템 전체에서의 조율을 뜻한다.
- sigintr
-
MT 안전성과 관련해 sigintr 표시가 된 함수들은 동시 변경 시 안전성을 보장하기 위한 어떤 안전 장치도 없이 GNU C 라이브러리의
_sigintr
라는 내부 자료 구조에 접근한다.하지만 이 함수들을 MT-Unsafe라고 표시하지는 않는다. 그 자료 구조를 변경하는 함수들에 모두 const:sigintr 표시를 하고 안전하지 않은 것으로 여기기 때문이다. 안전하지 않은 그 함수들은 여러 스레드가 돌거나 비동기 시그널이 활성화돼 있을 때 호출되지 않을 것이고, 따라서 그 맥락에서는 그 자료 구조가 실질적으로 상수라고 볼 수 있으며, 그래서 이 함수들이 안전해진다.
- cwd
-
MT 안전성과 관련해 cwd 표시가 된 함수들은 실행 중에 일시적으로 현재 작업 디렉터리를 바꿀 수 있으며, 그래서 다른 스레드나 비동기 시그널 핸들러 내지 취소 핸들러에서 상대 경로명이 예상치 못한 방식으로 해석될 수 있다.
그 때문에 그런 함수들에 MT-Unsafe 표시를 붙일 필요까지는 없겠지만, 이 동작 방식이 선택적일 때는 (가령 nftw(3)를
FTW_CHDIR
로 사용) 그 선택지를 피하는 게 전체 경로명이나 파일 디스크립터 기준 시스템 호출(가령 openat(2))을 쓰는 것보다 좋은 대안일 수 있다. - :식별자
-
때로는 표시 뒤에 식별자가 올 수 있다. 예를 들어 race와 const에서처럼 안전하지 않은 방식으로 자료 구조에 접근하는 여러 함수들을 한데 묶거나 sig로 표시한 함수에서 시그널 이름을 적는 것처럼 구체적인 정보를 제공하기 위해서이다. 향후에는 lock과 corrupt에도 적용될 수 있으리라 생각한다.
대부분의 경우에서 식별자는 함수군의 이름이 되겠지만 전역 객체나 함수 인자, 또는 그에 연계된 식별 가능 속성이나 논리적 요소의 이름이 될 수도 있다. 예를 들어 :buf(arg) 표기는 인자 arg에 연계된 버퍼를 나타내며 :tcattr(fd)는 파일 디스크립터 fd의 터미널 속성들을 나타낸다.
식별자를 사용하는 가장 흔한 경우는 해당 문맥에서 안전한 동작을 보장하기 위해 같은 동기화 요소로 보호할 필요가 있는 함수와 인자들을 논리적으로 묶는 것이다.
- /조건
-
어떤 안전성 표시는 조건부이다. 즉 인자나 전역 변수, 심지어 기반 커널까지 포함되는 어떤 불리언 식이 참으로 평가될 때만 적용된다. 예를 들어 /!ps와 /one_per_line은 인자 ps가 NULL일 때에만, 그리고 전역 변수 one_per_line이 0이 아닐 때에만 앞의 표시가 적용됨을 나타낸다.
어떤 함수를 안전하지 않게 하는 모든 표시들에 그런 조건이 붙어 있으며 그 조건들 중 어느 것도 성립하지 않을 때에는 그 함수를 안전한 것으로 여길 수 있다.
2015-03-02