리눅스 프로그래밍 - 2주차

Lellow_Mellow·2022년 10월 11일
0
post-thumbnail

🔔 학교 강의를 바탕으로 개인적인 공부를 위해 정리한 글입니다. 혹여나 틀린 부분이 있다면 지적해주시면 감사드리겠습니다.

Basic Concepts and Terminology

Unix Architecture

가장 바깥에서 안쪽으로 한 단계씩 거쳐가며 최종적으로 HW에 접근이 가능하며, kernel을 통해 HW에 접근하기 위해 system call을 사용한다.

Linux Scope

Linux를 나누는 기준은 다양하며, kernel 자체만 Linux라고 칭하기도 한다. 해당 강의에서는 Application, Libraries, Kernel까지 전체를 Linux로 칭하였다.

Shell

shellcommand line interpreter로 (interpreter : 프로그래밍 언어의 소스코드를 바로 실행하는 컴퓨터 프로그램 또는 환경) shell을 통해 command를 OS로 넘겨주며, 로그인을 하면 shell에 명령어를 입력할 수 있다.

최초의 shell이자 가장 기본적인 shell은 Bourne shell이며, 현재는 bash(Bourne Again shell)을 주로 사용한다. C shell(csh)도 존재하며, shell 상태에서 C의 구문들을 그대로 사용하여 script를 만들 수 있도록 해준다.

File and File system

unix(linux)에서는 process를 제외한 모든 것이 file로 관리된다.

File system

file system은 directory와 file의 계층적 구조이며, root에 해당하는 /로 항상 시작한다.

File name

/null은 file name에 포함이 불가능하며, directory가 생성되면 현 directory와 parent directory를 나타내는 ., ..가 자동으로 생성된다.

Working directory

모든 process는 working directory가 존재하며, 모든 상대 경로를 파악할 때 사용한다.

Home directory

로그인 시에, working directory가 home directory를 설정해준다.

Pathname

  • 절대 경로 : /, 즉 root 부터 시작하는 경로이다.
  • 상대 경로 : root가 아닌 현 working directory부터 시작하는 경로이다. 아래 그림에 대한 상대 경로의 예시로는 file1이다.

File types

file은 여러 종류로 분류되며 이는 아래와 같다.

  • regular file : binary나 text file 등 일반적인 file을 통칭하며, unix는 이러한 file의 차이를 구분하지 않는다.
  • directory file : 다른 file들의 이름과 경로를 담고 있는 file이다.
  • character special and block special file : device file이라고도 한다.
  • pipe : process간 통신에 관한 file type이다.
  • socket : network가 다른 경우 통신을 위한 file type이다.
    -> 즉 pipe와 socket은 communication을 위한 file type이다.

Ownership

linux는 multiuser os이므로 소유권을 고려할 필요가 있다. 각 file들은 특정한 사용자가 소유하고 있으며, 소유자는 파일과 관련된 권한을 선택할 수 있다.

Permission

크게 read, write, execute로 나뉘며, 파일 접근에 관한 권한을 의미한다.

file type을 표현하는 약자들

  • d : directory
  • b : block special file
  • c : character special file
  • p : pipe (FIFO)
  • - : regular file
  • l : symbolic link

Program & Process

program은 지시의 순서이며, data와 program 로딩과 실행을 위한 metadata를 포함한다. 아래 그림과 같이 build하여 나온 실행 파일이 program이며, 이를 실행하면 process이다. 즉, process는 실행중인 program의 인스턴스이다.

unix에서는 process 여러개가 동시에 동작할 수 있으며, process간 통신이 가능하다.

Inter-process communication (IPC) = process간 통신

  • Pipe
  • FIFO
  • Signals
  • Message queue
  • Shared memory
  • Semaphore
  • Socket (across a network)

System Calls

system call은 unix kernel을 호출하는 것으로, 프로그래머는 일반 C function을 호출하며, 실질적인 작업은 kernel 내부에서 이루어진다. 이에 대한 예시는 아래와 같다.

system call은 최소한 2번의 context switch를 동반하며, 이는 cost가 크다. 따라서 간단한 함수 호출보다 훨씬 오래 걸리며, 과도한 system call은 피하는 것이 좋다. 모든 system call은 header file에 정의되어 있다.

The File

File and File system

File

연속된 byte들이며, data를 저장하는 컨테이너다. linux에서 file은 특정한 format이 존재하지 않으며, byte 단위로 읽을 수 있다.

File system

컴퓨터의 file과 data를 저장하고 구성하는 방법이며, file을 쉽게 찾고 access할 수 있다. data 저장 장치를 사용하여 구성한다.

2.1 Unix File Access Primitives

File Access Primitives

아래 function들은 Linux가 따라야하는 standard인 POSIX standard의 일부에 해당한다. 이 function들은 kernel에서 system call을 호출한다.

File Descriptor

kernel에서 모든 열린 file들은 file descriptor로 제공되며, 이는 음수가 아닌 정수이다. 새로운 file descriptor는 사용되지 않은 가장 낮은 정수로 지정되며, 기존 file을 open하거나 새로운 file을 만들 때, kernel은 file descriptor를 반환한다. 또한, 동일한 file이어도 process마다 file descriptor가 부여되므로, process마다 달라질 수 있다.

shell에 의해 생성된 각 process들은 terminal과 연결된 3개의 file descriptor를 가지고 시작한다. 이는 아래와 같다.

Example

#include <fcntl.h>
#include <unistd.h>

int main() {
	int fd;
	ssize_t nread;
	char buf[1024];
	fd = open(“data”, O_RDONLY);
	nread = read(fd, buf, 1024);
	close(fd);
	return 0;
}

_t로 끝나는 type은 primitive system data type(기본 시스템 데이터 유형)이라 하며, <sys/types.h>에 정의되어있다. system에 따라 각기 다르게 선언해줘야 하는 경우가 발생하기 때문에 이를 위한 type들이다. 위 코드에서 ssize_t 역시 여기에 해당되며, 여기서 가장 앞의 ssigned를 의미한다.

또한 위 코드를 실행했을 때, file descriptor table은 아래와 같다.

System Call : open

#include <fcntl.h>
int open(const char *pathname, int flags, [mode_t mode]);

argument

  • const char *pathname : open할 file 경로
  • int flags
    필수적으로 하나를 선택해야 하는 flag
    - O_RDONLY #0 : read만 하기 위해 open
    - O_WRONLY #1 : write만 하기 위해 open
    - O_RDWR #2 : read와 write를 하기 위해 open
    추가적으로 사용이 가능한 flag
    - O_APPEND : write시에 file의 끝에서 시작
    - O_CREAT : file이 존재하지 않으면 새로 생성
    - O_EXCL : 없는 경우 새로 생성하지만, 있는 경우 error 발생
    - O_TRUNC : file이 이미 존재하면, 해당 파일을 비워줌. (기존 file의 permission 유지)
    - O_NONBLOCK : non-blocking file open
  • [mode_t mode] : O_CREAT가 있을때만 필요하며, file의 권한을 설정함

return

  • 성공 시 file descriptor return
  • error 발생 시 -1 return

이에 대한 예시는 아래와 같다.

File permissions

Unix에서는 file permission은 3개의 8진수로 표현된다. 이는 아래 그림과 같다.

Example : open

#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>

char *workfile = “junk”;

int main() {
	int fd;
	if ((fd = open(workfile, O_RDWR)) == -1) {
		printf(“Couldn’t open %s\n”, workfile);
		return 1;
	}
	return 0;
}

일반적으로 standard include file들은 /usr/include에 존재하며, process당 열 수 있는 file의 개수가 제한되어있다. 예전에는 20여개였으나, 최근에는 대략 1000개 정도이다. return 뒤의 숫자는 종료 상태를 의미하며, 관습적으로 0은 정상종료, 나머지는 비정상 종료를 의미한다.

System Call : create

#include <fcntl.h>
int creat(const char *pathname, mode_t mode);

argument

  • const char *pathname : 생성할 file 경로와 이름
  • mode_t mode : file 생성 시 permission

return

  • 성공 시 file descriptor return
  • error 발생 시 -1 return

create는 새로 file을 생성할 때 사용되며, 이미 존재하는 file일 경우, mode_t mode는 무시된다. 항상 write로 open하며, 해당 file을 비워서 file descriptor를 return해준다. creat는 아래와 같이 open으로도 표현할 수 있다.

Owner and permission of a new file

새 file을 만들 때, 새로운 link가 추가되므로, 상위 directory에 대한 write 권한이 필요하다. 여기서 directory는 내부에 있는 file 목록을 포함하고 있는 file이다. file의 소유자는 process의 euidegid가 된다.

System Call : close

#include <unistd.h>
int close(int filedes);

argument

  • int filedes : 닫을 file의 file descriptor

return

  • 성공 시 0 return
  • error 발생 시 -1 return

close는 file을 닫을 때 사용되며, program 실행이 완료되면 close를 쓰지 않아도 열려있는 모든 file이 자동으로 닫힌다.

System Call : read

#include <unistd.h>
ssize_t read(int filedes, void *buffer, size_t n);

argument

  • int filedes : read할 file의 file descriptor
  • void *buffer : 읽은 내용을 저장할 buffer
  • size_t n : 읽을 byte 수

return

  • 성공 시 읽은 byte 수 return
  • file의 끝일 경우 0 return
  • error 발생 시 -1 return

read는 현재 file position에서 memory로 byte를 복사한 이후, file position을 update해주는 역할을 한다.

File Position

각 file descriptor는 file position을 가지고 있으며, readn byte만큼 file을 읽고, file position을 n만큼 이동시켜준다. 이에 대한 예시 코드는 아래와 같다.

int fd;
ssize_t n1, n2, n3;
char buf1[512], buf2[512], buf3[512];

if( (fd = open(“foo”, O_RDONLY)) == -1)
	return -1;
							/* f_position : 0 */
n1 = read(fd, buf1, 512); 	/* n1 : 512, f_position : 512 */
n2 = read(fd, buf2, 512); 	/* n2 : 188, f_position : 700 */
n3 = read(fd, buf3, 512); 	/* n3 : 0, when read EOF */

foo700 bytes만 저장되어있을 경우, 주석의 내용과 같이 file position이 이동한다.

해당 주차에 추가로 학습한 command

  • touch 파일이름
    - 해당 이름의 파일이 없을 경우 생성, 존재할 경우 파일의 날짜를 현재 시간으로 수정
  • cp [option] sourceFile targetFile
    - 한 파일의 내용을 다른 파일에 복사
    - [option]
    -r : sub directory의 내용도 복사
    -f : 중복된 파일명이 있을 시 묻지 않고 강제 덮어쓰기 (강제 복사)
  • mv [option] sourceFile targetFile
    - 파일을 이동시키며, source와 target의 모든 경로가 일치하고 파일명 부분만 다르면 파일 이름을 변경한다.
    - [option]
    -f : 중복된 파일명이 있을 시 묻지 않고 강제 덮어쓰기 (강제 이동)
  • rm [option] filename
    - 파일을 삭제하는 명령어
    - [option]
    -r : 하위 directory 및 파일 함께 제거
    -f : 묻지 않고 강제 제거
    -> 명령어 입력 이후 바로 삭제하므로 사용 시 큰 주의를 요함
  • ls -l
    - 파일 접근 권한 확인 가능
  • chmod [option] xyz [arguments]
    - xyz : 각각 owner, group, others에 해당하는 3자리 8진수
  • chmod [option] [who][op][permission]
    - [who] : u(user), g(group), o(others)
    - [op] : +(접근 권한 추가), -(접근 권한 제거)
    - [permission] : r(read), w(write), x(execute)
    -> ,를 이용하여 한꺼번에 여러개를 적어줄 수 있음
    - [option]
    -R : 하위 directory 및 file 모드도 함께 변경
  • echo [text]
    - 사용자가 입력한 내용을 그래돌 출력장치에 보내주는 명령어 (standard out)
  • cat [option] filename
    - 파일의 내용을 보여주는 명령어
    - [option]
    -n : 각 줄에 번호 붙여줌
    -v : 탭, newline, from feed 를 제외하고 print할 수 없는 문자 보여줌
    -e : 각 줄의 끝에 $를 붙여줌 (-v와 함께 사용시에)
  • head [option] filename
    - 파일의 시작부분 보여줌
    - [option]
    -n number : 파일의 시작부터 number만큼의 줄을 보여줌
  • tail [option] filename
    - 파일의 끝부분 보여줌
    - [option]
    -n number : 파일의 끝부터 number만큼의 줄을 보여줌
  • sudo
    - command 앞에 붙여 root 권한으로 수행이 가능
    - sudo를 사용하기 위해서는 사용자에게 sudo권한을 부여해야함

vim editor

visual display editor로 linux에서 사용되는 대표적인 텍스트 편집기이다. 한 줄 단위가 아닌 한 화면을 편집한다. 크게 4가지 모드가 존재하며 아래와 같다.

Compile 과정

Compile 과정에 대한 설명은 아래와 같다.

  • Vim editor: source program 작성한다.
  • Pre-processor(cpp): source program을 modified source program으로 변환하고 #include, #define, 주석 제거 등을 수행한다.
  • Compiler(cc1): modified source program을 assembly program으로 변환한다.
  • Assembler(as): assembly program을 object file로 변환한다.
  • Linker(ld): 여러 object file을 묶어 하나의 실행 file로 생성한다.

하나의 file을 gcc로 compile하는 명령어는 아래와 같다.

$ gcc filename.c -o name

-o 뒤의 name 자리에 이름을 설정해주지 않으면 default로 a.out으로 compile되며, 실행은 ./name으로 가능하다. compile option은 여러개가 존재하며, 그중의 일부는 아래와 같다.

  • -o : 출력하고자 하는 실행파일 설정
  • -O : 최적화 옵션, 뒤에 숫자를 적어주지 않으면 자동이며, 숫자는 0부터 3까지 입력 가능. 숫자가 크면 클 수록 더 최적화됨
  • -c linking을 실행하지 않고 object file까지만 생성하며, filename.o로 생성됨

Makefile

컴파일 시 필요한 규칙을 명시한 script file이며, 파일들이 어떻게 서로 의존하고 있는지, target을 어떻게 빌드해야 하는지를 명세한 file이다. make 유틸리티가 이를 처리하며 기본 작성 방식은 아래와 같다.

<target> : <dependency>
	<recipe>
clean:
	<remove command>

make target -f makefilename으로 실행이 가능하며, 나머지를 전부 생략시에는 자동으로 끝까지 수행하게 된다. makefilename으로 makefile 지정도 가능하다.

profile
잔잔한 물결에서 파도로, 도약을 위한 도전. 함께하는 성장

0개의 댓글