c언어/시스템 프로그래밍

시스템 프로그래밍 2일차

시스템 엔지니어 2023. 3. 30. 21:25

시스템 프로그래밍 2일차

유튜브 강의 참고

Makefile & Make (리눅스 컴파일 환경)

GNU C compiler

  • 대부분의 유닉스/리눅스에서 기본 컴파일러로 사용중

명령어: gcc

$ gcc [options] filename
- Options
  - -c: object file(.o)만 생성
  - -o: execution file name 지정 (default: a.out)


$ gcc test.c
$ ls
a.out    test.c

$ gcc -o test test.c
$ ls
test    test.c

# 오브젝트 파일은 실행 파일이 아니기 때문에 실행 안됨
$ gcc -c -o test.o test.c
$ ls
test.o test.c

Makefile & Make

Makefile

  • Compile 방법을 기술 하는 파일
  • 관련 파일 정보, compile 명령, 실행 파일명 등
  • 여러 파일로 구성된 프로젝트 설정과 비슷한 개념

Make

  • 주어진 Makefile에 따라 compile을 수행하고, 실행파일을 생성
  • 최초 컴파일 이후에는, 변경이 있는 파일만 컴파일 함
컴파일 예제
$ gcc -c -o main.o main.c
$ gcc -c -o foo.o foo.c
$ gcc -c -o bar.o bar.c
$ gcc -o app.out main.o foo.o bar.o

Rule block

<Target>:<Dependencies>
  <Recipe>
Target
  • Build 대상 이름
  • 일반적으로 최종 결과 파일명 사용
  • Dependency
  • Build 대상이 의존하는 Target이나 파일 목록
  • Recipe
  • Build 대상을 생성하는 명령어

Implict rules (Built-in rules)

  • 자주 사용되는 규칙을 자동으로 처리 해줌
  • Source (.c)를 compile해서 Object 파일(.o)를 생성하는 규칙
  • 주의: Target에 대한 dependency 까지는 명시할 것
  • 명시 하지 않은 파일이 수정된 경우, 수정 내용이 반영되지 않을 수 있음
변경전
app.out: main.o foo.o bar.o
  gcc -o app.out main.o foo.o

main.o: foo.h bar.h main.c
  gcc -c -o main.o main.c
foo.o: foo.h foo.c
  gcc -c -o foo.o foo.c
bar.o: bar.h var.c
  gcc -c -o bar.o bar.c
변경후
app.out: main.o foo.o bar.o
  gcc -o app.out main.o foo.o bar.o

main.o: foo.h bar.h main.c
foo.o: foo.h foo.c
bar.o: bar.h bar.c

Variables (or Macro)

CC=gcc               # 컴파일러 이름
OFLAGS=-g -Wall      # 옵션
OBJS=main.o foo.o bar.o
TARGET=app.out

$(TARGET): $(OBJS)
  $(CC) -o $@ $(OBJS)  # $@ : $(TARGET) 정보가 자동으로 들어간다.

main.o: foo.h bar.h main.c
foo.o: foo.h foo.c
bar.o: bar.h bar.c

Clean rule

  • Build로 생성된 파일들 삭제하는 규칙
  • Clean build를 위해 사용 가능
clean: 
  rm -f *.o
  rm -f $(TARGET)

$ make clean

Make file Basic pattern

CC=<compiler>
CFLAGS=<options for compiler>
LDFLAGS=<options for linker>
LDLIBS=<a list of library to link>
OBJS=<a list of object file>
TARGET=<build target name>

all: $(TARGET)

clean:
  rm -f *.o
  rm -f $(TARGET)

$(TARGET): $(OBJS)
  $(CC) -o $@ $(OBJS)

파일 개요 & 기본 명령어 (파일 입출력 1/4)

File

File 이란?

  • 보조 기억 장치에 저장된 연관된 정보들의 집합
  • 보조 기억 장치 할당의 최소 단위
  • Sequence of bytes (물리적 정의)
  • OS는 file operation들에 대한 system call을 제공해야 함
  • create, write, read, reposition, delete, Etc..

파일의 종류

Regular file (일반 파일)
  • Text or binary data file
Directory
  • Unix/Linux에서는 directory도 하나의 파일
Special file (특수 파일)
  • 파일 형태로 표현된 커널 내 객체
  • 데이터 전송, 장치 접근 시 사용하는 파일

기본 명령어 (생략)

File access permission (생략)

File I/O

Low-Level File IO (system call)

  • system call을 이용해서 파일 입출력 수행
  • File descriptor 사용
  • Byte 단위로 디스크에 입출력
  • 특수 파일에 대한 입출력 가능

High-Level File IO (Buffered IO)

  • C Standard library를 사용해서 파일 입출력 수행
  • File pointer 사용
  • 버퍼(block) 단위로 디스크에 입출력

open (system call)

$ man -s 2 open
#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);
pathname (file path)
  • 열려는 파일의 경로 (파일 이름 포함)
flags (file state flags)
  • 파일을 여는 방법(access mode) 설정
mode (file access permission)
  • 파일을 새로 생성(O_CREATE) 할 때만 유효
Return: file descriptor

File descriptor

열려 있는 파일을 구분하는 정수(integer) 값
  • 특수 파일 등 대부분의 파일을 지칭 가능
  • Process별로 kernel이 관리
파일을 열 때 순차적으로 할당 됨
  • Process 당 최대 fd 수 = 1,024 (default, 변경 가능)
Default fds (수정 가능)
  • 0: stdin
  • 1: stdout
  • 2: stderr

flags (Man page 및 <sys/fcntl.h> 참조)

  • 여러 플래그 조합 가능 (OR bit operation (|) 사용)
  • read, write, RW

File table

열린 파일을 관리하는 표
  • kernel이 process 별로 유지
  • 열린 파일에 대한 각종 정보 관리
  • Access mode, file offset, pointer to files

mode (Man page 및 <sys/stat.h> 참조)

  • 파일 권한 설정 값 사용 (예, 644)
  • 정의 된 플래그 사용
  • 조합하여 사용 가능 (OR bit operation (|) 사용

조합 사용법

flag 조합의 예
  • O_WRONLY | O_TRUNC
  • O_RDWR | O_APPEND
mode 조합의 예
  • S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH

close (system call)

$ man -s 2 close

#include <unistd.h>

int close(int fd);
fd (file descriptor)
  • 닫으려는 file descriptor
return
  • 0: success
  • -1: error

open & close 파일 완성

Makefile

CC=gcc
CFLAGS=-g -Wall
OBJS=fileOpenClose.o
TARGET=fileOpenClose.out

all: $(TARGET)

clean:
        rm -f *.o
        rm -f $(TARGET)

$(TARGET): $(OBJS)
        $(CC) -o $@ $(OBJS)

fileOpenClose.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(void) {
  int fd;
  mode_t mode;

  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 644

  fd = open("hello.txt", O_CREAT, mode);
  if (fd == -1) {
        perror("Creat"); exit(1);
  }

  close(fd);

  return 0;
}

O_EXCL플래그 사용해 보기

  • O_CREAT 옵션과 함께 사용할 경우 기존에 없는 파일이면 파일을 생성하지만, 파일이 이미 있으면 파일을 생성하지 않고 오류 메시지를 출력한다.
fd = open("hello.txt", O_CREAT | O_EXCEL, mode); # | O_EXCL 추가)

# 오류 메시지 확인

root@test-ubuntu:~/test# ./fileOpenClose.out  
Creat: File exists

Error 핸들링

System call은 실패 시 -1 을 반환

Error code는 errno(변수)에 저장 됨

  • error.h에 선언되어 있음 (special variable)
  • extern으로 직접 접근 가능
  • extern int errno;

perror(3)

  • Error message를 출력 해주는 함수
$ man -s 3 perror  

#include <stdio.h>
void perror(const char \*s);

Descriptor 할당

#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>

int openFile(void) {  
int fd = open("hello.txt", O\_RDWR);  
if (fd == -1) {  
perror("File Open");  
exit(1);  
}  
return fd;  
}

int main(void) {  
int fd = 0;

fd = openFile(); printf("fd = %d\\n", fd);  
close(fd);

close(0);

fd = openFile(); printf("fd = %d\\n", fd);  
close(fd);

return 0;  
}

출력

# 0: stdin
# 1: stdout
# 2: stderr
# oepnFile() --> fd=3
# close(0) --> 0(stdin) close 됨
# openFile() --> fd=0

root@test-ubuntu:~/test# ./fileOpenClose.out  
fd = 3  
fd = 0