출처 :
http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/system_programing/File/File_Object
 
FILE 객체와 파일지시자 와의 차이점.. 시스템 프로그램 파일관련 작업을 할때, 우리는 int 형의 파일지시자 값을 돌려주 는 open 계열 함수와 FILE 객체를 돌려주는 fopen 계열함수중 택일 하여 사용한다. 이번장에서는 fopen 계열 함수와 open 계열 함수와의 차이점이 무엇이고, 어떠한 장정과 단점을 가지는지에 대해서 알아보도록 하겠다. 기본적으로 유닉스에서는 모든 것을 파일로 처리한다는것에 대해사 알고 있을 것이다. 이는 모든 표준입출력 표준에러, 파일, 소켓에등에도 동일하게 적용된다.
다음은 zipcode의 fork 버젼을 4444 번 port 로 실행시키고, zipcode 의 프로세스 상황을 출력한 결과 이다.
zipcode 의 fork 버젼은 다중연결서버 만들기 (1)의 서버프로그램이다.
[yundream@coco test]$ ./zipcode_m 
Usage : ./zipcode [port]
예    : ./zipcode 4444
[yundream@coco test]$ ./zipcode_m 4444
위와 같이 서버프로그램을 실행시키고 /proc 파일시스템을 이용해서 zipcode_m 의 프로세스 정보중 fd 정보를 출력해 보았다.
[yundream@hum test]$ ps -aux | grep zipcode_m | grep -v grep 
yundream 14987  0.0  0.2  1340  376 ttyq1    S    15:02   0:00 ./zipcode_m 4444
[yundream@hum test]$ cd /proc/14987/fd ; ls -al
합계 0
dr-x------    2 yundream 500             0  3월 18 15:15 .
dr-xr-xr-x    3 yundream 500             0  3월 18 15:15 ..
lrwx------    1 yundream 500            64  3월 18 15:15 0 -> /dev/ttyq1
lrwx------    1 yundream 500            64  3월 18 15:15 1 -> /dev/ttyq1
lrwx------    1 yundream 500            64  3월 18 15:15 2 -> /dev/ttyq1
lr-x------    1 yundream 500            64  3월 18 15:15 3 -> /home/mycvs/test/zipcode.txt
lrwx------    1 yundream 500            64  3월 18 15:15 4 -> socket:[587641]
[yundream@hum fd]$ 
위위 화면을 보면 알겠지만, 표준입력(0), 표준출력(1), 표준에러(2), 열린파일(3), 열린 소켓(4) 에 대해서 일련의 숫자로된 link 파일이 만들어지는걸 볼수 있을것이다.
표준입력,출력,에러는 자신의 터미널을 링크하고 있으며 열린파일은 파일, 소켓은 고유의 소켓번호를 링크하고 있음을 알수 있다.
INET(인터넷) 소켓의 경우에는 소켓통신을 위해서 파일을 생성하지 않고 커널에서 직접관리 하므로, INET 소켓의 경우 링크가 파일로 연결되지 않고, 커널의 소켓관리 번호에 연결된다는 점을 주의 하자

테스트도 할겸 0 번파일에 쓰기를 한번해보자.
[yundream@hum fd]$  echo "111" > 0
[yundream@coco test]$ ./zipcode_m 4444
111
그러면 ./zipcode_m 을 실행한곳에 111 이 출력 됨을 볼수 있을것이다.

open 계열
우리가 흔히 사용하는 open 계열의 함수를 사용하면 바로 /proc/pid/fd 밑에 있는 링크파일의 이름을 int 형으로 변환시켜서 되돌려주며, 이값을 이용해서 우리는 여러가지 입출력 작업을 할수 있게 되는것이다.
모든 입출력을 파일로 일관되게 처리할수 있으며, 프로그래밍 인터페이스와 /proc 파일의 인터페이스가 서로 동일하게 연결되어서 사용된다.
매우 효율적이고 편리한 입출력환경을 제공해준다는걸 느낄수 있을것이다.

이처럼 open 계열을 사용하면 각각의 입출력 파일을 직접 엑세스 할수 있으므로, 저수준의 작업, 예를들면 select, polling, 파일잠금, 레코드잠금, 파일속성변경등의 파일을 다루기 위한 세세한 일들을 할수 있는 장점을 얻게 된다.
반면 단점을 가지는데, 저수준으로 파일을 다루다 보니 간단한 일을 할기에는 너무 잔손이 많이 간다라는 점이다.
간단한 행입력을 받는 프로그램을 만든다고 생각해보자, open 을 사용하게 되면 개행문자 검사를 위해서 입력받은 문자를 바이트단위로 비교를 해주는 수고스러움을 감수해야 하며, 별도로 버퍼 관리를 해주어야 할것이다.

fopen 계열
이러한 문제를 해결하기 위해서(반드시 위의 문제때문에 나온것 만은 아니지만), f계열 의 좀더 고수준의 함수들을 제공한다.
f 계열함수들은 파일을 "FILE" 객체를 이용 해서 다루게 된다. FILE 객체는 fopen 함수를 이용해서 파일열기에 성공했을때 돌려주게 되는데,
/usr/include/stdio.h 를 열어보면 다음과 같이 선언되어 있다.
/* The opaque type of streams.  This is the definition used elsewhere.  */
typedef struct _IO_FILE FILE;
struct _IO_FILE 는 liblo.h 에 선언되어 있는데, 멤버변수가 꽤 많으니 직접 살펴 보기바란다. _IO_FILE 구조체를 보면 파일지시자와 함께, 파일 입출력을 위한 많은 버퍼를 별도로 관리하고 있음을 알수 있는데 fopen 계열함수를 이용하면, 이러한 구조체의 제어를 알아서 해준다.
결론적으로 파일을 객체로 다루게 됨으로 잔손질을 덜수 있게 된다.
아래의 예제코드를 보자

예제 : fopen.c
#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 

int main()
{
	FILE *fp;
	char buf[256];

	printf("끝낼려면 ^D\n");
	while(fgets(buf, 256, stdin) != NULL)
	{
		printf("%s", buf);
	}
}
위의 코드를 open 계열함수로 사용하면 아마도 코드량이 2배 정도로 늘어날것이다.

FILE 과 파일지시자 사이의 호환
최초에 저수준으로 파일을 다룰필요가 있어서 open 을 썼더라도, 고수준의 f 계열 함수를 쓰고 싶을때가 있을것이다.
또는 그 반대의 경우도 있을수 있는데, 당연히 이에 대한 해법도 존재한다.
둘의 호환의 방법을 생각해보면, 최초에 open 을 통해서 파일을 열었더라도, FILE 객체의 구성요소를 전부체워주고 여기에 여결시키면 될것이며, fopen 을 통해서 FILE 객체로 받았다 하더라도 FILE 객체 구조체에 어차피 파일지시자가 있으므로, 이 값을 이용해서 저수준의 작업을 하는데 사용하면 될것이다.
이러한 상호 호환을 위해서 별도의 함수가 존재한다.

fdopen(3)과 fileno(3) 가 그것으로 fdopen 가 파일지시자를 받아서 FILE 객체를 되돌려주는 함수이고, fileno 가 FILE 객체를 받아서 파일지시자를 돌려받는 함수 이다.
아래는 fdopen 의 간단한 예이다.

예제 : fdopen.c
#include <stdio.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 

int main()
{
	int		fd;
	FILE*	fp;
	char	buf[256];

	printf("끝내기 : ^D\n");
	fd = open(0, O_RDONLY);
	fp = fdopen(0, "r");
	while(fgets(buf, 256, fp) != NULL)
	{
		printf("%s", buf);
	}
	
}

이상 FILE 객체와 파일지시자 와의 차이점..에 대해서 알아보았습니다.
궁금한거나 잘못된점 있다면 댓글을 남겨주시면, 성심성의껏 답변드리도록 하겠습니다.

'It's my study ^^ 과연' 카테고리의 다른 글

CLangauge Complex Declaration  (0) 2007.12.27
Big endian vs Little endian  (0) 2007.12.21
open, fopen의 차이점  (0) 2007.12.18
hard link와 symbolic link  (0) 2007.12.18
FAT 파일 시스템(File System)  (0) 2007.12.17
open 계열
우리가 흔히 사용하는 open 계열의 함수를 사용하면 바로 /proc/pid/fd 밑에 있는 링크파일의 이름을 int 형으로 변환시켜서 되돌려주며, 이값을 이용해서 우리는 여러가지 입출력 작업을 할수 있게 되는것이다.
모든 입출력을 파일로 일관되게 처리할수 있으며, 프로그래밍 인터페이스와 /proc 파일의 인터페이스가 서로 동일하게 연결되어서 사용된다.
매우 효율적이고 편리한 입출력환경을 제공해준다는걸 느낄수 있을것이다.

이처럼 open 계열을 사용하면 각각의 입출력 파일을 직접 엑세스 할수 있으므로, 저수준의 작업, 예를들면 select, polling, 파일잠금, 레코드잠금, 파일속성변경등의 파일을 다루기 위한 세세한 일들을 할수 있는 장점을 얻게 된다.
반면 단점을 가지는데, 저수준으로 파일을 다루다 보니 간단한 일을 할기에는 너무 잔손이 많이 간다라는 점이다.
간단한 행입력을 받는 프로그램을 만든다고 생각해보자, open 을 사용하게 되면 개행문자 검사를 위해서 입력받은 문자를 바이트단위로 비교를 해주는 수고스러움을 감수해야 하며, 별도로 버퍼 관리를 해주어야 할것이다.

fopen 계열
이러한 문제를 해결하기 위해서(반드시 위의 문제때문에 나온것 만은 아니지만), f계열 의 좀더 고수준의 함수들을 제공한다.
f 계열함수들은 파일을 "FILE" 객체를 이용 해서 다루게 된다. FILE 객체는 fopen 함수를 이용해서 파일열기에 성공했을때 돌려주게 되는데,
/usr/include/stdio.h 를 열어보면 다음과 같이 선언되어 있다.
/* The opaque type of streams.  This is the definition used elsewhere.  */
typedef struct _IO_FILE FILE;
struct _IO_FILE 는 liblo.h 에 선언되어 있는데, 멤버변수가 꽤 많으니 직접 살펴 보기바란다. _IO_FILE 구조체를 보면 파일지시자와 함께, 파일 입출력을 위한 많은 버퍼를 별도로 관리하고 있음을 알수 있는데 fopen 계열함수를 이용하면, 이러한 구조체의 제어를 알아서 해준다.
결론적으로 파일을 객체로 다루게 됨으로 잔손질을 덜수 있게 된다.

'It's my study ^^ 과연' 카테고리의 다른 글

Big endian vs Little endian  (0) 2007.12.21
파일객체와 파일지시자 비교  (0) 2007.12.21
hard link와 symbolic link  (0) 2007.12.18
FAT 파일 시스템(File System)  (0) 2007.12.17
TCP 헤더 구조  (0) 2007.12.15
바로 위에서 link(이하 링크)가 몇번 언급되었었다. 이 링크에 대해서 자세히 알아보도록 하겠다.

링크는 파일을 가리키는 일종의 별칭으로 주로 관리의 목적으로 사용한다. 예를 들어 오픈오피스의 문서 프로그램을 실행시키기 위해서 /usr/local/openoffice/bin/openoffice_swrite를 실행시켜야 한다고 가정해보자. 이거 기억해서 수행시킬려면 보통 곤욕스러운 일이 아닐 것이다. 이 경우 링크를 이용해서 간단하게 문제를 해결할 수 있다.
# ln -s /usr/local/openoffice/bin/openoffice_swrite /usr/bin/swrite  
/usr/bin 은 환경변수 PATH에 등록이 되어있을 것이기 때문에, 간단하게 swrite만 입력하는 정도로 /usr/local/openoffice/bin/openoffice_swrite 를 실행할 수 있게 된다. 이제 ls를 이용해서 /usr/bin/swrite 의 정보를 확인해 보도록 하자.
$ ls -al swrite 
lrwxrwxrwx 1 root root 43 2007-11-26 23:44 swrite -> /usr/local/openoffice/bin/openoffice_swrite
swrite가 원본 openoffice_swrite 를 링크하고 있는 것을 확인할 수 있을 것이다.

직관적으로 이해할 수 있을 것이다. 그러나 link 는 두가지 종류가 있다. 심볼릭 링크와 하드링크가 그것이다. 이둘의 차이점에 대해서 알아보도록 하겠다.

hard link

앞서 파일은 장치내에서 식별되기 위해서 inode 를 가진다는 것을 언급했었다. 여기에 inode 가 1234 인 파일 myfile이 있다고 가정해보자. 이것을 다른 Directory에 복사하기 위한 가장 일반적인 방법은 파일을 copy 하는 것으로 이경우 새로운 inode 를 가지는 파일이 생길 것이다. 그럼 cp(1)를 이용해서 파일을 복사해보도록 하자.
# mkdir testdir 
# cp myfile testdir/myfile2
이제 두개 파일의 inode를 확인해 보도록하자. stat함수를 이용해서 프로그램을 만들 필요는 없다. ls 의 -i옵션을 사용하면 간단하게 파일의 inode 값을 알아낼 수 있다.
# ls -i myfile  
1131883 myfile
# ls -i testdir/myfile2
1163816 testdir/myfile2
내용은 동일하지만 완전히 다른 파일이 생성되었음을 알 수 있다.

이 방법은 대부분의 경우 유용하게 사용할 수 있겠지만 하나의 파일을 여러개의 디렉토리에 공유할 목적으로 사용하고자 할 경우 문제가 발생한다. 예를 들어 주소록 파일인 /home/yundream/mydata.txt 가 있다고 가정해보자. 이 파일을 /home/dragona 에 공유하길 원한다. 만약 mydata.txt에 새로운 내용이 추가되거나 삭제되면 /home/dragona 에도 그대로 적용되어야 한다. 단순히 copy 할경우에는 한쪽에서 변경하면, 다른 한쪽에는 반영되지 않을 것이다. 링크를 사용하면 이 문제를 간단하게 해결할 수 있다.

# ln mydata.txt /home/dragona 
이제 한쪽에서 파일을 수정해보자. 다른 쪽도 그대로 수정된 내용이 반영되어 있음을 확인할 수 있을 것이다. ls -i 로 확인해보면 두개의 파일이 동일한 inode를 가지고 있음을 확인할 수 있을 것이다. 이것을 링크라고 하며, 위에서와 같이 inode를 공유해서 사용하는 것을 하드 링크 라고 한다. 이해하기 쉽게 그림으로 나타내보자면 다음과 같다.

inode.png

file_name 1과 file_name2 가 서로동일한 inode를 가리키고 있음을 확인할 수 있다. 이러한 하드링크로 얻을 수 있는 장점은 데이터를 공유할 수 있다는 것 외에, 디스크를 아낄 수 있다는 장점도 가진다. 데이터를 직접복사하는게 아니기 때문이다. 원본은 하나이고 inode 만 공유할 뿐이다. 하드링크를 하나 생성하면 inode 공유 카운터가 1증가할 뿐이다. ls -al 로 mydata.txt 원본파일의 정보를 확인해 보자
# ls -al mydata.txt 
-rw-r----- 2 yundream yundream 192 2007-11-26 23:57 mydata.txt
하드링크 카운터가 하나 증가해서 2가 되어 있는걸 확인할 수 있을 것이다. 파일을 하나 지우고 나서 ls 결과를 보면 카운터가 하나 줄어서 1이 되는걸 확인할 수 있을 것이다.

하드링크를 사용할 때, 주의해야할 점이 있다. 하드링크는 inode 를 가리킨다. 이 때, inode 는 하나의 장치에서만 유일하므로 다른 장치로의 하드링크는 불가능 하다는 점이다. 왜냐하면 다른 장치에서 유일하다는 것을 보장할 수 없기 때문이다. 이런 경우에는 심볼릭링크를 사용해야 할 것이다.

심볼릭 링크

이와 달리 심볼릭 링크는 별도의 inode를 가지는 파일로 원본파일에 대한 inode와 함께 장치 정보까지를 가지고 있다. 어떤파일에 대한 inode와 장치정보를 알고 있다면, 전 시스템에서 유일한 파일을 가리킬 수 있기 때문에 장치에 관계없이 링크를 걸 수 있게 된다.

inode2.png

그럼 mydata.txt 를 원본파일로 하는 심볼릭링크 mydata2.txt를 만들어 보도록 하자. ln 명령에 -s옵션을 주면 심볼릭링크를 생성할 수 있다.
# ln -s mydata.txt mydataln.txt 
이제 -i 옵션을 이용해서 두개 파일의 inode를 비교해 보면 서로 다른 별개의 inode를 유지하고 있음을 알 수 있을 것이다. ls -l 을 이용해서 심볼릭링크가 가리키는 원본파일의 이름을 얻어올 수 있다.

# ls -l mydataln.txt  
lrwxrwxrwx 1 yundream yundream 10 2007-11-28 01:49 mydataln.txt -> mydata.txt

출처 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/system_programing/Book_LSP/ch03_Env

'It's my study ^^ 과연' 카테고리의 다른 글

파일객체와 파일지시자 비교  (0) 2007.12.21
open, fopen의 차이점  (0) 2007.12.18
FAT 파일 시스템(File System)  (0) 2007.12.17
TCP 헤더 구조  (0) 2007.12.15
open  (0) 2007.12.15
원문 :  http://kkamagui.springnote.com/pages/345737

들어가기 전에...

1.FAT 파일 시스템 개요

 FAT 파일 시스템은 전통적으로 DOS 시절부터 사용되어 왔다. FAT 파일 시스템이 다른 파일 시스템과 구별되는 점은 파일 내용을 클러스터 단위로 구성하고 이것을 링크드 리스트(Linked List)의 형태로 보관하는 것이다.

 말로 하면 어려우므로, 일단 아래 그림을 보도록 하자.

FAT1.PNG

<FAT의 간략한 구조>



 위 그림과 같이 파일은 고정된 크기의 클러스터(Cluster) 단위로 나누어져 있으며, 그 클러스터들은 클러스터 풀(Cluster Pool)에 존재하는 클러스터를 연결한 클러스터 채인(Cluster Chain)으로 되어있다. 실제 FAT 파일 시스템에서 클러스터 풀은 FAT(File Allocation Table)이라는 용어를 사용한다.

 클러스터(Cluster)란 별개 아니고 여러개의 섹터를 모아서 하나의 블럭 단위로 보는 것이다. 윈도우 포맷 프로그램을 이용해서 포맷을 하면 일반적으로 클러스터의 크기를 4Kbyte로 잡는데, 이 말은 한 섹터(Sector)의 크기가 512byte 이므로 8개의 섹터를 하나의 블럭으로 설정한다고 보면 된다.

 그럼 왜 클러스터라는 말을 쓰는 것일까? 그것은 섹터를 블럭으로 관리하면 얻는 이득이 있기때문이다. 아까 잠시 클러스터 풀에 대한 이야기를 했는데, 고용량일수록 클러스터 풀(FAT 영역)이 커지게 된다. 이것이 커질수록 파일을 읽고 쓸때 관리해야 하는 양이 늘어나게되고, 또한 디스크의 비어있는 공간이 줄어들게 된다. 이것을 블럭으로 관리하게 되면 이런 문제가 해결되며, 또한 블럭 단위로 읽기 쓰기를 수행함으로 얻는 효율로 인해 성능이 좋아지게 되는 것이다.

 특히 요즘처럼 파일의 크기가 큰 상황에서는 클러스터의 크기가 큰 것이 성능 향상에 도움이 된다. 하지만 클러스터의 크기가 무조건 크다고 좋은 것은 아니다. 아까 이야기 했듯이 클러스터의 단위로 작업을 하기 때문에, 작은 파일 같은 경우는 클러스터 하나를 할당받아서 대부분의 영역을 낭비하는 경우도 있으니 적당히 조절해야 한다.


 자 그럼 FAT 파일 시스템의 큰 특징을 알아봤으니 세부적인 내용을 알아보자

 FAT 파일 시스템은 총 3가지의 타입이 있다.

  • FAT12 : 클러스터의 개수가 1 ~ 0xFFF 까지를 가지는 타입. 플로피 디스크에서 주로 사용됨
  • FAT16 : 클러스터의 개수가 0xFFF ~  0xFFFF 까지를 가지는 타입. 소용량에 주로 사용됨
  • FAT32 : 클러스터의 개수가 0xFFFF ~ 0xFFFFFFFF 까지를 가지는 타입. 대용량에 주로 사용됨

 세가지 타입 모두 내부 포맷은 비슷하며, 차이점은 클러스터 개수라고 볼 수 있다.(물론 그것만 차이나는 것은 아니다. ㅡ,.ㅡ;;; 오해하지 말자.). 클러스터의 개수에 따라 FAT 타입을 분류한다고 했는데, 이 클러스터 개수는 어떻게 구하는 것일까? 실제 데이터 영역을 구성하는 크기를 가지고 계산하는 것일까?


 실제 타입 구분에 사용되는 클러스터의 크기는 디스크 전체 크기를 범위로 한다. 즉 아래의 공식으로 클러스터의 크기를 구하고 그것으로 타입을 유추하는 것이다.

클러스터의 개수 = 파티션 또는 디스크의 크기 / 클러스터의 크기

 이렇게 구한 클러스터의 개수는 실제로 사용가능한 데이터 영역의 클러스터의 개수와 비교하면 당연히 큰값을 가진다. 왜냐하면 디스크 또는 파티션 영역에 데이터만 들어가는 것이 아니고 FAT 파일 시스템을 관리하기위한 파일 시스템 메타 데이터(Meta Data)를 포함하기 때문이다.

 이제 파티션과 메타 데이터에 대해서 알아보자.


2.파티션(Partition)

 파티션에 대해서 익히 알고있고 들어봤을 것이다. 디스크 전체를 하나로 사용하는 것이 아니라 그 안에 C: D:와 같이 영역을 분할하여 사용하는 것이 파티션이다. 파티션을 나누면 디스크를 효율적으로 관리할 수 있는 장점 때문에 약간의 디스크 공간을 낭비하더라도 파티션을 해서 많이 사용한다(아래와 같이 나눌 수도 있다).

Partition.PNG


 그럼 어떻게해서 파티션이 나누어지는 것일까? 파티션을 나누게 되면 그 파티션에 대한 정보가 MBR(Master Boot Record)란 영역에 삽입되게 된다. MBR이란 디스크의 첫번째 섹터로 부트 코드와 여러가지를 정보를 포함하고 있다. MBR의 존재는 맨 마지막 510째부터 0x55 0xAA가 있는지 확인하여 이것이 있으면 MBR이 존재한다고 보면 된다(사실 다 있다. ㅡ_ㅡ;;; OS를 깔아 쓰면 저것 말고도 여러 정보가 MBR에 들어가 있다.)

 MBR의 세부구조를 한번 알아보자.

MBR.PNG

 헥사 에디터 프로그램으로 4개의 파티션으로 나누어진 USB 메모리의 MBR을  캡쳐한 화면이다. 좌측 상단에 0xEB 0x3C 0x90의 3Byte가 있는데, 이부분은 부트 코드의 시작으로 jmp 하라는 어셈블리어 명령이다. 굳이 필요한 것은 아니나 윈도우에서 MBR 인식을 위해서는 이 부분이 꼭 필요하다( 이부분이 0x00으로 비어져있는 경우에 윈도우가 MBR로 인식하지 못하는 것을 발견했다).

 우측 하단에 0x55 0xAA의 매직넘버가 보이고 그위에 붉은 색 줄이 쳐져있는 16byte의 라인이 보인다. 이부분이 파티션 데이터의 시작으로 그 앞부분은 모두 Boot Code의 용도로 사용되거나 아니면 사용되지 않는다. 파티션 정보는 16Byte씩 4개로 총 64Byte로 되어있으며 각 항목은 아래와 같은 구조체로 되어있다.

  1. /** 
  2.     Partition에 대한 정보를 포함하는 구조체 
  3. */ 
  4. typedef struct partitionStruct  
  5. {  
  6.     BYTE bBootIndicator;  
  7.     BYTE bStartingHeader;  
  8.     WORD wStartingSectorAndCylinder;  
  9.     BYTE bSystemID;  
  10.     BYTE bEndingHeader;  
  11.     WORD wEndingSectorAndCylinder;  
  12.     DWORD dwRelativeSector;  
  13.     DWORD dwTotalSector;  
  14. } PARTITION, * PPARTITION;  

  여기서 중요하게 봐야할 부분은 위에서 파란색으로 표시된 부분인데, 그 외 부분들은 옛날 도스 시절에나 사용된 필드기 때문에 무시해도 된다. 각 필드는 아래와 같은 의미를 가진다.

  • Boot Indicator : 부팅 가능한 파티션이면 0x80을 가짐. 그렇지 않으면 0x00
  • System ID : 파일 시스템의 ID. 일반적으로 윈도우에서 사용하는 값은 0x0B. 그 외의 값도 많으나 잘 쓰이지 않음
  • Relative Sector : 파티션이 시작되는 섹터.
  • Total Sector : 총 파티션의 크기

  위 정도값만 제대로 설정하면 윈도우에서 정상적으로 파티션 된 것으로 인식한다. MBR에 파티션 필드는 총 4개가 있으므로 파티션은 최대 4개까지 만들 수 있다. 그럼 도스 시절에는 파티션이 4개 이상이 가능했는데, 이것은 어떻게 한 것일까? 옛날 도스에서는 확장 파티션(Extension Partition)이라는 기능을 사용했다. 즉 MBR 파티션에는 확장 파티션 영역을 표시해 놓고, 확장 파티션 영역의 첫번째 섹터로 이동하면 그 첫번째 섹터에 다시 파티션 정보 + 확장 파티션 정보를 기입하는 식으로 체인으로 연결했던 것이다(이렇게 하면 디스크 용량이 허락하는 한 거의 무한대의 파티션을 생성할 수 있다. ㅡ,.ㅡ;;;;). 도스 이후에는 별로 사용하지 않으므로 일단 넘어가자. 궁금한 사람은 http://www.nondot.org/sabre/os/articles에 가서 Partition 쪽을 보면 된다.



3.파일 시스템 메타 데이터(File System Meta Data)

 FAT_메타_데이터_구조.PNG

 FAT 파일 시스템을 구성하는 메타 데이터는 위와 같은 순서로 구성된다. 각 항목에 대한 설명은 아래와 같다.

  • PBR : 파티션 부트 레코드(Partition Boot Record)의 약자로서 파티션에 대한 전반적인 정보를 저장하고 있음. 핵심적인 부분
  • Reserved : 특수한 용도(Root Directory의 시작위치를 조절하거나 특수한 데이터를 저장할 용도)로 사용되는 공간. 존재하지 않을 수도 있음
  • FAT1/2 : 클러스터 풀로써 클러스터 체인을 구성하는 부분. 뒤에오는 Data Cluster의 개수에 따라 가변적인 크기를 가짐. FAT1은 FAT2은 동일한 데이터를 가지며 FAT는 1만 존재할 수도 있음
  • Root Directory : 파일시스템의 최상위 디렉토리. FAT12/16의 경우 섹터단위로 설정할 수 있고, FAT32의 경우 클러스터 체인으로 구성함.
  • FSInfo : FAT32에만 존제하는 영역으로 FAT32에 속하는 기타 정보를 포함하는 영역
  • Backup Area : FAT32에만 존재하는 영역으로 PBR부터 FSInfo까지를 백업하는 영역. 완전히 복사하여 파일 시스템에 문제가 생기면 Backup 영역에서 데이터를 다시 복사하여 복원하는 역할

3.1 PBR(Partition Boot Record), BPB(BIOS Parmeter Block)

 PBR은 여러 이름으로 불리는데 다른 이름으로는 BPB라고도 한다. 옛날 도스 시절에 BIOS 콜을 통해 파티션에 대한 정보를 얻어오고 처리하고 했는데, 그 기본 정보가 되는 블럭이라서 저런 이름이 붙은 것 같다. 일단 여기서는 PBR이라고 하겠다.

 PBR과 MBR의 차이는 무엇일까? MBR은 하나만 존재하며 섹터 0에만 있고, PBR은 각 파티션의 시작에 존재하며 FAT에 대한 정보를 포함하고 있다는 것이 다르다. PBR에는 어떤 정보가 포함되어있을까? FAT Type에 따라 다르므로 각각 살펴보도록 하자.


 FAT16/32의 PBR에 포함된 데이터는 아래와 같다.

  1. /** 
  2.     FAT16에 대한 구조체 
  3. */ 
  4. typedef struct fat16Struct  
  5. {  
  6.     BYTE vbJumpCode[ 3 ];  
  7.     char vcCreatingSystemIdentifier[ 8 ];  
  8.     WORD wSectorSize;  
  9.     BYTE bSectorsPerCluster;  
  10.     WORD wReservedSectorCount;  
  11.     BYTE bNumberOfFATs;  
  12.     WORD wNumberOfRootDirectoryEntries;  
  13.     WORD wTotalSectors;  
  14.     BYTE bMediumIdentifer;  
  15.     WORD wSectorsPerFAT;  
  16.     WORD wSectorsPerTrack;  
  17.     WORD wNumberOfSides;  
  18.     DWORD dwNumberOfHiddenSectors;  
  19.     DWORD dwTotalSectors;  
  20.     // 여기 위까지는 FAT16/32 공통 
  21.     BYTE bPhysicalDiskNumber;  
  22.     BYTE bReserved;  
  23.     BYTE bExtendedBootRecordSignature;  
  24.     char vcVolumeIDNumber[ 4 ];  
  25.     char vcVolumeLabel[ 11 ];  
  26.     char vcFileSystemType[ 8 ];  
  27.     BYTE vbReservedForSystemUse[ 448 ];  
  28.     BYTE vbSignatureWord[ 2 ];  
  29. } FAT16, * PFAT16;  
  30.  
  31. /** 
  32.     FAT32에 대한 구조체 
  33. */ 
  34. typedef struct fat32Struct  
  35. {  
  36.     BYTE vbJumpCode[ 3 ];  
  37.     char vcCreatingSystemIdentifier[ 8 ];  
  38.     WORD wSectorSize;  
  39.     BYTE bSectorsPerCluster;  
  40.     WORD wReservedSectorCount;  
  41.     BYTE bNumberOfFATs;  
  42.     WORD wNumberOfRootDirectoryEntries;  
  43.     WORD wTotalSectors;  
  44.     BYTE bMediumIdentifer;  
  45.     WORD wSectorsPerFAT;  
  46.     WORD wSectorsPerTrack;  
  47.     WORD wNumberOfSides;  
  48.     DWORD dwNumberOfHiddenSectors;  
  49.     DWORD dwTotalSectors;  
  50.     // 여기 위까지는 FAT16/32 공통 
  51.     DWORD dwSectorsPerFAT32;  
  52.     WORD wExtensionFlag;  
  53.     WORD wFSVersion;  
  54.     DWORD dwRootCluster;  
  55.     WORD wFSInfo;  
  56.     WORD wBackupBootSector;  
  57.     BYTE vbReserved[ 12 ];  
  58.     BYTE bPhysicalDiskNumber;  
  59.     BYTE bReserved;  
  60.     BYTE bExtendedBootRecordSignature;  
  61.     char vcVolumeIDNumber[ 4 ];  
  62.     char vcVolumeLabel[ 11 ];  
  63.     char vcFileSystemType[ 8 ];  
  64.     BYTE vbReservedForSystemUse[ 420 ];  
  65.     BYTE vbSignatureWord[ 2 ];  
  66. } FAT32, * PFAT32;  

 상당히 많은 데이터를 포함하고 있는데, 일단 FAT16/32에 공통인 부분부터 살펴보자.

  • BYTE vbJumpCode[ 3 ] : Jump Code. 0xEB 0x?? 0x90 또는 0xE9 0x?? 0x??으로 구성되어야 함(이 부분을 제대로 넣지 않을 경우 윈도우에서 파티션 인식이 제대로 안됨)
  • char vcCreatingSystemIdentifier[ 8 ] : Format을 수행한 OS의 이름. 대충 집어넣으면 됨. "MSWIN4.1"와 같은 값.
  • WORD wSectorSize : 섹터 하나의 크기. 보통 512byte
  • BYTE bSectorsPerCluster : 클러스터를 구성하는 섹터의 수. 1Byte이므로 최대 255개까지 클러스터로 설정가능하나 1, 2, 4, 8, 16, 32, 64, 128이 일반적으로 유효한 값.
  • WORD wReservedSectorCount : 파티션 시작 영역부터 FAT 1영역이 시작되기 전에 존재하는 섹터의 수(위 그림 참조). FAT12/16에서는 1, FAT32에서는 32로 권장. 하지만 0xFFFF까지 값을 가질 수 있음. 이 값을 이용하면 Root Cluster의 시작을 적당한 위치로 설정할 수 있음
  • BYTE bNumberOfFATs : File Allocation Table(FAT)의 개수. 1개 또는 2개가 설정 가능하나 일반적으로 2의 값을 가짐.
  • WORD wNumberOfRootDirectoryEntries : Root Directory의 Entry의 개수. FAT12/16에서 사용하며 FAT16에서는 512 권장. FAT32에서는 0.
  • WORD wTotalSectors : 파티션의 크기가 0xFFFF 섹터 이하이면 이 필드 사용. FAT32에서는 0.
  • BYTE bMediumIdentifer : Media의 Type. 일반적으로 0xF8 사용.
  • WORD wSectorsPerFAT : FAT12/16에서 사용되는 값. FAT32에서는 0.
  • WORD wSectorsPerTrack : Track에 포함된 섹터의 수.
  • WORD wNumberOfSides : Size의 개수. 일반적으로 Cylinder의 수.
  • DWORD dwNumberOfHiddenSectors : 파티션 시작 이전에 존재하는 섹터의 수.
  • DWORD dwTotalSectors : 파티션의 크기가 0xFFFF 초과일때 사용되는 필드. FAT32에서는 반드시 이 필드 사용.
  •  BYTE bExtendedBootRecordSignature :  0x29 값으로 설정.
  • char vcVolumeIDNumber[ 4 ] : Volume ID. 일반적으로 일자/시간을 섞어서 만듬. 대충 만들어도 됨.
  • char vcVolumeLabel[ 11 ] : Volume Label. 볼륨 이름.
  • char vcFileSystemType[ 8 ] : "FAT12", "FAT16", "FAT", "FAT32" 같은 문자열.
  • BYTE vbSignatureWord[ 2 ] : 0x55 0xAA

 공통인 부분들에 대해 살펴봤으니, 이제 FAT32에 특화된 부분을 보자

  • DWORD dwSectorsPerFAT32 : FAT32용 FAT 영역 크기를 설정하는 부분
  • WORD wExtensionFlag : FAT1/2 영역이 존재할때, 하나만 사용할 것인지 아니면 둘다 동기화 해서 사용할 것인지 설정하는 플래그. 일반적으로 0으로 설정하여 둘다 동기화 하여 사용하는 것으로 설정.
  • WORD wFSVersion : File System Version. High Byte는 Major, Low Byte는 Minor를 나타냄. 일반적으로 0x00 0x00으로 설정.
  • DWORD dwRootCluster : Root Cluster의 번호. 일반적으로 2로 설정(클러스터 0과 1번은 Signature 같은 용도로 사용).
  • WORD wFSInfo : FSInfo가 위치하는 Sector Offset. 일반적으로 PBR 바로 뒤에 위치하므로 1의 값을 가짐.
  • WORD wBackupBootSector : Backup 영역이 존재하는 Sector Offset. 일반적으로 6의 값을 가짐.
  • BYTE vbReserved[ 12 ] : 예약된 영역. 무조건 0으로 설정.
  • BYTE bPhysicalDiskNumber : Drive Number. 일반적으로 0x80을 가짐.
  • BYTE bReserved : 예약된 영역. 무조건 0으로 설정.

  위의 필드 값을 FAT Type에 따라 PBR에 설정해 주면 반은 끝난 것이다. 약간 주의할 점은 Reserved Sector에 값을 설정했다면 파티션의 시작부터 Reserved Sector에 설정된 값만큼을 0으로 초기화해야한다(적극 권장). 위의 설명에도 나와있듯이 Reserved Sector의 값은 파티션의 시작부터 FAT1/2 이전까지의 섹터 수이므로, 절대 0이 될 수 없다. 왜? PBR이 있기 때문에 @0@)/~!!! 아무리 작아도 1 이상의 값을 가진다.

 이제 다음 메타 데이터를 살펴보자.


3.2File Allocation Table(FAT) 1/2 영역

 FAT 1/2 영역은 클러스터의 개수에 따라서 영역의 크기가 달라진다고 이야기 했다. 여기에서 말하는 클러스터의 개수는 실제 사용가능한 클러스터의 개수를 이야기하는데, 이 크기 이상만 된다면 얼마든지 좀 크게 잡아도 상관없다.

 FAT12의 경우 FAT를 구성하는 클러스터 링크의 비트수가 12bit가 이고, FAT16의 경우  16bit, FAT32의 경우 32bit이다. 따라서 한 섹터를 기준으로 FAT Type에 따라서 포함할 수 있는 클러스터 링크의 개수는 아래와 같이 된다.

  • FAT12 : 512Byte * 8 / 12 = 341.3333 개
  • FAT16 : 512Byte * 8 / 16 = 256 개
  • FAT32 : 512Byte * 8 / 32 = 128 개

 위에서 보는 것과 같이 FAT32로 갈수록 한 섹터에 담을 수 있는 클러스터 링크의 개수가 작아진다. 이것은 동일한 클러스터의 크기를 사용한다면 대용량일수록 FAT의 크기가 커진다는 것을 의미하고 FAT가 커지면 커질수록 실제로 사용가능한 클러스터의 크기가 줄어들게된다. 클러스터의 크기를 적당히 조절하여 FAT 크기를 너무 크지않게 하는 것이 좋다.

 FAT의 0번째 클러스터 링크와 1번째 클러스터 링크는 Signature의 용도 비슷하게 사용되는데, FAT Type에 따라 아래와 같이 설정해 준다.

  • FAT12 : 0xFF8, 0xFFF
  • FAT16 : 0xFFF8, 0xFFFF
  • FAT32 : 0xFFFFFFF8, 0xFFFFFFFF

 자 그럼 이제 FAT의 실제 크기를 한번 계산해 보자. 가장 간단하게 계산할 수 있는 방법은 파티션 전체의 크기를 클러스터의 크기로 나누어 구하는 방법이다. 실제로 이렇게 구하면 PBR + Rerserved Area + FAT1/2 영역의 크기도 사용가능한 것으로 인식하여 계산하기 때문에 낭비가 좀 있지만 계산은 편리하다. 특히 Root Directory의 시작 위치를 맞추거나 할때는 이렇게 계산하여 FAT의 크기를 구하고 여기에 Reserved Area의 값을 조절하여 맞출 수 있어 편리하다.

 또다른 방법은 전체 크기에서 PBR과 Reserved 영역을 제외하고 구하는 방법이다. 낭비가 좀 줄어들긴 한데, 식이 복잡해 진다. 아래는 그 계산 식이다(511은 올림을 위해 넣었다)

FAT의 크기(섹터) = [ ( 전체 파티션 크기(섹터) - PBR(1섹터) - Reserved Sector(?섹터)  )  / 클러스터의 크기(섹터) * 클러스터 링크의 크기 + 511 ] / 512

 이렇게 계산된 크기를 PBR에 wSectorsPerFAT나 dwSectorsPerFAT32 영역에 넣어주면 된다. 만약 wNumberOfFAT의 값을 2로 설정했으면 FAT1/2 영역을 연속해서 만들어줘야 하며 위의 FAT의 크기 * 2 만큼의 영역을 할당해 줘야 한다.



3.3 Root Directory Sector or Cluster 영역

  FAT16/32의 경우 위의 순서대로 PBR, FAT1/2 를 만들고 Root Directory Sector만 생성해 주면 정상적으로 윈도우에서 인식이 가능하다. 만약 Volume Label을 "NO NAME"으로 입력한 경우 Root Directory는 0으로 초기화 해주는 것으로 충분하다. 하지만 Volume Label을 사용한다면 Directory Entry Structure를 생성해서 Volume Label을 삽입해야 한다.


 Directory Entry Structure는 아래와 같이 구성되어있다.

  1. typedef struct directoryStruct  
  2. {  
  3.     char vcNameAndExtension[ 11 ];  
  4.     BYTE bAttribute;  
  5.     BYTE bReservedForNT;  
  6.     BYTE bCreatedTimeTenth;  
  7.     WORD wCreatedTime;  
  8.     WORD wCreatedDate;  
  9.     WORD wLastAccessDate;  
  10.     WORD wStartingClusterNumberHigh;  
  11.     WORD wTimeRecorded;  
  12.     WORD wDateRecorded;  
  13.     WORD wStartingClusterNumberLow;  
  14.     DWORD dwFileLength;  
  15. } DIRECTORY, * PDIRECTORY;  

 각 항목은 아래와 같은 의미를 가진다.

  • char vcNameAndExtension[ 11 ] : 파일 이름과 확장자 또는 Volume Label 저장. 0번째 값이 0x00 or 0xE5이면 Free Entry.
  • BYTE bAttribute : Entry의 속성을 표시. 여러가지가 있음(타입은 아래 참조)
  • BYTE bReservedForNT : NT를 위해 사용되는 필드. 0의 값으로 설정.
  • BYTE bCreatedTimeTenth : 생성된 초. 1/10초 단위까지 저장.
  • WORD wCreatedTime : 생성된 시간(포맷은 아래 참조)
  • WORD wCreatedDate : 생성된 날짜(포맷은 아래 참조)
  • WORD wLastAccessDate : 마지막으로 접근한 날짜(포맷은 아래 참조)
  • WORD wStartingClusterNumberHigh : 클러스터의 상위 16bit 번호
  • WORD wTimeRecorded : 마지막으로 쓴 시간(포맷은 아래 참조)
  • WORD wDateRecorded : 마지막으로 쓴 날짜(포맷은 아래 참조)
  • WORD wStartingClusterNumberLow : 클러스터의 하위 16bit 번호
  • DWORD dwFileLength : 파일의 크기

 조금 복잡한데, 일단 Attribute 부터 보면 아래와 같이 나와있다(첨부 항목에 White Paper 참조).

DIR_ATTRIBUTE.PNG

 일단 포맷 후에 Volume Label을 생성해야 하므로 ATTR_VOLUME_ID를 생성하면 된다. 이때 주의할 것은 Volume Label을 설정하는 Directory Entry의 경우 Name과 Attribute를 제외한 나머지는 모두 0으로 설정해야한다.

 위를 보면 낯 익은 값들도 있을 것이다. 만약 윈도우가 FAT Filesystem으로 포맷되어있다면 위의 항목들이 설정된 Directory Entry 구조체가 여기저기 널려있을 것이다. Hex Editor와 같은 프로그램을 이용해서 하드디스크를 열어서 확인해 보자 @0@)/~~!!!!. 각 항목에 대한 자세한 설명은 White Paper를 참조하자.


 Time과 Date 부분은 공통적인 포맷을 사용하는데, White Paper에 아래와 같이 나와있다(간단한 비트 연산으로 만들 수 있다).

DIR_DATETIME.PNG

 파일 시스템 분석을 하는 경우라면 ClusterNumber와 Name, 그리고 Attribute를 유심히 봐두는 것이 도움이 될 것이다. 다른 파일 시스템이 그러하듯이 Root Directory로부터 Sub Directory가 생성되고 트리 형태로 관리되기 때문에 Root Directory를 찾은 다음 Entry 구조만 안다면 전체를 Travel 하는 것은 어렵지 않으니까...


3.4 FSInfo

  FSInfo 영역은 FAT32에만 존재하는 영역이다. 역시 한 섹터 크기정도로 되어있고 별다른 정보를 포함하지 않는다. 단지 FAT32에 참고할만한 정보만 가지고 있다.

  1. typedef struct fsInfoStruct  
  2. {  
  3.     BYTE vbLeadSignature[ 4 ];  
  4.     BYTE vbReserved1[ 480 ];  
  5.     BYTE vbStructureSignature[ 4 ];  
  6.     DWORD dwFreeClusterCount;  
  7.     DWORD dwNextFreeCluster;  
  8.     BYTE vbReserved2[ 12 ];  
  9.     BYTE vbTrailSignature[ 4 ];  
  10. } FSINFO, * PFSINFO;  

  각 항목은 아래와 같은 의미를 가진다.

  • BYTE vbLeadSignature[ 4 ] : 0x52 0x52 0x61 0x41 설정
  • BYTE vbReserved1[ 480 ] : 예약된 영역. 0으로 설정.
  • BYTE vbStructureSignature[ 4 ] : 0x72 0x72 0x41 0x61 설정
  • DWORD dwFreeClusterCount : Free한 클러스터의 개수 저장. 알 수 없을 경우 0xFFFFFFFF. 권장 값이므로 100% 신용하면 안됨.
  • DWORD dwNextFreeCluster : Free한 클러스터의 첫번째 번호. 알 수 없을 경우 0xFFFFFFFF. 권장 값이므로 100% 신용하면 안됨.
  • BYTE vbReserved2[ 12 ] : 예약된 영역. 0으로 설정.
  • BYTE vbTrailSignature[ 4 ] : 0x00 0x00 0x55 0xAA 설정

 위에서 보는 것과 같이 FAT32의 부가적인 정보가 포함된 영역이고 특히 클러스터 관련 필드는 권장값이므로 절대 100% 믿으면 안된다.


4.마치며...

 여기까지 간단하게 FAT Filesystem에 대해서 알아보았다. 원래 훨씬 일찍 마무리 되었어야 하는데... 과제하느라 정신이 조금 없어서 찔끔 찔끔 정리하다보니 이제야 마무리를... ㅜ_ㅜ... 다소 부족한 감이 있지만 Formatter를 개발하면서 알아낸 정보를 기반으로 작성하였다.

 다음에는 위 정보를 기반으로 실제 FAT Filesystem을 분석해 보도록 하자.

'It's my study ^^ 과연' 카테고리의 다른 글

open, fopen의 차이점  (0) 2007.12.18
hard link와 symbolic link  (0) 2007.12.18
TCP 헤더 구조  (0) 2007.12.15
open  (0) 2007.12.15
Library 만드는 법  (0) 2007.12.14

TCP 는 다음과 같은 헤더 구조를 가진다. 두 호스트 사이에 전송되는 TCP 데이타 단위를 세그먼트라고 부른다. 그러므로 TCP 세그먼트는 TCP 헤더 + DATA 가 될것이다. 다음은 TCP 세그먼트의 구조이다.

그림 2. TCP 헤더 구조


2.2.1. SOURCE PORT/DESTINATION PORT

source port 는 메시지를 보내는 측에서 통신을 위해 사용하는 port 번호이며, destination port 는 목적지, 즉 메시지를 받는측의 통신 port 번호이다.

여기에 있는 port 번호와 더불어 IP 헤더에 있는 source/destination address 를 이용하면 유일하게 식별되는 통신연결을 만들수 있게 된다.

아마도 IP 의 출발지/목적지 주소와 TCP 헤더의 출발지/목적지 포트 번호가 어플리케이션간 통신을 위한 가장 핵심이라고 할수 있을것이다. 다른 정보들은 통신을 원할하도록 도와주기 위해서 부가적으로 존재하는 것이라고 볼수 있다.

이들 포트번호의 크기는 16bit 크기를 가진다. 그러므로 대략 65536 만큼의 포트를 가질수 있을것이다.


2.2.2. SEQUENCE NUMBER

TCP 세그먼트안의 데이터의 송신 바이트 흐름의 위치를 가리킨다. 다른 호스트로 전달되는 패킷은 여러개의 서로 다른 경로를 거치면서 전달되다 보니 패킷의 순서가 뒤바뀔 수 있다. 이를 수신측에서는 재 조립해야할 필요가 있는데, Sequence Number 를 이용해서 조립하게 된다.


2.2.3. ACK

acknowledgment number 라고 말한다. 다음에 받을것으로 예상되는 데이타 옥텟의 순서번호를 나타낸다.


2.2.4. HLEN

TCP 세그먼트의 길이를 정의한다.


2.2.5. RESERVED

현재는 사용하지 않지만, 나중을 위해서 예약된 필드이다.


2.2.6. (Control)CODE BITS

세그먼트의 용도와 내용을 결정하기 위해서 사용된다. URG, ACK, PSH, RST, SYN, FIN 6개의 비트가 정의되어 있다. TCP는 이러한 비트를 이용해서 패킷의 내용이 어떤 목적으로 전달될 것인지를 설정할 수 있다. 이들 비트중 SYN, ACK, RST를 주목할 필요가 있다.

SYN은 TCP연결을 만들 때, 양 호스간 sequence numbers의 동기화를 이루기 위한 목적으로 사용된다. ACK는 원격 호스트의 sequence number에 대한 응답을 위한 목적으로 사용된다. 즉 데이터를 잘 받았다는 걸 알려주기 위한 목적으로 사용되는데, 원격호스트의 sequence number의 번호에 +1을 해줘서 다시 전달하는 방법을 이용한다. SYN 비트는 특히 세번 악수 기법(three-way handshake)를 위해서 사용된다.

RST 비트가 설정되어 있을 경우 받은 호스트는 즉시 연결을 끊어 버리고 FIN 비트가 설정되어 있을 경우 여러가지 테스트를 거쳐서 연결을 끊게 된다. 일반적인 정상종료를 원한다면 FIN 비트를 설정해서 사용하게 된다. 이들 비트에 대한 자세한 내용은 2.3.3절를 참고하기 바란다.


2.2.7. OPTION & PADDING

옵션은 말그대로 옵션이다. TCP 헤더의 정보를 좀더 확장시키고자 할때 사용한다. PADDING 은 32bit 크기를 채우기 위해서 사용된다.


2.2.8. CHECKSUM

TCP 세그먼트 데이타는 중간에 훼손될수 있으며, 변조될수도 있다. 그러므로 이를 체크할수 있는 장치가 필요하다. CHECKSUM 을 만드는 방법(알고리즘)은 기회가 되면 별도로 설명하도록 하겠다.


출처 : http://teamblog.joinc.co.kr/yundream/212

'It's my study ^^ 과연' 카테고리의 다른 글

hard link와 symbolic link  (0) 2007.12.18
FAT 파일 시스템(File System)  (0) 2007.12.17
open  (0) 2007.12.15
Library 만드는 법  (0) 2007.12.14
구조체(Struct)  (0) 2007.12.13

사용법

#include <sys/types.h>

#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);


설명

open시스템호출로, 파일을 열거나 생성 할때 사용한다. 성공하면 해당파일을 지시하는 int 형의 파일지시자를 되돌려준다. path_name 은 생성하고자 하는 파일이름을 나타낸다. 보통 full path 이름을 적어주며, 단지 파일이름만 적을경우에는 현재 경로에 파일이 생성된다.

flag 는 파일을 어떠한 mode 로 열것인지를 결정하기 위해서 사용한다. "읽기전용", "쓰기전용", "읽기/쓰기" 모드로 열수 있다. 이들 모드 선택을 위해서 O_RDONLY, O_WRONLY, O_RDWR 이 존재 한다.

또한 다음중 하나이상의 mode 를 bitwise 연산시킬수도 있다.

O_CREAT

만약 pathname 파일이 존재하지 않을경우 파일을 생성한다.

O_EXCL

O_CREAT 를 이용해서 파일을 생성하고자 할때, 이미 파일이 존재한다면, 에러를 되돌려주며 파일을 생성하는데 실패한다. 이러한 특성때문에 때때로 잠금 파일을 만들기 위해 사용되기도 한다.

O_APPEND

파일이 추가모드로 열린다. 파일의 위치는 파일의 끝이된다.

O_NONBOLOCK, O_NDELAY

파일이 비봉쇄 모드로 열린다.

O_NOFOLLOW

경로명이 심볼릭 링크라면, 파일열기에 실패한다.

O_DIRECTORY

경로명이 디렉토리가 아니라면 파일열기에 실패한다.

O_SYNC

입출력 동기화 모드로 열린다. 모든 write 는 데이타가 물리적인 하드웨어에 기록될때까지 호출 프로세스를 블록시킨다.

또한 mode 를 이용해서 에 파일의 권한을 지정해 줄수도 있다.

S_IRWXU
          00700 모드로 파일 소유자에게 읽기, 쓰기, 쓰기 실행권한을 준다.

S_IRUSR

00400 으로 사용자에게 읽기 권한을 준다.

S_IWUSR

00200 으로 사용자에게 쓰기 권한을 준다.

S_IXUSR

00100 으로 사용자에게 실행 권한을 준다.

S_IRWXG

00070 으로 그룹에게 읽기, 쓰기, 실행 권한을 준다.

S_IRGRP

00040 으로 그룹에게 읽기권한을 준다.

S_IWGRP

00020 으로 그룹에게 쓰기권한을 준다.

S_IXGRP

00010 으로 그룹에게 실행권한을 준다.

S_IRWXO

00007 으로 기타 사용자 에게 읽기, 쓰기, 실행 권한을 준다.

S_IROTH

00004 으로 기타 사용자 에게 읽기 권한을 준다.

S_IWOTH

00002 으로 기타 사용자 에게 쓰기 권한을 준다.

S_IXOTH

00001 으로 기타 사용자 에게 실행 권한을 준다.


반환값

에러가 발생하면 -1 을 반환하며, 성공했을경우에는 새로운 파일 지시자를 반환한다. 에러시에는 적당한 errno 값이 설정된다.


에러

EEXIST

O_CREAT 와 O_EXECL 이 같이 사용되었을경우 발생한다. 이미 경로파일이 존재할경우 발생된다.

EACCES

파일 접근이 거부될경우이다. 주로 권한 문제 때문에 발생한다.

ENOENT

경로명의 디렉토리가 없거나, 심볼릭 링크가 깨져있을때.

ENOENT

경로명의 디렉토리가 없거나, 심볼릭 링크가 깨져있을때.

ENODEV

경로명이 장치파일을 참고하고, 일치하는 장치가 없을때.

EROFS

경로명이 read-only 파일시스템을 참조하면서, 쓰기로 열려고 할때.

EROFS

경로명이 read-only 파일시스템을 참조하면서, 쓰기로 열려고 할때.

EFAULT

경로명이 접근할수 없는 주소강간을 가르킬때

ELOOP

심볼릭 링크가 너무 많을때.


예제

// /usr/my.temp 파일을 읽기 전용으로 열고자 할때
fd = open("/usr/my.temp", O_RDONLY);
...

close(fd);

// 파일을 쓰기 전용으로 생성하며, 파일의 권한은 644 로 한다.
// 만약 이미 파일이 존재한다면 에러가 발생할것이다.
fd = open("/usr/my.temp", O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
...
close(fd);




출처 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/man/2/open

'It's my study ^^ 과연' 카테고리의 다른 글

FAT 파일 시스템(File System)  (0) 2007.12.17
TCP 헤더 구조  (0) 2007.12.15
Library 만드는 법  (0) 2007.12.14
구조체(Struct)  (0) 2007.12.13
이중 포인터  (0) 2007.12.13

정적라이브러리

static library라고 부르기도 한다. 이 라이브러리는 단순한 오브젝트의 모음일 뿐이다. 정적라이브러리는 ar이라는 프로그램을 통해서 만들 수 있다. 그럼 ar을 이용해서 위의 사칙연산을 위한 4개의 오브젝트를 모아서 libmycalc.a라는 이름의 정적라이브러리를 생성해보도록 하자. rc 옵션을 이용하면, 정적라이브러리를 만들 수 있다.

r은 정적라이브러리를 만들겠다는 옵션이고, c는 새로 생성을 하겠다는 옵션이다.
# ar rc libmycalc.a sum.o sub.o mul.o div.o 
libmycalc.a 라는 파일이 생성된걸 확인할 수 있을 것이다. t 옵션을 이용하면, 해당 라이브러리가 어떤 오브젝트를 포함하고 있는지도 확인할 수 있다. t 옵션을 사용하면 된다. 참고로 정적 라이브러리의 이름은 libNAME.a의 형식을 따라야 한다.
# ar t libmycalc.a 
div.o
mul.o
sum.o
sub.o

그럼 정적라이브러리를 이용해서 실행파일을 만들어 보도록 하자. 이전에는 4개의 오브젝트 파일을 모두 링크시켜줘야 했지만, 이제는 libmycalc.a 만 링크시켜주면 된다.

라이브러리의 링크방식은 오브젝트를 링크하는 것과는 약간 차이가 있다. library의 위치를 명확히 명시해 주어야 한다. -L 옵션을 이용해서 라이브러리가 있는 디렉토리의 위치를 명시해주고, -l옵션을 이용해서, 라이브러리 파일의 이름을 정해줘야 한다. 다음은 simplecalc.c 를 정적라이브러리를 이용해서 컴파일하는 방법을 보여준다.
# gcc -o simplecalc simplecalc.c -L./ -lmycalc 
-L./은 현재 디렉토리를 라이브러리 찾기 디렉토리로 하겠다는 의미가 된다. -l 옵션뒤에 붙이는 라이브러리 파일의 이름에 주목할 필요가 있다. 라이브러리 이름은 lib .a를 제외한 이름을 사용한다.

공유 라이브러리

공유 라이브러리는 함께 사용하는 라이브러리라는 의미다. 즉 정적 라이브러리 처럼 실행파일에 붙는 것이 아니고, 시스템의 특정디렉토리에 위치하면서, 다른 모든 프로그램들이 공유해서 사용할 수 있게끔 제작된 라이브러리다. 그러므로 공유 라이브러리를 사용하도록 제작된 프로그램은 실행시에 사용할 라이브러리를 호출하는 과정을 거치게 된다.

공유 라이브러리역시 오브젝트를 이용해서 만든다는 점에서는 정적라이브러리와 비슷하지만, 호출시에 링크하기 위한 부가적인 정보를 필요로 하므로, 정적라이브러리와는 전혀 다른 형태로 만들어 진다. 정적라이브러리와 이름이 헛갈릴 수 있으니, 라이브러리 이름은 mycalcso 로 하겠다.
# gcc -fPIC -c sum.c sub.c mul.c div.c 
# gcc -shared -W1,-soname,libmycalcso.so.1 -o libmycalcso.so.1.0.1 sum.o sub.o mul.o div.o

  1. 오브젝트 파일을 만들때 부터 차이가 있는데, -fPIC 옵션을 줘서 컴파일 한다.
  2. 그다음 -shared 옵션을 이용해서 공유라이브러리 파일을 생성한다.
위의 과정을 끝내고 나면, libmycalcso.so.1.0.1 이라는 파일이 생성이 된다. 이 라이브러리는 프로그램을 컴파일할때와 실행시킬때 호출이 되는데, 호출될때는 libmycalcso.so 를 찾는다. 그러므로 ln 명령을 이용해서 libmycalcso.so 링크파일을 생성하도록 하자.
# ln -s libmycalcso.so.1.0.1 libmycalcso.so 
이렇게 링크를 만들게 되면, 여러가지 버전의 라이브러리 파일을 이용할 수 있으므로 관리상 잇점을 가질 수 있다. 새로운 버전의 라이브러리가 나올 경우, 오래된 버전의 라이브러리를 쓰는 프로그램은 실행시 문제가 발생할 수 있는데, 이런 문제를 해결할 수 있기 때문이다.

이제 링크하는 과정이 남았다. 링크과정은 정적 라이브러리를 사용할때와 동일하다.
# gcc -o simplecalcso simplecalc.c -L./ -lmycalcso 

이제 프로그램을 실행시켜 보도록 하자. 아마 다음과 같은 에러메시지를 만나게 될 것이다.
# ./simplecalcso 
./simplecalcso: error while loading shared libraries: libmycalc.so:
cannot open shared object file: No such file or directory

이러한 에러가 발생하는 원인에 대해서 알아보도록 하자. 정적라이브러리는 실행파일에 라이브러리가 붙여지므로, 일단 실행파일이 만들어지면, 독자적으로 실행이 가능하다. 그러나 공유라이브러리는 라이브러리가 붙여지는 방식이 아니고, 라이브러리를 호출해서 해당 함수코드를 실행하는 방식이다. 그러므로 공유라이브러리 형식으로 작성된 프로그램의 경우 호출할 라이브러리의 위치를 알고 있어야만 한다.

위의 simplecalcso 프로그램을 실행시키면, 이 프로그램은 libmycal.so 파일을 찾을 것이다. 이때 파일을 찾는 디렉토리는 /etc/ld.so.conf에 정의 되어 있다.
# cat /etc/ld.so.conf 
/usr/lib
/usr/local/lib
만약 위에서 처럼되어 있다면, 프로그램은 /usr/lib 와 /usr/local/lib 밑에서 libmycal.so 를 찾게 될 것이다. 그런데 libmycal.so 가 없으니, 위에서와 같은 에러가 발생하는 것이다.

가장 간단한 방법은 라이브러리 파일을 ld.so.conf에 등록된 디렉토리중 하나로 복사하는 방법이 될 것이다. 혹은 환경변수를 이용해서, 새로운 라이브러리 찾기 경로를 추가할 수도 있다. 이때 사용되는 환경변수는 LD_LIBRARY_PATH 다.
# export LD_LIBRARY_PATH=./:/home/myhome/lib 
이제 프로그램을 실행시키면 LD_LIBRARY_PATH 에 등록된 디렉토리에서 먼저 검색하게 되고, 프로그램은 무사히 실행 될 것이다.

공유라이브러리와 정적라이브러리의 장단점

이들 2가지 라이브러리의 장단점에 대해서 알아보도록 하자. 장단점을 알게되면 어떤 상황에서 이들 라이브러리를 선택할 수 있을지 알 수 있을 것이다.

정적라이브러리의 장점은 간단한 배포방식에 있다. 라이브러리의 코드가 실행코드에 직접 붙어버리는 형식이기 때문에, 일단 실행파일이 만들어지면 간단하게 복사하는 정도로 다른 컴퓨터 시스템에서 실행시킬 수 있기 때문이다. 반면 동적라이브러리는 프로그램이 실행될때 호출하는 방식이므로, 라이브러리까지 함께 배포해야 한다. 라이브러리의 호출 경로등의 환경변수까지 덤으로 신경써줘야 하는 귀찮음이 따른다.

일반적으로 정적라이브러리는 동적라이브러리에 비해서 실행속도가 빠르다. 동적라이브러리 방식의 프로그램은 라이브러리를 호출하는 부가적인 과정이 필요하기 때문이다.

정적라이브러리는 실행파일 크기가 커진다는 단점이 있다. 해봐야 얼마나 되겠느냐 싶겠지만, 해당 라이브러리를 사용하는 프로그램이 많으면 많을 수록 X 프로그램수만큼 디스크 용량을 차지하게 된다. 반면 공유라이브러리를 사용할 경우, 라이브러리를 사용하는 프로그램이 10개건 100개건 간에, 하나의 라이브러리 복사본만 있으면 되기 때문에, 그만큼 시스템자원을 아끼게 된다.

마지막으로 버전 관리와 관련된 장단점이 있다. 소프트웨어 개발 세계의 불문율이라면 버그 없는 프로그램은 없다이다. 어떠한 프로그램이라도 크고작은 버그가 있을 수 있으며, 라이브러리도 예외가 아니다.

여기 산술계산을 위한 라이브러리가 있다. 그리고 정적 라이브러리 형태로 프로그램에 링크되었어서 사용되고 있다고 가정해보자. 그런데 산술계산 라이브러리에 심각한 버그가 발견되었다. 이 경우 산술계산 라이브러리를 포함한 A 프로그램을 완전히 새로 컴파일 해서 배포해야만한다. 문제는 이 라이브러리가 A 뿐만 아니라 B, C, D 등의 프로그램에 사용될 수 있다는 점이다. 결국 B, C, D 프로그램 모두를 새로 컴파일 해서 배포해야 하게 된다. 더 큰 문제는 어떤 프로그램이 버그가 있는 산술계산 라이브러리를 포함하고 있는지 알아내기가 힘들다는 점이다.

공유라이브러리 형태로 작성하게 될경우에는 라이브러리만 새로 컴파일 한다음 바꿔주면된다. 그러면 해당 라이브러리를 사용하는 프로그램이 몇개이던간에 깔끔하게 문제가 해결된다.

실제 이런 문제가 발생한 적이 있었다. zlib 라이브러리는 압축을 위한 라이브러리로 브라우저, 웹서버, 압축관리 프로그램등에 널리 사용된다. 많은 프로그램들이 이 zlib를 정적라이브러리 형태로 포함해서 배포가 되었는데, 심각한 보안문제가 발견되었다. 결국 zlib를 포함한 모든 프로그램을 새로 컴파일해서 재 설치해야 하는 번거로운 과정을 거치게 되었다. 공유라이브러리였다면 문제가 없을 것이다.

이상 정적라이브러리와 공유라이브러리를 비교 설명했다. 그렇다면 선택의 문제가 발생할 것인데, 자신의 컴퓨터나 한정된 영역에서 사용할 프로그램을 제작하지 않는한은 공유라이브러리 형태로 프로그램을 작성하길 바란다. 특히 인터넷을 통해서 배포할 목적으로 작성할 프로그램이라면, 공유라이브러리 형태로 작성하는게 정신건강학적으로나 프로그래밍 유지차원에서나 좋을 것이다.

출처  : http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/C/Documents/CprogramingForLinuxEnv/Ch12_module

'It's my study ^^ 과연' 카테고리의 다른 글

TCP 헤더 구조  (0) 2007.12.15
open  (0) 2007.12.15
구조체(Struct)  (0) 2007.12.13
이중 포인터  (0) 2007.12.13
대입연산자  (0) 2007.12.12
출처 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/C/Documents/CprogramingForLinuxEnv/Ch11_Structure

#include <stdio.h>
#include <string.h>

struct userinfo
{
char name[20];
int age;
};

int main(int argc, char **argv)
{
int age;
int i;
char buf[40];
struct userinfo myfriend[5];

for (i = 0; i < 5; i++)
{
printf("Name : ");
fgets(buf, 19, stdin);
buf[strlen(buf)-1] = '\0'; // <--- 1
sprintf(myfriend[i].name, "%s", buf);

printf("Age : ");
fgets(buf, 19, stdin);
age = atoi(buf);
myfriend[i].age = age;
}

printf("=======================\n");
for (i = 0; i < 5; i++)
{
printf("%12s : %d\n", myfriend[i].name, myfriend[i].age);
}
}
fgets(3)은 키보드로 부터 문자열을 입력받기 위해서 사용하는 함수다. 1은 키보드로 입력된 개행문자를 제거하기 위해서 사용했다.

'It's my study ^^ 과연' 카테고리의 다른 글

open  (0) 2007.12.15
Library 만드는 법  (0) 2007.12.14
이중 포인터  (0) 2007.12.13
대입연산자  (0) 2007.12.12
Primitive Data Types  (0) 2007.12.10

+ Recent posts