전체 글

EV6 5000km 주행 후기

2022. 2. 16. 17:09

내·외관 같은 경우는 다른 후기들에서도 많을텐데 내가 하고 싶었던 얘기는 잘 안 했길래 올려본다.

전기차를 산 이유

제동(완전정지) 질감

완전정지하는 순간의 울컥거림을 싫어한다. 그래서 내연차를 탈 때도 완전히 정지하기 직전에 브레이크를 살짝 풀어주는 습관이 있었다.

근데 전기모터는 작동 원리상 브레이크를 밟으면 모터가 작동을 안 하다가 브레이크를 살짝 풀면 가속을 해버린다. 토크도 세서 오히려 더 울컥거린다. 그래서 전기차·하브차는 구매 목록에서 제외했었다. 테슬라가 원페달 드라이빙을 업데이트하기 전까진...
테슬라가 원페달 드라이빙을 구현해내자 현기차에도 i-Pedal 모드가 생겼다. 그건 딱 내가 원하던 제동 질감이었다. 오히려 발 컨트롤을 하지 않아도 딱 원하던 질감이 나와서 더 편했다.

위와 같은 이유로 내연차도 오토홀드를 쓰지 않았다. 전기차는 같은 이유로 구매 목록에서 제외했던건데, 오히려 원 페달 드라이빙 이후로 내연차보다 좋아졌다. 오토홀드를 꺼도 정지 상태에서 홀드도 되고 제동 질감도 좋아졌으니...

집밥

아파트 충전기가 5개가 있는데, 하나는 최근 사용이 4달 전이었다. 공용 집밥은 집밥이 아니라지만 이 정도면 집밥 아닌가?

왜 하필 EV6 였냐?

시트 포지션이 일반적인 SUV와 달랐다. 오히려 세단에 가까운 포지션이었다.
발은 좀 앞으로 뻗는 느낌이 좋고, 시트는 낮은게 좋다. 배터리 때문에 시트를 낮추진 못하지만 차 바닥 기준으론 세단 느낌이 난다 ㅎㅎ.
시야도 최대한 세단 느낌 나게 하려고 잘 맞춘 것 같다.

코나, 니로는 i-Pedal이 안돼서 생각도 안 했다.

아이오닉5는 내 스타일이 아니었다. 디자인도 취향이 아니었을 뿐더러, 같은 플랫폼 기반인데도 시트포지션도 묘하게 SUV에 가까웠다.
가장 치명적인 건 변속기였다. 핸들 뒤에 변속 레버가 있는데, 컬럼식은 아니다... 오히려 다이얼식에 가까운 변속기이다. 그리고 RND 순서가 아니라 DNR 순서인데, UX 측면에선 맞는 말이지만, 기존 사용자 입장에선 완전히 반대인 것이다.
렌트카로 타면서 주차장에서 차 빼다가 전진해야 하는데 후진 할 뻔했다. 아니 후진을 해버렸다. 스토퍼도 없는 수평 주차장에서 뒤에 있는 차 쪽으로 더 주행 하니 동승자들 다 기겁하고 난리였다ㄷㄷㄷ. 적응하면 된다는데 적응 안 해도 되는 차 놔두고 아이오닉5를 선택 할 이유는 없었다.

당시엔 전기차 세단이 테슬라 모델 3밖에 없었다(물론 지금도...), 살까 말까 고민하는 사이에 모델 3 롱레인지가 판매 중단되고 모델 3 스탠다드가 6천만원대로 올랐다.
뭔가 손해보는 느낌(?!)이라 제외..

트림 선택

모델

모델은 EV6 4WD 19인치에 딥 포레스트 그린 색상이다.
에어트림에 드라이브 와이즈 + 메리디안 + 컨비니언스 옵션을 추가했다.

취소차로 11월 초부터 딜러분과 얘기해서 11월 말에 받았다. 한 달 채 안돼서 받았다.
원래는 무채색으로만 생각하고 있었는데 포레스트 그린이 전산에 올라와서 보니깐 이뻤다.
무엇보다 K8부터 있는 색상이라길래 와 이것도 고급차라는건가? 하는 속물적인 생각이 들었다 ㅋㅋㅋㅋㅋㅋㅋㅋ

어스 트림이 취소차가 제일 많이 나온다고 딜러분도 어스 뽑으라고 했었는데,
열선버튼이 터치식으로 바뀌는 것 때문에 300만원 태워서 다운그레이드 하는건 좀 아닌 것 같아서 에어트림 고집했다.
근데 나파가죽 시트를 위해서라도 어스를 뽑을걸 하는 후회가 인다... ㅋ

옵션

드라이브 와이즈는 필수 옵션이다. 안전옵션이라고 생각해서 어떤 일이 있어도 넣어야 하는 옵션이라고 생각한다.

메리디안 오디오는 요즘 기아 세단(K5, K8)들은 앞쪽 스피커를 A필러가 아닌 대시보드쪽에 놓는데, 그런 배치는 소리가 별로 좋지 않았다.
스포티지는 A 필러에 스피커 유닛이 있어서 K5 오디오 깡통 옵션보다 스포티지 깡통 오디오가 훨씬 좋았다.
세단이나 EV6 는 오디오 옵션을 넣어야 A필러에 스피커가 들어간다. A 필러와 대시보드까지 스피커유닛이 있으니 더 좋은 소리가 났다.

컨비니언스는 메리디안 오디오 옵션을 넣으려면 필수였다. 덕분에 하이패스도 잘 쓰고 있다.

그런데 옵션을 좀 보면 세단이 안 팔리는 이유를 알 것 같다.
풍절음을 줄여줄 수 있는 전면 차음유리, 옆에서 오는 햇빛을 차단해주는 슬라이딩 선바이저같은 옵션들은 중형 세단부터 넣어주면서 SUV는 준중형부터 들어간다. 애초에 세단은 많이 팔 생각도 없는 것 같다...

요즘 아른거리는 옵션들
  • 2열 열선. 급하게 취소차 뽑느라고 선택권이 별로 없었다.
    그래도 1열만 쓸거라 그냥 뽑았다. 2열에 손님 태울 땐 V2L로 전기매트를 가져오든지, 쓸 일 많아지면 12V 매트도 좀 찾아봐야겠다.
  • 가죽 시트 : 사실 인조가죽도 나쁘진 않다. 근데 이왕이면.... 차를 오래 쓰려고 하는데 내구성도 안좋다는 후기도 많다.
  • 오토 파워윈도우 : 에어 트림에는 오토 파워윈도우가 운전석에만 있다. 근데 창문을 열 땐 조수석까지 여는데 그럴 때마다 아쉬웠다.

연비

총 5000km를 주행했는데, 트립은 40km쯤 주행 후에 초기화를 한 것 같다.
트립 구간 누적 주행거리는 4961km 연비 5.5km/kWh, 주행시간은 173시간이다. (평속 28km/h)
차 뽑은 날부터 오늘까지 계속 겨울이었던거 고려하면 여름 지나고 나면 평균연비 6.0km/kWh나 그 후반까지 노려볼 수 있을 것 같다.

출퇴근용으로 사용했고, 한 번 강릉 갔다 왔다.
서울 대폭설 온 날에 강릉은 맑다는 예보를 보고 갑자기 친구랑 연락해서 3시간만에 숙소 예약 잡고 강릉 다녀왔다...
전기차는 공인 고속연비가 엄청 떨어지던데 실제로도 고속도로 다니니 연비가 많이 떨어졌다... 여행 중 연비가 4.8km/kWh인가 나왔던 것 같다.

완충하면 계기판에 주행 가능 거리 550km 정도 나온다.

회생제동

나는 회생제동 레벨 3 아니면 i-Pedal로 다닌다.
회생제동 레벨은 높게 쓰는 편이다. 시내주행만 하는 것도 있지만 회생제동 레벨을 낮추면 회생제동을 가속페달로 조절하는게 어려워서 차라리 높게 두고 페달로 제동량을 조절한다.
그래서 집에서 내부순환로 입구까지는 i-Pedal, 내부순환로 진입 후부터는 회생제동 레벨 3으로 다닌다. i-Pedal에선 상시사륜이기 때문에 연비에 손해를 보는 것 같다.(실측 근거는 없음) 답력 차이도 있어서 고속에선 회생제동 레벨 3으로 다닌다.
내연차 탈 때부터 발 조절하는게 싫어서 오르막길에서 킥다운보다는 기어시프트를 이용하곤 했기 때문에 회생제동 레벨을 바꾸면 악셀 답력이 바뀌는건 정말 편한 것 같다.

울컥거림...

아마 전기차 회생제동에서 가장 큰 이슈는 울컥거림이 아닐까 싶다. 그리고 이 얘기를 하고 싶어서 위에 장황한 글들을 썼다..
난는 차를 사자마자 에코모드로 사용해서 별 문제를 못 느꼈는데, 얼마 전부터는 악셀 답력 때문에 노멀 모드로 다니니까 울컥거림이 느껴졌다.
브레이크 울컥거림도 싫어하는 사람이 에코모드에서는 회생제동 울컥거림을 못 느끼는 정도이니 다른 사람들도 울컥거림을 덜 느낄 것 같다.

현기차 쓰시는 분들 울컥거림이 느껴지신다면 에코모드 사용해보길...
그리고 제동으로 바뀌는 시점에서 좀만 더 발컨을 연마 한다면 울컥거리는 건 없을거라 생각한다.
예전에 아이오닉5 렌트카로도 친구 4명이랑 강원도 여행 갔었고, EV6 사서도 친구 3명과 강원도 여행 갔었는데 울컥거리거나 멀미한다는 사람은 없었다.

실수로 지운 파티션 복구하기

2021. 3. 22. 12:10

파티션을 나누거나, 포맷을 할 때면 실수로 파티션 테이블을 지우거나 바꿀 때도 있다.
파티션 삭제를 파일 삭제와 비슷한 사고라고 생각하고 파일 복구 방법을 찾는 경우가 많다.
하지만 실상은 파티션 복구만 해낸다면 모든 파일을 제자리로 돌릴 수 있다. 심지어 파티션 복구는 파일 복구보다 쉽고, 빠르고, 복원 가능성도 높다.

파티션이란?

우선 파티션의 구조 먼저 이해해야 한다. 일반적으로 MBR(Master Boot Record)에 있는 파티션 테이블에 파티션 정보를 기록한다.

파일 하나를 찾을 때, MBR의 파티션 테이블에서 파티션의 위치를 찾고 그 위치에 가서 파티션 구간 내부의 구조를 확인한다(흔히 포맷이라고 하는 NTFS, APFS, EXT4등이 그 구조에 대한 정보를 담고 있다).
이 구조에 있는, 파일 구조가 명세 된 파일에서 파일의 위치를 찾아 파일을 불러오는 것이다.

요즘은 MBR 대신 GPT를 쓰는데, 그 곳에 파티션 테이블이 포함되어 있다.
결론은, 파티션을 아무리 지우고 수정해도 파일이 저장되는 부분과는 독립적인 공간이고, 파일이 저장되는 부분은 건드리지 않는다는 것이다.

파티션과 파티션 포맷

보통 파티션을 변경하면 파티션 포맷도 같이 하기 때문에 파티션과 파티션 포맷을 연결해서 생각하기 쉽다.
하지만 파티션과 파티션 포맷은 엄연히 다른 것이다.

포맷은 틀을 잡는 것이다. 파티션 된 공간 안에서 데이터를 기록할 수 있도록 틀을 구성한다. 파일 구조가 명세 된 파일을 지우고 다시 구축한다. 포맷에 따라 파일 명세가 달라지기 때문에 필수적이다.

파티션은 저장 장치에 가장의 벽을 설치하기 위해 파티션 테이블을 수정한다. 파티션 포맷이나 파일 구조를 수정하지 않는다. 파티션 테이블에 포맷 정보가 담겨있기 때문에 파티션을 수정하면 파티션의 포맷이나 파일 구조를 읽어 올 수 없다.
파티션 테이블을 변경하면 포맷 정보를 읽어 올 수 없게 되므로 일반적으로는 파티션을 변경하면 포맷도 한번에 수행한다.

현실로 비유하자면, 파티션은 가벽 파티션을 생각하면 되고, 포맷은 책상을 생각하면 된다.

  • 파티션을 만들더라도 책상이 없으면 물건을 놓을 수 없다.
  • 공간이 좁아지도록 파티션을 옮기면 책상이 잘려서 책상을 사용할 수 없다.
  • 공간이 넓어지도록 파티션을 옮기더라도, 책상을 새로 들이지 않으면 물건을 올려 둘 수 있는 공간은 그대로다.

이런 구조이기 때문에, 파티션 테이블을 완전히 삭제한 후 재구축 한 상태라도 파티션 테이블을 복구할 수 있다면 모든 파일이 그대로 남아있게 된다!

앞에 언급한 것처럼 대부분의 소프트웨어들은 파티션을 변경하면 포맷도 같이 진행한다. 그래서 파티션을 관리하는 특수한 소프트웨어를 사용해야 한다.
그 특수한 소프트웨어를 이용해 파티션을 복구해보자.

MiniTool Partition Wizard 9.0

대표적으로 MiniTool Partition Wizard가 있다.
현재 13.5 버전까지 출시되었지만, 파티션 복구 기능을 무료로 사용할 수 있는 버전은 MiniTool Partition Wizard 9.0이다.

복구 방법 설명을 위해 방금 만든 가상 HDD에 파일 하나만 저장한 후 다시 파티션을 삭제했다.

설치 파일 다운로드

파일 크기 제한으로 분할압축 한 파일들을 다운로드 받아 설치한다.
또는 여기(kollhong.com)에서 한번에 다운로드한다.

pwfree9.zip
0.87MB

 

pwfree9.z01
9.77MB

 

pwfree9.z02
9.77MB

 

pwfree9.z03
9.77MB

비밀번호 kollhong.com
공식 사이트가 아닌 곳에서 다운로드 할 때는 반드시 checksum 확인, 서명 확인!!

실행

Partition Recovery Wizard 실행

파티션 관리 도구에 파티션 복구 기능도 제공되는 것이어서 파티션 복구 위자드를 실행해야 한다.

왼쪽 Wizards 메뉴에서 Partition Recovery Wizard 실행

다음 클릭

 

복구 할 디스크 선택

 

스캔 범위 선택. 실수로 파티션을 삭제한 후 다시 파티션을 만든 게 아니라면 Unallocated Space로 충분하다.

 

스캔 방식 선택. 마찬가지로 실수로 파티션을 지운 후 다시 파티션을 만들지 않았다면 Quick Scan을 선택하면 된다.

 

그러면 위와 같이 검색 된 파티션이 보인다. 남겨 둘 파티션을 선택하는 것이므로 현재 HDD에 남아있는 파티션도 선택해야 한다.

 

왼쪽 상단의 Apply 버튼을 누른다.

 

진행 후 위와 같은 창이 뜨면 성공

드라이브 레터 지정

파티션을 복구하더라도 Windows 탐색기에는 복구 한 파티션이 보이지 않을 수 있다.
파티션은 존재하지만 파티션에 드라이브 레터가 존재하지 않아서 마운트 되지 않은 것이다.

'하드 디스크 파티션 만들기 및 포맷' 실행

 

복구 한 파티션 선택, 우클릭, 드라이브 문자 및 경로 변경

 

추가 클릭

 

원하는 드라이브 문자 할당. 보통 C부터 순서대로 할당되므로 특별한 경우가 아니면 바로 '확인'을 클릭하여 저장하면 된다.

 

복구를 성공적으로 완료하면 기존의 파일까지 전부 보인다.

번외 - 파일 복구 프로그램

같은 업체에서 제공하는 파일 복구 프로그램이다.
파티션 복구와 달리 파티션이 있던 구간 전체를 스캔해야 하므로 시간이 오래 걸린다.

다행히 지워진 파일을 찾았다.
하지만 파일 이름을 인식하지 못한다. 뿐만 아니라 확장자 또한 html이 아닌 htm으로 되어 있다. 파일 내용에 따라 형식을 추론하기 때문이다. 파일 구조 가 명세 된 파일은 포맷에 관련 되어 있기 때문에 포맷을 복구하는 프로그램이 아니라면 인식하기 어렵다.

포스팅을 위해 방금 새로 만든 HDD임에도 불구하고 알 수 없는 파일들이 굉장히 많다.

VM을 Docker로 구현

2020. 11. 23. 16:47

Docker는 가상화가 아니다!

처음에는 Docker에 Ubuntu나 CentOS를 설치해 사용했다.
사용하려는 목적이 Synology NAS에서 apt 패키지 매니저를 사용하기 위한 목적이었고, 쉘 사용이 쉬워서 OS 컨테이너를 사용했다.
그러니까... 가상화의 목적으로 사용했다는 것이다.

그러나 systemctl이나 네트워크 구성이 어려운 점, Docker의 목적과 달리 백업, 배포가 어려운 점 때문에 Docker에 OS를 돌리는 건 그만 두기로 했다.
그래서 아래와 같은 방법으로는 docker를 사용하지 않을 건데, 혹시 나중에라도 필요하게 될까봐 기록으로 남겨본다.

아래 방법을 따라하면 CentOS 이미지를 사용하여 Docker를 구성하고, systemctl을 사용할 수 있고, 고유의 IP를 부여할 수 있다.

VM을 Docker로 구현하려 함

VM

  • 모든 컴퓨터 하드웨어를 가상화하고 OS를 실행 함. 약 30% 성능 저하가 있음.
  • 최근에는 Hypervisor를 사용하여 CPU가상화를 하지 않아 성능 저하가 낮은 편. 하지만 기타 IO장치들은 가상화를 하기 때문에 IOPS가 낮음. (최대 50% 저하)
  • OS 설치해야 함. 메모리 및 디스크 관리를 호스트 OS와 가상 OS에서 동시에 수행하며, 가상 OS에 할당한 메모리는 호스트에서 사용할 수 없음.

Docker

Docker의 원래 목적은 OS 자체를 가상화하는 것이 아니고 프로그램별로 containerize(컨테이너화) 하는 것이다.

  • 운영체제를 설치하지 않고, 서비스에 필요한 패키지만 설치하고 설정파일을 import 하여 컨테이너로 만드는 방식이다.
  • OS가 없기 때문에 메모리, 디스크 관리를 호스트 OS에서만 관여하므로 오버헤드가 적다.
  • 단점은 OS가 없기 때문에 ssh같은 원격 접속 도구나 yum 같은 패키지 매니저가 없다는 것이다.
  • 같은 환경의 컨테이너를 몇 줄의 텍스트로 만들 수 있으며, 따라서 배포가 쉽다. 이런 방식 덕분에 낯선 방식이지만 서버 유지/보수는 더 쉽다.
  • docker의 목적상 호스트 컴퓨터의 환경을 따르기 때문에 기존의 가상머신처럼 개별적 IP를 부여하기 어렵다.
Docker로 환경을 구축하는 두 가지 방법
  1. OS 자체를 컨테이너에 구현하는 방법이다. 개별적인 IP를 부여하고자 할 때는 그 과정이 복잡하다. 지금까지는 이 방법으로 블로그를 운영했다.
    그리고 이 방법으로 컨테이너를 만드는 방법을 오늘 소개하려고 한다.
  2. Docker의 원래 목적대로 각 애플리케이션 별로 컨테이너를 만든다. 별도의 IP는 부여하지 않으며, 새로 구성할 때는 컨테이너를 새로 만들어야 한다.
    이 방식이 원래 Docker의 용도대로 이용하는 방법이다.
    2번 방법은 구축/유지 보수가 쉬운 장점이 있지만 낯선 방식 때문에 Docker에 익숙한 관리자가 필요하다.

Docker 설치

Docker repo 추가 및 설치

yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install docker-ce docker-ce-cli containerd.io

systemctl enable docker

docker 볼륨 위치 이동, docker 네트워크에 dns 정보 강제 지정

nano /etc/docker/daemon.json
{
    "data-root": "/mnt/RAID-Storage/docker/",
    "dns": ["192.168.0.1"]
}

Docker에서 디스크 크기 제한을 가능하도록 변경

RAID-Storage에 quota 마운트 옵션 지정

nano /etc/fstab

UUID=...... /mnt/RAID-Storage xfs defaults,prjquota 0 1

원래 defaults인 부분을 defaults,prjquota로 변경한다.
이러면 xfs자체에서 quota 기능을 구현하고, docker는 xfs의 기능을 사용한다.

이후 컨테이너를 만들 때 --storage-opt size=120G 옵션 사용.

고유 IP 할당

고유 IP를 할당하기 위해 Linux 커널에서 기본으로 제공하는 가상 네트워크 어댑터를 활성화한다.

서버 부팅시에 macvlan을 자동으로 구성하는 스크립트.
cd /etc/sysconfig/network-scripts
wget https://raw.githubusercontent.com/larsks/initscripts-macvlan/master/ifdown-macvlan
wget https://raw.githubusercontent.com/larsks/initscripts-macvlan/master/ifup-macvlan
chmod +x ifup-macvlan ifdown-macvlan
macvlan 구성
nano /etc/sysconfig/network-scripts/ifcfg-macvlan

DEVICE="macvlan"
TYPE=macvlan
DEVICETYPE=macvlan
MODE=bridge
MACVLAN_MODE=bridge
PHYSDEV=br0
MACVLAN_PARENT=br0
BOOTPROTO=static
IPADDR=192.168.0.170
NETMASK=255.255.255.0
GATEWAY=192.168.0.1
NM_CONTROLLED=no
ONBOOT=yes
Docker에서 macvlan 네트워크 생성
docker network create -d macvlan \
    --subnet=192.168.0.170/24 \
    --gateway=192.168.0.1 \
    -o parent=br0 \
    -o macvlan_mode=bridge \
    macvlan

이 후 컨테이너를 생성할 때 --network macvlan 옵션, --ip 192.168.0.XXX 옵션을 사용하고
각 컨테이너마다 docker exec -it "컨테이너 이름" "echo 'nameserver 192.168.0.1' >> /etc/resolv.conf" 를 수행해야 한다.

systemctl (서비스 관리 도구) 사용

docker는 기본적으로 systemctl을 사용할 수 없다. OS를 가상화하는 목적이 아니기 때문이다.
systemctl을 사용하기 위해서는 systemctl에 필요한 파일을 만들어야 한다.

빈 디렉토리에 Dockerfile 만들고 이미지 빌드
mkdir docker && cd docker
nano Dockerfile
FROM centos:7
ENV container docker
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in ; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/;\
rm -f /etc/systemd/system/.wants/;\
rm -f /lib/systemd/system/local-fs.target.wants/; \
rm -f /lib/systemd/system/sockets.target.wants/udev; \
rm -f /lib/systemd/system/sockets.target.wants/initctl; \
rm -f /lib/systemd/system/basic.target.wants/;\
rm -f /lib/systemd/system/anaconda.target.wants/*;

# 언어셋 설치
RUN yum clean all \
&& yum repolist \
&& yum -y update \
&& sed -i "s/en_US/all/" /etc/yum.conf \
&& yum -y reinstall glibc-common

# epel-release 활성화
RUN yum install epel-release -y

# 기본적으로 필요한 OS 패키지를 설치한다.
RUN yum -y install tar unzip vi vim telnet net-tools iproute curl openssl \
apr apr-util apr-devel apr-util-devel nano firewalld git \
elinks locate python-setuptools

# ssh 서버 설치
RUN yum -y install openssh-server \
&& echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config \
&& echo 'PasswordAuthentication no' >> /etc/ssh/sshd_config \
&& mkdir /root/.ssh && echo 'ssh-rsa "키" kollhong@localhost.localdomain' >> /root/.ssh/authorized_keys\
&& yum clean all

RUN systemctl enable sshd

# 타임존 변경
ENV LANG=ko_KR.utf8 TZ=Asia/Seoul

# 컨테이너 실행시 실행될 명령
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]

Dockerfile에 기반하여 컨테이너 이미지 생성

docker build --rm -t local/centos7 .

컨테이너 만들기

이제 docker run 명령어로 컨테이너를 만들어서 가상머신처럼 사용할 수 있다.

컨테이너를 만들 때 local/centos7 이미지로 container를 만들면 systemctl을 사용할 수 있다.

docker run --memory "2g" --storage-opt size=120G -v /sys/fs/cgroup:/sys/fs/cgroup:ro -v "디스크 디렉토리":"컨테이너 마운트 위치" --privileged --network macvlan --restart always --ip "컨테이너 IP" --dns 192.168.0.1 -t -d --name "컨테이너 이름" local/centos7

-v 옵션은 호스트 컴퓨터의 볼륨을 컨테이너에 마운트한다.

systemctl을 사용하기 위해서는 아래 옵션이 필수적이다.
-v /sys/fs/cgroup:/sys/fs/cgroup:ro : 이 옵션은 systemctl을 사용하는데 필요한 컨트롤 그룹 정보를 컨테이너에서 확인할 수 있게 한다.
-t, -d : 이 옵션은 유사 TTY 환경을 구현하고, detatch 모드로 컨테이너를 실행한다.

예시

docker run --memory "2g" -v /sys/fs/cgroup:/sys/fs/cgroup:ro -v /mnt/RAID-Storage/docker_volumes/bio-processor/:/opt --privileged --network macvlan --restart always --ip 192.168.0.171 --dns 192.168.0.1 -t -d --name "bio-processor" local/centos7

컨테이너(게스트 컴퓨터) 접속

docker exec -it "컨테이너 이름" "명령"
명령은 보통 /bin/bash이다.

컨테이너 백업

현재 컨테이너를 image로 저장

docker commit "컨테이너 이름" "이미지 이름"

image를 tar로 내보내기

docker save -o "파일 이름.tar" "이미지 이름"

tar를 이미지로 불러오기

docker load -i "파일 이름.tar"

이미지 확인

docker image ls 명령을 사용해서 불러온 이미지가 있는지 확인

docker run... 명령을 사용할 때 이미지를 선택하여 컨테이너 생성할 수 있다.

CSS local font에 맑은 고딕만 인식되는 이유

2020. 9. 23. 16:41

이번에는 글 제목이 이상합니다.

원래는 서론, 본론, 결론부로 정확히 구분하여 포스팅을 작성했었죠.
그러다보니 글 작성 주기도 길어지고, 파트 구분이 어려운 글감은 버려지기도 했습니다.

앞으로 개발 이야기를 자주 쓰려고 하는데, 개발 이야기는 파트 구분이 어려운 주제인 듯합니다.
그래서 적어도 개발 이야기는 상당히 이상한 제목, 짧은 본문으로 자주 올려보려고 합니다.

문제의 인지

원래 블로그에 폰트를 Malgun Gothic, Apple SD Gothic Neo 두가지를 사용했습니다.
보통 모바일에도 둘 중 하나는 설치되어 있기 때문에 이렇게 설정했는데, 아이폰에서는 작동하지 않았습니다.
분명 Apple SD Gothic Neo가 폰트 이름이 맞는데 작동하지 않았습니다. 맑은 고딕 폰트는 잘 적용이 됐는데 말이죠.

구글링

대부분은 일관적인 사이트 경험을 위해 로컬 폰트보다 서버에 woff폰트를 저장하고 사용했습니다.
저는 HDD 서버를 사용했고, 반응형 페이지이기 때문에 폰트에 따라 사이트 경험이 크게 달라지지 않았죠. 그래서 시스템 폰트를 사용할 예정이었습니다.

그래서 폰트 이름이 인식되지 않는 문제로 아무리 구글링을 해봐도 서버에 업로드 된 woff 폰트의 경로와 이름을 잘 쓰면 인식 된다는 답변 뿐이었습니다.
게다가 맑은 고딕 폰트는 인식되고, 산돌 고딕 폰트는 인식되지 않으니 검색하기는 더 힘들었습니다. 산돌 고딕은 한글 폰트인데, 구글링을 할 때는 영어 위주로 검색해왔기도 하구요.

폰트의 Full Name을 사용해라

그러다가 로컬 폰트가 인식되지 않는 이유를 찾았습니다.
저는 CSS에서 폰트 패밀리를 인식해서 폰트의 weight가 클 경우 Bold 폰트를 불러오는 줄 알고 font-face를

src: local('Apple SD Gothic Neo'), local('Malgun Gothic');

로 적었습니다. 하지만 그게 아니었습니다.

PostScript name 또는 Full name을 사용해야 합니다.
맑은 고딕의 경우는 Regular Font의 Full name이 Malgun Gothic이기 때문에 CSS에서 인식할 수 있었지만,
애플 산돌 고딕의 경우는 Apple SD Gothic Neo Regular가 Full name이었습니다.
그래서 아이폰에서는 CSS에 정의 된 폰트를 인식할 수 없었던 것입니다.


위가 맑은 고딕 폰트입니다. Full name이 Malgun Gothic이죠.


애플 산돌 고딕은 다릅니다. Font family만 Apple SD Gothic Neo이고 Full name이나 PostScript name은 "Regular" 까지 써야 합니다.

아직 남은 의문

Full name은 언어에 따라 다릅니다. 한글 OS에서는 맑은 고딕, Apple SD 산돌고딕 Neo이고, 영문 OS에서는 Malgun Gothic, Apple SD Gothic Neo입니다.

지금까지는 영문으로만 적어도 적용 되는걸로 보이는데, 추가 확인이 필요합니다.

키보드, 한글 쿼티가 뭐야?

2020. 6. 14. 18:01

어디서 이런 단어가 나온걸까?

한글 쿼티라는 단어를 처음 접한 것은 삼성 스마트폰이 나온 이후이다.
갤럭시S의 한글 키보드 설정에 '쿼티'라는 단어를 넣은 것이다.

마침내는 커뮤니티 사이트에서도 '한글 쿼티'를 사용하기에 이르렀다.

의미 전달에는 문제가 없다. 하지만 실제로는 정말 말도 안 되는 표현이다.
그 이유를 얘기해보려 한다.

키보드 결정 요소

키보드를 경정하는 요소는 두 가지가 있다.
키보드 종류(Keyboard Type), 입력 방법(Input Method)

입력 방법 대신에 키보드 배열(Layout)이라고도 한다.
하지만 키보드 종류와 섞어 쓰는 경향이 있기 때문에 정확한 구분을 위해 입력 방법이라고 표현한다.

키보드 종류와 입력 방법을 선택할 수 있다.


윈도우 설치를 해봤다면 이런 화면 몇 번쯤 봤을 것이다.
윈도우 설치 과정에서 키보드의 종류와 키보드 입력 방법을 선택할 수 있다.

키보드 종류

키보드 종류는 쉽게 말하면 키보드가 보내는 신호의 종류를 말한다.
초기에는 키보드에 컴퓨터 제조사마다 신호가 달랐고, 윈도우는 범용 호환성이 타겟이었기 때문에 여러가지 키보드에 대한 호환성을 갖고 있다.

fn키 조합으로 101키를 입력할 수 있는 키보드

일반적으로 101키 키보드를 사용한다. 한/영 버튼이나 한자 버튼이 있다면 한글 103/106키를 사용하게 된다.
윈도우의 경우 101키 키보드로 한글을 입력할 경우 오른쪽 Alt를 한/영 버튼으로, 오른쪽 Ctrl을 한자 버튼으로 사용한다.

키보드의 종류에 따라 키보드의 모양이 바뀌고, 컴퓨터에 전송되는 신호가 달라진다. 또한 키보드 종류를 바꾸면 입력에 문제가 생기거나 한/영 전환이 불가능 할 수 있다.
하지만 키보드와 키보드 종류를 알맞게 선택한다면 어떤 언어도 문제 없이 입력할 수 있다.

키보드 입력 방법

키보드에 인쇄된 문자를 말한다.
키캡에 인쇄된 물리적인 차이만 존재할 뿐, 키보드 내부에는 어떤 차이도 존재하지 않는다.
키보드에 한글이 인쇄되어 있든 영문이 인쇄되어 있든 상관 없이 컴퓨터에 같은 신호를 보낸다.

한글 두벌식 표준의 'ㅂ'이나 영문 QWERTY의 'Q'나 같은 신호가 전송된다.
단지 차이는 컴퓨터에서(또는 운영체제에서) 같은 입력 신호에 대해 어떻게 해석하느냐의 차이인 것이다.
입력 방법이 영문 쿼티일 경우 한글 입력 방법으로 바꾸지 않는 한, 한글 입력은 불가능하다.

영문 : QWERTY

가장 큰 특징은 숫자키 아랫줄이 QWERTY로 시작한다는 것이다.
타자기에서 사용하던 배열으로, 입력 속도가 빠르면 타자기가 꼬이는 문제 때문에 최대한 비효율적으로 설계했다는 이야기가 있다.

QWERTY

영문(라틴어) : QWERTZ, AZERTY

QWERTZ는 독일, 오스트리아, 스위스 등에서 사용하는 키보드이다.
AZERTY는 프랑스, 벨기에 등에서 사양한다. 두 입력 방법은 발음 구별 기호를 입력할 수 있다.

숫자키 아래의 첫 줄이 각각 QWERTZ, AZERTY로 시작하는 특징이 있으며, 키보드 종류는 QWERTY와 같다.

한글 : 두벌식 표준

한글 키보드 중에서 자음과 모음으로 구분 된 두벌식 키보드이며, 그 중에서 표준 키보드 배열이다.

두벌식 표준

숫자키 아래의 첫 줄이 ㅂㅈㄷㄱㅅㅛ로 시작하는 특징이 있으며, 키보드 종류는 QWERTY와 같아도 된다.

한글 : 세벌식 3-91(세벌식 391 또는 세벌식 최종)

세벌식은 초성 중성 종성 세 부분으로 구분된 키보드를 말한다.

세벌식 최종


위 키보드는 그 중에서도 세벌식 3-91 키보드 배열이다.
세벌식은 대체로 숫자키로도 한글 입력을 하게끔 되어 있다.

기존 두벌식 키보드와 크게 다르지만, 키보드 종류가 컴퓨터와 호환된다면 어떤 키보드라도 세벌식 입력 방법을 사용할 수 있다.

한글 쿼티

위에서 한글 입력 방법의 종류, 쿼티의 의미를 알아봤다.
또한 키보드의 모양을 의미하는 키보드 종류도 알아봤다.

그렇다면 한글 쿼티라는 단어에 굉장히 큰 모순이 생기는 것을 알 수 있다.

1. 한글 입력 방법을 지정하지 않았다는 데에 문제가 있다.

위에서 본 것처럼 한글 키보드에는 종류가 많다.
두벌식은 대체로 표준만 사용하지만 세벌식까지 확장하면 5종류가 넘는다.

그렇다면 한글 쿼티는 어떤 입력 방법을 말하는 것일까?
좀 우겨보면 이런 것도 한글 쿼티라고 말할 수 있을 것이다.

천지인 쿼티?

키보드 첫 줄이 QWERTY로 시작하고, 한글 입력이 가능한 키보드이다.
그러면 문제 없는 것 아닌가?

2. 영문 키보드를 QWERTY로 고정한 데에 문제가 있다.

나는 키보드 입력을 많이 하기 때문에 DVORAK도 써봤다.
그러나 한글 입력에는 전혀 지장이 생기지 않았다. 왜냐하면 한/영 전환을 누르면 '두벌식 표준' 입력 방법을 사용할 수 있기 때문이다. DVORAK을 사용하든 Colemak을 사용하든 한글 입력 상태에서 왼쪽 Shift 키의 오른쪽에 있는 버튼을 누르면 여전히 'ㅋ'이 입력된다. 하지만 키보드 첫 줄이 QWERTY로 시작하지 않는다.

그렇다면 나는 한글 DVORAK 입력 방식이었을까? 아니면 한글 QWERTY 입력 방식이었을까?

0. 원래 '한글 쿼티'의 의도는 키보드 종류를 말하고 싶었던 것이다.

QWERTY와 두벌식 표준의 공통점은 키보드의 모양이다. 그것 뿐이다. 키보드의 종류가 같다는 것이다.
컴퓨터에서는 이 공통점을 말할 때 '101키 키보드'라고 한다. 하지만 스마트폰은 대부분의 키가 없으므로 101키라고 말할 수 없다. 실제로는 40개 내외의 키밖에 없다.
101키라고 말할 수 없으니 한글 QWERTY라는 세상 신박한 표현을 사용하게 된 것이다.

신토불이 vs 완전 현지화

블리자드에서 오버워치의 사운드를 담당했다는 분이 했던 말이 있다. 오버워치가 서든어택을 제치고, 잠시나마 롤까지 제칠 수 있었던 이유 중 하나가 완전 현지화라는 것이다. 이 부분은 믿거나 말거나 이지만 그 근거가 상당히 인상적이어서 소개한다.

오버워치는 캐릭터의 보이스를 대부분 한국인이 녹음했고, 그래서 한국인의 정서에 맞는 분위기를 연출할 수 있었다는 것이다.
오버워치를 안 해본 사람이라도 "이 것도 너프해 보시지!", "석양이 진다" 등의 대사는 다들 한 번쯤 들어봤을 것이다. 또한 한국어가 아니어도 한국인이 더빙했다. 류승룡 기모찌로 알려진 "류진노 켄오 쿠라에!"는 한국 블리자드에서 더빙한 음성이다.
외국 게임이지만 최대한 한국인에게 맞게 새로 만들었다는 것이다.

반면에 서든어택은 한국 게임이지만 외국 게임처럼 보이려고 노력했다는걸 지적했다.

단지 해당 담당자의 말일 뿐이다. 나는 그 근거가 인상적이었다고 말 하고 싶었을 뿐이고, 오버워치가 성공하고 서든어택이 망한 이유가 '사운드 때문이다' 라고 말하고 싶은 건 아니다.

신토불이 스마트폰?

한국인이 한국폰을 선호하는건 나쁜게 아니다. 게다가 한국 브랜드가 뒤쳐지는 브랜드도 아니니 말이다.
하지만 문제를 제기하고 고칠 필요는 있을 것 같다.

한국

삼성

구글에 "한글 쿼티"라고 검색해보자. 그러면 삼성 갤럭시 얘기 뿐이다.

항상 갤럭시라는 단어가 함께 등장한다.

타자기 역사는 1829년부터 시작됐고, 한글 타자기는 1913년부터 시작됐다.
전자식 타자기가 나올 때부터 한글과 영문을 동시에 입력할 수 있게 됐다.
두벌식 표준 입력 방식은 1985년 제정됐다.

입력기의 역사는 100년이 넘었고, QWERTY나 두벌식 표준 입력 방식이 바뀌지 않은 것은 35년이 넘었다.
그럼에도 불구하고 구글 검색에서는 삼성 스마트폰 얘기 뿐이다. '한글 쿼티'라는 표현은 근래에 사용하기 시작한 것이다.

 

LG

LG도 마찬가지로 '쿼티 키패드'라고 표현한다.

 

외국

왠만한 외국 기업들은 '두벌식 표준'이라고 표현하고 있다.

구글

구글 입력기에서는 두벌식 표준이라고 표현한다.

애플

애플은 표준, 10키로 구분한다.

보통은 '두벌식 표준'이라고 하는데, '표준'이라고 써져 있다.
조금 애매하긴 하다. 그러나 두벌식은 자음과 모음으로 구분된 것을 의미하고, 그 중에서도 표준 입력방식이 우리가 사용하는 자판인 것을 생각해보면 적절한 표현으로 볼 수 있다.

마치며...

한국 서드파티 키보드들 중에서도 '한글 쿼티' 라고 말하는 경우는 거의 드물다.
내가 스마트폰을 사용하면서 '한글 쿼티'를 본건 LG 기본 입력기와 삼성 기본 입력기 뿐이라고 해도 과언이 아니다.

그렇다면 두 업체는 어째서 '한글 쿼티'라고 말하기 시작한거고, 왜 여태까지 수정하지 않는걸까?

Optimus EX용 커널(SmartassV2+Lazy)

2020. 3. 16. 19:56

변경점

  • 백원만님 커널 기반
  • 기본 Governer : Lazy (하지만 init.rc에서 가버너를 바꾸는 듯함)
  • 기타 여러가지

적용법


adb, terminal에서 su dd if=(img 파일 입력) of=/dev/block/mmcblk0p7

KT젤리빈용입니다

boot.img
4.8 MB

'안드로이드 > 옵티머스 EX' 카테고리의 다른 글

Optimus EX 롬 백업  (0) 2016.12.28

블로그에 다크모드가 적용되었습니다!

2019. 9. 25. 00:25

추후 강좌도 작성하겠습니다.

사파리의 경우 아래와 같은 개발자 모드 화면에서 다크모드를 강제로 켜거나 끌 수 있습니다.

 

 

크롬에서도 개발자모드를 통해 미적용 상태와 비교가 가능할 것으로 보입니다.

 

 

마크다운 사용 이전의 게시물은 가독성이 현저히 떨어지는 부분이 있습니다.

최대한 옛날 글에서도 가독성을 잃지 않도록 바꿔나갈 예정입니다.

'끄적끄적' 카테고리의 다른 글

EV6 5000km 주행 후기  (2) 2022.02.16
Tistory에 Flash Player 없이 사진 업로드하기  (0) 2018.12.16
티스토리 초대장 나눠드립니다~  (38) 2016.12.28
래안텍 32C144 사용기  (0) 2016.10.07

[macOS 한글 호환성 2편] 한글 깨짐 수정법

2019. 8. 29. 15:42

 

맥을 사용하면서 자주 접하게 될 한글 인코딩 호환성 문제

인코딩이 윈도와 다르다. 맥에서 만든 파일은 윈도에서 자음과 모음이 분리되어 보인다. 많은 사람들이 이를 자소 분리현상이라고 말한다. 또한, 일부 사이트는 한글이 깨지거나, 다운로드한 파일의 이름이 깨지는 경우가 많다.`

이번 글에서 다룰 것은 아니지만, 이 현상은 iOS에서도 나타나는 현상이다. 하지만 지금까지 iOS 사용자들은 이런 불편함을 느끼지 못했다. 파일 공유가 어려워서, 다운로드 받을 파일을 수정하는 경우는 많지만, 파일을 만드는 경우는 거의 없었다. 또한, 주요 클라우드 서비스는 파일을 업로드하면 자소 분리를 해결해주기 때문에 한글 인코딩 문제가 크게 다가오지 않았다. 그런데, 앞으로는 iOS 사용자들도 자주 겪게 될 것이다. 9월 중 정식 공개될 iOS 13에서는 File 앱을 통해 파일 관리를 쉽게 할 수 있다. 문제는 맥과 같은 인코딩을 사용하기 때문에 베타 사용자들 입장에서는 iOS 13에서 한글 처리에 문제가 생긴 것으로 느끼기도 할 수 있다는 것이다.

어쨌든, 이번 글을 통해 두 가지를 다룰 것이다.

  1. 업로드 하는 파일의 자소 분리 문제와 해결법
  2. 다운로드하는 파일의 텍스트 깨짐과 해결법

원인

이 현상의 원인은 인코딩이 달라서가 아니다. 윈도우에서 CP949라는 구식 인코딩 방식을 사용하기 때문이다. CP949는 EUC-KR의 확장 버전이라고 볼 수 있다. 이 인코딩 방식은 MS에서 초기 윈도우를 개발할 때, 한글 지원을 위해 만든 인코딩 방식이다. 따라서 현재는 개량된 인코딩이 많이 나와 있으며, 많은 운영체제에서 사용하지 않는 인코딩 방식이다(지원하지 않는다는 의미는 아니다). 요즘은 영어를 제외한 대부분의 문자를 Unicode로 인코딩한다. 영어도 특수한 문자를 표시하기 위해 유니코드로 인코딩하는 경우가 많다.

그럼에도 불구하고 윈도우는 여전히 기본 인코딩 방식이 CP949이다. 그래서 기본 텍스트 편집기로 작성한 파일이나 Visual Studio에서 작성한 코드들이 다른 운영체제에서 보면 깨져서 보인다. 특이하게도 한글 윈도우만 기본 인코딩이 CP949이다. 한글 지원을 위한 인코딩이니... 당연한 결과다. 영문 윈도우는 기본 인코딩이 CP949가 아니다. 영문 윈도우에서 작성한 한글 텍스트 문서들은 다른 운영체제에서도 잘 보인다는 얘기다.
영문 윈도우가 한글을 지원하지 않는 것도 아닌데, 한글 윈도우는 왜 기본 인코딩이 CP949인지 의문이다.

그래서, 일부 웹페이지들이 CP949로 인코딩되어 있다. 이런 사이트는 일부 운영체제에서 글자가 깨져서 보인다. 앞서 언급했듯이, 한글 윈도우만 기본 인코딩이 CP949이기 때문에 다른 언어의 윈도우나 다른 운영체제에서 작성한 웹페이지는 한글이 깨지지 않는다. 가끔은 웹페이지는 잘 보이지만, 파일을 다운로드하면 파일 이름이 깨져있기도 하는데, 이 또한 파일을 전송하는데 인코딩을 제대로 바꾸지 않아서 그렇다.

증상

글자가 깨진다는 것은 아래와 같은 증상을 말한다.

텍스트파일의 내용 중 한글이 보이지 않는다.
간혹 다운로드한 파일의 이름이 위와 같이 깨지기도 한다.

이 증상은 1편의 자소 분리 현상에 비할 수 없이 불편하다. 자소 분리 현상은 파일을 공유하면 다른 사용자가 겪게 되는 문제이면서도 인코딩을 바꾸지 않더라도 읽고 이해할 수는 있다.

하지만 위의 한글 깨짐 현상은 맥의 사용자가 직접 겪을 문제이고, 인코딩을 바꾸지 않는 한, 사용자는 읽고 이해하기 어렵다.

해결법

저 텍스트를 유니코드 인코딩으로 변환하면 된다.

이번에는 iconv명령을 사용한다. iconv -f cp949 -t UTF-8 "$f"

iconv 설치

iconv는 MacOS에 내장되어 있기 때문에 따로 설치하지 않아도 된다.
하지만 MacOS에 내장된 버전은 2009년에 공개된 버전이고, 충돌 문제가 있는 것 같아 brew에서 설치하는 것을 권한다.

터미널을 열고 brew install libiconv를 입력한다.

iconv는 MacOS에 내장되어 있기 때문에 brew에서 환경변수 PATH를 설정해주지 않는다.
대신 설치가 끝나면 brew에서 설치한 iconv를 환경변수 PATH를 설정하는 방법을 알려준다.

그래서 brew가 알려주는 과정을 따라하지 않으면 터미널에서 iconv를 실행해도 MacOS에 내장된 버전이 실행된다.
위 과정을 따라하지 않더라도 추후 brew에서 설치한 버전을 사용하려면 절대경로를 사용해서 실행해주면 된다.

2022.12.14
Intel 기반 맥의 경우 /usr/local/opt/libiconv/bin/iconv를 사용하면 된다.
Apple Silicon 기반 맥의 경우 /opt/homebrew/opt/libiconv/bin/iconv를 사용하면 된다.

2021.11.17
iconv 설치 과정을 추가하였습니다.
Apple Silicon 기반 맥에서 변환 과정에 문제가 발생하는 것 같아 brew에서 다운로드 한 바이너리를 사용하도록 변경하였습니다.

Automator로 만들기

기존의 터미널은 파일을 직접 입력해야 한다. 하지만 오토메이터를 사용하면 Finder에서 파일을 선택하여 바로 실행할 수 있고, 또한 여러 파일을 자동화할 수도 있다.

실행 가능한 Workflow 파일 다운로드

깨진 한글 컨텐츠 수정_Apple_Silicon.workflow.zip
0.17MB
깨진 한글 파일명 수정_Apple_Silicon.workflow.zip
0.01MB

 

깨진 한글 컨텐츠 수정_Intel.workflow.zip
0.09MB
깨진 한글 파일명 수정_Intel.workflow.zip
0.01MB

2021.05.31
iconv가 인코딩을 인식하지 못하는 경우 파일 내용이 사라지는 문제 발생
iconv 변환에 문제가 없는지 확인 후 변환을 시도하도록 변경
변환 결과 알림 추가

2021.11.17
Apple Silicon 기반 맥에서 텍스트가 사라지는 문제가 발생하고 있습니다. 변환 전에 반드시 백업하시길 바랍니다.
이 문제는 Intel 기반 맥에서는 재현되지 않고 있기 때문에 제가 수정할 수 있는 부분이 아닙니다.
혹시 몰라 최신 버전의 iconv를 설치하는 과정도 추가하였으니 참고하시기 바랍니다.

Finder에서 사용할 수 있게 등록하기

  • 시스템 메뉴(애플 로고)
  • 시스템 환경설정(System Preferences)
  • Extentions
  • Finder
  • 방금 만든 Quick Action 선택

Quick Action에 등록된 서비스를 실행하면 아래처럼 파일 내용이 정상이 된다.

2021.05.31 파일 내용이 사라지는 문제 수정
2021.11.17 Apple Silicon 기반 맥에서 발생할 수 있는 문제 공지
2022.12.14 Apple Silicon에서 brew 바이너리들이 설치되는 위치 확인 및 수정

[macOS 한글 호환성 1편] 한글 자소분리 해결법

2019. 8. 28. 20:06

맥을 사용하면서 자주 접하게 될 한글 인코딩 호환성 문제

macOS에서 사용하는 한글 인코딩은 Windows와 다르다. 맥에서 만든 파일은 윈도우에서 자음과 모음이 분리되어 보인다. 많은 사람들이 이를 자소 분리현상이라고 말한다. 또한, 일부 사이트는 한글이 깨지거나, 다운로드한 파일의 이름이 깨지는 경우가 많다. 이 또한 인코딩이 달라서 생기는 문제이다.

이번 글에서 다룰 것은 아니지만, 이 현상은 iOS에서도 나타나는 현상이다. 하지만 지금까지 iOS 사용자들은 이런 불편함을 느끼지 못했다. 파일 공유가 어려워서, 다운로드한 파일을 수정하는 경우는 많지만, iOS 장치에서 파일을 생성하여 공유하는 경우는 거의 없었다. 또한, 주요 클라우드 서비스는 파일을 업로드하면 자소 분리를 해결해주기 때문에 한글 인코딩 문제가 크게 다가오지 않았다. 그런데, 앞으로는 iOS 사용자들도 자주 겪게 될 것이다. 9월 중 정식 공개될 iOS 13에서는 Files 앱을 통해 파일 관리를 쉽게 할 수 있다. 문제는 맥과 같은 인코딩을 사용하기 때문에 베타 사용자들 입장에서는 iOS 13에서 한글 처리에 문제가 생긴 것으로 느끼기도 할 수 있다는 것이다.

어쨌든, 이번 글을 통해 두 가지를 다룰 것이다.

  1. 업로드하는 파일의 자소 분리 문제와 해결법
  2. 다운로드하는 파일의 텍스트 깨짐과 해결법

원인

맥과 윈도우는 인코딩 방식이 다르다.

윈도우는 NFC(Normalization Form Canonical Composition) 방식을, 맥은 NFD(Normalization Form Canonical Decomposition) 방식을 사용한다. 한국어로는 보통 완성형, 조합형이라고 말한다.

조합형은 '콜'을 저장할 때, 'ㅋ' + 'ㅗ' + 'ㄹ'로 저장한다

문자 코드
11
12
13

이라고 하면 실제 저장 내용은 111213으로 저장된다
'ㅋ', 'ㅗ', 'ㄹ'을 입력하는 동안 코드는 11, 1112, 111213으로 바뀐다.

윈도우는 '콜'이라는 문자에 다른 코드를 할당한다.

문자 코드
11
12
13
2032
222355

이라고 하면 실제 저장 내용은 222355가 된다.

'ㅋ', 'ㅗ', 'ㄹ'을 입력하는 동안 코드는 11,2032,222355로 바뀐다.

위는 예시일 뿐이며, 실제로는 완성형 중에도 여러 가지, 조합형 중에도 여러 가지 인코딩 방법이 존재한다.

증상

두 가지 모두 표준 정규화 인코딩 방식이다. 맥은 윈도우에서 만든 파일을 제대로 처리할 수 있으니, 맥은 두 가지 표준을 모두 지원하는 셈이다. 다만 한국에서 지정한 KS 표준에서는 완성형 인코딩 방식을 한글 인코딩의 표준으로 정의하고 있다. 따라서 맥이 국내 표준을 준수하지 않은 것으로 봐야 한다. KS 위키 문서

2022.02.24
인코딩 출력에 관하여 책임 소재(?) 수정
인코딩 관련하여 "민중택배"님이 중요한 의견 주셨다.
오리지널을 중요하게 생각하는 사람으로서 댓글에 있던 부분을 그대로 남겨두는 방식으로 정보를 전달하고자 했다.
그러나 질답글이 많아지고 댓글을 읽기 어려워지면서 이 글이 윈도우가 표준을 미지원하는 문제의 근거로 사용되는 것을 확인했다.
따라서 해당 부분을 본문에 반영한다.

출처 : clien.net

해결법

하지만 점유율 부분에서 이런 문제를 신경 써야 하는 쪽은 맥 사용자다. 다행히도 맥에서는 완성형으로 인코딩 된 파일을 표시할 수 있다. 그래서 파일 명을 완성형으로 인코딩하면 된다.

convmv 설치

우선 convmv를 설치해야 한다. convmv는 CONVerts filenames from one encoding to another and MoVe라는 뜻으로, 인코딩을 바꾸는 툴이다. mv는 파일을 이동하는 명령인데, 이름을 바꾸는 데에도 사용한다. 그러므로 인코딩을 바꾸는 툴이라고 봐도 된다.

터미널을 열고 brew install convmv 를 입력한다. brew가 설치되어 있지 않다면 brew를 먼저 설치해야 한다. brew는 리눅스용으로 컴파일된 프로그램을 실행할 수 있게 해주는 환경을 만들어준다.

convmv 사용법

convmv -f utf8 -t utf8 --nfc --notest <filename>

텍스트 인코딩을 utf8에서 utf8로 바꾸는데, nfc 정규화 방식을 사용한다는 의미이다.

자세한 내용은 man convmv를 참고하면 된다.

2021.03.17
일부 시스템에서 convmv가 NFD로 정규화 된 파일을 인식하지 못하는 문제가 있어 수정 하였습니다.
원인을 정확히 할 수는 없지만, Argument로 파일 이름을 전달할 경우 convmv 프로그램이 인식하지 못하는 것 같습니다.
따라서 디렉토리를 인수로 사용하고, 해당 디렉토리에서 모든 파일을 검색하여 convmv를 실행하도록 변경하였습니다.

2021.05.21
3월 17일에 업데이트 한 내용 원상 복귀하였습니다.
지난 1여년 간 같은 문제가 계속 반복되어 문제를 우회할 수 있도록 코드를 수정하였으나,
최신 환경에서 문제가 해결된 것으로 보여 원상 복귀하였습니다.

추가로 프로그램 실행 결과가 알림으로 나타나도록 수정하였습니다.

Automator로 만들기

기존의 터미널은 파일을 직접 입력해야 한다. 하지만 오토메이터를 사용하면 Finder에서 파일을 선택하여 바로 실행할 수 있고, 또한 여러 파일을 자동화할 수도 있다.

실행 가능한 Workflow 파일 만들기

  • 오토메이터 실행
  • 새로 만들기
  • Quick Action 선택
  • 왼쪽 사이드바에서 Run Shell Script를 선택하고, 오른쪽으로 끌어온다.
  • 왼쪽 사이드바에서 Set Value of Variable을 선택하고, 오른쪽으로 끌어온다.
  • 왼쪽 사이드바에서 Display Notification을 선택하고, 오른쪽으로 끌어온다.
  • 오른쪽 패널을 이미지와 같이 바꾸고 입력한다.

for i in "$@"; do
    convmv -f utf-8 -t utf-8 --nfc --notest "$i"
done
  • 파일을 ~/Library/Services에 저장한다.

만약에 convmv를 찾을 수 없다는 메시지가 뜰 경우,
Intel 기반 맥에서는 convmv/usr/local/bin/convmv로 변경한다.
Apple Silicon 기반 맥에서는 convmv/opt/homebrew/bin/convmv로 변경한다.

Finder에서 사용할 수 있게 등록하기

  • 시스템 메뉴(애플 로고)
  • 시스템 환경설정(System Preferences)
  • Extentions
  • Finder
  • 방금 만든 Quick Action 선택

Finder에서 사용하기

이젠 아래 이미지처럼 사용하면 된다.

2021.03.17 일부 시스템에서 convmv가 작동하지 않던 문제 수정
2021.05.21 convmv 소스코드 원상 복귀 및 알림 추가
2021.11.17 Apple Silicon 기반 맥에서 convmv를 찾을 수 없는 문제 수정
2022.02.24 인코딩 출력에 관하여 책임 소재(?) 수정

Windows 10 설치 USB 쉽게 만들기

2019. 7. 12. 14:41

UEFI 바이오스에서 윈도 설치 디스크 만들기가 굉장히 쉬워졌다. 그러나 여전히 rufus와 같은 유틸리티를 사용하여 복잡하게 만드는 것이 일반적이다.

이 글에서는 다른 프로그램 없이 바로 부팅 가능한 윈도 설치 USB를 만들어보기로 한다. 방법이 굉장히 간단하기 때문에 본문도 굉장히 짧다.

그래서 서론으로 UEFI는 어떤 특징이 있는지 알아보고, 윈도 설치 ISO를 다운로드하는 방법을 알아본다.

그리고 본문으로 UEFI의 특징을 살려 윈도우 설치 디스크를 만드는 방법을 알아보고자 한다.

UEFI

UEFI의 가장 큰 특징은 기존의 BIOS 시스템에 비해 더 큰 시스템이라는 것이다.

옛날의 BIOS는 기본적인 설정과, POST(Power On Self Test)기능만 제공한다.

하지만 UEFI는 좀 더 많은 기능을 제공한다. 기본 설정과 POST 뿐 아니라, 커서 기능, GUI기능, 빠른 부팅, CLI를 제공한다. 빠른 부팅은 POST 결과를 저장해서 다음에는 POST 단계를 줄이는 기능이다. CLI는 BIOS 단계에서 명령을 실행할 수 있는 환경을 제공한다는 의미이다.

여기서 중요한 게 CLI이다. CLI로 명령을 실행하려면 BIOS가 외부 저장장치에 엑세스 할 수 있어야 한다.
UEFI가 외부 저장장치에 엑세스 할 수 있다는 또 다른 증거가 있다. UEFI는 설정 화면의 스크린샷을 연결된 USB 메모리에 저장할 수 있다는 것이다..

그리고 이런 기능의 지원을 위해 UEFI 바이오스는 FAT32 포맷의 외부 저장장치에 접근할 수 있다.

UEFI 설정 스크린샷

출처 : forum.asrock.com

UEFI는 스크린샷 단축키를 클릭하면 연결되어 있는 FAT32 장치에 스크린샷을 저장한다.

그 외에 여러가지 포맷을 읽을 수 있는 것으로 알고 있지만, 모든 OS에서 사용할 수 있는 포맷은 FAT32와 exFAT 뿐이기 때문에 이 튜토리얼은 FAT32로 진행한다.

FAT32로 윈도우 설치 USB 만들기

우리는 UEFI가 FAT32를 읽고 쓸 수 있고, 윈도우가 FAT32를 읽고 쓸 수 있으며, 윈도우가 ISO파일을 열 수 있다는 것을 알고 있다.

그러면 윈도우 설치 디스크를 만들기는 굉장히 쉽다.
ISO파일을 열고 그 파일을 FAT32 장치에 쓰기 작업을 하면 된다.

ISO 파일 다운로드 하기

MS의 공식 홈페이지에서 다운로드 할 수 있다. 다만, 윈도우에서 접속할 때와 다른 기기에서 접속할 때의 방법이 조금 다르다.

윈도우에서 접속하면 ISO 링크를 주지 않고, 윈도우에서 실행하는 다운로더를 주기 때문이다. 만약 다운로더를 사용하고 싶지 않다면 UA를 변경하면 된다.

프로그램 이용

다운로드 링크 : https://www.microsoft.com/ko-kr/software-download/windows10

다운로드 안내 페이지

위와 같은 사이트가 나타난다. 우리는 컴퓨터를 업데이트 할 것이 아니라, ISO를 만들 것이기 때문에 '지금 도구 다운로드'를 클릭한다. 'MediaCreationTool'이 다운로드 된다.

약관을 반드시 읽어보아야 한다.

프로그램을 실행하고 프로그램 사용권을 자세히 읽은 후 동의한다면 동의 버튼을 눌러 진행한다.

공식 문서이든, 블로그 문서이든, 수많은 튜토리얼을 보면 대부분 게시물들이 위와 같은 약관이나 사용 조건에 대한 동의 화면이 나오면 무조건 동의 버튼을 누르라고 하고 있다.
물론 대부분의 약관은 사용자를 방해하거나 목을 조르려는 용도로 만들지는 않지만, 반드시 읽어보도록 안내 해주었으면 하는 바람이 있다.
이 약관은, 프로그램 제조사와 사용자 사이의 법적으로 유효한 '계약'이며, 소비자의 보호, 제조사의 보호 또는 분쟁시 분쟁 중재를 위한 내용이다.

다운로드 과정

'다른 PC용 설치 미디어 만들기' 를 클릭하고 다음을 누른다.

다운로드 과정

어와 아키텍처를 선택하고 다음을 누른다. 에디션은 통합본으로 나오기 때문에 한 종류 뿐이다. 아키텍처는 Windows 8이나 그 이후 버전이 설치되어 출시된 컴퓨터라면 64비트를 선택하면 된다.

다운로드 과정

ISO 파일을 만들 것이기 때문에 'ISO 파일'을 선택하고 다음을 클릭한다.

USB 플래시 드라이브를 선택하더라도 FAT32로 포맷한다. 따라서 'USB 플래시 드라이브'를 선택하고 진행해도 결과는 같다.
단지 이 튜토리얼은 FAT32로 포맷하는 것이 얼마나 편리한지를 보여주기 위한 것이기 때문에 'ISO 파일'로 진행한다.

파일 위치 선택 대화상자가 나타나면 다운로드할 위치를 설정한다.

다운로드 중

다운로드 중

그러면 다운로드가 시작되고, 미디어를 만드는 작업을 한다.

다운로드 완료

ISO 만들기가 완료됐다. DVD 버너를 사용할 것이 아니기 때문에 '마침'을 눌러 종료한다.

ISO 파일 열기 단계로 넘어가면 된다.

ISO 다운로드 링크 이용

다운로드 링크 : https://www.microsoft.com/ko-kr/software-download/windows10ISO

원하는 윈도우 버전을 선택하고 다운로드 한다.

Windows가 아닌 OS에서 접속할 때 나타나는 ISO 다운로드 페이지

ISO 파일 USB 드라이브로 복사하기

ISO 파일 미리보기

다운로드한 Windows.iso 파일을 실행하면 위와 같은 파일들이 보인다. 이 파일들 모두 FAT32로 포맷된 USB 드라이브에 복사하면 된다.

미립자팁

마지막으로, FAT32의 장점을 활용하는 방법이다. 같은 파일 시스템 안에서는 파일을 이동하는데 걸리는 시간이 굉장히 짧다. 그래서 하나의 USB 안에 여러가지 에디션들을 넣어놓고 필요한 버전을 USB 메모리의 최상단으로 꺼내면 해당 에디션을 설치할 수 있게 된다.

설치 디스크로 부팅하기

윈도우에서 바로 진입

앞서 언급했다시피, UEFI는 빠른 부팅을 지원한다. 빠른 부팅을 할 경우 다른 불필요한 장치에 대해 POST를 진행하지 않는다. 따라서 윈도우 설치 USB 디스크를 삽입하더라도 USB는 확인하지 않고 바로 윈도우로 부팅될 수 있다.

이런 경우에 대비하여 윈도에서 직접 부팅 장치를 선택하거나, UEFI 설정에 진입할 수 있는 방법을 제공한다.

시작 -> 전원 -> (Shift 버튼을 누른 상태로) 다시 시작을 누른다

Use a device를 클릭해서 Windows 설치 디스크를 선택한다.

UEFI 설정에서 부팅 순서 변경

제조사마다 다르므로 제조사 문서를 참고해야 한다. 보통은 부팅 우선순위, Boot Priorities와 같은 표현을 사용한다.

2장-2 변수의 크기와 오버플로우, 언더플로우

2019. 5. 5. 22:01

크흠.... 기존 학교에서 배운 것들을 티스토리에 요약하고, 컴퓨터에서 삭제하려는 목적으로 '컴퓨터공학' 카테고리를 만들었습니다.

그런데 이렇게 3년이 흘러버리니 1학년 때 적어둔 것들이 어떤 순서로 필기된건지 모르겠더군요. 순서가 뒤죽박죽이어서 후에 배울 것들을 앞 장에서 응용하고 있다 보니 게시글의 순서가 뒤죽박죽이 되어 버렸습니다.

티스토리의 경우 게시된 날짜를 기준으로 정렬되는 듯하여 모든 정리가 끝나면 다시 한번 순서를 조절하도록 하겠습니다.

변수의 크기 알아보기

우선 이전 장([컴퓨터공학/C] : 2장-1 자료형)에서 각 자료형이 몇 Bytes의 크기인지 배웠습니다. 실제로 프로그래밍을 할 때 변수들의 크기가 중요합니다. 뒤에 배우게 될 구조체도 크기를 아는 것이 중요합니다.

간단한 정수형이나 실수형 변수의 크기는 외울 수 있다고 하더라도 프로그래머가 직접 구현하는 구조체의 크기까지 외울 수 있을까요? 아마 불가능할겁니다.

그래서 sizeof();연산자가 있습니다.

이 연산자는 함수가 아닙니다. 연산자입니다. 왜냐하면 프로그램이 실행될 때 sizeof();를 실행해서 크기를 계산하는 게 아니고, 프로그램을 컴파일 할 때 컴파일러가 size를 계산해서 상수로 바꿉니다. 그러니까 크기를 외워서 크기를 입력하는 것과 sizeof(); 연산자를 사용하는 것은 성능상에 차이가 없다는 의미입니다.
상수가 뭔지도 추후에 배울 예정입니다. 상수는 코드를 읽기 쉬우면서 프로그램은 가볍게 만들기에 유리합니다.

#include <stdio.h>
void main(){
    char x;
    float y;

    printf("변수 x의 크기  %d\n", sizeof(x));        //<- printf("변수 x의 크기 %d\n", 1); 과 성능 차이 전혀 없음
    printf("변수 y의 크기 %d\n", sizeof(y));
    printf("double의 크기%d\n", sizeof(double));
}

이런 식으로 활용하게 됩니다. sizeof();는 연산자가 아니기 때문에 파라미터의 형식도 상관 없고, 그게 심지어는 자료형이라도 작동합니다.

오버플로우(OverFlow)와 언더플로우(UnderFlow)

변수의 크기를 알 필요가 있는 가장 큰 이유입니다. 제대로 말하자면 조금 다릅니다. 정확한 표현으로는 오버플로우와 sizeof()를 자주 사용해야 하는 이유가 같습니다.

Overflow란, 직역 의미 그대로 넘쳐 흐른다는 의미입니다. 변수의 크기가 너무 커서 변수의 공간 안에 저장할 수 없는 경우이죠. Underflow는 반대의 의미입니다. 너무 작아서 변수의 공간 안에 담을 수 없는 경우입니다.
2장-1에서 배우기로 char형은 1 Byte 크기를 가지고 있고 -128~127의 범위를 갖고 있습니다. 그리고 1 Byte는 8 bits로 구성되어 있습니다.
만약 127을 char형 변수에 저장한다면 실제로는 0111 1111로 저장됩니다. 그리고 여기에 1을 더하면 1000 0000이 되죠. 그 값은 - 128입니다. 이런 것을 Overflow라 합니다. 반대로 -128에서 1을 빼면 127이 되는 현상을 Underflow라고 합니다.

그런데 궁금한게 있습니다! 이전 장에서도 변수의 범위까지는 언급을 했지만 왜 1000 0000이 -127이 되는지는 말하지 않았습니다. 상식적으로 맨 앞이 부호를 표시한다면 1111 1111이 -128이 되어야 하지 않을까요?
컴퓨터는 구조상 뺄셈 연산을 하지 못합니다. 구조를 단순화 하기 위해, 뺄셈은 음수를 더하는 연산을 하죠. 그래서 음수의 표현보다는 음수의 연산에 초점을 맞춰서 개발한 표현방식입니다. 이런 음수의 표현을 '2의 보수'라고 합니다.

127과 -127를 더하면 그 값은 0이 됩니다. 사람이 생각하는 대로 표현했다면 0111 1111 + 1111 1111은 1 0111 1110이라는 이상한 숫자가 나옵니다. 8 Bits 공간을 가지고 있기 때문에 Overflow는 버리고 0111 1110이 됩니다.

하지만 2의 보수를 만들어주면 덧셈이 쉬워집니다. 2의 보수는 양수에서 각 자릿수마다 ~(NOT)연산을 하고 1을 더한다고 보면 됩니다. 그러니까 0111 1111의 2의 보수를 만들면 1000 0001이 -127이 됩니다. 두 수를 덧셈 연산하면 1 0000 0000이 되죠. 위에서와 같이 Overflow를 버리면 0이 됩니다!

변수의 최댓값과 최솟값

변수의 크기는 알았는데, 그럼에도 최댓값과 최솟값은 알아야 Overflow를 방지할 수 있는것 아닌가요? 그래서 몇몇 변수에 대해서 최댓값과 최솟값을 미리 정의해둔 헤더가 있습니다. limits.h인데요, 여기서 CHAR_MIN, CHAR_MAX, UINT_MIN, UINT_MAX 등의 표현으로 정의되어 있습니다. 바로 사용해볼까요?

#include<stdio.h>
#include<limits.h>

void main(){
    char x = CHAR_MAX;
    printf("MAX of CHAR : %d \n", x);
    char y= x+ 1;
    printf("Overflow : %d \n", y);
    printf("127+(-128)  = %d\n", x+y);                 //출력은 -1이 됩니다.
}

이런 식으로 사용할 수 있는겁니다. char의 최댓값을 저장하면 127이 됩니다. 그리고 Overflow된 값(-128)을 y에 저장합니다. 그리고 이 두 수를 더하면 -1이 됩니다.

요약

  • 변수의 크기를 알 수 있다.
  • 오버플로와 언더플로를 이해할 수 있다.
  • 2의 보수를 이해하고, 음수와의 연산에서 오버플로와 언더플로를 활용하는 것을 알 수 있다.
  • 변수의 최댓값과 최솟값이 정의된 헤더를 불러올 수 있다.

변수의 크기를 알아야 하는 이유?

마지막으로 한 가지 더!

오버플로를 설명하면서 변수의 크기를 알아야 하는 이유라고 언급했습니다. 하지만 아무리 생각해봐도 관련성이 없어보입니다. 나중에 배열, 포인터나 주소의 개념을 갖고 나면 이해가 될 부분입니다. C언어는 LowLevel언어이기 때문에 메모리 주소에 직접 접근할 수 있습니다. 그만큼 프로그램이 빠르지만 프로그래머가 메모리를 고려해야 하는 단점이 있습니다.

예를 들어서 char형의 변수가 5개로 구성된 배열 name이 있습니다. 이 변수는 1 Byte 변수가 5개가 있는 것이고, 총 5 Bytes로 구성됩니다. name의 주소가 0x0020이라고 하면, 첫번째 칸은 0x0020이고, 다섯번째 칸은 0x0025가 됩니다.
그런데 프로그래머가 6번째 배열에 접근하려고 하면 C언어는 0x0026에 접근합니다. name을 통해 접근할 수 있는 주소의 범위는 0x0020~0x0025까지인데, 이에 상관없이 0x0026에 접근하는 겁니다. 물론 이런 비정상적인 접근은 OS에서 차단하고 프로그램을 강제 종료합니다. 계속 실행되면 다른 프로그램의 정보를 탈취할 수 있기 때문에 아예 종료해버리는 겁니다.

출처 : reddit.com
그 결과가 이겁니다. 옛날에는 시도 때도 없이 보던 겁니다. 수 년이 지나면서도, Windows가 업데이트 되고 업그레이드 되면서도 꾸준히 보던 경고입니다. 지금 보니 메모리 접근을 고려하는게 얼마나 어려운지 아시겠죠?

물론 다른 고급 언어들도 비슷합니다. 객체 지향 언어들도 배열은 같거든요. 하지만 대부분은 객체를 사용합니다. 그리고 그 객체는 리스트(배열 개념을 객체로 표현한 것이다. C언어는 Linked List(연결리스트)로 구현할 수 있다.)와 리스트의 갯수까지 갖고 있기 때문에 프로그래머는 마지막 변수가 필요할 때, 리스트의 크기를 가져오면 됩니다.

리스트의 개념은 C언어에서도 구조체를 이용해 연결리스트로 표현이 가능합니다. 단지 구현 방법이 좀 다를 뿐이죠. 이는 후에 배열, 포인터 부분에서 학습하게 됩니다.

다음에는 진수의 표현에 대해서 올리도록 하겠습니다.

'컴퓨터공학 > C' 카테고리의 다른 글

4장-2 비트 연산과 연산 순서  (0) 2019.01.14
4장-1 산술 연산자  (0) 2019.01.14
3장 printf scanf 함수  (0) 2019.01.10
2장-1 자료형  (0) 2019.01.10
1장-1 Hello World  (0) 2018.12.16

Windows 10 코어파킹 (4) 부스트클럭편 노트북편

2019. 1. 19. 23:24

모바일을 위한 코어파킹

코어파킹에 대한 포스트 제목이 "더 조용하게, 더 오래!"였지만 실제로 모바일이나 배터리에 관한 언급은 거의 없었습니다.

오히려 고성능 위주의 HEDT에 적절한 설정값을 공유하거나 추천해드렸습니다.

이번에는 모바일에 대하여 말해보려고 합니다. 클럭이 높을수록 에너지 효율이 떨어지기 때문에 클럭이 낮게 유지될 수 있어야 합니다. 단기적으로는 더 조용하고 오래 사용할 수 있습니다. 발열량이 감소하기 때문에 높은 성능이 필요한 순간에 높은 성능을 낼 수 있고, 장기적으로는 더 높은 성능까지 낼 수 있어야 합니다. 하지만 장기적으로 빠른 처리를 하기 위해 단기적인 관점에서 성능 하락이 크면 안될겁니다.

기존에 코어파킹 강좌에서 언급했거나 설명했던 항목들은 전체적인 부분을 조절하는 항목들입니다.

CPU의 사용량에 따라 설정한 가장 낮은 클럭부터 설정한 가장 높은 클럭 사이를 조절하는 항목이거나, CPU의 사용량에 따라 활성화 된 코어를 설정한 최솟값과 최댓값의 사이에서 조절하는 항목입니다.

이번에는 단순한 클럭 조절을 위한 코어 파킹 설정이 아닙니다. 부스트 클럭을 조절하기 위한 항목에 대해 언급하려고 합니다.

부스트 클럭과 TDP

CPU 제조사들은 코어를 늘리면서 발열을 해소하기 위해 Base Clock을 낮추고, 동시에 특수한 상황에서는 오히려 성능이 낮아지는 단점을 해소하기 위해 Boost Clock이라는 개념을 적용했습니다.

그리고 부스트 클럭과 베이스 클럭의 운영을 조절하기 위해 TDP를 도입합니다.

모바일은 TDP로 발열을 제어합니다. 데스크톱도 발열제어를 하지만 큰 영향을 받지는 않습니다.

TDP에 의한 발열 제어는 CPU만 고려합니다. 그러다보니 CPU의 성능은 남고 GPU의 성능은 부족한데 TDP제한이 작동하지 않았던 경우도 있습니다. 그래서 라이젠 모바일 프로세서의 경우는 TDP를 더 낮춰서(클럭이 더 낮게 유지됨) 더 높은 게이밍 퍼포먼스를 달성하기도 하였습니다. 인텔이든 AMD든 상관 없이 클럭이 높기 때문에 부스트 클럭을 제한함으로써 배터리와 발열은 개선하면서도 체감 차이는 거의 없는 후기가 올라오기도 하였습니다.

데스크톱 프로세서라고 예외는 아닙니다. TDP에 의한 발열 제어는 데스크톱 프로세서와는 관련이 없지만, 부스트 클럭의 조절이 필요합니다. Intel Core i9-9900K의 터보 클럭 스펙은 다음과 같습니다.

출처 : wikichip.org

부스트 클럭으로 작동하는 코어가 많아질수록 최대 클럭은 낮아집니다.
만약 싱글 쓰레드 위주의 프로그램을 실행할 때 모든 코어가 부스트 클럭으로 유지되고 있다면 어떨까요? 해당 쓰레드는 실제로는 4.7Ghz처럼 처리되겠죠. 게다가 파일 탐색기나, 마우스 커서의 움직임처럼 간단한 작업을 처리하는 프로세스들도 불필요하게 4.7Ghz로 처리될겁니다.
하지만 부스트 클럭으로 작동하는 코어를ㄹ 잘 조절하면 높은 성능이 필요한 쓰레드는 5.0Ghz로 작동할 겁니다. 다른 코어에서 실행되는 프로세스들은 베이스 클럭인 3.6Ghz의 속도이지만 체감 차이 없이 처리될 수 있습니다.

코어 수가 많아지면서 베이스 클럭과 부스트 클럭의 차이가 커지는 양상을 보이고 있습니다. 또한 부스트 클럭으로 작동하는 코어의 수에 따라 부스트 클럭도 달라집니다. 클럭이 높을 수록 에너지 효율이 떨어지고 연산량 대비 발열량이 증가합니다. 그렇기 때문에 베이스 클럭은 낮추고, 부스트 클럭도 활성 코어 수에 따라 변하게 하는거죠.

베이스 클럭 자체도 낮은 성능은 아닙니다. 그렇기 때문에 베이스 클럭은 자주 사용되고 부스트 클럭은 덜 사용된다면 발열 제어에 여유가 생겨서 필요할 때에 높은 성능을 낼 수도 있고, 체감 성능 하락도 작아질 것입니다. 그렇기 때문에 부스트 클럭을 적절히 조절하면 높은 성능을 유지할 수 있습니다.

부스트 클럭 설정 조정

윈도우는 작업 관리자에 베이스클럭이 표시됩니다. 즉, 윈도우는 베이스 클럭과 부스트 클럭을 인식할 수 있다는 거죠.(물론 맥이나 리눅스도 마찬가지입니다)

그리고 윈도우는 부스트 클럭에 관한 조절 기능도 제공하고 있습니다. 하지만 기본값이 일반 베이스 클럭과 다를 바 없이 대하도록 되어 있습니다. 그렇기 때문에 웹 브라우저를 실행하고, 웹서핑하는 내내 높은 클럭이 유지됩니다.

반면 MacOS의 경우는 베이스클럭에 있는 시간이 부스트 클럭에 있는 시간보다 깁니다. 웹 브라우저를 실행할 때는 부스트 클럭으로 작동하지만, 웹서핑할 때는 베이스 클럭보다 높은 주파수로 올라가지 않습니다. 그러다가 링크를 클릭하여 다른 웹 페이지로 이동할 때는 부스트 클럭으로 작동합니다.

아래 설정을 통해서 윈도우에서도 부스트 클럭에 머무르는 시간을 줄일 수 있습니다.

Windows 10 코어파킹 더 조용하게, 더 오래!

  • 우선 Windows 10 코어파킹 더 조용하게, 더 오래!편을 참고하여 전원 옵션을 활성화 해주세요.
  • 그리고 제어판 -> 시스템 및 보안 -> 전원 설정에 들어갑니다.
  • 사용 중인 프로필의 오른쪽에 있는 "설정 변경" 버튼을 클릭합니다.

  • 그리고 "컴퓨터를 절전 모드로 설정" 아래에 있는 "고급 전원 관리 옵션 설정 변경"을 클릭합니다.

  • 나타나는 창에서 '프로세서 전원 관리' -> 프로세서 성능 향상 모드(영문OS를 사용 중이기 때문에 한글 표현은 다를 수 있습니다.)로 이동합니다.


그러면 위와 같은 옵션들을 확인할 수 있습니다.

꺼짐
활성
적극적
효율적으로 활성
효율적으로 적극적
보장될 때 적극적
보장될 때 효율적으로 적극적

등의 옵션이 있습니다. 번역이 이해하기 조금 힘들긴 하지만 잘 생각해보면 어려운 표현은 아닙니다.

추가로 Microsoft에서 이와 관련한 설명을 제공하고 있습니다.

Name P-state-based behavior CPPC behavior
0 (Disabled) Disabled Disabled
1 (Enabled) Enabled Efficient Enabled
2 (Aggressive) Enabled Aggressive
3 (Efficient Enabled) Efficient Efficient Enabled
4 (Efficient Aggressive) Efficient Aggressive

출처 : Microsoft

여기서 P-state-based behavior와 CPPC는 CPU 성능에 관한 메시지를 전달하는 방법입니다.

꺼짐 : 부스트 클럭은 사용하지 않습니다.
활성 : (P-state : 활성, CPPC : 효율적으로 활성)
적극적 : (P-state : 활성, CPPC : 적극적)
효율적으로 활성 : (P-state : 효율적, CPPC : 효율적으로 활성)
효율적으로 적극적 : (P-state : 효율적, CPPC : 적극적)
보장될 때 적극적 : (P-state : 효율적, CPPC : 효율적으로 활성)
보장될 때 효율적으로 적극적 : (P-state : 효율적, CPPC : 적극적)

Microsoft 문서에는 "at Guaranteed(보장될 때)"에 대한 설명은 없습니다. 하지만 이 설정을 사용하면 부스트 클럭을 사용하지 않는 것과 다름이 없을 정도라는 커뮤니티 후기가 있습니다.

부스트 클럭 유지 시간 : 적극적 > 활성 > 효율적으로 적극적 > 효율적으로 활성 > 보장될 때 적극적 > 보장될 때 효율적으로 적극적

서버용 Windows의 경우 기본값이 "효율적으로 활성"입니다.

MacOS와 같은 CPU 성능 조절을 원한다면 "효율적으로 활성"이 가장 적절할 것 같습니다.

4장-2 비트 연산과 연산 순서

2019. 1. 14. 19:40

비트 연산, 시프트 연산, 연산 순서

2장 자료형에서 비트에 대한 얘기를 잠깐 하고 지나갔었죠?

컴퓨터는 모든 데이터를 0과 1로 저장합니다. 그걸 bit라고 하죠. 2진수로 볼 수 있습니다.
비트를 이용해 표현할 수 있는 수는 아래와 같습니다.

1비트로는 0, 1.
2비트로는 00, 01, 10, 11.
3비트로는 000, 001, 010, 011, 100, 101, 110, 111.

int는 4bytes(32bits)로 표현되는 정수입니다.

그런데 이런 데이터를 산술 연산이 아닌 bit끼리 비교하는 방법이 있습니다. 그걸 비트 연산이라고 합니다.
뭐랄까, 메모리의 데이터를 그대로 비교한다는 느낌일까요?

그래서 비트 연산은 사칙 연산이 아닌 다른 연산을 할 때 주로 사용합니다. AND, OR, XOR, NOT, 그리고 시프트 연산이 있습니다.

AND, OR, XOR, NOT 연산

비트 연산에 적용하기 전에 이 연산자들이 어떤 연산을 하는지 먼저 알아보도록 하겠습니다.

보통 비트 연산에서는 0이 거짓, 1이 참이라고 생각하면 편하게 계산할 수 있습니다.

AND(&)연산은 두 비트가 모두 1일 경우에 결과값이 1이 됩니다. a & bab 모두 참인지 물어보는 연산이라고 생각하면 됩니다.

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

OR(|)연산은 두 비트 중 하나라도 1일 경우에 결과값이 1이 됩니다. a | bab 둘 중 하나라도 참인지 물어보는 연산입니다.

0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

XOR(^)연산은 두 비트가 같으면 결과값이 1이 됩니다.

0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0

NOT(~)연산은 좀 다릅니다. 두 데이터를 비교하는 게 아닌, 하나의 데이터를 반전하는 연산입니다.

0 = 0
1 = 1
~0 = 1
~1 = 0

컴퓨터에서 비트 연산

변수는 1비트로 이루어져 있지 않습니다. 참과 거짓만을 저장하는 boolean을 제외하고 가장 작은 자료형이 char입니다. 1byte 자료형이고, 비트로는 8bits입니다.

그렇다면 여러 자리의 bits는 어떻게 연산할까요? 이 글의 초반에 메모리의 데이터를 그대로 비교하는 느낌이라고 했던 것 기억하시나요? 비트 연산을 할 때, 컴퓨터는 메모리의 값이 어떤 의미를 가지고 있는지는 상관하지 않습니다. 단지 하나의 bit 끼리만 비교하는 거지요.

여기, 1 byte크기의 데이터가 두 개 있습니다.

각각 a=0010 0101(정수 37), b=1000 0110(정수 -122) 라고 해보지요. 1 byte는 8 bits데이터입니다. 8 bits 크기의 데이터를 하나의 bit 끼리 비교한다면 각 자리별로 총 8번 'bit 연산'을 수행하면 됩니다.

이제 두 데이터 a와 b를 AND, OR, XOR, NOT연산을 해보겠습니다.

AND 연산

a = 0010 0101(37)
&
b = 1000 0110(-122)

A = 0000 0100(4)

OR 연산

a = 0010 0101(37)
|
b = 1000 0110(-122)

A = 1010 0111(-89)

XOR 연산

a = 0010 0101(37)
^
b = 1000 0110(-122)

A = 1010 0011(-93)

NOT 연산

a = 0010 0101(37)
~a = 1101 1010(-38)

b = 1000 0110(-122)
~b = 0111 1001(121)

NOT연산은 정수값을 보니 어떤 의미인지 좀 보이지 않나요?ㅎㅎ

Shift(시프트 연산)

비트 연산은 메모리의 데이터가 의미하는 바에 상관 없이 비트 끼리만 연산하는 거라고 했습니다.

그렇다면 그 비트를 왼쪽이나 오른쪽으로 옮기는 연산도 있지 않을까요?

그것을 Shift 연산이라고 합니다.

#include<stdio.h>
void main(){
    int i = 64;
    i = i << 1;

    printf("%d", i);

    i = i >> 1;

    printf("%d", i);
    return ;  
}

와 같은 형식으로 쓰입니다.

그런데 이 연산의 결과가 재미있습니다.

값이 64인 i를 왼쪽으로 한 번 시프트하면 i는 원래의 두 배인 128이 됩니다. 두 번 시프트 하면 256이 되죠. 반대로 오른쪽으로 시프트하면 128, 그리고 64가 됩니다.

한 번 왼쪽으로 시프트하면 값이 두 배가 됩니다. 오른쪽으로 한번 시프트 연산을 하면 값이 반이 됩니다. 어떻게 이런 결과가 나올까요? 바로 이진법의 원리 때문입니다.

이진법으로 숫자를 써보면 아래와 같습니다.

1 = 0000 0001
2 = 0000 0010
4 = 0000 0100
8 = 0000 1000
32 = 0010 0000
128 = 1000 0000(unsigned char)
-128 = 1000 0000(signed char)

-128인 이유는 '2의 보수' 개념을 사용하기 때문입니다. 2장-2글을 참고하세요

앗, 그러면 두 배를 곱할 때는 i = i * 2; 보다는 i = i << 1;이 빠르지 않을까요? 논리적으로는 맞습니다. 그런데 굳이 그럴 필요는 없습니다. 우리는 컴퓨터에 직접 명령하는 게 아니라, 명령을 적으면 컴파일러가 적절히 최적화 하기 때문이죠.

마지막을 보면 << 연산을 하면 64가 -128이 됩니다. 변수의 크기, 부호의 여부에 따라 음수가 나옵니다. 예상하던 결과와는 다른 값이 나옵니다. 그런데 곱셈 연산도 마찬가지입니다. 컴파일러는 부호가 있는(signed) 변수인 것을 알고 있지만 똑같이 -128이 되죠.

-128을 << 연산을 하면 어떻게 될까요? 비트로 보자면 1 0000 0000입니다. 하지만 8 bits만 할당 된 변수에서는 가장 앞의 1은 버려지고 결과는 0000 0000(0)이 됩니다. 곱셈 연산을 하더라도, 컴파일러는 char 자료형이 8 bits 변수인 것을 알지만 같은 결과가 나타납니다. 똑같이 예상하던 결과와 달른 값이 나오죠.

결국 컴파일러는 같은 연산으로 처리한다는 것을 어림잡아 볼 수 있을 것 같습니다.

3장에서 나누기 연산에 사용되는 두 변수가 모두 정수형이면 정수형으로 결과가 나온다고 했던 것 혹시 기억하시나요?

오른쪽 시프트 연산의 결과는 정수형의 나눗셈 연산과 결과가 같습니다.

변수 0101 0101(85)가 있습니다.

Rsh 1 회 : 0010 1010(42)
Rsh 2 회 : 0001 0101(21)
Rsh 4 회 : 0000 0101(5)
Rsh 6 회 : 0000 0001(1)

오른쪽 시프트를 할 때, 가장 왼쪽의 bit가 다음 bit에 복제됩니다. 그래서 부호를 나타내는 bit는 유지됩니다. 그렇기 때문에 음수에서도 나눗셈의 결과를 볼 수 있습니다.
하지만 음수에 대해서는 시프트 연산과 나눗셈이 다릅니다. 비트연산은 소숫점 자리를 버린다고 했죠? -12.1보다 작은 가장 큰 자연수는 -13입니다. 일반적으로 생각하는 버림과는 조금 다르죠.

음수인 1010 1010(-86)으로 비교해볼까요?

횟수 Bit 연산 정수 연산
1 1010 1010(-86) -86
2 1101 0101(-43) -43
3 1110 1010(-22) -21
4 1111 0101(-11) -10
6 1111 1101(-3) -2
8 1111 1111(-1) 0

비트 연산에서는 소수점 자리를 버립니다. 음수가 더 작은 수가 맞기 때문에 단순한 시프트 연산에서는 맞는 결과이죠. 정수의 나눗셈 연산에서는 사람에게 자연스럽게 소숫점 자리를 버립니다.

1111 1111>> 연산을 아무리 반복해도 -1입니다. 왜냐하면 -1을 2로 나누면 -0.5인데, 이걸 내림연산 하면 -1이 되기 때문입니다.

정수의 나눗셈에서는 -1을 2로 나누면 -0.5이고, 소수점 자리를 버리면 -0 = 0 이 되는 것입니다.

왼쪽 시프트를 얘기하면서 곱셈 대신 시프트를 사용하면 더 빠르다고 말했습니다. 하지만 컴파일러가 최적화를 하기 때문에 시프트 연산을 사용할 필요는 없다고 했죠.
그런데 최적화의 문제만이 아니라 연산의 결과가 달라질 수도 있습니다. 그러니 오류를 무릅쓰고 시프트 연산을 사용할 필요는 없다는 결론을 내릴 수 있습니다.

연산자 순서

산술 연산과 비트 연산들을 소개하면서 연산 순서에 관한 얘기를 몇 번 했습니다.

일단 순서와 관련한 표를 볼까요?

순서 연산자 설명 Associativity (연산자 내 순서)
1 ++a --a Suffix/postfix 증감 연산자 Left-to-right
() Functional forms
[] 배열 지정 연산자
. 구조체와 공용체 접근자
-> 포인터를 통한 구조체와 공용체 접근자
(type) {list} Compound literal
2 a++ a-- Prefix 증감연산자 Right-to-left
+a``-a 단항 연산자 : a를 음수로 만들거나 양수로 만드는 연산자
!``~ 비트연산 NOT, 논리연산 NOT
(type) 형 변환
* Indirection (dereference) (참조)
& 변수의 주소를 가리킴
sizeof Size-of(크기 확인용 연산자)
_Alignof Alignment requirement
3 * / % 곱셈, 나눗셈, 나머지 Left-to-right
4 + - 덧셈, 뺄셈
5 << >> 왼쪽 시프트, 오른쪽 시프트
6 < <= 비교 연산자
> >= 비교 연산자
7 == != 비교 연산자
8 & Bitwise(비트 연산) AND
9 ^ Bitwise XOR (exclusive or)
10 | Bitwise OR (inclusive or)
11 && Logical AND
12 | | Logical OR
13 ? : Ternary conditional(3항 조건 연산자) Right-to-Left
14 = 대입 연산
+= -= 복합 산술 연산
*= /= %= 복합 산술 연산
<<= >>= 복합 비트 연산
&= ^= |= 복합 비트 연산
15 , Comma Left-to-right

출처 : cppreference.com

너무 많죠? 너무 많아요... 하지만 뒤쪽을 배우다 보면 금방 외웁니다. 아니, 외울 필요도 없습니다. 너무 당연한 순서이기 때문이죠.

예시를 하나 볼까요?

#include <stdio.h>
void main(){
    int a = -43, b = ~(6 - 5) - 1 - 3;
}

와우 정말 쓸 데 없는 프로그램입니다.

가 아니고...

int a = -43, b = ~(6 - 5) - 1 - 3;안에 있는 연산자들을 위 표에서 찾아보세요.
Functional Forms (), 대입연산자 =, 단항연산자 -, 콤마 , , 비트 연산 ~, 산술 연산자 -까지 보입니다.
이 연산자들이 어떤 의미일지 한 번 보세요. 그리고 순서를 한 번 보세요. 꽤나 상식적인 부분이 많습니다.

이 중에 첫번째인 것은 ()입니다. 순서에 따라 연산하면 int a = -43, b = ~1 - 1 - 3; 이 되겠네요.

두번째 연산자는 단항 연산자 -~입니다. 그러면 int a = -43, b = -2 - 1 - 3; 가 됩니다. 단항 연산자는 딱히 표시할 방법이 없습니다. 0010 10111101 0101되었다고 보면 될 것 같습니다.

세번째 연산자는 산술 연산자 -입니다. 그런데 -는 연산자 내에서도 순서가 있죠. 처음에는 int a = -43, b = - 3 - 3; 이 되고, int a = -43, b = - 6; 이 됩니다.

네번째 연산자는 =입니다. 각각의 값을 a와 b에 대입합니다. ,의 순위가 가장 낮기 때문에 a와 b를 한 줄에서 선언해도 서로 영향을 주지 않습니다. 또한 ,는 왼쪽 부터 연산을 하기 때문에 a에 먼저 -43을 대입하고 b-6을 대입합니다.

요약

  • AND, OR, XOR ,NOT연산을 이해할 수 있다.
  • <<연산과 >>연산을 이해할 수 있다.
  • 곱셈과 나눗셈, 시프트 연산의 차이를 알 수 있다.
  • 연산의 순서를 이해하고 외울 수 있다.

'컴퓨터공학 > C' 카테고리의 다른 글

2장-2 변수의 크기와 오버플로우, 언더플로우  (0) 2019.05.05
4장-1 산술 연산자  (0) 2019.01.14
3장 printf scanf 함수  (0) 2019.01.10
2장-1 자료형  (0) 2019.01.10
1장-1 Hello World  (0) 2018.12.16

4장-1 산술 연산자

2019. 1. 14. 19:40

Computer의 의미는 연산 장치입니다.
그러면 이제 본격적으로 연산을 시켜봐야겠죠?

산술연산자 + - * / % =

사칙연산

기본적으로 C언어는 덧셈 뺄셈 곱셈 나눗셈을 모두 사용할 수 있습니다.
덧셈과 뺄셈은 +, -기호를 사용하면 됩니다.
곱셈과 나눗셈은 키보드에 기호가 없기 때문에 조금 다릅니다. 곱셈은 *, 나눗셈은 / 기호를 사용합니다.

#include <stdio.h>
int main(void){
    printf("20 X 30 = %d\n", 20*30);
    return 0;
}

와 같은 방식으로 사용이 가능합니다. 콘솔에서는 20 X 30 = 600으로 보이겠죠.

앞서 배운 scanf를 활용하면

#include <stdio.h>
int main(void){
    int a, b;
    printf("숫자 입력 : ");
    scanf("%d %d", &a, &b);
    printf("20 X 30 = %d\n", a*b);
    return 0;
}

도 가능합니다. 그런데 나눗셈에서는 주의해야 할 게 있습니다.

#include <stdio.h>
int main(void){
    int a = 10, b = 3;
    printf("%d", a/b);
    return 0;
}

이런 프로그램이 있습니다. 10 / 3의 연산 결과를 출력하는 프로그램이죠.
수학에서 10 / 3 = 3.3333...입니다. 그런데 컴퓨터 언어는 다릅니다.
초등학교 때 배웠던 수학 기억하시나요? 10 을 3으로 나누면 몫은 3이었죠. C언어는 정수와 정수로 나눗셈을 하면 몫도 정수가 됩니다.

printf("%f", a/b);를 사용하면 괜찮지 않을까요? 안타깝게도 그렇지 않습니다.
두 변수가 정수일 경우 컴파일러는 정수연산을 합니다. 그러면 결과값도 정수이기 때문에 %f를 사용하면 결과만 이상해집니다.
그렇기 때문에 나눗셈 결과에 소수점이 필요하다면 변수를 선언할 때부터 실수형으로 선언해야 합니다.

다행히도 두 변수 중 하나라도 실수형 변수라면 컴파일러는 다른 변수도 실수형으로 변환해서 연산할 수 있습니다.

#include <stdio.h>
int main(void){
    float a = 10;
    int b = 3;
    printf("%f", a/b);
    return 0;
}

이 프로그램을 실행하면 b는 정수형으로 선언했음에도 불구하고 제대로 된 결과값이 출력됩니다.

%=연산

%는 나머지 연산입니다.
나머지 연산이 없다면, 나눗셈을 하고 나누는 수와 몫을 곱해서 원래의 수에서 빼야 합니다.
하지만 나머지 연산으로 표현이 간단해졌죠. printf("10 나누기 3 의 나머지 = %d\n", a%b); 처럼 사용할 수 있습니다. 결과는 10 나누기 3의 나머지 = 1이 됩니다

=은 대입 연산입니다.
수학에서는 등호라고 하지만 컴퓨터언어에서는 대입 연산이라고 불립니다. 그 이유는 연산 순서와 방향이 정해져 있기 때문이죠.
대입 연산은 오른쪽의 값을 왼쪽으로 대입하는 연산입니다. 우리는 변수를 선언할 때 a=10;과 같이 사용합니다.
하지만 10=a;라고는 사용할 수 없습니다. 오른쪽의 값을 왼쪽으로 대입하는 연산이기 때문에 10이라는 정수에 a의 값을 대입할 수 없기 때문이죠.

좀 깊게 들어가볼까요?

#include <stdio.h>
int main(void){
    int a= 20, b = 10;
    a = b = 5;
    printf("%d, %d",a, b);
    return 0;
}

이렇게 하면 어떻게 출력될까요?
처음에 a는 20, b는 10입니다.
그런데 다음 줄에서... 갑자기 멍해집니다. 이런 부분 때문에 연산의 순서를 알아둘 필요가 있습니다.

뒤쪽에서 배우겠지만 대입 연산은 오른쪽 부터 연산합니다.
대입 연산이 오른쪽의 값을 왼쪽으로 대입하는 연산이라고 했죠? 그렇기 때문에 ab의 값을 대입해야 합니다. 그 전에, 대입 연산은 오른쪽 부터 연산하기 때문에 b = 5연산을 먼저 하는것이죠. 그러므로 b는 5입니다.
콘솔 출력은 5, 5가 되겠죠.

대입 연산의 특성을 사용한 방법이 하나 더 있습니다. a = a + 5;와 같은 연산이죠. 수학에서는 말도 안되는 식입니다.
하지만 대입 연산에서는 가능합니다. 대입 연산은 오른쪽의 값을 왼쪽에 대입합니다. 그러면 오른쪽의 값을 먼저 알아야겠죠. 오른쪽의 값은 a + 5입니다. 그러니까 원래의 a 값에 5를 더한 값을 a에 대입하는 거죠. 이 연산을 계속 반복하면 a에 5를 더하는 연산을 계속 반복하는 겁니다.

복합대입연산자(Compound Assignment operator)와 증감연산자

이 두 가지 연산은 산술연산을 더 간단히 표현하는 방법입니다.
우선 복합 대입연산자는 대입할 변수에 어떤 연산을 수행해서 그 결과를 대입하는 연산을 간단히 표현하는 겁니다.
바로 위에서 배웠던 a = a + 5;가 그 예가 될 수 있겠죠. 이걸 줄여서 a += 5; 로 표현할 수도 있습니다.
덧셈 뿐 아니라 뺄셈, 곱셈, 나눗셈, 나머지 연산에도 사용할 수 있습니다. +=, -+, *=, /+, %=이렇게요. 뒤에서 배우게 될 비트연산자나 시프트 연산에도 사용할 수 있습니다!

a &= b;
a |= b;
a ^= b;
a <<= b;
a >>= b;

증감연산자는 변수에 1을 더하고 빼는 연산자입니다. a++; 같은 식으로 쓰는 연산자입니다. 이 연산을 하면 a에 1이 더해지죠.
이 연산자는 덧셈과 뺄셈 밖에 없습니다. 1을 곱하거나 나누는 것은 의미가 없으니까요.

증가 연산에도 두 가지 표현이 있습니다. a++;뿐 아니라++a;로도 쓸 수 있거든요. 이번엔 딱 봐도 알겠죠? 연산 순서에 따라 달라진다는 것을요. ++a는 덧셈 연산을 먼저 하고 다음 연산을 하는 겁니다. a++는 다른 연산을 먼저 하고 덧셈을 합니다.

여기서 다른 연산은 한 문장에서 덧셈 외의 연산같은 것들을 말합니다.
예를 들어 a = 5;인 변수가 있을 때 printf("%d",a++);가 있을 수 있습니다. 이 명령은 5가 출력됩니다. 하지만 printf 연산 후에 a는 6이 되죠.
printf("%d",++a);는 6을 출력합니다. 그리고 출력 이후에도 a 값은 6입니다.

내용이 너무 길기도 하고 연산 치고는 성격이 좀 다른 내용이어서 다음 편에서 이어서 하도록 하겠습니다.
다음 편에서는 비트 연산과 시프트 연산, 그리고 연산 순서에 대해 알아보겠습니다.
연산 순서는 여러가지 연산자들 사이에서 어떤 순서로 처리되는지 알아야 원하는 대로 프로그램이 실행되기 때문에 중요합니다.

요약

  • 산술 연산자로 덧셈(+), 뺄셈(-), 곱셈(*), 나눗셈(/)을 할 수 있다.
  • 정수 나눗셈과 실수 나눗셈의 차이를 알 수 있다.
  • 나머지 연산(%)과 대입 연산(=)을 할 수 있다.
  • 대입 연산의 순서를 알 수 있다.
  • +=, -= 같은 복합 대입 연산자를 사용할 수 있다.
  • a++, ++a같은 증감연산자를 사용할 수 있다.

'컴퓨터공학 > C' 카테고리의 다른 글

2장-2 변수의 크기와 오버플로우, 언더플로우  (0) 2019.05.05
4장-2 비트 연산과 연산 순서  (0) 2019.01.14
3장 printf scanf 함수  (0) 2019.01.10
2장-1 자료형  (0) 2019.01.10
1장-1 Hello World  (0) 2018.12.16

3장 printf scanf 함수

2019. 1. 10. 00:10

저수준 언어

언어의 수준(Level)은 상대적이지만 요즘은 코볼이나 어셈블리를 배우지 않는 점에서 C언어는 Low-Level 언어에 해당합니다. 코볼이나 어셈블리는 C언어에 대해 상대적으로 더 낮은 수준인 언어입니다.

Level이 낮으면 하드웨어에 직접 엑세스하기 쉬워집니다. 그만큼 처리가 빨리지죠. 반면에 Level이 높아지면 복잡한 구조를 단순하게 만들 수 있다는 점에서 코딩이 편해집니다. 속도는 느려지지만 쉬운 코딩으로 버그가 줄어들고, 복잡한 프로그램을 만들 수 있습니다.
가장 낮은 수준인 어셈블리어는 CPU의 명령어에 맞게 직접 코딩을 해야 합니다. 그러다보니 코딩을 하기 위해선 컴퓨터 구조에 대한 학습은 필수였습니다. 컴퓨터 구조에 맞게 명령을 입력해야 했거든요. 보다 높은 수준인 C언어는 덧셈, 비교, 변수 지정 등을 한 줄로 끝낼 수 있게 되었습니다. 대학교에선 여전히 컴퓨터 구조를 학습하긴 하지만 컴퓨터 언어를 사용하는 데에는 중요하지 않게 되었습니다. 그래서 어셈블리어에 대해 상대적으로 고급 언어입니다.

객체지향 언어는 당연히 객체 개념을 통해 C언어에 비해 고급 언어로 칭해집니다. 객체지향 언어는 메모리에 대한 고려를 거의 하지 않습니다. 그래서 String 형의 변수를 다른 변수에 저장할 때에도 대입 연산을 이용할 수 있습니다. 무엇보다 객체 지향 언어는 객체를 이용하는게 특징입니다. 프로그램 실행에 Stack구조(객체를 계속 쌓는 방식)를 사용하기 때문에 이 전의 코드를 조금 바꾸어 재사용하거나 프로그램 최적화 및 관리가 쉽습니다.
그래서 고급 언어로 사용자와 상호작용을 하기 위한 Front End를 담당하고 저급 언어로 내부의 처리를 담당하기도 합니다. 자바나 코틀린을 사용하는 것으로 잘 알려진 안드로이드도 중요한 시스템 부분은 C언어로 만든 바이너리입니다.

C언어에서 사용하는 scanf는 키보드 입력을 받는 함수인데, 실제 사용자의 입력을 받기 쉽지 않습니다. scanf는 2바이트 문자인 한글을 입력받기 쉽지 않습니다. 또한 키보드로 입력되는 데이터를 버퍼에 저장하는데, 그 버퍼를 자주 비워줘야 프로그램이 의도한 대로 동작합니다. 또한 보안 문제로 대체 함수가 도입되어 있습니다. printf_s, scanf_s이죠. 대부분 보안 프로그램이 scanf를 사용한 경우 프로그램 실행을 차단합니다.

위와 같은 특성으로 인해 printfscanf를 사용자의 입력을 받는 용도로 사용하지 않습니다. 다만 C언어 개발을 배우는 과정에서 콘솔로 입/출력을 받을 때는 가장 자주 사용되는 함수입니다. 실제 프로그램 개발에서는 printf_s를 사용해야 하고, 일반적으로는 객체지향 언어로 프로그래밍 합니다.

예제

보통 C언어 프로그램은 Console(콘솔)로 실행됩니다. Console에서 사용자의 입력 및 출력을 담당하는 함수가 printf, scanf입니다.

int main(void)    //진입함수 main()
{
    int a;    //정수변수 선언
    printf("숫자 입력 : ");
    scanf("%d",&a);    //키보드로부터 값을 입력받아 변수에 저장
    printf("덧셈 결과 %d \n",a+3);     //%d의 숫자와 대응되는 값의 수는 일치
}

printf함수를 통해 숫자 입력 :를 콘솔 화면에 출력합니다. 줄 내림을 하지 않았기 때문에 콘솔의 커서는 줄 내림을 하지 않고 콜론 오른쪽에 있습니다.

숫자를 입력하고 엔터를 누르면 scanf가 입력 값을 받아서 a에 저장합니다. 큰 따옴표 안에 %d가 있고, 큰 따옴표 뒤에 반점으로 구분하여 변수 이름이 적혀 있습니다.

printf함수를 다시 호출합니다. 큰 따옴표 안에 %d\n이 있지만 콘솔에는 보이지 않습니다. 왜 그런지 아래에서 알아보겠습니다.

printf

printf는 단순히 문장만 출력하지 않습니다. 변수를 출력할 수도 있지요. 그래서 같은 문장이라도 변수에 따라 출력이 달라지게 됩니다.
출력할 문장에 변수를 포함하는 방법이 %d를 사용하는 방법입니다. 그리고 그 뒤에 파라미터를 추가하여 %d에 대응하는 값을 출력하는 겁니다.

%d decimal(10진수)
%x hexadecimal(16진수)
%o octal number(8진수)
%e exponentiation(지수형)
%u unsigned(부호가 없는 자료형)
%f float(실수형)
%p pointer(포인터)
%c char(문자형)
%s string(문자열)

변수 여러개 출력하기

printf는 변수를 여러개 출력할 수 있습니다.

printf("아스키코드 %d은 %c입니다.", 97, 97);

%d%c를 두개 넣었습니다. 그리고 파라미터로 두개를 전달했습니다.
그랬더니 아스키코드 97은 a 입니다. 라고 출력됩니다.

2장 자료형에서 살짝 언급했는데, 같은 97을 전달받고도 정수형으로 표현한 것과 문자형을로 표현한 것이 다릅니다.

변수의 자릿수 정렬하기

printf는 자릿수를 정할 수 있습니다.
아래와 같이 입력하면

printf("%d \n",192);
printf("%d",6);
192
6

위와 같이 출력됩니다. 그런데 아래와 같이 입력하면

printf("%3d\n",192);
printf("%3d",6);
192
  6

위와 같이 출력됩니다. 자료형을 나타내는 문자 앞에 숫자를 붙이면 자릿수를 미리 지정할 수 있습니다.

실수형에서는 좀 더 복잡한 정렬도 가능합니다.

#include<stdio.h>
int main(){
    float a = 3.14159;
    int b = 192;
    printf("%6.3f\n",a);
    printf("%.3f\n",a);
    printf("%6d\n",b);
}

위와 같은 프로그램을 실행하면

  3.142
3.142
   192

와 같은 결과를 얻을 수 있습니다.

실수형은 전체 자릿수와 소숫점 아래 자릿수를 제한할 수 있고, 반올림하여 표현할 수 있습니다. 또한, 전체 자릿수는 지정하지 않고 소숫점 아래 자릿수만 제한할 수도 있습니다.

정수형과 실수형을 모두 6자리로 제한하니 출력에서는 마지막 숫자가 같은 위치에 있는 것을 알 수 있습니다.

printf줄 내림을 위해 특별한 표현을 사용합니다.

printf는 한 줄로 명령이 이루어집니다. 게다가 컴파일러는 줄 내림을 무시합니다. 그렇다면 줄 내림을 출력하는 방법은 무엇일까요?

바로 *이스케이프 시퀸스(Escape sequence) *라는 것을 사용하면 됩니다!. 문자열을 입력하고 있는데 \를 통해 escape해서 컴파일러의 처리를 한다고 보면 될 것 같습니다.

printf("줄 내림\n테스트");

라고 입력하면

줄 내림
테스트

라고 뜨게 됩니다.

그 외에도

\t 탭
\r 엔터(carriage return)
\v 수직 탭(vertical tab)
\b 지우기(backspace)
\f 폼 피드, 페이지 피드
\a 경고(beep음 경고)

가 있습니다. 대부분은 직접 입력이 가능하지만 알아두면 좋습니다.

그렇다면 컴파일러가 인식하고 콘솔에 출력하지 않는 문자를 콘솔에 출력하고 싶으면 어떻게 해야 할까요? 큰 따옴표는 문자열을 의미하는 것이어서 큰 따옴표를 입력하면 문자열이 끝나버리고 말죠. %도 %d, %f를 위해 할당된 문자여서 출력이 안됩니다. 도 마찬가지죠.
이러한 문자를 출력하는 것도 Escape Sequence 입니다.

\\
\"
\?
%%

와 같은 방법이죠. 음... Escape Sequence로부터 한번 더 Escape한다는 느낌입니다.

printf("\"큰 따옴표\" \\n %%d");

라고 입력하면

"큰 따옴표" \n %d

라고 출력됩니다.

scanf

scanf의 기본적인 사용법은 같습니다. printf와 다른 부분은 변수를 작성하는 뒤의 변수 이름 부분에 a가 아닌 &a를 사용한다는 것입니다.
추후 포인터를 학습하면서 자세히 다룰 부분인데, 변수의 메모리 주소를 의미합니다. scanf는 입력받은 데이터를 변수에 저장하는 것이 아니라 변수가 저장된 메모리 주소에 저장하기 때문에 &a를 사용합니다.

복습

#include<stdio.h>
int main()
{
    int salary;//월급
    int deposit ;//저금액

    printf("월급을 입력하시오: ");
    scanf("$d",&salary);

    deposit = 10 * 12 * salary;

    printf("10년간 저축액: %d \n", deposit);
    return 0;
}
#include<stdio.h>
int main()

    float radius;
    float area;

    printf("반지름을 입력하십시오: ");
    scanf("%f", %radius);

    area = 3.14 * radius * radius;

    printf("원의 면적: %f\n", area);
    return 0;
}
#include<stdio.h>
int main(){
    int id, pass;
    //ID와 PW자리에 '_'가 4개가 있고, ID 또는 PW를 입력하면 '\'가 없어지는 프로그램

    printf("ID와 PW를 4개의 숫자로 입력하세요:\n");
    printf("id:____\b\b\b\b");
    scanf("%d", &id);
    printf("pass:____\b\b\b\b");
    scanf("%d", &pass);
    printf("\a입력된 아이디는 \"%d\"이고 패스워드는 \"%d\"입니다.\n", id, pass);
    return 0;
}

요약

  • 출력을 위해 printf를 사용할 수 있다. 변수를 출력할 수도 있다.
  • 입력을 받기 위해 scanf를 사용할 수 있다.
  • printf나 scanf에 변수를 포함하도록 할 수 있다.
  • escape squence를 사용하여 원하는 문자열을 출력할 수 있다.

'컴퓨터공학 > C' 카테고리의 다른 글

2장-2 변수의 크기와 오버플로우, 언더플로우  (0) 2019.05.05
4장-2 비트 연산과 연산 순서  (0) 2019.01.14
4장-1 산술 연산자  (0) 2019.01.14
2장-1 자료형  (0) 2019.01.10
1장-1 Hello World  (0) 2018.12.16

2장-1 자료형

2019. 1. 10. 00:10

C언어에서는 변수를 저장하는데 그 변수의 형식을 지정해줘야 합니다.

서로 다른 형식끼리 구분해주기도 하고, 이 값을 어떻게 저장해야 할지 컴파일러에게 알려주기도 합니다.

#include <stdio.h>
int main() {
  int a;
  a = 10;
  printf("a 의 값은 : %d", a);
  return 0;
}

a라는 이름의 변수를 선언했습니다. 자료형은 int입니다.

printf 함수를 호출합니다. a의 값은 : 1이라는 결과를 얻을 수 있습니다. %d가 왜 10으로 바뀌는지는 다음 장에서 배울겁니다.

자료형 종류

대부분 언어에서 자료형은 같습니다. 심지어는 데이터베이스에서도 비슷한 표현을 사용하죠.

자료형은 크게 세 가지로 구분됩니다. 문자형, 정수형, 실수형이 있습니다.
각 자료형별로 메모리를 사용하는 크기에 따라 자료형이 여러가지로 나뉩니다. 또한 부호의 유무로 자료형의 범위가 결정됩니다. 항상 양수인 자료형을 unsigned, 부호가 있어야 하는 자료를 signed로 선언합니다. 기본값은 signed입니다.

컴퓨터에서 저장하는 방식에 대하여

1bit는 0과 1을 저장할 수 있는 이진수 입니다. 1byte는 8bits입니다. 그래서 1byte는
28(256)개의 숫자를 구분할 수 있습니다.

Group Type names* Notes on size / precision
Character types char Exactly one byte in size. At least 8 bits.
char16_t Not smaller thanchar. At least 16 bits.
char32_t Not smaller thanchar16_t. At least 32 bits.
wchar_t Can represent the largest supported character set.
Integer types (signed) signed char Same size aschar. At least 8 bits.
signed short int Not smaller thanchar. At least 16 bits.
signed int Not smaller thanshort. At least 16 bits.
signed long int Not smaller thanint. At least 32 bits.
signed long long int Not smaller thanlong. At least 64 bits.
Integer types (unsigned) unsigned char (same size as their signed counterparts)
unsigned short int
unsigned int
unsigned long int
unsigned long long int
Floating-point types float
double Precision not less thanfloat
long double Precision not less thandouble
Boolean type bool
Void type void no storage
Null pointer decltype(nullptr)

출처 : cplusplus.com

문자형

문자형은 문자 한개를 저장합니다.

char a;와 같은 식으로 선언합니다. char은 character라는 뜻을 갖고 있습니다. char는 1바이트입니다.
한글은 2바이트 문자이기 때문에 한글을 다룰 땐 조심해야 합니다. 나중에 배열과 함께 배웁니다.

1바이트이므로 256개의 숫자를 구분할 수 있습니다. unsigned char는 부호가 없는 숫자로 0 ~ 255까지 저장할 수 있습니다. signed char는 부호가 있는 숫자로 -128 ~ 127까지 저장할 수 있습니다.

참고로, 문자형도 정수를 저장합니다. 숫자를 읽어와서 문자에 대응해서 표시하는겁니다.
따라서 문자형은 메모리 크기가 다른 정수형이라고 생각하셔도 됩니다.
정수형은 가장 작은 자료형이 4byte를 차지하기 때문에, 작은 숫자를 저장할 때는 char를 사용하기도 합니다.

정수형

정수형은 정수를 저장합니다.

4바이트를 저장하는 int가 기본형입니다. int는 integer라는 뜻입니다.

1바이트를 저장하는 char, 8바이트를 저장하는 long int, 16바이트를 저장할 수 있는 long long int가 있습니다.

4바이트인 int는 4,294,967,296개의 숫자를 구분할 수 있습니다. unsigned int는 0 ~ 4,294,967,295, signed int는 -2147483648 ~ 21473647까지 저장할 수 있습니다.

실수형

실수형은 실수를 저장합니다.

정수형이나 문자형과는 좀 다른 형태로 저장합니다.
실수형은 숫자와 소숫점의 위치를 저장합니다.

1.7 X 10-3

와 같은 형태로 저장합니다. 10은 제외하고, 1.7과 -3만 저장하는 형태죠. 여기서 숫자 1.7에는 굳이 소수점 자릿수까지 저장할 필요는 없겠죠?
그래서 실수형은 메모리의 크기와 숫자의 크기가 비례하지 않습니다. 숫자의 크기는 윗첨자에 영향을 받는건데, 가장 큰 메모리를 차지하는 부분은 앞에 있는 숫자 부분이기 때문이죠. 이걸 위의 표에서는 '정확도'라고 표현했습니다. 앞의 숫자가 길어질수록 반올림하거나 버림하는 단위가 작아진다는 의미니까요.

char과 ASCII 코드

신기한 것은 char형은 문자도 저장하고, 정수도 저장한다는 것입니다. 내용물에 따라 형식을 구분하는 걸까요? 아닙니다.

사실 C에서는 문자를 숫자로 저장합니다. 정확히는 ASCII(아스키)코드를 사용해서 저장합니다. 아스키 코드 위키에서 변환 표를 확인할 수 있습니다.

그래서 char형 변수를 사용할 때는 숫자형식으로 불러와도 되고, 문자형식으로 불러와도 됩니다. 자료형이 char인 변수에서 97을 문자로 불러오면 a, 숫자로 불러오면 97이 됩니다.
그러므로 프로그램 내부에서는 어떻게 처리하든 상관 없지만 그것을 출력할 때는 문자인지 정수인지 제대로 지정해줘야 Console에 프로그래머가 의도한 대로 표시됩니다.

다음 장에서는 printfscanf를 배우게 됩니다. printfscanf는 가장 기본적인 입/출력 함수입니다. 이 함수에서 문자와 정수를 구분하는 방법을 자료형과 연관지어 다룰겁니다

복습

#include<stdio.h>

int main()
{
    char ch = 'A'; //변수 선언
    printf("%c의 아스키코드= %d \n", ch, ch);
    ch = ch + 1;
    printf("%c의 아스키코드= %d \n", ch, ch);

    return 0;
}

요약

  • 아스키라는 텍스트 인코딩 방법을 알 수 있다.
  • 자료형의 종류를 알 수 있다.
  • 변수의 사용 목적에 따라 signed와 unsigned를 지정할 수 있다.

'컴퓨터공학 > C' 카테고리의 다른 글

2장-2 변수의 크기와 오버플로우, 언더플로우  (0) 2019.05.05
4장-2 비트 연산과 연산 순서  (0) 2019.01.14
4장-1 산술 연산자  (0) 2019.01.14
3장 printf scanf 함수  (0) 2019.01.10
1장-1 Hello World  (0) 2018.12.16

램 선택의 기준: 램 업그레이드가 필요할까?

2018. 12. 31. 00:34

이 글을 쓰는 이유

램 선택의 기준은 물론 '필요하냐 필요하지 않냐'가 기준입니다.
그런데, 그 필요성을 따지는 것을 단순한 체감으로 판단하기는 쉽지 않습니다. 왜냐하면 사용자가 컴퓨터를 업그레이드를 하기도 전에 업그레이드 된 컴퓨터의 속도를 알 수 없기 때문이죠.
그래서 보통은 현재 컴퓨터 리소스(RAM, CPU 등)의 사용량을 확인합니다. 하지만 컴퓨터의 운영을 고려해보면 단순히 리소스의 사용량을 확인하는 것은 확실성을 떨어트릴 수 있습니다.
이 글에서는 업그레이드에 좀 더 확실한 근거를 제시하려고 합니다. 특히 램을 중점적으로 다뤄보겠습니다.

이 글은 추가적인 램이 필요한지에 대한 객관적인 기준을 다루는 것이 아닙니다. 어차피 물컵에 물이 반이 남아 있어도 '반이나 남았네!'라고 생각하는 사람도 있고, '반 밖에 안남았구나..ㅠㅠ' 라고 생각하는 사람도 있으니까요.
하지만 물컵도 보지 않고 '물은 충분히 남아있어!'라고 생각하지 않기를 바라는 마음에서 주관적인 평가를 내리기 위한 보다 객관적인 자료를 제시하는 겁니다.

가상메모리(Virtual Memory)

운영체제는 프로그램이 마음껏 메모리를 사용할 수 있도록 가상메모리를 할당합니다.

위 사진을 보면, 실제 사용량은 308MB이지만 운영체제에서 해당 프로세스에 할당한 가상 메모리는 86GB입니다. 저 크기의 데이터를 실제 메모리에 확보해두려고 한다면 당연히 시스템 운영이 불가능할 겁니다. 프로세스가 사용할 수 있는 메모리 공간(86GB)과 실제로 메모리를 점유하는 크기(308MB)는 다르다는 것이죠.
이것을 '가상메모리'라고 합니다. 가상 메모리 덕분에 (보조 기억장치의 공간이 충분하다면) OS는 해당 프로세스가 실제로 86GB 를 요구해도 문제 없이 실행되도록 하는 것이죠. 단, 예상하다시피 속도는 처참할 겁니다.

가상 메모리란, 각 프로그램에 실제 메모리 주소가 아닌 가상의 메모리 주소를 주는 방식을 말합니다(위키피디아). 애플리케이션이 '메모리의 한계를 계산하고 그에 따라 메모리 사용량을 줄이도록 하는 복잡한 코딩/연산과정'을 생략할 수 있도록 하는 기법입니다.
실제로 프로그래밍을 할 때에 변수를 만들면서 메모리의 남은 공간을 확인하고 변수를 만들지는 않습니다. 오히려 프로그래머들은 연산을 시작하기 전에 일단 변수 먼저 만듭니다. CPU의 연산 속도에 비해 램이 느리기 때문에 먼저 공간을 만들어두는 것이죠.
이런 식의 프로그램은 메모리 점유율이 커질 수밖에 없습니다. 그러나 메모리의 한계에 다다른다 하더라도 프로세스가 종료되거나 멈추지 않습니다.

이러한 방식은 멀티태스킹 운영 체제에서 흔히 사용되고, 실제 주기억장치보다 큰 메모리 영역을 제공하는 방법으로도 사용됩니다(위키피디아). OS는 애플리케이션에 메모리가 충분히 많다고 전달하고, 애플리케이션은 그 충분한 공간을 사용할 수 있게 됩니다. OS는 애플리케이션의 요구에 맞춰 메모리에 데이터를 저장합니다.

메모리 압축과 Swap (Paging)

그런데 만약 실제 메모리가 부족하다면 어떻게 할까요?
어차피 프로세스들은 OS를 통해 메모리에 접근하는 것이기 때문에 프로세스가 요구하는 메모리 데이터가 어디에 저장되든 상관 없습니다. 그 점을 이용해서 OS는 메모리 압축이나 Swap을 통해 공간을 확보합니다. 자주 사용하지 않을 것 같은 데이터는 압축하거나, 보조 기억 장치에 저장하는 거죠.

메모리가 부족하다는 것은 메모리 압축이나 Swap(스왑)이 발생할 때를 말합니다.

메모리 압축

압축은 딱히 설명할 게 없습니다. 메모리에서 자주 사용하지 않는 코드를 압축하는 거죠. 압축을 하면 50%넘게도 크기를 줄일 수 있습니다.
압축의 단점은 CPU 리소스를 사용한다는 것입니다. 컴퓨터의 모든 리소스 중 가장 빠른 리소스이지만, 컴퓨터의 성능에 가장 큰 영향을 미치는 리소스이기도 하죠.
압축한 데이터에 다시 접근해야 한다면 압축을 풀어야 합니다. OS가 사용자의 요구를 미리 예측할 수 없기 때문에 메모리에 접근하고자 할 때에 메모리 압축 해제를 하게 됩니다. 이 때 성능에 큰 영향을 끼치는거죠.

Swap (Paging)

스왑은 보조 기억장치를 주 기억장치처럼 활용하는 기법을 말합니다. 보조 기억장치는 CPU에 비하면 압도적으로 느립니다(다음 절에서 설명). 그렇기 때문에 스왑을 하더라도 일단은 압축을 해야 합니다. 그만큼 CPU 리소스를 사용할 수밖에 없습니다. 그리고, 발열 전력소비에도 영향을 끼치죠. 노트북의 경우는 발열이 늘면 성능 감소로도 이어집니다.
여기서도 메모리 압축과 같은 문제가 발생합니다. 압축하고 스왑한 데이터를 다시 사용하려면 보조 기억장치에서 주 기억장치에 불러와서 압축을 풀어야 합니다. 보조 기억장치의 기나긴 응답을 기다리고, 메모리에 로드 되면 성능에 가장 큰 영향을 미치는 CPU 리소스를 사용해 압축을 풀어야 합니다.

메모리 압축과 Swap의 결론

컴퓨터는 하드웨어 간의 속도 차이를 극복하기 같은 계층 구조를 사용합니다. CPU가 가장 빠르고, CPU 안에 있는 L1, L2, L3 캐시, 그 다음으로 주 기억장치, 보조 기억장치 순서입니다. 최고의 성능 발휘를 위해서 프로그래머나 OS, CPU 연구원들은 캐시를 활용하여 RAM에 엑세스하는 것을 최대한 줄여야 합니다. 사용자는 RAM이 부족해서 압축이나 Swap을 위해 보조 기억장치에 엑세스하는 것을 최대한 줄여야 합니다.

SSD도 느린가요?

만약 보조 기억장치의 리소스 사용량이 적고, 주 기억장치와 CPU의 리소스 사용량이 많은 연산을 한다면 압축은 하지 않고 Swap만 하는게 유리할 수도 있습니다. 하지만 보통은 그러지 않습니다. 보조 기억장치로 사용되는 SSD가 아무리 빨라졌다 하더라도 주 기억장치인 램에 비하면 굉장히 느립니다.

2018년 기준 최상급인 삼성 SSD 970 Pro나 Intel SSD 900P의 응답 속도가 70마이크로초~150마이크로초 수준입니다. 램은 2018년 기준 낮은 수준인 2133Mhz 메모리의 응답 속도가 50나노초 정도로, 마이크로초로 계산하면 0.05마이크로초가 됩니다. 1400배 빠른 응답 속도를 갖습니다. 이런 차이가 장기적으로 누적되면 큰 지연이 생기죠.

SSD보다도 1000배 이상 빠른 RAM도 연산 장치인 CPU에 비하면 굉장히 느립니다. 이런 속도 차를 극복하기 위해 CPU는 L1 캐시를 사용합니다. 최근에는 그 속도차이가 더 커져서 L2, L3, L4캐시도 등장했습니다.

Computer Action Avg latency 1초 정규화
3Ghz CPU Clock Cycle 0.3 ns 1s
L1 Cache Access 0.9 ns 3s
L2 Cache Access 2.8 ns 9s
L3 Cache Access 12.9 ns 43s
RAM Access 50ns 2.75 m
NVMe SSD I/O 70 ~ 150 us 2h ~ 2 d
Rotational disk I/O 1~10 ms 11d ~ 4 m

캐시

반대로 보조 기억장치가 느린 것을 보상하기 위해 메모리를 활용하는 기법이 있는데, 이를 캐시라고 합니다.

사진을 보면 Memory 열에서는 214MB, Real Memory 열에서는 309MB라고 나옵니다. 프로세스가 사용하는 메모리는 214MB인데, OS에서는 이 프로세스에 309MB를 사용합니다. OS가 자주 사용되는 코드, 그래서 추후 다시 사용 될 것 같은 코드를 미리 프로세스에 붙여두는거죠. 보조 기억장치가 너무 느리기 때문에 운영체제는 자주 사용되는 코드를 메모리에 보관하여 보조 기억장치에서 로드해야 할 코드를 메모리에서 찾는 겁니다.

OS의 가상메모리 운영

가상메모리에 신경써야 할 요소가 더 있습니다.

애플리케이션이 잘못 만들어진 것이 아니라면 갑자기 100GB이상의 메모리 데이터를 만들진 않을 겁니다. 하지만 1~2GB의 메모리 공간을 갑작스럽게 요구할 경우는 있겠죠. 그런데, 이 때 메모리가 부족하다면 어떻게 될까요?

운영체제는 애플리케이션이 메모리 공간을 추가로 요구할 때마다
  1. CPU 자원을 사용해서 자주 사용하지 않는 코드를 찾아내고
  2. CPU 자원을 사용하여 압축하고
  3. 보조 기억장치에 보관하는 동안 CPU 자원은 유휴 상태로 대기하고
  4. 보조 기억장치에 저장되면 메모리를 비우고 실행중인 애플리케이션에 할당

하는 작업을 반복 하는 걸까요?

만약 사실이라면 컴퓨터에서 애플리케이션을 실행하는 속도는 처참할 겁니다. OS는 애플리케이션이 어느 정도의 메모리 공간을 요구하는 것을 예측할 수 없기 때문에 애플리케이션이 1~2GGB의 공간이 필요하다 하더라도 실제로는 작은 크기의 데이터를 할당받는 것을 수백 번 반복하는 것입니다. 그 말은 자주 사용하지 않는 코드를 찾아 압축하는 작업을 수백번 반복하는 것이 됩니다.

개발자가 애플리케이션의 실행에 필요한 공간을 계산하여 미리 할당받는 방법도 있긴 하지만, 메모리 사용량을 예측할 수 없다면 필요할 때마다 추가로 요청해야 합니다. 그러라고 가상메모리라는 개념을 만든 것이기도 합니다.
게임으로 예를 들자면, 처음에 최대한 많은 데이터를 메모리에 로드해두는 방법도 있을겁니다. 하지만 대부분 게임은 라운드가 시작되거나 맵이 바뀔 때마다 다시 로딩을 합니다. 그런데 이렇게 애플리케이션이 실행되는 중에 로드하는 데이터들의 크기를 프로그래머가 예측하고 미리 할당받아두기는 쉽지 않습니다.
맵을 로드할때, 건물 개체와 그 개체에 그려질 그림(텍스처)를 로드해야 하는데, 미리 그 크기를 계산할 게 아니라면 건물 개체를 로드하고 다시 텍스처를 로드하는 방법을 사용해야 합니다. 게임 안에 건물이 몇개 나올까요? 엄청 많을겁니다.
그러므로 애플리케이션에서 수행하는 대용량의 메모리 공간 요청도 작은 크기의 메모리 할당을 여러번 반복하는 것이죠.

이러한 요청을 빠르게 수행하기 위해서는 1, 2, 3 단계를 건너뛸 방법이 필요합니다.

운영체제는 항상 어느 정도의 공간을 비워두도록 합니다. 덕분에 갑작스럽게 메모리를 요구할 때도 즉시 메모리를 할당하고 해당 애플리케이션이 다음 명령을 실행할 수 있습니다. 보통 OS는 10%~30%정도의 공간을 남겨둡니다.
주 기억장치가 8GB인 시스템에서는 2.5GB이하의 공간만 남아있다면 성능에 영향을 줄 정도로 압축이나 스왑이 된 것을 의심해야 합니다. 16GB의 시스템에서는 5GB이하의 공간만 남아있다면 성능에 영향을 줄 정도로 압축이나 스왑이 된 것을 의심해야 하는 겁니다.

본격적인 판단 기준

처음에도 언급했듯이, 주관적인 판단을 위한 객관적 지표인겁니다.
바로 윗 단에서 2.5GB 이하의 공간만 남아있다면 성능에 영향을 줄 정도라고 했습니다.
하지만 이 '성능'은 '체감 성능'이 아닙니다. '성능'만을 따진다면 메모리는 100GB로도 부족할겁니다. 캐시 기능이 덕분에 메모리는 많을수록 빨라집니다.
물론, 그 돈을 투자해서 CPU를 업그레이드 하는게 체감 성능은 더 좋아지겠죠. 이런 주관적인 판단을 위해 사용할 객관적 지표 '가상메모리'를 배웠습니다.

그러면 캐시는 없더라도, 압축이나 스왑이 없는 정도로 업그레이드 하면 될까요?
그것도 아닙니다. 압축이나 캐시가 있어도 됩니다. 아무리 시간에 민감한 사람이라도 '체감 성능' 기준으로 차이가 느껴지지 않을 수 있습니다.

1. 불필요한 데이터의 압축

메모리 압축은 자주 사용하지 않는 데이터를 대상으로 수행됩니다. 압축된 메모리를 사용할 때는 다시 압축을 해제해야 하기 때문에, 자주 사용하는 데이터에 적용하지는 않을겁니다.
그 중에서도 정말로 사용하지 않는 코드인 경우에는 압축되거나 Swap되어도 성능에 영향이 없습니다. 프로세스가 통채로 압축되는게 아니고 프로세스의 일부만 압축할 수도 있습니다. 그래서 모든 프로세스를 골고루 사용중이라도 그 중 일부 기능은 자주 사용하지 않는다면 압축되어도 체감 차이는 없을 수 있습니다.

2. 부속 장치를 기다림

다른 장치를 기다려야 하는 애플리케이션이라면 체감되지 않을 수 있습니다다.

HDD 불량 확인 프로그램으로 HDD를 검사한다고 가정합시다. 이 컴퓨터의 메모리가 부족하여 Swap 되더라도 컴퓨터는 하드디스크의 불량 확인 속도에 체감하는 차이가 없을 것입니다.
여기서 생기는 가장 큰 지연은 HDD의 지연이기 때문입니다. CPU나 램은 Swap 때문에 연산량이 늘어났지만, 어차피 HDD를 기다려야 하는 시간 때문에 유휴 시간이 더 깁니다.

간단히 커서를 움직이는 동작을 비교해봅시다. 한 컴퓨터는 1Ghz 듀얼코어 컴퓨터이고, 한 컴퓨터는 4Ghz 옥타코어입니다. 그 외 스펙은 모두 같습니다. 아무것도 없는 바탕화면에 커서만 움직이는데 체감 차이가 느껴질까요? 아닙니다. 분명 같은 연산을 수십배 이상 느린 컴퓨터에서 수행하는건데 왜 체감 차이가 없을까요?
그 이유는 이 상황에서는 모니터의 지연이 원인이기 때문입니다. 보통의 60Hz모니터는 약 16ms의 지연을 가집니다. 1Ghz 듀얼코어 시스템이라 하더라도 마우스를 움직이는데 16ms까지 필요하지는 않은게 그 이유입니다..

메모리 확인 방법

위에서 알아본 이론적인 구조를 직접 확인할 수 있어야 메모리 업글레이드가 필요한지 알 수 있습니다.
보조 기억장치는 느립니다. 그렇기 때문에 보조기억장치를 사용하는 Swap은 최후의 단계라고 보면 됩니다. 개인적으로는 Swap 크기가 2GB이상일 경우는 누구라도 속도 차이를 느낄 수 있다고 생각합니다. 메모리 압축은 5GB 이상이라면 누구라도 속도 차이를 느낄 수 있을 것이라고 생각합니다.(16GB램 기준)
여기서 함께 확인해야 할 것은 자신의 사용 환경과 가격 부분을 고려해야 합니다. 만약에 백그라운드형 프로그램이 굉장히 많고, 이 프로그램들이 반복적인 코드만을 수행한다면 스왑이나 압축이 굉장히 커도 큰 차이는 없을 겁니다. 필요 없는 부분만 압축했기 때문이죠. 또한 체감 차이가 생긴다 하더라도 메모리 가격 대비 유익한 체감차이인지도 생각해봐야 하고요.

맥OS(MacOS)

맥은 메모리가 항상 캐시로 가득 차 있습니다. 어느 정도 사용하고 나면 여유 메모리는 300MB미만이 됩니다.
또한 맥은 Swap을 하는 한이 있어도 일단은 캐시는 어느 정도 남겨둡니다. iOS도 같습니다.

활성 상태 보기(Activity Monitor) 앱을 실행합니다. 그리고 메모리 탭을 클릭하면 위와 같이 나옵니다.
아래쪽에 Swap Used와 Compressed를 통해 전체적인 압축량과 스왑 사용량을 확인할 수 있습니다. 맥은 활성 상태 보기에서 앱별 압축된 양도 확인할 수 있습니다.
메뉴바에서 '보기 -> 열 -> 실제 메모리, 압축 된 메모리'를 체크하면 됩니다. 여기서 각 프로세스별로 압축된 양을 보고 시스템 업그레이드가 필요할지 주관적으로 판단하면 됩니다.

맥의 경우는 Hibernate를 할 때에 메모리의 데이터를 최대한 압축하고 스왑한 후에 컴퓨터를 끕니다. 덕분에 16GB 메모리를 사용 중이라도 Hibernate 파일은 3GB를 넘지 않죠. 그런데 시스템이 복원 된 후에도 다시 사용하지 않는 코드는 메모리가 여유롭다 하더라도 스왑 상태로 남습니다. 이를 속도가 저하되는 요인이고, 메모리 업그레이드 후에 체감 차이가 생긴다고 여기면 안됩니다.

주 : Hibernate : 메모리의 데이터를 보조기억장치에 저장하고 컴퓨터를 끕니다. 컴퓨터를 켜면 이 데이터를 복원하고 잠자기(Sleep)에서 깨어난 것처럼 작동합니다.

윈도우(Windows)

윈도우즈가 올바른 외래어 표기입니다. 하지만 한국 MS가 윈도우라고 표기합니다. 고유명사가 표기법보다 우선이니 '윈도우'라고 해야 합니다.

윈도우는 작업 관리자에서 성능 탭을 보면 됩니다.

Swap을 윈도우에서는 페이징(Paging)이라고 합니다. 윈도우의 경우 페이징은 정확한 수치로 나오지 않습니다. 여기서 확인해야 할 것은 Committed(커밋 됨)과 In use(사용 중), 그리고 Compressed(압축 됨)입니다. Commit은 OS에서 메모리를 할당한 부분입니다. 캐시를 포함하여 압축, 페이징 크기도 Committed에 포함됩니다..

'In use(사용 중)' + 'Available(사용 가능)' = 사용 가능한 RAM 크기
: 캐시나, 페이징에 대한 부분은 계산에서 제외됩니다. 그래서 실제 메모리 사용량과는 다릅니다.

'Committed(커밋 됨)' = 주소를 가진 데이터의 크기
: 압축 된 데이터나 페이징 된 데이터는 압축 이전의 크기로 표시될 수 있습니다.
'In use(사용 중)', 'Paged pool(페이징 풀)*', 'Non-paged pool(비 페이징 풀)**', 'Paging***' 등을 포함합니다.
'Cached(캐시 됨)'은 'Committed(커밋 됨)'에 포함되지 않습니다.
*페이징 풀은 RAM에 있는 커널 개체에 대한 가상 주소를 보관합니다.
**비 페이징 풀은 RAM 외의 장치에서 가져올 수 있는 데이터에 대한 가상 주소를 보관합니다.
***페이징 파일이 있을 경우에는 Committed(커밋 됨) 항목의 분모가 전체 메모리 크기보다 큽니다(커질 가능성이 높다).

CPU가 보조 저장장치에 비해 압도적으로 빠르기 때문에 페이징을 할 때도 압축을 거친 후에 보조 저장장치에 저장합니다.
그런데 윈도우에서는 실제 페이징 크기나 압축 크기가 아닌, 압축 이전의 크기만 나옵니다. 그래서 압축 전의 데이터 크기와 페이징 이전의 크기를 가지고 주관적으로 판단해야 합니다.

하지만 윈도는 또 다른 객관적 자료를 제공합니다.

Resource Monitor(리소스 모니터)의 메모리 탭에서 Hard Faults(하드 폴트)를 확인할 수 있습니다.
이 열은 프로세스가 초당 하드 페이지 폴트 요청 횟수의 1분 동안의 평균치를 보여줍니다. 자주 사용하는 프로그램이 하드 폴트가 많으면 메모리를 늘릴 필요가 있는겁니다.
MacOS에서는 페이지 폴트를 서드파티(3rd Parties) 프로그램을 이용하면 확인할 수 있습니다.

마치며…

처음부터 강조한 것인데, 개인의 선택에 달린겁니다. 하지만 단순히 OS가 표시하는 RAM의 사용량만 보는 것은 선택에 대한 근거로는 부족하죠.
그래서 OS가 메모리를 관리하는 방법을 알아보고 메모리가 얼마나 사용되고 있는지 더 정확하게 추정한 후에 주관적으로 결정해야 합니다. 사용자의 컴퓨터가 사용자가 하고자 하는 작업에 사용자가 요구하는 성능을 보여줄 수 있는지 생각해봐야 합니다.

OS는 상당히 복잡하여 현재의 구성에서 각종 프로세스와 하드웨어를 조절하여 각종 요소의 균형을 유지할 수 있도록 동적으로 관리합니다. 때문에 단순히 컴퓨터의 리소스 사용량만을 보고 판단하기 어렵죠.
이는 RAM 뿐 아니라 다른 장치들도 마찬가지입니다. CPU는 캐시의 속도에 따라서도 성능 차이가 생기고, 디코더의 속도에 따라서도 성능 차이가 생깁니다. 그래서 CPU사용량이 30%정도라 해도 업그레이드를 하면 성능 차이가 느껴질 수 있죠. 캐시 또한 성능에 영향을 미치는 요소이지만 작업 관리자에 나타나지 않습니다.
SSD도 마찬가지입니다. 단순히 읽기 속도가 빠른 것만이 장점인 것은 아닙니다. SSD는 읽기/쓰기 요청에 대해 응답속도가 굉장히 빠른 것이 장점입니다. 이 장점을 살리는 인터페이스가 NVMe구요. 초당 IO 처리 횟수를 의미하는 IOPS는 단순한 파일 복사로는 판단할 수 없습니다. 작은 크기의 파일을 여러 개 복사해야 IO요청이 여러개 생기기 때문이죠. 이건 벤치마킹 소프트웨어를 통해 판단할 수 있습니다. GPU와 HDD에 대해서도 단순히 사용량만 확인할 것이 아니라 좀 더 다양한 기준을 찾아 평가할 필요가 있습니다.

1장-1 Hello World

2018. 12. 16. 23:34

대체로 컴퓨터 언어에 대한 강좌라고 하면 이론적인 부분을 먼저 설명하고 그것을 활용한 예시 코드를 보여주게 마련입니다. 물론 종이 교재도 마찬가지구요.

하지만 좀 다른 접근으로 먼저 코드를 보고 나서 그 설명을 읽게 하고 싶었습니다. 개인적 경험으로는 이런 방법이 더 이해하기 쉬웠기 때문입니다.

하지만 설명해야 할 부분 중에서도 기초적인 부분은 먼저 설명해야 코드를 볼때 짐작이라도 가능한 부분이 있으니 이 점 참고해서 너무 난해하다면 아래쪽을 먼저 읽어보길 권합니다.

1장 Hello World!

모든 프로그래밍 언어는 Hello World!로 시작합니다.
단순히 프로그램 실행 결과에 Hello World를 출력하는 프로그램이죠.

#include <stdio.h>

int main(void)
{
    printf("Hello World!");
    return 0;
}

첫 장이기도 하고, 원래 Hello World는 이론 보다는 '실행해보기'에 의미가 있기 때문에 구조만 확인하면 됩니다.

이렇게 끝내기엔 글이 너무 짧아 참고할 만한 내용을 적어봤습니다.

주석(comment)

영어로는 comment. 주석을 사용하면 Compiler는 해당 문장을 인식하지 않습니다.
언어마다 다르지만 보통은 두 가지 형식을 사용합니다.

C, JAVA 등의 계열은 // comment으로 한 줄을 주석처리하고, /* comment */로 여러 줄을 주석처리 할 수 있습니다.
HTML이나 CSS등의 언어는 <!-- comment -->로 주석처리 할 수 있습니다.
터미널 셸에서는 # comment
php에서는 ; comment 로 주석처리 할 수 있습니다.

#include

#include 를 통해서 다른 코드를 참조할 수 있습니다. 위의 파일만으로는 'Hello World'를 출력할 수 없습니다. 컴퓨터는 printf가 뭔지 모르기 때문이죠. 하지만 #include 를 통해 stdio.h 파일을 참조하도록 하면 'Hello World'를 출력할 수 있습니다

개발자가 stdio.h파일을 만들지 않았지만, C언어를 위한 IDE(컴파일러만 따로 설치해도 됨)를 설치하면서 함께 제공됩니다. < > 안에 있는 파일(소스)은 컴파일러가 지정한 디렉터리에서 찾습니다. 그리고 이 디렉터리에는 stdio.h 뿐 아니라 여러가지 미리 정의된 파일들이 있습니다. 그렇기 때문에 개발자는 stdio.h 파일의 위치를 몰라도 사용할 수 있습니다.

그렇다면 개발자가 미리 정의한 파일들은 어떻게 불러와야 할까요?
그럴 때는 꺾쇠 괄호 대신 쌍따옴표를 사용합니다. #include "mycode.h" 처럼 사용하면 됩니다.
그러면 컴파일러는 원래 파일과 같은 디렉터리에서 mycode.h 파일을 찾아 참조합니다.
가장 메인이 되는 파일이 너무 복잡하다면 다른 소스에 코드를 작성하고 불러오기만 하면 되죠.

stdio는 STandarD Input/Output의 뜻을 가지고 있습니다.
아래에 사용하는 printfstdio.h 를 참조했기 때문에 사용할 수 있는 것입니다.

int main(void)

int는 return(반환) Type(형식)을 말합니다. 이런 종류는 void, int, char, long, string 등이 있는데, 이를 자료형 이라고 합니다. C언어에서는 배우지 않지만 추후 객체지향(Object Oriented) 언어를 사용하면 무엇이든 반환할 수 있게 됩니다!.
C언어는 객체(Object)라는 개념이 없습니다. 비슷한 개념으로 구조체(Structure)가 있는데, C언어는 구조체 자체를 반환할 수는 없고, 포인터(Pointer)를 이용하는 방법이 있습니다. 함수(Function) 안에서 사용했던 구조체의 주소를 저장하는 포인터를 반환하면, 반환을 받은 함수에서 사용할 수 있습니다.

main 은 함수 이름입니다. C언어는 처음 실행할 때 무조건 main 함수가 실행됩니다. 이름은 숫자로 시작할 수 없습니다.
main 함수는 int 를 반환하도록 하는 것이 좋습니다. 윈도용 C컴파일러의 경우 void main{}을 허용하지만, 리눅스나 유닉스 계열에서는 반드시 int형을 반환해야 합니다.

(void) 는 함수에 전달하는 인자(Parameter, Argument)를 정합니다. 파라미터가 있을 경우, myfunction(int i, char s) 와 같이 변수의 형식과 변수의 이름을 지정하여 전달합니다. 변수가 여러 개이면 반점(,)으로 구분하여 추가할 수 있습니다. void 대신 빈 칸이어도 됩니다.
여기서 인자로 전달되는 변수는 지역변수(Local Variable)입니다. 이 함수 안에서만 사용되는 변수라는 뜻입니다. 함수 밖에서는 영향을 주지 않는다는 의미로, 다른 함수에서 파라미터로 전달하는 변수가 논리적으로는 같은 변수라 하더라도 이름이 달라도 됩니다.
아래 예시를 봅시다.

void print(int id); //main함수에서 print함수를 사용하기 전에 print함수가 존재한다는 것을 미리 선언.

void main(){
    int name = 15; //name 변수가 15를 갖고 있다.
    print(name); //print를 호출하면서 name이 갖고 있는 15를 전달한다.
}
void print(int id){ //15를 받아서 지역변수 id로 저장한다.
    printf("%d",id); //15를 출력한다.
}
{}

앞으로 가장 자주 사용할 것이고, 제일 신경써야 하는 부분입니다.

코드를 한 묶음으로 정해주는 역할입니다. 중괄호 사이의 세 줄은 int main(void) 함수에 포함된 코드라는 의미입니다.

여러 줄을 포함하기 때문에 중괄호가 열리는지, 닫히는지 파악하기 어렵습니다.
혹시나 다른 위치에서 중괄호를 닫으면 정상적으로 실행은 되지만 개발자가 원하던 출력이 나오지 않을 수도 있습니다.

void main(){
    int i =1, j = 1;
    if(i == 1){ // i 가 1이라면 수행한다.
        if(j == 2){ // j가 2이라면 수행한다.
        //지금은 if문이 충족되지 않으므로 이 부분은 무시한다.
        }
        printf("j=2");
    }
}

이런 코드를 봅시다. 이런 코드는 분명 정상적으로 작동할겁니다.
하지만 printf("j=2");if(j == 2) 구문 밖에 있습니다. 원래는 j가 2인지 확인하고 j=2를 출력했어야 합니다. 하지만 그것과 상관 없이 j=2를 출력하게 됩니다.

printf("Hello World!");

stdio.h에 포함된 함수인 printf로 'Hello World!'를 출력하는 함수입니다.
printf()함수에 'Hello World!'를 파라미터로 전달했고, printf 함수가 실행되면서 인자를 출력하게 됩니다.

큰 따옴표를 사용하면 그 안의 내용은 char형 배열(array)을 의미합니다. 객체지향 언어에서는 char형 배열을 String이란 객체로 구현해뒀습니다. 나중에 객체지향 언어를 학습할 때 char배열과 String에 어떤 차이가 있는지 확인해보면 객체지향을 이해하는데 조금이나마 도움이 될 겁니다.

;

;(세미콜론)은 명령 한 줄이 끝남을 의미합니다.
C언어를 포함하여 세미콜론으로 명령을 구분하는 언어들은 컴파일러가 줄 내림을 인식하지 않습니다. 띄어쓰기도 한칸만 인식합니다.

#include <stdio.h>
int main(void){printf("Hello World!");return 0;}

그렇기 때문에 위와 같이 코드를 작성해도 결과는 같습니다. 다만, 주석이 있는 부분은 컴파일러가 무시하기 때문에 반드시 줄 내림을 해야 합니다.

return 0;

함수 실행 결과를 리턴하고 함수를 종료합니다. return 을 하고 나면 그 함수는 완전히 종료됩니다. 그러므로 return 이후의 명령은 모두 무시됩니다.
리턴값이 없는 void형식의 함수라도 return ;를 하면 함수가 종료됩니다.
if문으로 분기하면서 함수를 종료할 필요가 있다면 return으로 종료할 수 있습니다.

리턴 값은 처음 함수를 선언할 때 지정했던 형으로만 반환할 수 있습니다. 함수를 int로 선언했기 때문에 int형 값(value)인 0을 반환했습니다.

다 섞어서 응용하기

#include <stdio.h>
void main(){    //1+1 계산하기!
    int sum = calc(1,1);
    printf(%d, sum);
    //return;
}
int calc(int i, int j){
    return i+j;
}

이제 이 정도는 이해할 수 있겠죠? 갑자기 진도를 너무 많이 나갔나요?ㅎ...

main 함수가 실행이 되고, //1+1 계산하기! 는 무시됩니다.
int 형 변수인 sum을 선언합니다. sumcalc 함수를 실행한 결과를 저장합니다.
calc 함수는 파라미터로 1 두개를 받습니다. 큰 따옴표가 없기 때문에 int 형입니다. String은 큰 따옴표로 구분하지만, int 형을 지정하는 표시는 없기 때문에 변수 이름은 숫자로 시작할 수 없습니다.
calc 함수는 두 숫자를 i, j라는 이름으로 전달받습니다. 그리고 두 변수 ij를 더한 값을 return을 통해 반환합니다.
결과적으로 sum은 1+1을 연산한 값이 저장됩니다. printf는 digit형식의 문자를 출력합니다. sum이 그 digit형식에 해당하는 변수입니다.
main 함수는 반환 형식이 void이기 때문에 return문이 없어도 종료됩니다. 아무것도 반환하지 않는 return;로도 함수를 종료할 수 있습니다.

요약

  • Hello World를 출력할 수 있다.
  • #include 로 다른 코드를 참조할 수 있다.
  • // 또는 /* */로 주석을 작성할 수 있다.
  • {}로 코드를 묶을 수 있다.
  • return 을 사용할 수 있다. 함수를 선언할 때 반환되는 종류를 지정해야 한다.
  • 함수를 호출할 수 있다. 호출 할 때 소괄호로 파라미터를 전달할 수 있다.

'컴퓨터공학 > C' 카테고리의 다른 글

2장-2 변수의 크기와 오버플로우, 언더플로우  (0) 2019.05.05
4장-2 비트 연산과 연산 순서  (0) 2019.01.14
4장-1 산술 연산자  (0) 2019.01.14
3장 printf scanf 함수  (0) 2019.01.10
2장-1 자료형  (0) 2019.01.10

Tistory에 Flash Player 없이 사진 업로드하기

2018. 12. 16. 22:32

Flash Player

Flash Player는 개선할 수 없는 보안 취약점을 발견한 이후로 점차 줄여가는 추세이다. 구조상 보안 취약점을 개선할 수 없거나, 대체제가 있기 때문에 더 이상 개선의 필요성이 없는 것일수도 있다.

플래시를 최종적으로 개발하고 배포하는 Adobe사에서 Flash 대신 HTML5의 이용을 권장했고, Google 은 2017년부터 플래시를 이용한 Adsense 광고를 모두 제거하기로 했다. 네이버도 플래시를 이용한 보안 로그인을 제공했지만, 그 기능을 다시 없애버렸다.

인터넷 보안에 있어서 플래시를 사용하지 않는 것 외에 또 다른 양상이 있는데, 그것이 HTTPS의 사용이다. HTTPS는 HTTP 연결에 TSL 또는 SSL 보안을 사용하는 것이다. 이전에는 로그인 페이지에만 보안 전송을 사용했지만, 이제는 모든 페이지에 대해 보안 연결을 사용한다.

심지어 추억 속에만 있을 줄 알았던 사이트마저 보안 연결을 사용한다.

Google Chrome

이런 양상을 Chrome 브라우저가 빠르게 반영하고 있다. 최근 크롬이 업데이트 되면서 플래시 플레이어를 실행하려면 페이지에 방문할 때마다 플래시 플레이어의 실행을 허용해줘야 한다. 또한 어떤 페이지든 HTTPS를 사용하지 않는 사이트는 보안 경고를 표시하고 있다.

Tistory...

에디터가 바뀌면서 이제는 플래시를 사용하지 않는다.
하지만 (티스토리가 아닌) 몇몇 사이트에서는 여전히 플래시를 사용하고 있다. 이에 이 글을 그대로 남겨두기로 결정했다.(2019.10.28 개정)

그럼에도 불구하고 보안이 불필요한 페이지 중 일부는 여전히 플래시를 이용하고 있고, 그 중 하나가 티스토리의 사진 업로드이다.

사진 업로더에서 여전히 플래시 플레이어를 이용하는 이유는 사진 편집기능 때문이다. 블러 효과를 주거나, 스티커를 붙이거나, 크롭기능을 제공한다. 하지만 이런 기능이 필요 없거나 다른 프로그램에서 먼저 편집을 하는 경우라면 업로드 하고 저장하는 것 외에는 사용하지 않는다.

구지 사진 편집을 할 필요가 없다면 위험하게 플래시 플레이어의 실행을 허용하고, 사진을 업로드하지 않아도 된다.

보안 상의 이유로 플래시 플레이어의 실행이 복잡해지고 귀찮아졌다는 것을 기억하자!

이미지를 컴퓨터에서 복사해서 티스토리 에디터에 붙여넣으면 처음 저장할 때에는 사진이 보인다. 하지만 조금 시간이 지나면 이미지를 표시할 수 없다고 뜬다.

대신 파일 업로드를 사용하면 된다.

티스토리 에디터에서 파일 업로드를 클릭하고, 나타나는 팝업창에서 원하는 이미지를 선택하면 된다!

그러면 첨부파일로 인식하지 않고 이미지 업로드로 인식한다!

파일 업로드로 업로드한 이미지도 에디터에서 사진으로 인식되고, 기존에 이용할 수 있었던 간단한 편집이 가능하다.

예전에도 확인을 했던 것 같은데, 그 때는 파일 업로드가 되었었다.업데이트 된 것인지, 잘못 기억하고 있는지는 잘 모르겠다.

'끄적끄적' 카테고리의 다른 글

EV6 5000km 주행 후기  (2) 2022.02.16
블로그에 다크모드가 적용되었습니다!  (0) 2019.09.25
티스토리 초대장 나눠드립니다~  (38) 2016.12.28
래안텍 32C144 사용기  (0) 2016.10.07

1인미디어 시대, 게임방송, 나도 해볼까? part.4 : 비교와 결론

2018. 12. 16. 21:53

결론 및 종합

앞서 소개한 녹화 방식에 따라 차이를 비교해보기로 한다.

레인보우식스 시즈의 벤치마킹 기능을 이용했다. 그래픽 옵션은 프리셋 최상에, 텍스처는 Very High, 안티앨리어싱과 텍스처 필터링은 끈 상태로 진행했다. 인코딩은 6000kbps로 설정했다.

GPU는 소수연산에 효율적이기 때문에 그걸 강조하기 위해 전력소비도 비교해봤다. 전력소비는 소프트웨어 기반이기 때문에 정확하지 않을 수 있다.

게임 플레이

Time CPU [%] GPU [%] FPS [FPS] CPU Package Power [W] GPU Chip Power [W] Power Combined
max 61.3 100 157 62.268 149.082 206.956
min 20 87 90 46.378 123.465 174.619
avg 44.3 99.8421052631579 120.031578947368 54.7037263157894 135.544189473684 190.247915789474

CPU사용량은 최대 61%, 평균 44%이다.

FPS는 최대 157FPS, 최소 90FPS이고, 평균은 120FPS이다. 편차는 60FPS.

CPU와 GPU의 최대 전력소비량은 207W이다.

게임 플레이 + CPU 인코딩

Time CPU [%] GPU [%] FPS [FPS] CPU Package Power [W] GPU Chip Power [W] Power Combined
max 79.5 100 144 82.233 145.098 225.527
min 32.6 88 88 64.547 127.582 198.656
avg 60.3 98.7894736842105 112.252631578947 73.780 134.084557894737 207.864284210526

CPU사용량은 최대 80%, 평균 60%이다. 인코딩을 하지 않을 때에 비해 평균값은 16%차이, 최대는 19%가까이 차이난다.

FPS는 최대 144FPS, 최소 88FPS, 평균 112FPS이다. 녹화하지 않을 때보다 8FPS정도 떨어진다. 편차는 56FPS.

최대 전력소비는 225W이다. 18W차이이고, 10%가 늘어난 것이다.

게임 플레이 + GPU 인코딩

Time CPU [%] GPU [%] FPS [FPS] CPU Package Power [W] GPU Chip Power [W] Power Combined
max 88 100 141 66.048 143.406 200.306
min 18.7 87 88 50.234 128.52 182.815
avg 49.3631578947369 99.2210526315789 111.536842105263 56.3260315789474 135.858810526316 192.184842105263

CPU사용량은 최대 88%, 평균 49%이다.

FPS는 최대 141FPS, 최소 88FPS, 평균 111FPS이다. 프레임은 CPU인코딩에 비해 낮다. 편차는 53FPS.

최대 전력소비는 200W이다. 녹화하지 않을 때에 비해 오히려 줄어들었다.

뒤쪽에 그래프를 게시할 것인데, 녹화 때문에 프레임이 줄어드니 오히려 전력소비가 감소한 것으로 추측해 볼 수 있다.

게임 플레이 + NDI 전송

Time CPU [%] GPU [%] FPS [FPS] CPU Package Power [W] GPU Chip Power [W] Power Combined
max 51.6 100 147 80.255 145.574 215.345
min 13.7 90 94 32.769 131.828 169.890
avg 39.2178947368421 99 119.326315789474 54.938 139.662978947368 194.600694736842

CPU사용량은 최대 51%, 평균 39%이다. 평균값이 GPU인코딩과 별 차이 없다.

FPS는 최대 147FPS, 최소 94FPS, 평균 119FPS이다. 편차는 53FPS.

최대 전력소비는 215W이다. 다른 컴퓨터에서 인코딩을 해야 하니 결코 적은 수치는 아니다.

차트 비교하기

단순 게임 플레이

GPU사용량에 변화가 없이 안정적이다.

CPU사용량은 FPS에 따라 조금씩 변하고 있다. 13번째~41번째 스캔에서는 FPS에 따라 낮아지고, 거의 40%이하로 유지되고 있다.

CPU 인코딩

GPU사용량이 출렁이는 지점이 많다.

CPU사용량은 거의 60%대에서 변화한다. 갑자기 CPU 사용량이 떨어지는 지점은 인코딩 과부화로 영상 출력이 끊기는 부분이었다.

전체적인 FPS그래프가 인코딩을 하지 않을 때와 비슷하다.

GPU 인코딩

GPU 사용량이 출렁이는 지점이 많다. CPU 인코딩에 비해서는 적다.

65번째~75번째 스캔에서 CPU 사용량이 크게 증가한다. 위 표에서 GPU 인코딩 중의 CPU 사용량 최댓값이 88이어서 GPU 인코딩의 장점을 부각하지 못했다. 녹화 영상이나 게임 벤치마킹에서는 별 문제가 발견되지 않았다. 인코더가 불안정하거나 다른 프로세스가 갑자기 실행된 것일 수도 있다.

꼭짓점이 부드럽다고 해야할까, 전체적인 FPS 그래프가 좀 다르다.

NDI 인코딩

CPU 사용량이 다른 인코딩에 비해 안정적이다.

GPU 사용량은 자주 출렁인다. 하지만 다른 인코딩 방식처럼 크게 떨어지지 않는다.

결론

각각의 장단점이 있다. NDI는 리소스를 분산시킬 수 있지만 전체적으로는 가장 비효율적인 방법이다. 또한 좋은 네트워크 장비가 요구된다.

GPU는 전체적으로 CPU, GPU사용량에 큰 변화 없이 인코딩이 가능하다. 프레임 감소는 제일 크다.

CPU는 전력소비는 크지만, 대체로 안정적이다. FFmpeg라는 인코더가 안정적인 것도 있지만, CPU는 리소스 분배에 있어 비교적 여유롭기 때문이다.

1인미디어 시대, 게임방송, 나도 해볼까? part.1

1인미디어 시대, 게임방송, 나도 해볼까? part.2: OBS 설정

1인미디어 시대, 게임방송, 나도 해볼까? part.3: CPU 인코딩 설정

1인미디어 시대, 게임방송, 나도 해볼까? part.4 : 비교와 결론

+ Recent posts