push_swap, so_long 과제에서 고생했던 메모리 누수 체크와 디버깅!
앞으로 많이 활용될 것 같으니 따로 정리해 놓기

시스템 종료 시 메모리 누수 확인

// main문 추가
system("leaks 시스템명");
void	fn(void)
{
	system("leaks so_long");
}

atexit(fn);
  • 이건 평가하다 알려주고 가신거..!
// system("leaks --list [program name]") -> leaks function
// export MallocStackLogging=1

 

시스템 실행 시 메모리 누수 확인

# leakcheck.sh 작성해서 사용하면 편함!
while [ true ]
do
	leaks --list so_long
	sleep 1
done

 

segmentation fault 포인트 확인

gcc -g3 -fsanitize=address 파일명(ex. *.c)

-g 옵션 : 디버깅 실행파일 생성 옵션? 3을 붙이는 이유는 정보가 더 많이 나와서
-fsanitize=address : 자동완성 되니 일부 문자만 입력후 tab

./a.out 실행시 보이는 코드

#5 → #0 까지 순차적으로 확인하며 어느 부분에서 오류가 나는지 확인!!

 

변수값 확인하며 하나씩 실행하는 법 (vscode)

1) Debug C/C++ File 오른쪽 상단 번호 클릭

launch 실행 전에 Debugger 모드에서 App, Process에서 앱을 시작하는 방법
attach 이미 실행중인 App, Process에 연결하는 방법
  • tasks.json
{
    "tasks": [
        {
            "type": "cppbuild",
            "label": "C/C++: gcc build active file",
						// 실행하게 되는 명령어
            "command": "/usr/bin/gcc",
						// 명령어 인자
            "args": [
                "-fdiagnostics-color=always",
                "-g",
                "${file}",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}"
            ],    
            "options": {
                "cwd": "${fileDirname}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "Task generated by Debugger."
        }
    ],
    "version": "2.0.0"
}
  • launch.json
{
	// Use IntelliSense to learn about possible attributes.
	// Hover to view descriptions of existing attributes.
	// For more information, visit: <https://go.microsoft.com/fwlink/?linkid=830387>
	"version": "0.2.0",
	"configurations": [
		{
			"name": "(lldb) Launch",
			// 
			"type": "cppdbg",
			"request": "launch",
			// launch 하는 대상 프로그램 
			"program": "${workspaceFolder}/a.out",
			"args": [],
			"stopAtEntry": false,
			"cwd": "${fileDirname}",
			"environment": [],
			"externalConsole": false,
			"MIMode": "lldb"
		}

	]
}

 

https://jaeseokim.dev/C/C-vscode-debugger-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0/

 

[C] vscode debugger 사용하기!

intro 이전부터 42seoul의 프로젝트를 진행하면서 vscode debugger 를 사용하는 일이 많아지면서 한번 간단하게 예시를 이용하여 정리를 해보았다! vscode debugger! vscode에서는 다양한 언어에 대하여 디버

jaeseokim.dev

 

2) RUN AND DEBUG tab 클릭 → (lldb) Launch 실행 (왼쪽 상단)

🚨 실행이 되지 않을때 -g3 옵션을 사용해서 컴파일을 해놓기!

  • gcc -g3 *.c
  • 실행시 컴파일이 함께 되는데 뭔가 오류가 생기는 것 같음,,

 

3) break 포인트 선택후 디버깅 실행

순서대로 실행, 함수외부 실행, 함수 내부실행, ?, ?, 중지

 

2 Circle - so_long

간단한 2D 게임 만들기
텍스쳐, 스프라이트, 기본적인 게임플레이 요소들 다뤄보기
✅ minilibx_opengl(정적 라이브러리) 활용

 

MiniLibX란?

MiniLibX는 42에서 만든 그래픽 라이브러리다. #include <mlx.h> 또는 "mlx.h" 로 사용 가능하다. mlx.h에 있는 함수들은 Appkit과 OpenGL을 기반으로 만들어졌다고 한다. 그래서 mlx.h를 사용하려면 Appkit과 OpenGL을 링크해줘야 한다.

mlx는 minilibx_opengl(정적 라이브러리), minilibx_mms(mojave, metal, swift) (동적 라이브러리) 두 가지 버전이 있다. 둘 중 하나만 사용하면 된다. 참고로 mms버전에서는 검은줄 이슈가 있는 듯 하다.

 

Window 화면 띄우기

#include "./minilibx_opengl_20191021/mlx.h"

int main(void)
{
	void	*mlx;
	void	*win;

	mlx = mlx_init();
	win = mlx_new_window(mlx, 500, 500, "mlx_project");
	mlx_loop(mlx);
}

1) mlx_init()

  • mlx 함수가 포함된 프로그램과 디스플레이의 그래픽적인 연결을 담당하는 mlx 시작 함수
  • mlx_ptr 반환

2) void *mlx_new_window (void *mlx_ptr, int size_x, int size_y, char *title)

  • 화면에 그래픽 구현을 위한 새 윈도를 띄우고 win_ptr를 반환
  • x축의 길이와 y축의 길이, 창의 제목 지정 가능
  • mlx_ptr 인자 = mlx_init()의 리턴값
  • 실패하면 NULL 반환

3) int mlx_loop(void *mlx_ptr)

  • 프로그램을 종료시키지 않고 무한 루프로 동작하게 만듦
  • 이벤트를 받으면 사용자 정의 함수 호출
  • 이벤트 : 키보드나 마우스를 누르는 등의 행위
gcc minilibx_opengl_20191021/libmlx.a main.c -framework OpenGL -framework AppKit

 

key hook 구현하기

# define X_EVENT_KEY_PRESS 2
# define X_EVENT_KEY_RELEASE 3

# define KEY_ESC 53
# define KEY_W 13
# define KEY_A 0
# define KEY_S 1
# define KEY_D 2

typedef struct s_param{
	int		x;
	int		y;
}				t_param;

https://harm-smits.github.io/42docs/libs/minilibx/events.html#x11-events

 

Events

Find code examples, optimization tricks, and much more.

harm-smits.github.io

 

images 사용해서 이미지 넣어보기

int	main(void)
{
	void	*mlx;
	void	*win;
	void	*empty;
	void	*wall;
	void	*player;
	void	*item;
	void	*exit;
	int		img_width;
	int		img_height;

	mlx = mlx_init();
	win = mlx_new_window(mlx, 500, 500, "jaeyyoo's_soLong");
	empty = mlx_xpm_file_to_image(mlx, "./images/empty.xpm", &img_width, &img_height);
	wall = mlx_xpm_file_to_image(mlx, "./images/wall.xpm", &img_width, &img_height);
	player = mlx_xpm_file_to_image(mlx, "./images/player.xpm", &img_width, &img_height);
	item = mlx_xpm_file_to_image(mlx, "./images/item.xpm", &img_width, &img_height);
	exit = mlx_xpm_file_to_image(mlx, "./images/exit.xpm", &img_width, &img_height);
	mlx_put_image_to_window(mlx, win, empty, 0, 0);
	mlx_put_image_to_window(mlx, win, wall, 60, 0);
	mlx_put_image_to_window(mlx, win, player, 120, 0);
	mlx_put_image_to_window(mlx, win, item, 0, 60);
	mlx_put_image_to_window(mlx, win, exit, 60, 60);
	mlx_loop(mlx);
}

🚨 png 이미지는 xpm 파일로 변환해야 한다! -> png는 가끔 segmentation fault 난다고 함

https://anyconv.com/ko/png-to-xpm-byeonhwangi/

 

온라인에서 PNG를 XPM로 변환하십시오 - AnyConv

AnyConv는 5 성급 PNG XPM 변환기입니다 ⭐ 온라인에서 png를 xpm로 몇 초 안에 변환하십시오 👍 소프트웨어 설치가 필요하지 않습니다 👍 무료로 👍 완전히 안전합니다. PNG를 XPM로 쉽게 변경할 수

anyconv.com

🚨 사이즈를 동일하게 해야한다.!

이미지 사이즈가 다를 경우 왼쪽처럼 되어버림

파일 가져와서 맵 만들기

  • 이미지 띄우는 함수 파일 따로 만들기
  • map 읽어서 담을 구조체 만들기
    • get_next_line 으로 파일 읽기 → 담을 때마다 길이도 체크하기
  • map 문자 확인 후 해당 문자에 해당하는 이미지 띄우기

 

게임처럼 동작하도록 만들기

  • wasd 적절히 움직이기
    • key를 움지이면 player를 해당 방향으로 이미지를 새로 해야함
      • 현재의 위치를 저장할 변수 → 현재 ‘P’ 위치
  • P → E (출구)
    • 코인 개수가 최초와 같으면
      • 현재 위치 0으로 바꾸고
      • 다음 위치 예티와페페로 바꾸고
    • 다르면
      • 무시 (못감)
  • P → C (아이템)
    • 현재 위치 0으로 바꾸고
    • 다음 위치 P로 바꾸고
    • 코인 개수 늘리고
  • P → 0 (바닥)
    • 현재 위치 0으로 바꾸고
    • 다음 위치 P로 바꾸고
  • P → 1 (벽)
    • 무시 (못감)

⇒ 움직일 때마다 이미지를 다시 띄워주면서 자연스럽게 움직이도록 하기

 

에러 처리

  • 파일명 에러처리
    • 인자 2개가 아닐때
    • fd가 오류날 때
    • 확장자가 ber가 아닐 때
  • 지도가 직사각형이 아닌 경우
  • 지도가 벽으로 둘러쌓여 있지 않을 경우
  • 맵에 출구/시작지점/수집품이 없는 경우
  • 상단의 x버튼 누르면 프로세스 종료
  • 탈출할 수 없는 경우의 맵
    • 플레이어가 출구까지 도달 할 수 없을 때
    • 플레이어가 수집하지 못하는 코인이 있을 때
  • empty map, newline map 체크
  • 파일이 너무 클 경우 (화면을 초과하는 경우 에러처리 함 ex-> width max = 32 )
  • leaks check !!

 

레퍼런스

  • so_long 시작하기

[42 서울] so_long을 빠르게 끝내보자

 

[42 서울] so_long을 빠르게 끝내보자

서론 push_swap을 끝내고 참여한 사이드 프로젝트가 끝나가면서 여유도 조금 생긴 것도 있지만, 당장 이번 주에 과제를 안내면 월급을 못 받을지도 모르는 상황에 처해서 급하게 과제를 하나 해치

techdebt.tistory.com

  • DFS 알고리즘

[so long] 맵 안에 유효한 경로 확인, DFS로 해보자!


 피그마로 결과물 예시까지 미리 만들어보며 열심히 진행한 과제..
empty 나 newline이 포함된 맵까지 에러처리를 하는것은 생각치 못했다
모든 평가자들이 메모리누수를 꼼꼼히 확인했던 과제라 시스템 실행중과 종료시 메모리 누수 모두 확인 필수 !!

귀여운 나으 쏘롱..

2 Circle - push_swap

 이번 과제에서는 스택에 있는 데이터를 한정된 명령어를 이용하여 최대한 적은 횟수 내에 정렬하는 것을 목표로 합니다. 성공하기 위해서는 다양한 정렬 알고리즘을 조작해 보고, 최적화된 데이터 정렬에 가장 적합한 알고리즘을 선택하여야 합니다.

 

Subject 해석

  • 정수 값 세트, 스택 2개, 명령어 세트를 마음대로 사용할 수 있습니다. 두 스택을 모두 조작합니다.
  • 인수로 받은 정수를 정렬하는 프로그램
# 실행 방식
$>./push_swap 2 1 3 6 5 8
sa
pb
pb
pb
sa
pa
pa
pa
$>./push_swap 0 one 2 3
Error
$>

 

Subject 해결 순서

1. 파싱하기

  • [x] 일단 들어온거 다 “ “으로 join하기
  • [x] 스페이스 기준 split 하기

2. 인자 에러처리

  • [x] 자른거 숫자로 바꿔서 배열에 담기
    • [x] 숫자 / - 아니면 에러
    • [x] 중복되면 에러
    • [x] integer values 가 아니면 에러 (-2147483648 ~ 2147483nt647) → atoi에 추가
  • [x] 가장 큰 수 기준으로 인덱스 번호 메기기
  • [x] 일단 담고 싹다 b로 보내서 a에 차곡차곡 → 이러면 횟수 넘을듯.. 모래시계 알고리즘이란 무엇인가.. 이것을 다시 이해해보자

3. 개발 순서

  • [x] 양방향 링크드리스트 만들기
  • [x] main에서 인자값을 입력받을 수 있게 만들기
  • [x] 비어있을때, 인자가 숫자가 아닐때, 과제의 요구사항에 맞춰 예외처리 하기
  • [x] 링크드리스트에 데이터 담기
  • [x] 각 커맨드 액션들 구현하기
  • [x] 정렬로직 구현하기 - 모래시계
  • [x] push_swap 인자값이 " " 일 때 처리하기

 

양방향 연결리스트

// 양방향 연결리스트 자료구조
typedef struct s_node
{
	int				data;
	struct s_node	*prev;
	struct s_node	*next;
}	t_node;

// 양방향 연결리스트 자료 생성
t_node *newNode(int data)
{
	t_node *node;

	node = malloc(sizeof(t_node));
	node->data = data;
	node->next = node->orev = NULL;
	return node;
}

https://jhdroid.tistory.com/22

 

deque (Double Ended Queue)

양 끝이 앞이 되는 큐 : 양 쪽 끝에서 삽입과 삭제가 가능한 자료구조
-> 스택과 큐의 성질을 모두 가지고 있다.

// 데큐 자료구조
typedef struct s_deque
{
	t_node			front;
	t_node			rear;
	unsigned int	size;
}	t_deque;

// 데큐 자료 생성
t_deque *createDeque() {
	t_deque deque;

	deque = malloc(sizeof(t_deque));
	deque->front = deque->rear = NULL;
	deque->size = 0;
	return deque;
}

https://doing7.tistory.com/93

 

[자료구조] 스택, 큐, 데큐

💡 Stack(스택) : 스택은 한쪽 끝으로만 자료를 넣고 뺄 수 있는 자료 구조이다. : LIFO(Last In, First Out) 구조이다. 💡 스택의 특징 [ 장점 ] : 구조가 단순해서 구현이 쉽다. : 데이터 저장/읽기 속도가

doing7.tistory.com

 

모래시계 알고리즘 

Push_swap 맛있게 부어먹기

 

Push_swap 맛있게 부어먹기

터미널에 ./push_swap "3 2 5 8 7" 1 0 "15 79"등 숫자를 입력하면 해당 숫자들을 stack a에 저장한 후 stack b를 활용하여 해당 숫자들을 sa, sb, ss, pa, pb, ra, rb, rr, rra, rrb, rrr 만을 활용하여

velog.io

42Seoul - push_swap

 

42Seoul - push_swap

문제개요 > 스택구조인 a와 b가 주어진다. 겹치지 않은 n개의 숫자가 랜덤으로 a스택에 존재한다. 스택b를 활용해 특정한 명령어만으로 스택a에서 n개의 순자를 오름차순으로 정렬하게 해야한다.

velog.io

push swap 문제와 해결 알고리즘 복잡도 증명

 

push swap 문제와 해결 알고리즘 복잡도 증명

push_swap 문제

eeeuns.github.io

push_swap 모래시계 알고리즘

 

push_swap 모래시계 알고리즘

슬기로운 모래시계 평가 가이드

80000coding.oopy.io

 

a 스택 top 분기 처리

  1. count 보다 작을 때 pb
  2. count 보다 크고, count + chunk 값보다 작을 때 pb + ra
  3. count + chunk 보다 클 때
    1. 총 길이 / 2 보다 count가 작으면 rra
    2. 아니면 ra

 b 스택 정렬하며 큰 수부터 a로 넘기기

  1. 최상위값 위치 찾기
  2. b의 길이를 절반으로 나눴을 때
    1. 위면 rb
    2. 아래면 rrb
  3. top 이 최상위값이면 pa

🚨 5개 이하 하드코딩 필요!!

2 Circle - minitalk

✅ 클라이언트와 서버 형태로 통신 프로그램 생성하기
✅ SIGUSR1/SIGUSR2 시그널을 사용하여 server에서 client들의 메세지를 받는 것이 목표!! 

 

허용함수 정리

1) signal

void (*signal(int signum, void (*handler)(int)))(int);
  • 시그널 처리 방법을 설정한다.
  • 어떤 시그널들은 미리 정의된 행동을 함으로써 처리되는데, 이처럼 기존에 정의된 행동을 그대로 할 지, 시그널을 그냥 무시할 지, 사용자 정의 행동을 하도록 바꿔줄 지 선택할 수 있다
  • sig는 처리해줄 시그널 번호로, 매크로 SIGABRT, SIGALL, SIGILL, SIGINT, SIGFPE, SIGIO, SIGOTHER, SIGSEGV, SIGTERM, SIGUSR1, SIGUSR2중 하나여야 하며, signal.h에 정의된다.
  • handler는 시그널을 처리해줄 핸들러 SIG_IGN을 인자로 넘겨주면 해당 시그널을 무시한다
  • 함수포인터를 넘겨주면 시그널이 들어왔을 때 특정 함수를 호출한다.

2) sigemptyset

int sigemptyset(sigset_t *set);
  • 시그널 집합을 초기화하여 모든 시그널을 포함하지 않도록 설정한다.
  • 성공하면 0을 반환하고, 실패하면 -1을 반환

3) sigaddset

int sigaddset(sigset_t *set, int signum);
  • 시그널 집합에 특정 시그널을 추가한다.
  • 성공하면 0을 반환하고, 실패하면 -1을 반환

4) sigaction

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  • 특정 시그널에 대한 동작을 설정하거나 변경한다.
  • 시그널 처리에 대한 세부적인 제어를 제공
  • 이전에 설정된 시그널 핸들러의 정보를 저장하는 struct sigaction 구조체를 반환
  • 오류 발생시 -1 리턴

5) kill

int kill(pid_t pid, int sig);
  • 특정 프로세스에 시그널을 보낸다.
  • pid에 시그널을 받을 프로세스의 id가 들어간다.
  • pid > 0: 해당 pid에 시그널이 보내짐
  • pid == 0: 시그널을 보낸 프로세스와 같은 그룹에 속한 모든 프로세스에 시그널이 보내짐
  • pid == -1:
    • 유저가 관리자 권한을 가지고 있다면 시스템 프로세스와 현재 시그널을 전송하려는 프로세스를 제외한 모든 프로세스에 시그널을 전송
    • 유저가 관리자 권한을 가지고 있지 않다면, 해당 유저와 같은 uid를 가진 프로세스에만 현재 시그널을 전송
  • sig에 해당 프로세스에 보낼 시그널이 들어간다. sig에 0을 넣을 경우 에러 체킹을 시도하며, pid의 유효성을 검사할 수 있다.
  • 이때 인자값으로 들어갈 signal은 위에서 말한 매크로에 이미 정의되어 있어야 함
  • 정상적으로 kill함수가 수행되면 0을 리턴, 에러 발생 시 -1 리턴 및 errno 세팅됨.

6) getpid

pid_t getpid(void);
  • 현재 프로세스의 아이디를 리턴한다
  • 아이디는 모든 프로세스가 다 고유한 값을 가지기 때문에 임시 파일명을 지을 때 좋음
  • getpid, getppid는 무조건 성공하기 때문에 실패했을 경우 반환하는 값은 존재하지 않음

7) pause

int pause(void);
  • kill() 함수에 의해 시그널을 받기 전까지 현재 스레드를 일시정지시킨다.
  • pause()를 통해 스레드가 일시정지된 상태에서 시그널 처리기가 멈출 경우 pause()는 반환값을 반환한다.
  • 어떤 경우에서든 무조건 -1을 반환한다.

8) sleep

unsigned int sleep(unsigned int seconds);
  • seconds 초만큼 스레드를 일시정지한다. 만약 sleep중간에 시그널이 발생한다면 바로 복귀 후 남은시간이 반환된다.

9) usleep

int usleep(useconds_t usec);
  • microseconds 마이크로초만큼 스레드를 일시정지한다.

10) exit

void exit(int status);
  • 프로그램을 종료하고, 상태(exit status)를 운영 체제에 반환한다.
  • stdlib.h에 정의되어 있다.
  • exit(0)은 '정상종료', exit(1)은 '에러메시지 종료'

 

개념 정리

pid_t

  • 프로세스 ID(Process ID)를 나타내는 데이터 타입 (=int)
  • 일반적으로 프로세스 식별자를 저장하는 데 사용
  • <sys/types.h> 또는 <unistd.h>에서 정의

SIGUSR1, SIGUSR2

  • signal은 사용자가 정의할 수 있는데, 해당 과제에서 사용 할 수 있는 시그널은 2가지
  • 이 조건때문에 2진수(true, false) signal을 보내 처리한다.

비트연산

& 연산자 | 연산자
연산 결과 연산 결과
0 & 0 0 0 & 0 0
0 & 1 0 0 & 1 1
1 & 0 0 1 & 0 1
1 & 1 1 1 & 1 1
<< 연산자 >> 연산자
비트를 왼쪽으로 이동하는 shift 연산 비트를 오른쪽으로 이동하는 shift 연산

 

Client

  • 기본 동작만 구현해보기
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

int	main(int argc, char **argv)
{
	pid_t	pid;

	if (argc != 3)
	{
		ft_printf("Error : 프로그램을 종료합니다.\\n");
		exit(1);
		return (-1);
	}
	pid = atoi(argv[1]);
	// server에 SIGINT 시그널 보내기
	kill(pid, SIGINT);
	return (0)
}

무조건 8번 반복하게 만들어 ..
1이면 1 보내
0이면 0 보내
나머지 0 보내
→ 보내면 안돼,, 거꾸로 보내야돼

값이 있을때만 반복하는게 아니라 무조건 8번 반복하게 .. 반복.. 하게 .. 반복…
→ 성공 !

 

Server

  • 기본 동작만 구현해보기
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

void	test(int signum)
{
	printf("test...\\n");
}

int	main(int argc, char **argv)
{
	pid_t	pid;

	(void)argv;
	if (argc != 1)
	{
		ft_printf("Error : 프로그램을 종료합니다.\\n");
		exit(1);
		return (-1);
	}
	// PID 출력
	pid = getpid();
	printf("PID : %d\\n", pid);
	// SIGINT 시그널 받는거 확인
	signal(SIGINT, test);
	while (1)
		pause();
	return (0);
}

 

Bonus 추가 개념

유니코드란?

https://sweetday-alice.tistory.com/179

 

[문자 인코딩] 유니코드란 무엇인가?

부제: 유니코드란 무엇인가? 컴퓨터는 이진수만 안다. 컴퓨터와 내가 문자를 입력하고 출력할 수 있도록 해주는게 바로 문자 코드다. 그리고 문자코드와 숫자를 매칭시킨 표를 문자표라고 한다.

sweetday-alice.tistory.com

 

레퍼런스

https://www.joinc.co.kr/w/Site/system_programing/Book_LSP/ch06_Signal

 

리눅스 시스템 프로그래밍 6장 - Signal

리눅스 시스템 프로그래밍 6장 - Signal의미를 전달하기 위해서 사용하는 일반적인 방법으로 신호와 메시지가 있다. 메시지는 언어기반의 전달방식이며, 여러의미를 내포하고 있는 비교적 복잡한

www.joinc.co.kr

 

1 Circle - Born2beroot

✅ 가상머신을 사용하여 자신만의 운영체제 구축하기

 

가상머신의 작동방식

  • 물리적으로 우리가 사용하는 컴퓨터가 아닌 다른 컴퓨터를 만들어내는 가상의 컴퓨터를 의미 == 에뮬레이션
    ⇒ 가상머신을 사용하려면 나름 고급 하드웨어가 있어야 하고 그렇지 못하면 상대적으로 매우 느리게 실행되므로 잘 사용하지 않음
    ⇒ 소프트웨어가 가상머신이 제공하는 환경과 자원에 제한을 받으며 우리가 만든 가상세계를 벗어날 수 없다.
  • 가상머신의 목적?
     동시에 여러 운영체제를 설치하고 실행하기 위해 컴퓨터 메모리를 분할하여 단일 컴퓨터에 여러 개의 격리된 동일한 실행환경을 생성
    1. 하나의 컴퓨터로 서로 다른 두 개 이상의 운영체제를 실행하고자 할 때
    2. 하나의 컴퓨터 자원을 여러 사용자에게 나누어 주는 상황에서 상호 간섭을 없애고 싶을 때 ex) 클라우드 가상머신
    3. 컴퓨터의 다른 부분에 영향을 주지 않는 독립 환경을 만들고 싶을 때 ex) 악성코드 분석시 활용
    host : 실제로 사용하는 컴퓨터 guest : 가상환경에서 설정한 컴퓨터

 

Rocky와 Debian 의 차이점

1) Rocky

  • 레드헷 (Red Hat) 계열 → 기업용서버 os (상용화)
  • CentOS 업그레이드 버전
  • 버전 주기가 긴 편. 시스템이 안정적이고 보안이슈, 버그가 아니면 업데이트 안함
  • 업그레이드가 어려운 편. (업그레이드 보다는 새 버전을 설치하는걸 공식사이트도 권장함)
  • 간단한 GUI가 없음.

2) Debian

  • 데비안 프로젝트가 개발한 자유(무료) 컴퓨터 운영체제
  • 다양한 패키지가 존재함
  • 버전 주기, 릴리즈가 빠르고 자주 있는 편. 시스템 안정화까지 시간이 걸려 상용화 서버로는 선호되지 않음
  • 패키지 설치 및 안정화된 버전간의 업그레이드가 쉽다.
  • 데스크톱 친화적 GUI가 있음

 

apt와 aptitude 의 차이점과 APPArmor

  • apt : 소프트웨어의 설치과 제거를 처리하는 소프트웨어. 그래픽 인터페이스 (GUI) 없이 명령어로 사용
  • aptitude : 사용자 인터페이스를 추가해 사용자가 대화형으로 패키지를 검색하고 설치 제거 할 수 있는 패키징 (apt의 프론트엔드 프로그램)
  • APPArmor : 시스템 관리자가 프로그램 프로필 별로 프로그램의 역량을 제한할 수 있게 해주는 리눅스 커널 보안모듈

 

파티션

  • lsblk : 리눅스 디바이스 정보 출력 ⇒ 파티션 확인 가능
  • 파티션을 왜할까?이 때, 하드웨어 둘 사이에서 분할해야하기 때문에 사용!
    ⇒ 일반적으로 하드웨어는 운영체제를 실행하는데 사용되는데 가상머신을 이용할 때는 기본운영 체제 내에서 두번째 운영체제를 사용.

 

LVM (Logical volume manager)

  • 논리적 공간을 관리해주는 프로그램
  • 디스크나 대용량 스토리지 장치를 유연하고 확장이 가능하게 다룰 수 있는 기술
  • LVM 의 장점
    1. 여러개의 디스크 공간을 합쳐 하나처럼 사용할 수 있다.
    2. 사용하기 애매한 공간의 디스크 파티션을 활용할 수 있다.
    3. 기존에 사용중인 디스크의 공간을 유연하게 사용할 수 있다.
  • LVM과 파티션의 차이점
    • LVM은 파티션 대신 볼륨이라는 단위로 저장장치를 다룸
    • 물리 디스크를 볼륨 그룹으로 묶고, 이걸 논리 볼륨으로 분할해서 관리
    • 스토리지 확장, 변경시 서비스 변경을 할 수 있고, 스토리지 확장에 유연하게 대응할 수 있음

 

Sudo

cd /var/log/sudo/00/00

Superuser do

  • sudo (명령어) 일반 사용자가 root 권한을 빌려 명령 실행
  • su (계정명) 현재 사용자를 로그아웃 하지 않는 상태에서 다른 사용자의 계정으로 전환하는 명령어
  • su - (계정명) 다른 사용자의 계정으로 완전히 전환하고, 전환한 사용자의 환경설정을 불러오는 명령어
    ⇒ root는 무한대의 권한을 가지고 있는데 root 권한을 아무나 가지고 있느면 침입자들이 마음대로 바꿀 수 있게 됨
    ⇒ root의 권한은 시스템 관리자, 시스템 커널자체, 서비스 데몬의 3가지 경우로 한정 지어놓아야 함
  • 데몬? == 멀티태스킹 운영 체제에서 사용자가 직접적으로 제어하지 않고, 백그라운드에서 돌면서 여러 작업을 하는 프로그램
    시스템 로그를 남기는 syslogd처럼 보통 데몬을 뜻하는 ‘d’를 이름 끝에 달고 있으며, 일반적으로 프로세스로 실행된다.
$ sudo --version
$ sudo usermod -aG sudo <new_user>

su 대신 sudo의 장점

  • root password를 효율적으로 관리할 수 있다.
  • root가 시스템상에서 작업하는 시간이 짧아야 한다.
  • log 추적이 쉬워진다 (su root 사용시 로그 확인이 안된다)
  • 침입자를 탐지할 수 있다. (시스템 침입자는 sudo를 사용할 수 없다.)

 

UFW/SSH

$ sudo ufw status
$ systemctl status ssh

 

1) UFW (Uncomplicated Firewall - 방화벽)

  • 미리 정의된 보안 규칙에 기반한, 들어오고 나가는 네트워크 트래픽을 모니터링하고 제어하는 네트워크 보안 시스템
  • 일반적으로 신뢰할 수 있는 내부 네트워크, 신뢰할 수 없는 외부 네트워크(예: 인터넷) 간의 장벽을 구성
  • 프레임워크를 관리 및 조작하기 용이하기 위해 사용
  • sudo apt install ufw 명령어로 설치
$ sudo ufw allow 8080
$ sudo cat /etc/ufw/user.rules
# 삭제
$ sudo ufw delete allow 8080

2) SSH (Secure Shell Protocol)

  • 컴퓨터와 컴퓨터가 인터넷과 같은 Public Network를 통해 서로 통신을 할 때 보안적으로 안전하게 통신을 하기 위해 사용하는 프로토콜
  • public key와 private key를 만들어 public key를 통해 메세지를 전송하기전 암호화를 하고 private key 로 암호화된 메세지 복호화 가능
  • sudo nano /etc/ssh/sshd_config
  • 포트 포워딩 사용법
$ ssh jaeyyoo@192.168.68.1 -p 4242

$ systemctl status sshd
$ systemctl start sshd
$ systemctl restart sshd

 

크론(cron) Command Run On (UNIX scheduler)

vi /root/monitoring.sh
  • 정기적으로 작업을 처리하며 소프트웨어를 실행시키도록 시간 기반 잡 스케줄러 성격의 데몬 프로세스
    ⇒ 특정시간 or 특정시간마다 어떤 작업을 자동으로 수행하게 하는 명령어

1) crontab

  • 크론작업을 설정하는 파일 → /etc/crontab에 설정된 것을 읽어 작업을 수행

2) cron 명령어

# 크론 스크립트 실행 안되게
$ sudo systemctl disable cron
$ sudo reboot
$ sudo service cron status

# 다시 실행
$ sudo systemctl enable cron

# stop, start 같은 부가 명령어도 기억하기 !! => 1회성 명령어? 현업에서 자주 사용함!
$ sudo systemctl stop cron
$ sudo systemctl start cron

3) monitoring.sh 

# 운영체제와 커널의 버전에 대한 구조
printf "#Architecture: "
uname -a

# 물리 프로세서들의 수
printf "#CPU physical : "
nproc --all

# 가상 프로세서들의 수
printf "#vCPU : "
cat /proc/cpuinfo | grep processor | wc -l

# 현재 서버에서 사용가능한 RAM과 백분율로 표현된 사용률
printf "#Memory Usage: "
free -m | grep Mem | awk '{printf"%d/%dMB (%.2f%%)\n", $3, $2, $3/$2 * 100}'

# 현재 서버에서 사용가능한 memory와 백분율로 표현된 사용률
printf "#Disk Usage: "
df -a -BM | grep /dev/map | awk '{sum+=$3}END{print sum}' | tr -d '\n'
printf "/"
df -a -BM | grep /dev/map | awk '{sum+=$4}END{print sum}' | tr -d '\n'
printf "MB ("
df -a -BM | grep /dev/map | awk '{sum1+=$3 ; sum2+=$4 }END{printf "%d", sum1 / sum2 * 100}' | tr -d '\n'
printf "%%)\n"

# 백분율로 표현된 프로세서들의 현재 사용률
printf "#CPU load: "
mpstat | grep all | awk '{printf "%.2f%%\n", 100-$13}'

# 마지막으로 재시작된 날짜와 시간
printf "#Last boot: "
who -b | awk '{printf $3" "$4"\n"}'

# LVM의 활성화 여부
printf "#LVM use: "
if [ "$(lsblk | grep lvm | wc -l)" -gt 0 ] ; then printf "yes\n" ; else printf "no\n" ; fi

# 활성화된 연결들의 개수
printf "#Connections TCP : "
ss | grep -i tcp | wc -l | tr -d '\n'
printf " ESTABLISHED\n"

# 서버를 사용하고 있는 유저의 수
printf "#User log: "
who | wc -l

# 서버의 IPv4 주소와 MAC 주소
printf "#Network: IP "
hostname -I | tr -d '\n'
printf "("
ip link show | awk '$1 == "link/ether" {print $2}' | sed '2, $d' | tr -d '\n'
printf ")\n"

# sudo 프로그램으로 실행된 명령의 수
printf "#Sudo : "
ls -l /var/log/sudo/00/00 | grep 'root' | wc -l | tr -d '\n'
printf " cmd\n"

 

비밀번호 변경 옵션 정리

vi /etc/login.defs
vi /etc/pam.d/common-password
  • retry : 재시도 횟수 제한
  • minlen : 최소길이 설정
  • ucredit (upper case credit) : 대문자 포함 옵션
  • lcredit (lower case credit) : 소문자 포함 옵션
  • dcredit (decimal credit) : 숫자 포함 옵션
    ⇒ credit 옵션 뒤에 붙는 숫자는 minlen 값을 계산하는 크레디트 값으로 적용된다.
  • maxrepeat : 연속으로 같은 문자의 허용
  • reject_username : username 거부
  • enforce_for_root : root 사용자도 암호정책을 지켜야함
    ⇒ root 사용자로 로그인 한 후 일반사용자 계정의 암호를 재설정할 때, 함오정책에 위배되어도 에러메세지만 출력하고 계속 진행됨.
  • difok : password 만료 7일 전 알람

1 Circle - ft_printf

✅ printf 함수 구현하기

Program name libftprintf.a
Turn in files Makefile, *.h, */*.h, *.c, */*.c
Makefile NAME, all, clean, fclean, re
External functs. malloc, free, write, va_start, va_arg, va_copy, va_end
Description Write a library that contains ft_printf(), a function that will mimic the original printf()

 

스택(stack)과 힙(heap)의 이해

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com

1) 스택 (stack)
- 함수의 호출과 관계되는 지역 변수와 매개변수가 저장되는 영역
- 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸

2) 힙 (heap)
- 사용자가 직접 관리할 수 있는, 직접 관리해야하는 메모리 영역
- 사용자에 의해 메모리 공간이 동적으로 할당되고 해제

 

함수 오버로딩 개념의 이해

int sum (ina a, int b){return a+b;}
double sum (double a, double b){return a+b;}

=> 같은 이름을 가진 함수를 여러번 선언하여 어떤 타입의 인자가 들어오던 매칭할 수 있도록 하는 것
=> 공통 기능의 함수를 하나의 이름으로 묶어주는 것

 

가변인자 개념의 이해

  • 포인터의 작용으로 이루어지는 것
  • 인자 개수를 모르니 스택(stack)에 쌓았다가 하나씩 빼서 사용
  • 결론적으로, 인자들을 배열에 저장해서 쓰는 것
  • 최소 1개 이상의 고정인수가 있어야 하며 '...'은 파라미터 순서 상 가장 마지막에 위치
// [예시]
#include <stdio.h>
#incldue <stdarg.h>
int sum(ins num_args, ...)
{
	va_list ap;
	va_start(ap, num_args);
	int arg = 0, result = 0;
	fot (int i=0; i<num_args; i++)
	{
		arg = va_arg(ap, int);
		result += arg;
	}
	va_end(ap);
	return result;
}
  • va_list : 인자 리스트에서 추출하고 싶은 인자의 주소를 가리키는 포인터 역할(가변공간)
                   → 함수가 아니라 데이터 타입
  • va_start : va_list로 정의한 변수가 맨 첫 번째 가변인수를 받을 수 있도록 초기화 하는 함수
                     → va_list로 선언된 인스턴스(ap) 와 고정인수를 인자로 받음
  • va_arg : ap포인터가 위치한 부분의 데이터를 읽어 반환하는 함수
  • va_end : va_list 타입을 널로 초기화하는 함수

 

  • 16진수 표현 시 비트연산자(>>)를 사용하여 4바이트씩 밀면서 출력
  • 주소 표기시 0x000076 → 0x76 으로 표기. idx와 출력값을 비교하며 0일 경우 출력x
  • %p와 %i 는 표기형식 같음 !
  • %x와 %X 는 소문자, 대문자 배열 담아놓고 함수 돌리기
  • Makefile에 경로 추가하기 → 안해도 됨 !!
  • uintptr_t → 주소값을 담기 위한 자료형, 범용성이 좋음 !
int	ft_putnbr_un(unsigned int n, unsigned int len, char *base)
{
	unsigned int	nb;

	nb = n;
	if (nb >= len)
		ft_putnbr_un(nb / len, len, base);
	ft_putchar(base[nb % len]);
	return (ft_putnbr_len(n, len));
}

 

 

정적 라이브러리 컴파일 방법

→ 평가받다 발견한 개념!

gcc main.c -L. -lftprintf
  • -L. 정적 라이브러리 현재부터 아래 모두 사용 ??
  • 보통의 아카이브 라이브러리는 lib를 사용하기 떄문에 l로 생략 가능

 

1 Circle - get_next_line

✅ 파일에서 한줄씩 읽어서 반환하는 함수 만들기
 

c언어 파일 읽기

1) open

#include <fcntl.h>

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

*pathname : 절대경로 or 상대경로 (파일명 or 경로)
반환값 : file descriptor(fd) / 실패시 -1

[flag 종류]
O_RDONLY : 읽기 전용으로 파일 열기
O_WRONLY : 쓰기 전용으로 파일 열기
O_RDWR : 쓰기와 읽기 전용으로 열기

2) close

#include <unistd.h>

int close(int fd);

반환값 : 정상 종료시 0 / 실패시 -1

3) read

#include <unistd.h>

ssize_t read(int fd, void *buff, size_t nbytes);

*buff : 읽은 데이터를 저장할 공간에 대한 포인터

반환값 : 정상 종료시 읽어온 바이트 수 / 실패시 -1 / 읽을 데이터가 없으면(파일의 끝에서 시도) 0

 

static 변수

1) static 변수란?
static 변수는 Global 변수 (전역 변수), Local 변수 (지역 변수) 어느 것으로 이용이 가능하다.
전역이든 지역이든 static 변수는 Data Segment에 위치한다.
2) 외부 정적 변수
전역으로 선언된 static 변수는 외부 정적 변수라고도 불리며, 별도의 초기화 구문이 없어도 0으로 초기화된다.
Data Segment의 BSS 영역에 위치하여 0으로 초기화된다.
초기화 구문 존재 시에는 Data Segment의 Data 영역에 위치한다.
[Linux] BSS란 무엇인가?

[Linux] BSS란 무엇인가?

1. BSS란? BSS는 block started by symbol의 약어이다. .bss나 bss는 초기에 오직 제로 값으로 표시된 정적으로 할당된 변수가 포함된 데이터 세그먼트의 일부로 컴파일러나 링커에 의해 사용된다. 즉, 초기

dreamlog.tistory.com

3) 내부 정적 변수
특정 함수나 클래스 내부에 선언된 지역 변수는 내부 정적 변수라고도 불리며, 외부 정적 변수와 마찬가지로 별도의 초기화 구문이 없어도 0으로 초기화 된다.
또한 내부 정적 변수의 경우에도 프로세스의 메모리가 할당되는 프로그램의 시작 시점에 이뤄지기 때문에
함수 실행 등으로 인한 선언문에서의 실행으로는 초기화가 이뤄지지 않고 무시된다.
초기화 시점이 프로그램의 시작이라서 함수 실행 시 초기화 구문에서 초기화가 안 된다고 했는데,
이렇게 되어도 문제가 없는 이유는 static 변수는 함수 혹은 클래스에 대해서 내부 정적 변수로 이용되는 경우
각 함수 별 혹은 클래스 별로 공유되는 일종의 공유 변수로 이용되기 때문이다.

#include <stdio.h>

void	increase_num(void)
{
	static int	num = 4;

	printf("%d\\n", num);
	++num;
}

int		main(void)
{
	increase_num();
	increase_num();
	increase_num();
	return (0);
}

위의 경우 static int num이라는 내부 정적 변수의 초기화는 프로그램의 시작에 이뤄지며 초기 값은 4가 된다.
이 때 static int num은 increase_num이라는 함수의 지역 변수처럼 보여 Stack에 위치할 것 같지만,
실제로는 (이 경우에는 초기화 구문이 존재하므로 BSS 영역이 아닌) Data 영역에 위치하고 있다.
위에서 언급했던 초기화 구문이 동작하지 않는다는 얘기는
increase_num 함수 내의 초기화 구문인 static int num = 4가 매 함수 실행마다 이뤄지지 않는다는 말이다.
또한 내부 정적 변수는 특정 함수 혹은 클래스 간 공유되어 사용된다고 했기 때문에 위 main 함수의 실행 결과는 4, 5, 6가 된다.


첫번째 평가를 받았는데 read 함수에서 지역변수 버퍼의 사이즈를 정적으로 할당해서 int 최대값에 가까운 값으로 컴파일을 했을때 오버플로우가 발생하는 문제를 발견했다.. 생각치도 못했던 부분이었는데 평가자분께서 프로세스 메모리 구조에 대한 내용부터 설명을 해주셔서 오류가 나는 원인에 대해 정확하게 파악할 수 있었다. 재평가 준비 해야겠다 ...

+ Recent posts