2016년 출판된 가메출판사의 Advanced! 리눅스 시스템 네트워크 프로그래밍(김선영 저)을 공부 목적으로 정리한 것입니다.


Ch 2 파일처리

 


1 파일처리

 

1.1 저수준 및 고수준 파일 처리

저수준 파일 처리:

파일기술자라는 번호를 사용하여 입출력하는 방식

소켓이나 디바이스들을 핸들링할 때 사용하는 인터페이스는 모두 저수준 파일 처리와 같은 방식 사용

POSIX 공유 메모리의 IPC 관련 함수와 유사함

pread, pwrite를 통해 원자적 실행 보장

고수준 파일 처리:

FILE 구조체를 사용하여 입출력하는 방식

C언어 표준이며 추상화된 FILE 구조체를 사용(모든 운영체제에 포팅)

라이브러리 레벨의 버퍼링 존재

원자적 실행:

preadpwrite의 경우 파일의 커서 위치가 아닌 절대적인 오프셋 주소를 사용하기 때문에 스레드에서 사용해도 안전함(파일 출력이 섞이지 않음)

파이프에 입출력할 떄는 read, writePIPE_BUF 이내의 길이로 입출력하는 경우 원자성 보장됨

UNIX 계열에서 PIPE_BUF는 일반적으로 큰 값을 지원


1.2 형식화된 출력 기능

dprintf – 형식화된 출력을 하기 위해 snprintfwrite 함수를 함께 사용해야 했던 것을 보완함

len = snprintf(buf, sizeof(buf), “counter : %d”, i); write(fd, buf, len);

=>  dprintf(fd, “counter: %d”, i);

(형식화된 입력은 아직 지원되지 않음)

 


2 저수준 파일 처리의 사용


open저수준 파일 처리의 핵심인 파일기술자를 얻는 함수

 

2.1 Synchronized I/O로 열기

현대 OS는 상대적으로 느린 디바이스들(디스크, 네트워크)에 대해서 좀더 빠른 디바이스(메모리)캐시하고 이후 실제적 기록을 하여 동기화함(우선순위 높은 작업부터 처리 => 응답성

몇몇 경우 실제 디바이스에 즉각적으로 기록된 내용이 반영되어야 함 => 동기화 기능 필요

동기화를 시키는 방법

1)     수동 동기화 필요 시점에 fsyncfdatasync 함수 호출

2)     입출력이 발생 시(I/O 관련 함수 호출될 시) – open 함수 호출 시 O_SYNC(파일 내용과 메타 데이터 모두 동기화)O_DSYNC(메타 데이터 제외/파일 내용만) 옵션 플래그 설정 / O_RSYNC는 읽기 작업 동기화 추가

 

2.2 Non-blocking Asynchronous I/O

생략


2.3 close-on-exec로 열기

open 호출 시 O_CLOEXEC 플래그를 지정

=> exec 계열 함수 때문에 다른 프로세스 이미지로 교체될 시 자동으로 해당 파일기술자를 닫도록 함

 

2.4 파일 닫기

힙 메모리 사용 후 free를 해주듯, 파일도 닫지 않으면 자원 누수 발생

파일을 열고 닫을 때 파일 관련 정보를 읽고 버퍼를 할당하는 과정이 숨겨져 있기 때문에, 특정 파일에 계속 I/O가 발생할 땐 열어 두고 쓰는 것이 효율적

최대 열 수 있는 파일의 개수에 제한 존재(소켓이나 파이프 같은 통신용 디바이스 포함/max open files 설정은 ulimit -n 명령으로 확인)

=> 한두 번 쓰는 파일의 경우 쓰고 난 뒤 닫아주는 것이 좋음

 

2.5 파일 사용 패턴 조언

posix_fadvise - 열린 파일기술자를 앞으로 순차적으로 읽을 것인지 아니면 랜덤하게 접근할 것인 것 혹은 한 번만 쓰고 다시 쓰지 않을 지 알려주는 기능

순차적으로 접근 시, 현재 읽은 데이터의 다음 데이터를 프리패칭해서 미리 가져오도록 함 => 레이턴시

한 번만 사용 시, 해당 파일을 읽을 때 사용한 메모리 퇴출 => 메모리 효율적 사용 가능



3 고수준 파일 처리의 사용


C언어 표준 / 운영체제 레벨의 작업은 몰라도 쉽게 이해 가능 / 다양한 포매팅, 버퍼링 제공

저수준 파일 처리에 비해 비효율적 / 원자적 실행 힘듦

유닉스 표준 C언어 표준

 

3.1 FILE 구조체와 버퍼링

구조체 내부 구조는 임플리먼테이션별로 다름

파일 스트림 - FILE 구조체를 통해 얻어지는 파일 I/O 매개물 / 가상화된 흐름 표현 => 사용자는 데이터가 어디에 어떻게 있는지 상관할 필요X

버퍼링을 제어하기 위한 함수 – fflush(수동으로 버퍼를 비우는 작업을 하므로 강제로 출력하게 됨) / setvbuf(버퍼링을 제어하는 방법, 기본은 완전 버퍼링)

버퍼링이 너무 길 시 반응속도나 버퍼 공간의 비효율적인 사용을 하게 됨

Forkexec 계열 또는 posix_spawn 함수를 사용하는 경우, fflush를 실행하여 버퍼를 비우는 행위 필요기존에 버퍼링된 데이터의 순서가 역전되거나 파괴될 수 있기 때문

 

3.2 바이너리 데이터 입출력

fwrite, fread와 같은 함수를 사용하여 바이너리 데이터 사용

구조체를 사용하면 팩화된 경우나 주소 경계가 정렬되어 패딩이 발생하는 경우가 생길 수 있음 - 읽거나 쓸 때 정확한 위치 경계를 잡는 것이 중요 / 정적 캐스팅으로 데이터를 읽어오거나 사용할 때 매우 중요한 문제를 일으킬 수 있음

 


4 저수준과 고수준 파일 처리의 혼용


하나의 파일기술자를 복제하거나 파일기술자로부터 고수준 파일 처리 구조체를 생성하면, 실제로는 모두 한 개의 파일을 보고 있고 같은 채널에서 복사했기 때문에 fd0의 현재 오프셋 위치를 이동하면 공유된 모든 형태가 같이 변경된다. – linked channels

같은 파일을 여러 번 열 때는 linked channels가 아닌 independent channels로 열림

BUT independent channels이여도 하나의 파일에 여러 프로세스나 스레드가 입출력하면 순서 역전 문제 발생 가능 운영체제의 스케줄링 영향을 받음

=>  하나의 파일에 복수의 채널을 만드는 것을 지양해야 함

 


5 Padding/PackXDR


CPU는 메모리 접근 시 특정 바이트의 배수로 정렬된 주소로 접근하면 더 효율적으로 작동

정렬되지 않은 주소에 접근하면 BUS ERROR 일으키며 프로세스를 종료 시키기도 함

=> 대부분의 임플리먼테이션은 구조체와 같이 다양한 크기의 멤버 변수가 모이는 경우 멤버 변수의 시작하는 주소를 특정한 짝수 바이트의 배수로 주소 경계를 맞춤 – XDR(External Data Representation) 규칙


5.1 XDR, RFC 1832

XDR의 목적 서로 다른 아키텍처 간에 데이터 교환 시 버스 오류 같은 현실적 문제나 성능 저하를 최소화하기 위해 정의된 데이터 표현 방식 => 언어 중립적인 프로토콜

32bit64bit의 주소 경계 권장

 

5.2 묵시적인 패딩과 명시적인 패딩

Implicit padding – 임플리멘테이션들이 정렬된 주소 경계를 만들기 위해 구조체에 정렬 패딩을 넣어준 것으로 숨겨져 있음

=> 혼란을 일으킬 수 있으니 Explicit padding으로 수정하는 것이 좋음(직접 패딩을 포함해 코드 작성)

패딩 포함 28바이트인 구조체와 32바이트인 구조체가 있을 때, 28바이트 구조체가 메모리를 절약할 수 있으나 캐시 라인의 크기에 정렬시켜 성능 향상을 꾀할 수도 있음

Implicit padding은 구조체 내에 2바이트 멤버가 있으면 2바이트 경계, 48바이트 멤버가 있다면 4바이트 경계로 패딩을 넣는다.

 

5.3 팩화 구조체

Implicit padding을 제외한 모든 구조체의 멤버를 붙이는 경우. 하드웨어 레벨 프로그래밍에서 주로 사용됨

-       gcc 컴파일 시 –pack-struct 사용

-       Source code 내의 특정 struct__attribute__((packed)) 지시자 사용

Cf. 정렬 기준 지정할 때도 __attribute__((align(#)) 지시자 사용 가능

 


6 대용량 파일 지원(LFS-Large File Summit/Support)


일반적으로 파일을 다루는 함수들은 32bit 머신의 어드레싱 공간 제한인 2GB영역 까지만 지원함

=> 대용량 파일을 다루기 위해선 LFS에 대해 알아야함(64bit 시스템은 해당 없음)

 LFS를 사용하기 위한 조건

1)     파일 시스템 포맷의 LFS 지원 여부

2)     라이브러리(glibc)LFS 지원 여부

두 가지 조건이 만족할 시, 두 가지 방식으로 64bit LFS 사용 가능 – 32 bit 파일 시스템과 호환성 때문

 

6.1 32bit64bit 파일 관련 함수를 따로 사용하기

기존의 open, read, write, lseek 등의 파일 관련 함수는 그대로 32bit의 한계를 가지도록 두고 새로운 open64, read64, write64, lseek64와 같은 64bit 버전의 함수를 따로 쓰는 방식

#define _LARGEFILE_SOURCE / #define _LARGEFILE64_SOURCE

OR

gcc …(생략) -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE …(생략)

 

6.2 32bit 함수를 64bit로 모두 변환하기

가장 간단함

_FILE_OFFSET_BITS=64 매크로를 통해 자동으로 open64로 확장 됨

#define _LARGEFILE_SOURCE / #define _FILE_OFFSET_BITS 64

gcc …(생략) -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 …(생략)

2016년 출판된 가메출판사의 Advanced! 리눅스 시스템 네트워크 프로그래밍(김선영 저)을 공부 목적으로 정리한 것입니다.


Ch1 프로세스



1 프로세스


1.1 전통적인 프로세스 복제 방법

fork – 유닉스 계열에서 프로세스를 복제하는 전통적인 방법

프로세스 복제 이유 – Multitasking.

복제된 자식 프로세스에 복수개의 task를 일임, 부모 프로세스와 독립적으로 작동 – 복수 개의 CPU가 설치된 경우 뛰어난 응답성과 성능, BUT 프로세스끼리 데이터 통신 처리에 드는 비용이 클 시 성능 하락 가능.

=>  서로 독립적으로 작동 OR 프로세스 간 통신 비용으로 발생하는 단점 << 멀티 프로세싱 성능 향상 – 멀티 프로세스 구조 적합

=>  복제할 프로세스 개수 제한(통신 비용 총합 고려)

Shell은 명령어를 받아들이면 fork를 해 자식 프로세스를 만들고 바로 exec를 호출하여 /bin/ls 프로그램 이미지로 교체함 / inetd 또한 연달아 fork-exec를 호출함


1.2 확장된 프로세스 실행 방법

fork-exec를 대체하는 기능 필요 – WHY?

1)     Fork가 부모 프로세스 복제 시 모든 정적 정보를 복제(부모 프로세스의 heap 메모리, 정적 메모리, IPC 자원 ID, 열린 파일, 시그널 마스크 등) BUT, fork–exec 연달아 호출 시 부모 프로세스의 열린 파일, IPC 자원을 쓰지 않는 경우가 다수 -> 쓰지 않는 자원 복제 -> 오버헤드 존재 -> 대형 시스템에서 큰 영향

2)     Realtime processing이 중요한 서비스의 경우

=>  posix_spawn – 부모 프로세스의 자원 중 6가지(열린 파일, 프로세스 그룹ID, 유저 및 그룹 ID, 시그널 마스크, 스케줄링)의 자원을 선택적 복제 및 관리 가능



2 fork


pid_t fork(void);

Return value :

0 - 자식 프로세스에게 리턴되는 값

양수 - 부모 프로세스에게 리턴됨, 자식 프로세스의 PID

-1 – error

=> 3가지 케이스에 대해 코딩해야 함(조건문 사용) / 분기문을 잘 작성하지 않으면 재귀적으로 fork하게 되어 문제 발생 가능성 UP


2.1 vfork와 성능 문제

Vfork는 페이지 테이블을 복제하지 않음 – exec가 호출될 때 페이지 테이블이 해제되기 때문(오버헤드 방지)

현재는 fork 또한 copy-on-write 기능을 도입해 페이지 테이블 복사를 미룸(부모와 자식 프로세스의 페이지 테이블이 달라지는 시점에 복제)

BUT 여전히 페이지 테이블 제외한 모든 정적 자원 그대로 복제되는 오버헤드 존재

=>  posix_spawn 계열 함수가 추가됨



3 exec(3) 계열 함수


Exec 계열 함수는 현재 실행 중인 프로세스 이미지를 새로운 프로세스 이미지로 대체함 – 실행 코드는 교체되나 기본적인 PID, PPID, 파일기술자 등 프로세스 정보는 유지

1st parameter - 프로그램 파일의 절대경로/상대경로

파일명만 넣을 시 함수에 따라 현재 작업 디렉터리(parameter의 이름이 path일 경우) 또는 PATH 환경 변수에 등록된 디렉터리(parameter의 이름이 file일 경우)를 검색하여 실행 프로그램 찾음

2nd parameter – execl 계열은 variable parameter(NULL로 끝남), execv 계열은 array

이미지 교체 후엔 기존 코드가 실행되지 않음


3.1 상속되지 않는 파일기술자

*File descriptor란? Low level I/O의 file reference number

UNIX에서 모든 I/O 시스템 호출은 파일기술자를 통해 열려 있는 파일을 참조, 3번부터 할당됨

exec 호출 시 파일기술자의 상속을 막기 위해, FD_CLOEXEC 플래그(close-on-exec)를 사용해 fcntl 함수 호출 => 자식 프로세스가 쓰지 않는 파일이 복제되는 오버헤드를 피할 수 있음


3.2 system 함수

셸을 실행시켜 명령어 실행(fork-exec 간단히 구현한 형태)

System은 실행 명령어가 작동되는 동안 부모 프로세스 잠시 정지 / 자식 프로세스의 정지, 종료 상태를 통보해주는 시그널인 SIGCHLD 블록 / 종료 시그널 SIGINT, SIGQUIT 무시 => 무한 대기의 위험으로 권장하지 않음


Samba

SMB/CIFS 프로토콜을 사용한 파일, 프린트 서비스를 제공하는 소프트웨어

Linux/Unix 서버와 데스크탑을 액티브 디렉토리 환경으로 완벽하게 통합하기 위한 구성요소

도메인 컨트롤러 혹은 일반 도메인 멤버로써 기능함


간단히 말하면, 서로 다른 운영체제 사이의 자원을 공유하기 위해 개발된 서버가 바로 Samba서버다.

Windows에서 리눅스/유닉스의 공유 폴더와 프린트 같은 자원을 사용하거나, 그 반대로 사용하기 위해 사용된다.

'Study > 서버' 카테고리의 다른 글

HTTP 프로토콜과 웹 서버  (0) 2017.10.23
메일 관련 프로토콜(SMTP, POP3, IMAP)과 메일 서버  (0) 2017.10.23

HTTP (HyperText Transfer Protocol)

World Wide Web 상에서 정보를 주고 받을 수 있는 프로토콜로써,

주로 HTML 문서를 주고 받는 데 이용 (현재는 이미지, 비디오, 음성 등 거의 모든 형식의 데이터 전송 가능)

TCP와 UDP를 사용하며, 80번 포트를 사용

클라이언트와 서버 사이에 이루어지는 요청/응답 프로토콜

이미지 출처: http://www.ktword.co.kr/abbr_view.php?nav=&m_temp1=648&id=1283


웹 서버

웹 브라우저와 같은 클라이언트로부터 HTTP 요청을 받아들이고, HTML 문서와 같은 웹 페이지를 반환하는 프로그램

HTTP 프로토콜을 구현하며, 사용자 요청 결과를 HTML 형태로 변환하여 전달함

웹 서버 소프트웨어로는 Apache web server, IIS, GWS 등이 있다.


*웹 어플리케이션 서버(WAS)란?

웹 서버가 HTML 문서 같은 정적 컨텐츠를 처리하는 반면,

웹 어플리케이션 서버는 asp, php, jsp 등의 개발언어를 읽고 처리하여 동적 서버 콘텐츠, 웹 응용 프로그램 서비스를 처리함

'Study > 서버' 카테고리의 다른 글

Samba 서버란 무엇인가  (0) 2017.10.23
메일 관련 프로토콜(SMTP, POP3, IMAP)과 메일 서버  (0) 2017.10.23

메일 관련 프로토콜


SMTP (Simple Mail Transfer Protocol)

이메일을 보내고 받는데 사용되는 TCP/IP 프로토콜

그러나, 수신을 위해서는 POP3나 IMAP 중 하나의 프로토콜을 쓰는 것이 보통

25번 포트 사용


POP3 (Post Office Protocol)

이메일을 수신하기 위한 표준 프로토콜

서버에 있는 자신의 메일 수신함을 확인하여, 수신된 이메일을 사용자 PC로 다운로드할 수 있도록 해주는 클라이언트/서버 프로토콜

110번 포트 사용


IMAP (Internet Mail Access Protocol)

로컬서버에서 이메일을 액세스하기 위한 표준 프로토콜

메일서버와 동기화 가능

143번 포트 사용


*POP3와 IMAP의 차이

POP3를 이용하면 사용자가 메일을 읽을 때 즉시 사용자의 컴퓨터로 다운로드 되어 서버에 더이상 남아있지 않게 됨

IMAP은 원격지 파일서버 같은 개념. 서버에서 메일을 관리하기 때문에 사용자가 메일을 다운로드한 후에도 서버에 메일이 남아있게 됨.



메일 서버


메일서버란 사용자 간에 이메일을 주고 받을 때 사용되는 응용 프로그램으로, MTA(Mail Transfer Agent)라고 불리기도 함

전형적인 메일서버 프로그램으로는 sendmail, postfix 등이 있음

'Study > 서버' 카테고리의 다른 글

Samba 서버란 무엇인가  (0) 2017.10.23
HTTP 프로토콜과 웹 서버  (0) 2017.10.23

1. 컴퓨터의 데이터 표현

컴퓨터는 모든 데이터를 2진수로 표현.

한 비트는 0 또는 1을 표현할 수 있고, 이 비트들을 가지고 데이터를 2진수로 표현.



2. 정수의 표현

앞서 말한 이유로, 10진수를 사용하는 인간과 달리 컴퓨터는 2진수로 정수를 표현.

이 때, 먼저 정수를 표현할 데이터의 크기(magnitude)를 먼저 지정해 주어야 함. ex) C언어의 int, short, long (int)

정수에는 양의 정수와 음의 정수가 있는데, 이 것을 표현하기 위해서 두가지 방법이 사용된다.


2-1. Sign-and-Magnitude (부호와 크기)

MSB(Most Significant Bit, 최상위 비트)를 사용하는 방법.

MSB, 즉 가장 왼쪽에 있는 비트를 부호로 사용함.

MSB가 0이면 양의 정수, 1이면 음의 정수.

데이터 크기를 4bit로 가정했을 때,

범위는 (1,111)부터 (0,111)까지며, 이때 (1,000)과 (0,000)은 같은 수. (총 15개의 수를 표현할 수 있음)

S&M 방법은 MSB가 다를 경우 계산이 복잡하다는 단점을 가진다.


2-2. Two's complement (2의 보수)

양의 정수의 경우, 자연수와 같이 표현하지만,

음의 정수의 경우, 음의 정수로 표현하고자 하는 양의 정수의 1의 보수를 구한 후, 1을 더한 2의 보수를 사용한다.

(1의 보수를 구하는 방법 : 모든 비트를 반전 시켜 구함. 0은 1으로, 1은 0으로.)

이 방법을 사용하여 정수를 표하는 경우에도 MSB가 1일 때 음수다.

계산이 용이해 현재 컴퓨터에서 사용되는 방법.


ex) 데이터 크기를 8bit, 즉 1byte로 가정했을 때,

임의의 양의 정수 55를 음의 정수 표현하려면,

55 = 0011 0111의 1의 보수인 1100 1000에 1을 더하면 된다.

즉 -55 = 1100 1001.

앞서 구한 0011 0111과 1100 1001을 더하면 0이 됨을 알 수 있다.

또한 -1은 1111 1111이다. -0은 존재하지 않음.

표현 범위는 -128부터 127까지.



3. 실수(real number)의 표현

실수의 경우 floating point(부동 소수점) 표현을 사용해 표현함.

Sign(부호), Exponent(지수), Mantissa(가수)로 이뤄져 있다.

Single precision: 부호 1bit + 지수 8bit + 가수 23bit

Double precision: 부호 1bit + 지수 11bit + 가수 52bit

부호와 지수를 표현하고 남은 비트를 사용하여 가수 표현.

이 부분의 비트 길이에 따라 얼마나 정확히 실수를 표현할 수 있느냐가 결정됨.

컴퓨터로 완전히 정확한 실수를 표현할 수는 없으며 단지 가까운 수를 표현한다.


ex) Single precision의 경우,

+1000110.0111 -> +2^6 x 1.0001100111 -> (+, 6, 0001100111) -> [1bit: 0, 8bit: 133, 23bits: 0001100111]

이 때, 지수 표현은 Excess 127 표현을 사용하여 6승을 133으로 저장함.

(Excess 127 표현 : 255를 절반으로 나눠 ~ -2승 = 125 -1승 = 126 1승 = 127 2승 = 128 ~ 으로 표현함




* 네트워크에선 16진수를 자주 사용하는데,

이는 16진수가 4bit임으로 1byte를 16진수 2개처럼 표현하기 용이하기 때문이다.



+ Recent posts