레이블이 kernel인 게시물을 표시합니다. 모든 게시물 표시
레이블이 kernel인 게시물을 표시합니다. 모든 게시물 표시

linux write atomic

리눅스에서 멀티 프로세스가 하나의 파일에 쓰기 작업을 할 경우 어떻게 될까?
우선 파일쓰기시 fprintf, fwrite, 등의 C standard library 에서 제공하는 함수를 사용할 수 도 있고 write 와 같은 시스템 함수를 사용할 수 도 있다.
fprintf 의 경우 내부 버퍼링을 하고 write 시스템 콜을 수행하게 된다.

참고

write 함수의 경우 파일이 추가 가능 모드로 열렸다면 파일의 끝 offset 과 쓰기 연산은 atomic 하게 동작한다고 한다.
POSIX.1(.1 은 C standard library 를 규정, .2 shell 관련 규정)에 따라 SSIZE_MAX 까지 쓸 수 있다고 한다. 

SSIZE_MAX 는 /usr/include/bits/posix1_lim.h 에 정의 되어 다음과 같이 정의 되어 있다.
#ifndef SSIZE_MAX
# define SSIZE_MAX  LONG_MAX
#endif

LONG_MAX 는 /usr/include/limits.h 에는 /bits/posix1_lim.h 를 포함하고 있고 다음과 같이 정의 LONG_MAX 가 정의 되어 있다.
#  if __WORDSIZE == 64
#   define LONG_MAX 9223372036854775807L
#  else
#   define LONG_MAX 2147483647L
#  endif

64비트 머신에서 다음과 같이 프로그래밍 해보면
#include <stdio.h>
#include <limits.h>
int main()
printf("SSIZE_MAX : %ld\n", SSIZE_MAX);
return 0;
}

SSIZE_MAX : 9223372036854775807 결과를 보인다.

결국 다음과 같이 write 로 파일 쓸때는 64비트머신에서 거의 무한대로 쓸 수 있는데
On Linux, write() (and similar system calls) will transfer at most 0x7ffff000 (2,147,479,552) bytes, returning the number of bytes actually transferred.(This is true on both 32-bit and 64-bit systems.)

문제는 3.14 이전 커널에서 atomic 하지 않다는 문제가 있다고 한다.

BUGS
Among the APIs subsequently listed are write() and writev(2).  And the effects that should be atomic across threads (and processes) are updates of the file offset.  However, on Linux before version 3.14, this was not the case: if two processes that share an open file description (see open(2)) perform a write() (or writev(2))at the same time, then the I/O operations were not atomic with respect updating the file offset, with the result that the blocks of data output by the two processes might (incorrectly) overlap. This problem was fixed in Linux 3.14.

참고 리눅스 바닐라 커널(kernel) http://man7.org/linux/man-pages/man2/write.2.html

여기서 파일 오동작의 경우가 2개가 있는데
interleave(끼우다) : 데이터 쓰는중에 다른 데이터가 껴들어간것
overlap(겹치다): 데이터를 쓰는중 다른 데이터가 덮어 써지는것
위에서 설명한건 offset 이 atomic 하지 않아 overlap 된다는것.
uname -r 로 확인해보면 3.10.0 버전을 사용하는데 어떻게 될지...?
멀티 쓰레드 환경에서 하나의 파일에 동시 쓰기 테스트

https://github.com/ysoftman/test_code/blob/master/cpp/multi_thread_write_same_file.cpp
로 테스트 하니 멀티쓰레드에서는 문제가 없고 멀티 프로세스 환경에서 fprintf()를 사용시문제가 발생한다.

리눅스 버전 의미 참고 http://unix.stackexchange.com/questions/9743/numbering-convention-for-the-linux-kernel

테스트 환경은 centos7 이고 커널버전은 3.10.0-514.6.2.el7.x86_64
centos 에서는 기본 리눅스 바닐라 커널(3.10.0)을 베이스로 하고 이슈가 발생된 부분은 패치 적용하는 방식을 취한다고 한다. 그래서 3.10 버전이지만 위와 같은 오래전 이슈는 이미 수정되어 반영된것으로 보인다.

Build Linux kernel and install

# 리눅스 커널 소스 다운로드 (bz2) 의 경우
wget http://www.kernel.org/pub/linux/kernel/v3.0/linux-3.5.1.tar.bz2
tar jxvf linux-3.5.1.tar.bz2

# 최근 리눅스 커널 소스는 xz 로 압축해서 배포하고 있다.
# 리눅스 커널 소스 다운로드 (xz) 의 경우 xz -> tar 두번 압축해제
wget https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.10.105.tar.xz
xz -d linux-3.10.105.tar.xz
tar xvf linux-3.10.105.tar

# 빌드에 필요한 툴 설치(Ubuntu 기준)
sudo apt-get install ncurses-dev

# 리눅스 소스 경로로 이동
cd linux-3.5.1

# 이전 커널의 오브젝트파일, 버전정보, 컴파일 환경 설정 값등 현재 컴파일할 커널에 영향을 줄 수 있는 정보 삭제
make mrproper

# 빌드 환경 설정 
# defconfig:디폴트설정
# i386_defconfig:32bit 디폴트설정
# x86_64_defconfig:64bit 디폴트설정
# config:텍스트모드
# menuconfig:텍트스메뉴모드
# xconfig:X윈도우
make x86_64_defconfig

# 빌드(make bzImage(일반용) 와 make zImage(임베디드용) 는 kernel2.6 이후 make 로 통합)
make clean
make -j10

# 모듈 설치 (/lib/modules/버전 에 설치됨)
sudo make modules_install
# 커널 설치 (/boot 에 설치됨)
sudo make install

# 필요시 grub 부트로더 갱신
# grub 일 경우
sudo vi /boot/grub.conf
# grub2 일 경우
sudo update-grub