전자제품에 쓰이는 중요장치 CPU와 MCU

차이를 좀 쉽게 알아볼까해요.

이미지 출처 : https://www.hackerschool.org/Sub_Html/HS_University/HardwareHacking/04.html

 

CPU(Central Processing Unit) : 중앙처리장치

컴퓨터를 배울때 CPU, CPU 많이 얘기하잖아요? CPU가 중앙처리장치라고 해서 우리의 두뇌같은 역할을 한다고 이런 추상적인 말은 많이 들었을 겁니다. 저처럼 아무 생각이 없는 사람은 논외로 하고 대부분의 사람은 머리가 시키는 대로 팔, 다리가 움직이고, 보고 들으면서 반응을 하게 됩니다. CPU가 바로 이러한 기억과 연산, 제어를 종합적으로 처리하는 장치라고 보시면 됩니다. CPU는 이런 기능을 하고는 있지만, 단독으로는 사용할 수 없습니다. 그래서 메인보드, 주기억장치(RAM), 보조기억장치(하드디스크), 버스 등 주변 장치들이 필요합니다.

CPU를 단일 IC칩에 직접시켜 만든 소자가 MPU(Micro Processing Unit)라고 합니다. 자, 간단하게 이름을 보시면 더 이해하기 편합니다. 아주 작은(Micro) 연산 장치(Processing Unit)이라는 의미로 CPU를 소형화시킨 것이라고 이해하시면 됩니다. MPU는 결국 CPU의 한 종류이지만, CPU가 MPU인것은 아닙니다. 하지만 요즘에는 이 둘을 통용해서 CPU라고 칭합니다. 또 CPU는 프로세서(Processor)라고도 합니다. 

이미지 출처 : https://computechlog.blogspot.com/2017/03/operating-system2.html

 

코어(Core)

이전에는 CPU하나가 모든 일을 전부 담당을 했었죠. 이제는 그렇지 않습니다. CPU안에서도 코어라는 개념을 두게 되었습니다. 코어는 일을 실제 하는 단위라고 보시면 됩니다. 그래서 코어가 두개이면 여러 일을 두 코어가 같이 처리하게 됩니다. 여러분들도 회사에서 혼자 일하는 것보다 두 사람이 일해서 빨리 끝나서 칼퇴해야되잖아요. 그런 개념입니다. 코어가 하나인 CPU는 싱글코어(Single Core)라고 합니다. 듀얼 코어 CPU는 물리적인 CPU 2개가 일하는 것과 비슷하다고 생각하시면 됩니다. 2개의 Core는 듀얼코어(Dual Core), 4개의 코어는 쿼드코어(Quad Core), 8개는 옥타코어(Octa Core)라고 합니다. 물론 클록수가 현저하게 차이나면 결과는 다르지만 코어가 많을 수록 좋습니다(한 사람이 100개의 일을 1시간에 처리하는 것이 2명이 10개의 일을 한시간에 처리하는 것보다 더 빠른 처리가 가능하겠죠.). 

 

MCU(Micro Controll Unit) : 마이크로 컨트롤러

MCU는 작은 컴퓨터라고 해서 one-chip microcomputer 라고 합니다. MCU는 일반 CPU에 비해 작으면서, 구동할 수 있는 최소한의 필수적 기능만을 포함하면서, 그로인해 연결할 수 있는 핀의수도 적습니다. 이렇기 때문에 전력을 적게 쓸 수 있고, 비용도 저렴합니다. 일반적인 CPU와는 다르게 MCU는 이름에서도 알 수 있듯이 칩 안에 메모리, 버스, EEPROM 등 우리가 알고 있는 컴퓨터를 작게 축소해놓은 것과 같습니다. 그래서 매우 작은 컴퓨터(microcomputer)라고 지칭하는 것이겠죠. 이를 줄여서 마이컴(MICOM, micro-computer)라고도 합니다. 그렇기 때문에 기능이 그렇게 많이 정해져있지 않은 작은 전자제품(차량용 ECU 등)에 많이 쓰입니다. 

EEPROM(Electrically Erased Programmable ROM) : 전기적으로 프로그래밍할 수 있는 비휘발성 메모리입니다. 이곳에다가 프로그래밍하여 값을 쓰게 되면 EEPROM에 기억이 됩니다. 비휘발성이라는 뜻은 직접 데이터를 지우지 않는 이상은 데이터가 보존된가는 뜻입니다. 시스템이 꺼져도 유지가 된다는 겁니다. 그렇기 때문에 MCU에서는 EEPROM이 하드디스크를 대체한다고 생각하시면 될 것 같습니다. 물론 대용량은 안되구요. 그리고 사용횟수에 제한이 있다는 점.

아래는 간략하게 CPU와 MCU를 비교한 그림입니다. 이와 같이 CPU가 주기억장치, 보조기억장치 등을 따로 연동시켜야하지만, MCU는 이를 포함하고 있죠. 아주 미니미니하게 포함하고 있다는 것입니다.

이미지 출처 : https://www.hackerschool.org/Sub_Html/HS_University/HardwareHacking/05.html

 

지금까지 CPU와 MCU에 대해서 간단히 알아보았습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

https://www.acmicpc.net/problem/17299

 

17299번: 오등큰수

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째에 수열 A의 원소 A1, A2, ..., AN (1 ≤ Ai ≤ 1,000,000)이 주어진다.

www.acmicpc.net

 

설명

문제에서 말하는 오등큰수는 이 수가 나타난 수보다 더 많이 나타난 수 중 가장 가까운 오른쪽 수를 의미합니다. 특정 수의 오등큰수가 없을 수가 있습니다. 이런 경우 그 수의 오등큰수는 -1로 정해줍니다. 문제의 예제는 이렇습니다.

A = [1, 1, 2, 3, 4, 2, 1]

우선 F(Ai)는 빈도수라고 하여 차례대로 빈도수를 구하면 이렇게 되겠네요. 

F(1) = 3, F(2) = 2, F(3) = 1, F(4) = 1

이렇게 빈도수가 구해지면 NGF라는 오등큰수를 구할 수 있습니다. NGF(1)을 구하는데, 1보다 빈도수가 큰 수는 없으므로 NGF(1) = -1이 됩니다. NGF(2) = 1이됩니다. 왜냐면 2보다 빈번하게 나타난 수는 1이고 이 수가 가장 가까운 오른쪽에 있는 수가 되기 때문이죠. 예제의 답은 그래서 아래와 같습니다.

-1 -1 1 2 2 1 -1

 

풀이

이 문제는 스택으로 풀 수 있습니다. 우선 F(Ai)를 구해야하는데요. 이건 간단합니다. 비어있는 배열 F에 입력받은 수를 index삼아서 하나씩 증가시키면 되기 때문입니다. 이제 F를 구했으면 스택을 이용해서 본격적으로 문제를 풀어봅시다. 

아래의 블록은 F를 나타냅니다. 높을 수록 F가 높다는뜻입니다.

왼쪽의 빨간 블록의 오등큰수를 구하려면 오른쪽에서 찾아야합니다. 그런데 자신보다 큰 오등큰수가 두개가 있습니다. 4와 3이 있네요. 이때 빨간색의 오등큰수는 4가 됩니다. 이때 3은 이 다음 왼쪽 수의 오등큰수가 될 수 있을까요? 이미 4한테 막혀있고 4가 더 큰값이기 때문에 가능성이 없습니다. 그래서 3은 지워버립니다. 대신 4가 왼쪽의 오등큰수가 될 수 있는것을 알 수 있네요.

따라서 현재 자신보다 큰 값의 오등큰수가 존재하면 스택에 저장하고 작거나 같으면 스택에서 지워버리는 식으로 문제를 풀 수 있습니다. 

 

코드

문제 해답에 대한 전체코드는 아래와 같습니다.

#include <cstdio>
#include <stack>
using namespace std;

int n;
int F[1000001], ans[1000001], nums[1000001];
stack<int> st;
int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d", &nums[i]);
		F[nums[i]]++;
	}

	for (int i = n - 1; i >= 0; i--) {
		int number = nums[i];
		int height = F[number];
		while (!st.empty()) {
			int topNum = nums[st.top()];
			int topHeight = F[topNum];
			if (height >= topHeight) st.pop();
			else break;
		}
		ans[i] = -1;
		if (!st.empty()) ans[i] = nums[st.top()];
		st.push(i);
	}
	for (int i = 0; i < n; i++)
		printf("%d ", ans[i]);
	printf("\n");

}

반응형
블로그 이미지

REAKWON

와나진짜

,

https://www.acmicpc.net/problem/3986

 

3986번: 좋은 단어

이번 계절학기에 심리학 개론을 수강 중인 평석이는 오늘 자정까지 보고서를 제출해야 한다. 보고서 작성이 너무 지루했던 평석이는 노트북에 엎드려서 꾸벅꾸벅 졸다가 제출 마감 1시간 전에

www.acmicpc.net

설명

같은 글자를 짝지어 주는 문제입니다. 아치형으로 서로 짝을 지어주고 모든 짝을 지어줄 수 있다면 그 단어는 좋은 단어라고 합니다. 단, 아치형이 서로 겹치지 않게 짝을 이뤄줘야합니다. 아래의 그림을 보면 바로 이해가 가실겁니다.

다음의 경우에는 좋은 단어입니다. 아치형이 겹치지 않죠

 

좋은단어

 

아래의 경우는 아치가 겹치므로 좋은단어가 아닙니다.

안좋은 단어

 

풀이

이 문제의 힌트는 바로 스택을 활용한 괄호 짝 맞추기 문제입니다. 가장 최근에 나온 짝이 자신의 짝인지 확인하는 문제와 컨셉이 같은데요. 여기서 괄호( '(', ')' )만 살짝 'A','B'로 바꾼것일 뿐입니다.

ABBA를 예로 들겠습니다.

1. A는 스택이 이미 비어있으니 스택에 집어넣습니다. (현재 스택 - A)

2. B는 스택 꼭대기 글자가 A이므로 B는 스택이 저장합니다. (현재 스택 - A B)

3. B는 스택 top의 글자가 B이므로 이전의 B를 스택에서 pop합니다. (현재 스택 - A)

4. A는 현재 스택 top의 글자와 같으므로 A를 스택에서 pop합니다. (현재 스택 - ' ' )

 

코드

아래는 전체 문제 풀이 코드입니다.

#include <cstdio>
#include <stack>
#include <string.h>
using namespace std;

char str[100001];
int n;
int main() {

	int ans = 0;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%s", str);
		stack<char> st;
		int m = strlen(str);
		for (int j = 0; j < m; j++) {
			if (!st.empty()&&st.top()==str[j]) st.pop();
			else st.push(str[j]);
		}
		if (st.empty()) ans++;
	}
	printf("%d\n", ans);
}

 

어렵지 않게 코드가 이해가실거라 생각합니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

BOJ 1968 트리의 지름

https://www.acmicpc.net/problem/1967

 

1967번: 트리의 지름

파일의 첫 번째 줄은 노드의 개수 n(1 ≤ n ≤ 10,000)이다. 둘째 줄부터 n-1개의 줄에 각 간선에 대한 정보가 들어온다. 간선에 대한 정보는 세 개의 정수로 이루어져 있다. 첫 번째 정수는 간선이 연

www.acmicpc.net

 

 

이 문제는 가중치가 주어진 트리에서 leaf과 leaf 사이, 혹은 루트와 leaf 사이의 모든 가중치의 합 중 가장 큰 값을 구하는 문제입니다. 

예제에서 주어진 그래프에서는 45의 값으로 답이 됩니다. 빨간색 선을 따라가면 9 - 5 - 3 - 6 - 12의 노드를 지나게 되고 가중치 합은 45라는 것을 알 수 있습니다. 이 값보다 큰 값은 존재하지 않습니다.

 

이 문제는 트리를 사용하여 풀 수 있습니다. 트리의 잎부터 시작해 올라오면서 서브트리의 양쪽 잎까지의 가중치 합서브트리의 루트와 잎까지 합 중 가장 큰 값으로 계속 업데이트를 하면 됩니다.

 

설명 및 전체 코드

입력받은 값으로 트리를 구성해야하는데, 이때 Node라는 구조체로 노드를 만들겠습니다. 이 Node 구조체는 자식 Node들을 가지고 있고, 부모로 향하는 weight값을 가지고 있는 구조체로 정의되어 있습니다. 노드의 자식은 여러개일 수 있으므로 vector를 사용했습니다.

typedef struct Node {
	vector<Node*> children = vector<Node*>();
	int weight;
};

 

기저사례로는 자식이 없을 경우 바로 부모로 향하는 weight를 return해줍니다. 더 이상 진행할 필요가 없으니까요. 그리고 자식이 한개일 경우에는 weight와 그 밑으로 향하는 큰 값중 하나를 더해서 반환해주면 됩니다.

if (root->children.size() == 0) return root->weight;

if (root->children.size() == 1) 
	return root->weight + traverse(root->children[0]);

 

그 외에는 자식이 둘 이상이겠죠. 양쪽으로 분기되는 값 중 가장 큰 값 두개를 선택해서 더해준 값현재까지 업데이트된 값하고 비교하여 둘 중 가장 큰 값으로 업데이트해줍니다. 현재까지 업데이트된 값을 value라고 임시로 정해줍시다.

이 과정을 루트로 올라올때까지 진행해주면 답을 구할 수 있습니다. 아래는 정답 코드입니다.

#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;

int n;
typedef struct Node {
	vector<Node*> children = vector<Node*>();
	int weight;
};

Node nodes[10001];
int value = 0;
int traverse(Node *root) {
	//자식이 없는 경우 부모쪽으로 가는 간선의 값 return
	if (root->children.size() == 0) return root->weight;
	//자식이 하나일 경우 자신의 weight을 더해 return
	if (root->children.size() == 1) 
		return root->weight + traverse(root->children[0]);
	
	//여기서부터는 자식이 둘 이상
	vector<int> weights = vector<int>(root->children.size());

	//자식들 모두 다 돌때까지
	for (int i = 0; i < root->children.size(); i++) 
		weights[i] = traverse(root->children[i]);
	
	//오름차순으로 정렬
	sort(weights.begin(), weights.end());

	//가장 큰 값을 가진 두 값을 더해서 value와 비교하여 value업데이트
	value = max(weights[root->children.size()-1]
		+ weights[root->children.size()-2], value);
	
	//현재의 weight와 다 더한값의 제일큰 weights값을 더하여 return
	return root->weight+weights[root->children.size()-1];
}

int main() {
	
	scanf("%d", &n);
	nodes[1] = Node();
	nodes[1].weight = 0;
	n--;
	for (int i = 0; i < n; i++) {
		int from, to, weight;
		scanf("%d %d %d", &from, &to, &weight);
		nodes[to] = Node();
		nodes[to].weight = weight;
		nodes[from].children.push_back(&nodes[to]);
	}
	int ret = max(value, traverse(&nodes[1]));
	printf("%d\n", ret);
}

 

이상으로 트리에 관한 문제를 풀어보았습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

BOJ 17298 오큰수

https://www.acmicpc.net/problem/17298

 

17298번: 오큰수

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에 수열 A의 원소 A1, A2, ..., AN (1 ≤ Ai ≤ 1,000,000)이 주어진다.

www.acmicpc.net

 

문제 설명

어떤 수열이 있는데요. 이 수열의 한 원소를 기준으로 오른쪽에 있는 자신보다 큰 원소 중 가장 왼쪽에 있는 수가 오큰수라고 합니다. 이때 수열에 대해 모든 원소의 오큰수를 구하는 것이 문제입니다. 

이때 오큰수가 없는 경우도 있습니다. 가장 큰 원소이거나 가장 오른쪽에 있는 원소는 오큰수가 없을 수가 있죠. 이때는 -1로 오큰수를 정해줍니다.

예를 들어 3 5 2 7 6 8이라는 수열이 있다면 5 7 7 8 8 -1이 해답이 됩니다. 5의 오른쪽에 있는 큰 수들은 7 6 8이 있는데, 이때 가장 왼쪽에 있는 수가 7이므로 5의 오큰수는 7이 됩니다. 여기까지 이해를 했으면 이제 문제를 풀어보겠습니다.

 

풀이

이 문제에 대해서 왼쪽부터 시작하지 말고 오른쪽부터 시작하면 접근이 쉽습니다. 가장 오른쪽에 있는 오큰수는 없으니까 -1로 시작하게 됩니다.

 

가장 최근에 큰 수(A)을 기억하고 있다가 그것보다 작은 수(B)가 나오게 되면 B의 오큰수는 A가 됩니다. 이때 B는 그냥 버리면 안됩니다. 왜냐면 B가 왼쪽 어떤 C의 오큰수 일 수 있으니까요. 그래서 B를 기억하고 있어야합니다. 아래와 같은 상황에서 B는 C의 오큰수가 됩니다. 이때 C도 역시 기억하고 있어야됩니다. C도 어떤 수의 오큰수가 될 수 있게 되니까요. 아래의 그림은 이해하기 쉽게 도형의 크기로 수의 크기를 표현하였습니다.

 

하지만 C가 B보다 큰 경우 B는 오큰수가 아니게 되겠죠? C 이전의 수들은 B가 필요없게 됩니다. 왜냐면 C가 B보다 크면서 더 왼쪽에 있으니까요. 그래서 B는 이제 기억에서 사라지고 C를 기억하고 있어야됩니다.

 

자, 이제 뭔가 좀 감이 잡힐 수 있습니다. 가장 최근에 것을 상황에 따라 보관하고 있다가 비교하거나 버리고 하는 것을 알 수 있게 됩니다. 그래서 스택이라는 자료구조가 이 문제에서 사용됩니다.

Stack에는 가장 오른쪽부터 시작해서 오큰수들이 들어가게 됩니다. 이때 value는 현재 수열의 비교할 값을 말한다고 합시다. 스택의 가장 위(top)는 어떠한 오큰수가 들어가게 되는데, value보다 작다면 이 오큰수는 쓸모가 없게 되므로 스택에서 pop하면 됩니다. stack의 top이 value보다 더 클때까지 계속 pop하게 되면 결국 stack이 비게 되거나, value보다 큰 오큰수가 존재하게 됩니다. 

이때 스택이 비어있다면 value의 오큰수가 없다는 것, 비어있지 않다면 스택에 있는 top의 값이 오큰수가 되게 됩니다.

위 설명을 따른 예제에 대한 그림 풀이가 아래에 있습니다. 참고하시기 바랍니다.

 

전체코드

전체 코드는 아래와 같습니다. 주석처리하여 추가설명하였습니다.

#include <cstdio>
#include <stack>

using namespace std;
int n, numbers[1000001], ans[1000001];

int main() {

	stack<int> st = stack<int>();

	scanf("%d", &n);

	for (int i = 0; i < n; i++) 
		scanf("%d", &numbers[i]);
	
	st.push(numbers[n - 1]);	//가장 오른쪽에 있는 수는 stack에 push
	ans[n - 1] = -1;			//가장 오른쪽의 있는 수는 오큰수가 없으므로 -1
	for (int i = n - 2; i >= 0; i--) {	//거꾸로 for문
		int value = numbers[i];			//stack의 top과 비교할 숫자
		while (!st.empty() && value >= st.top()) {	//스택이 비어있지 않고, 스택의 top이 value보다 작거나 같으면
			st.pop();								//그 수는 버린다.
		}
		//이렇게 되면 두가지 상황이 발생하는데,
		//스택이 비어있으면 value 오른쪽에 오큰수가 없다는 것으로 value의 오큰수는 -1
		//숫자가 남아있다면 value보다 큰 수가 있는 것으로 오큰수는 st.top
		ans[i] = st.empty() ? -1 : st.top();
		
		//그리고 value를 stack에 push
		st.push(value);
	}

	for (int i = 0; i < n; i++) printf("%d\n", ans[i]);
	printf("\n");
}

 

이상으로 포스팅을 마치겠습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

ASCII to Hex

간단히 말해서 입력이 String인데, 이것을 16진수 값으로 변환하는 코드를 구현해보도록 하겠습니다.

간단히 "CAFECAFE0102"라는 이런 문자열을 입력을 받았는데 16진수로 변환하고 싶은 것입니다.  즉, 0xCA, 0xFE, 0xCA, 0xFE, 0x01, 0x02의 숫자 배열로 입력을 변환하는 것이죠.

가장 간단하게 구현하기 위해서 매우 정상적인 입력값만 들어온다고 가정하겠습니다. 그러니까 16진수의 범위에 있지 않은 글자('G'를 넘어가는 알파벳)는 들어오지 않는다고 가정하겠습니다.

설명은 아래의 주석으로 대체하도록 하겠습니다.

 

 

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


//입력의 str은 모두 대문자인 16진수 변환가능한 문자열이라고 가정
//size는 str의 크기
//hex는 변환된 16진수 배열
unsigned int ascii_to_hex(const char* str, size_t size, uint8_t* hex)
{
    unsigned int i, h, high, low;
    for (h = 0, i = 0; i < size; i += 2, ++h) {
        //9보다 큰 경우 : 알파벳 문자 'A' 이상인 문자로, 'A'를 빼고 10을 더함.
        //9이하인 경우 : 숫자 입력으로 '0'을 빼면 실제 값이 구해짐.
        high = (str[i] > '9') ? str[i] - 'A' + 10 : str[i] - '0';
        low = (str[i + 1] > '9') ? str[i + 1] - 'A' + 10 : str[i + 1] - '0';
        //high 4비트, low 4비트이므로, 1바이트를 만들어주기 위해 high를 왼쪽으로 4비트 shift
        //이후 OR(|)연산으로 합
        hex[h] = (high << 4) | low;
    }
    return h;
}

int main() {
    char str[128] = "CAFEcafe0102";
    uint8_t hex[128] = { 0, };
    size_t size = strlen(str);
    int i;

    //소문자 cafe를 대문자로 만들기 위해 strupr함수 사용
    strupr(str);
    //hex에 실제 16진수의 값이 들어감
    ascii_to_hex(str, size, hex);
    
    //size는 반으로 줄어듦, ex) hex[0]=0xCA;
    for (i = 0; i < (size/2); i++)
        printf("0x%02X\n", hex[i]);
}

 

1바이트의 unsigned int 자료형인 uint8_t를 사용하기 위해서는 stdint.h를 include해야합니다.

결과

 

 

사실 ascii에 대한 개념과 char 자료형이 정수처럼 계산할 수 있다는 사실, 그리고 비트 연산(SHIFT, OR) 연산만 이해하고 알고 있다면 그렇게 어려운 구현은 아니라고 생각이 됩니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

mount

mount라는 뜻은 '올라타다'라는 영어의 뜻을 가지고 있습니다. 

리눅스에서도 비슷한 의미로 쓰이는 것 같아요. 왜 비슷한 의미로 쓰이는 지는 포스팅을 보시면서 느껴보시기 바랍니다. 

요즘 컴퓨터들보면 CD를 넣었을때나 DVD를 넣었을때, 그리고 USB를 넣었을때 시스템이 자동으로 인지하고 실행시키거나 읽어주죠? 너무 편리합니다. 하지만 이전에 시스템, 혹은 가벼운 전자 제품에 리눅스와 같은 커널을 사용할 경우 시스템이 저절로 인식하지 못할 수도 있습니다. USB나 CD를 넣었을때 인식하고 사용하기 위해서 필요한 명령어가 바로 mount 명령입니다. 간단하게 USB를 mount해보도록 합시다.

USB mount 해보기

0. 먼저 mount를 그냥 치게되면 현재 마운트된 시스템의 하드웨어들이 보이게 될겁니다. 그 중 sda, sdb 등이 디스크라고 보시면 됩니다. 아래는 저의 시스템에 마운트된 sda입니다.

1. 옹골진 USB를 일단 꽂습니다.

2. fdisk -l로 USB 메모리가 꽂힌 블록 디바이스 파일(/dev/ 밑에 있는 파일) 이름을 확인합니다. sda, sdb, sdc,, ... 처럼 앞에 'sd'가 붙는 것이 일반적입니다. 가장 간단하게 확인할 수 있는 것은 새로 생긴 sd*를 보고 용량을 보면 알 수 있습니다. 참고로 USB가 128G 용량이라고 해서 128G 온전히 다 잡히지는 않습니다. 한 116G정도로 약간 적게 잡힙니다.

제 경우에는 sdb1이라는 이름입니다.

3. 아래의 명령으로 mount합니다. 

mount [-t file_system_type] [USB로 인식된 블록 디바이스 파일] [마운트 지점]

앞에 file_system_type은 생략 가능한데, 리눅스가 그 파일 시스템을 지원할때 가능합니다. 그것이 아니라면 명시적으로 지정해주어야합니다. 물론 리눅스 파일 시스템인 ext계열 파일 시스템은 지원가능하지만 윈도우즈(ntfs)의 파일 시스템의 경우나 CD-Rom을 위한 파일 시스템(iso9660)은 지원하지 않을 수 있습니다.

저의 usb의 경우에는 FAT32 파일 시스템을 사용하고, 혹시 지원하는지 확인하기 위해서 그냥 -t 옵션 사용하지 않고 해본 결과 잘되는 것을 확인했습니다.

mount /dev/sdb1 /tmp/usb

 

파일 시스템(File System)이란?

사실 말이 어려워서 File System이지, 간단하게 생각하면 별거 없습니다. 자, 여러분이 책을 정리할때를 생각해보세요. 저같은 경우는 일단 책을 보지 않습니다.  어떤 사람은 책꽂이 맨윗줄은 만화책, 그리고 그 다음 줄에는 전공서적, 그리고 그 다음 줄은 소설책 등으로 카테고리를 정리할 수 있습니다. 그리고 각 카테고리마다 또 사전순으로 책을 잘 정리하겠죠. 그래서 만약 "원피스"라는 만화책을 찾으려면 맨 윗줄, 'ㅇ'으로 시작되는 만화책 부분을 찾으면 되겠군요. 그리고 다 본후에는 다시 원위치에 꽂아서 넣으면 됩니다. 이렇게 책을 관리하는 나름대로의 체계가 있듯이 컴퓨터도 파일을 정리할때 체계가 존재합니다. 그래서 어떻게 파일을 삭제하고, 파일을 기록하고, 찾는지 등을 쳬계화해놓은 것이 File System이라고하며 저장 매체나 OS마다 각기 다른 파일 시스템을 사용하고 있습니다. 여러분이 책 정리할때 반드시 위의 사람과 같이 정리하지는 않듯이 말이죠.

여기서 간략하게 OS마다 어떤 파일 시스템을 갖는지만 살펴보도록 하지요.
Linux : ext, ext2, ext3, ext4, xfs
Windows : FAT12, FAT16, FAT32, exFAT, NTFS
Mac : HFS, HFS+

한가지 자주쓰이는 옵션은 -r옵션인데요. 파일을 오직 읽기(read-only)만 하는 용도로 사용하겠다는 옵션입니다. 예를들어 CD겠죠? CD에 파일을 추가하거나 삭제할 수는 없으니까요. 또한 민감하거나 삭제되지 말아야할 경우에 쓰이기도 합니다. -o ro와 같은 옵션이기도 합니다.

혹시 mount에서 read-only로 마운트된 것을 read, write 가능하게 바꾸려면 아래의 명령을 사용하시면 됩니다. 단, rw가 가능한 파일시스템에 대해서만 입니다. CD-ROM은 당연안됩니다.

mount -o rw,remount [마운트지점]

4. 마운트를 해제할 경우 umount 명령을 사용하여 해제할 수 있습니다.

umount [마운트 지점]

그래서 umount /tmp/usb라는 명령어로 마운트 해제할 수 있습니다.

 

여기서 한가지 의문점이 들지 않나요?

왜 굳이 mount해서 쓰는 거지? 어차피 /dev/sdb1와 같이 자동으로 인식해주는데, 뭐하러 디렉토리를 만들고 mount하고 쓰는 것일까?

여러분이 기억하셔야할 점은 리눅스에서 모든 장치들은 파일로 취급한다는 점입니다. sdb 역시 파일로 인식되지요. 그래서 'cd /dev/sdb'와 같은 명령으로 디렉토리같이 사용할 수 없다는 것입니다. 또 어떻게 파일을 추가하고, 파일을 추가할때 이름은 몇자까지 제한이 되며, 파일을 삭제할때는 어떻게 삭제하는지도 모릅니다. 이러한 동작 방식들은 파일 시스템에서 정의하고 있기 때문입니다. 그렇기 때문에 우리는 파일 시스템을 명시하여 파일의 동작(operation)을 알려주면서 USB를 사용할 수 있게 됩니다(물론 아까도 말씀했다시피 지원되는 파일 시스템은 명시적으로 지정해주지 않아도 알아서 파일 시스템을 찾아줍니다.).

그래서 mount 명령어로 인식된 sdb에 대해서 파일이 동작하는 방식은 파일 시스템을 딱 알려주고 이것을 /tmp/usb 디렉토리에 얹어 쓰겠다고 리눅스에게 말해주는 것이죠. (아래 그림에서 sdb가 아니고 sdb1입니다.)

 

반응형
블로그 이미지

REAKWON

와나진짜

,

ADMOB Error 3 : ad failed to load 3

애드몹을 쓰던 중, 잘만 나오던 광고가 아래 오류가 어느 순간 갑자기 나오기 시작하면서 테스트 배너 광고가 나오지 않았습니다. 그래서 어떤 오류인가 했더니 아래와 같은 오류였네요.

광고 요청은 성공(그러니까 구현에는 문제가 없다는것)했는데, 인벤토리에 광고가 없어서 광고를 못보여준다고합니다. 어, 하지만 스토어에 있는 내 앱에는 광고가 잘 나오는데요? 저의 증상은 출시 앱에는 광고가 잘 나오고, 오히려 테스트 ID를 써서 앱 개발할때에는 테스트 광고가 나오지 않는 것이었습니다.

구글링을 해보니 광고가 잘 나오다가 이와 같은 증상을 겪은 사람들의 공통점이 어느 순간 갑자기 안나온다는 것이었습니다. 몇가지 조치할 수 있는 방법이 있는데 한번 확인해보세요.

 

1. 구글 플레이 스토어에 구글 광고가 포함되어있다고 체크했는지 확인

구글 플레이 콘솔 -> 출시 앱 선택 -> 왼쪽 하단 앱 콘텐츠 선택 -> 중간쯤에 광고란 확인

 

2. 부정클릭이 발생했는지 확인. 이 경우 30일간 광고가 일시 정지되거나 영구정지 될 수도 있다고 하네요. 어떤 경우라고 자신의 광고를 클릭하지 맙시다.

 

3. app-ads.txt를 추가하고 테스트 기기 등록했는지 확인

저의 경우는 3번이었습니다. 갑자기 안나온것이라 생각했는데, 기억을 더듬어 보니 app-ads.txt를 추가한 이후에 테스트 광고가 안나오는 것이었어요. 역시 나만 갑자기라고 생각했나봐요..

이것에 대한 해결 방법에는 두가지가 있습니다.

1. app-ads.txt에 아래의 구문 추가

google.com, pub-3940256099942544, DIRECT, f08c47fec0942fa0

이 방법은 간단하긴 하지만 반영되기까지 최대 24시간이 있어야한다는 군요.

 

2. Test Device 등록 

이 방법은 바로 적용이 가능한 방법입니다. 저는 바로 확인을 원하기 때문에 2번, 테스트 기기를 등록하는 방법을 사용할 것입니다.

그래서 이번 포스팅은 안드로이드 테스트 디바이스를 설정하는 방법에 대한 것입니다. 이때 테스트 광고 ID를 쓰는 것이 아니고 발급받은 ID를 사용해야한다는 점입니다.

아래의 코드를 추가하세요.

	MobileAds.initialize(this);
        //앱 출시시 반드시 주석 처리
        List<String> testDeviceIds = Arrays.asList("Your Device ID");
        RequestConfiguration configuration =
                new RequestConfiguration.Builder().setTestDeviceIds(testDeviceIds).build();
        MobileAds.setRequestConfiguration(configuration);
        //앱 출시시 반드시 주석 처리

Arrays.asList에는 자신의 고유 Device ID를 기재해주어야합니다. 어떻게 아냐구요?  안드로이드 스튜디오 Logcat에 나와있습니다.

I/Ads: Use RequestConfiguration.Builder.setTestDeviceIds(Arrays.asList("33BE2250B43518CCDA7DE426D04EE231"))
to get test ads on this device."

Logcat에서 RequestConfiguration을 검색하여 찾아보세요.

 

다음으로 중요한 것은 Test 광고 ID를 사용하는 것이라고 했죠? adUnitId를 발급받은 광고 ID로 바꿔주세요. 

banner_ad_unit_id

        <com.google.android.gms.ads.AdView
            xmlns:ads="http://schemas.android.com/apk/res-auto"
            android:id="@+id/ad_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_alignParentBottom="true"
            ads:adSize="SMART_BANNER"
            android:layout_alignParentEnd="true"
            ads:adUnitId="@string/banner_ad_unit_id" />

 

이렇게 하면 테스트 광고가 잘 나올겁니다. 

반응형
블로그 이미지

REAKWON

와나진짜

,

Module Import 버그 해결 방법

Android Studio Arctic Fox의 버그인지는 모르겠지만 Native Template의 모듈을 가져오는게 안됩니다. Native Template를 받으려면 아래의 주소에서 다운받으시면 됩니다.

https://github.com/googleads/googleads-mobile-android-native-templates

 

GitHub - googleads/googleads-mobile-android-native-templates

Contribute to googleads/googleads-mobile-android-native-templates development by creating an account on GitHub.

github.com

 

압축풀면 아래와 같이 nativetemplates라는 폴더하나가 생기게 됩니다. 이 폴더를 Import하는 것이 목표입니다.

 

원래는 아래와 같이 Import하시면 됩니다.

일반적인 경우

1. Android Studio를 켜서 File -> New -> Import Module를 누릅니다.

 

2. 아까 다운 받은 그 폴더를 설정해주고 Finish를 누르면 됩니다. 아래 사진에서는 Module Name이 :nativetemplate이지만 :nativetemplates로 해주셔야해요. 저는 어떤 이유인지는 모르겠지만 Finish가 활성화되어있지 않고 이 방법으로 모듈을 Import할 수 없었습니다.

 

다른 방법으로 Import

1. 아래와 같이 다운받은 폴더를 같은 프로젝트에 둡니다. 

 

2. settigs.gradle 파일에 nativetemplates를 추가시켜줍니다. 

프로젝트 수준의 settigns.gradle

include ':app',':nativetemplates'

 

3. 한가지 추가적으로 nativetemplates의 build.gradle의 버전을 맞춰줘야합니다. 자신의 기존 프로젝트와 같이 sdk버전을 맞춰주시면 됩니다. 그리고 sync를 눌러주세요.

nativetemplates의 build.gradle

기존 프로젝트 sdk 버전과 일치시켜야함

 

여기까지 완료했으면 아래와 같이 implementation해도 오류나지 않을 겁니다. 

implementation project(':nativetemplates')

 

아래와 같이 TemplateView를 가져올 수 있는것 확인할 수가 있네요.

 

도움이 되셨다면 좋아요부탁드립니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

Calendar 클래스

Calendar 클래스는 날짜와 시간을 다루기 위해서 Date와 함께 많이 쓰이는 클래스 중 하나입니다. Calendar를 사용하기 위해서는 java.util.Calendar를 import 시켜야합니다. 

가장 기본적으로 현재 날짜와 시간을 가져올 수도 있고, 특정 시간으로 시간을 되돌리거나 뒤로갈 수도 있습니다. 이는 Calendar에서 제공하는 상수들을, 예를 들면 YEAR, MONTH, DAY_OF_MONTH 등을 이용하여 설정할 수 있습니다. 어떤 필드가 있는지 볼까요? 아래의 필드를 이용해서 get, set 메소드를 통해 값을 얻어오거나 설정할 수 있습니다.

Field(static int) 설명
YEAR 년도를 나타냅니다.
MONTH 월을 나타내는데, 이때 1월을 상수 0으로 대응이 됩니다. 그래서 실제 월을 구할때는 +1을 해주어야합니다.
DATE, DAY_OF_MONTH 월의 날짜를 의미합니다.
DAY_OF_WEEK 일주일에 해당되는 요일을 의미합니다. 일요일부터 시작이며 일요일은 1입니다. 수요일은 4의 값을 갖습니다.
HOUR 시간을 표시하는데 12시간 단위의 시간을 의미합니다.
HOUR_OF_DAY 시간을 표시하는데 24시간 단위의 시간을 의미합니다.
MINUTE 분을 의미하는 필드입니다.
SECOND 초를 의마하는 필드입니다.
MILLISECOND 밀리 세건드 단위를 의미하는 필드입니다.

 

아래의 필드는 get, set으로 얻지는 않고 비교할때 사용할 수 있습니다.

Field(static int) 설명
JANUARY 1월을 나타냅니다. 0의 값을 갖고 있습니다.
월을 나타내는 필드는 전부 대문자입니다. 2월을 FEBURARY, 3월은 MARCH입니다. 각 숫자는 월-1에 값을 갖습니다.
SUNDAY 일요일에 해당하는 값이며 1을 가집니다. 요일을 나타내는 상수도 마찬가지로 전부 대문자로 표시할 수 있으며 SUNDAY의 1부터 SATURDAY의 7까지 나타낼 수 있습니다.

 

이번 포스팅에서는 Calendar클래스를 어떻게 사용하고 다루는지 예를 통해서 설명하도록 하겠습니다.

1. 현재 시간의 정보를 표시하는 예제

    public static void main(String[] args){
    	Calendar cal=Calendar.getInstance();	//getInstance()로 객체 생성
    	System.out.println("현재 날짜:"+cal.get(Calendar.YEAR)+"-"+(cal.get(Calendar.MONTH)+1)+"-"+cal.get(Calendar.DAY_OF_MONTH));
    	System.out.println("일주일 중 오늘은 "+cal.get(Calendar.DAY_OF_WEEK)+"번째 요일 (1은 일요일)");
    	System.out.println("일년 중 오늘은 "+cal.get(Calendar.DAY_OF_YEAR)+"번째 날");
    	System.out.println("현재 시간 "+cal.get(Calendar.HOUR_OF_DAY)+":"+cal.get(Calendar.MINUTE)+":"+cal.get(Calendar.SECOND)+":"+cal.get(Calendar.MILLISECOND));
    	System.out.println("현재 시간 "+cal.get(Calendar.AM_PM)+":"+cal.get(Calendar.MINUTE)+":"+cal.get(Calendar.SECOND));
    	System.out.println("이번 주는 일년 중 "+cal.get(Calendar.WEEK_OF_YEAR)+"번째 주");
    }

 

Calendar객체는 new 키워드로 객체를 생성할 수 없고 getInstance() 메소드로 객체를 생성할 수 있습니다. 위 예제는 현재 날짜와 시간을 나타내며 위에 설명한 표의 데이터가 어떤 값을 나타내는 지 확인하는 예제입니다.

현재 날짜:2021-10-20
일주일 중 오늘은 4번째 요일 (1은 일요일)
일년 중 오늘은 293번째 날
현재 시간 19:36:39:959
현재 시간 1:36:39
이번 주는 일년 중 43번째 주

 

2. set 메소드로 날짜 설정

    public static void main(String[] args){
    	Calendar cal=Calendar.getInstance();	//getInstance()로 객체 생성. 기본 현재 날짜
    	cal.set(Calendar.MONDAY,Calendar.DECEMBER); 	//12월로 설정
    	cal.set(Calendar.HOUR_OF_DAY,14);	//오후 2시로 Calendar 객체 설정
    	System.out.println("설정된 날짜 - "+(cal.get(Calendar.MONTH)+1)+"월 "+cal.get(Calendar.DATE)+"일");
    	System.out.println("설정된 시간 - "+cal.get(Calendar.HOUR_OF_DAY)+":"+cal.get(Calendar.MINUTE)+":"+cal.get(Calendar.SECOND));
    	
    }

 

이번에는 set 메소드로 month와 hour를 설정해보았습니다. 아래처럼 원래의 date와 minute, second는 현재의 시간과 동일하며 month와 hour만 바뀐 것을 알 수 있네요.

설정된 날짜 - 12월 20일
설정된 시간 - 14:43:55

 

이렇게 개별적으로 바꿀 수도 있습니다만, 만약 한꺼번에 바꾸고 싶다고 하면 오버로딩된 set메소드를 활용하시면 됩니다.

    public static void main(String[] args){
    	Calendar newYear=Calendar.getInstance();
    	newYear.set(2022,Calendar.JANUARY,1);	//년, 월, 일 설정
    	System.out.println(newYear.get(Calendar.YEAR)+"년 "+(newYear.get(Calendar.MONTH)+1)+"월 "+newYear.get(Calendar.DATE)+"일");
    	
    	newYear.set(2022,Calendar.JANUARY,1,0,0);	//년, 월, 일, 시, 분 설정
    	System.out.println(newYear.get(Calendar.YEAR)+"년 "+(newYear.get(Calendar.MONTH)+1)+"월 "+newYear.get(Calendar.DATE)+"일");
    	System.out.println(newYear.get(Calendar.HOUR_OF_DAY)+"시 "+newYear.get(Calendar.MINUTE));
    }
2022년 1월 1
2022년 1월 1
0시 0

 

3. 밀리초로 1970년 1월 1일 00시 00분부터 흐른 시간 구하기

    public static void main(String[] args){
    	Calendar today=Calendar.getInstance();	//getInstance()로 객체 생성. 기본 현재 날짜
    	System.out.println("1970년 00시 00분부터 흐른 초 :"+today.getTimeInMillis()/1000);
    	
    	SimpleDateFormat format=new SimpleDateFormat("a hh:mm:ss");	
    	System.out.println("현재시간 "+format.format(today.getTimeInMillis()));	//SimpleDateFormat으로 출력
    	
    	
    	Calendar newYear=Calendar.getInstance();	//현지 시간으로 설정
    	newYear.set(Calendar.YEAR, 2021);
    	newYear.set(Calendar.MONTH, Calendar.OCTOBER);
    	newYear.set(Calendar.DAY_OF_MONTH, 21);
    	
    	long diff=newYear.getTimeInMillis()-today.getTimeInMillis();
    	Calendar dDay=Calendar.getInstance();
    	dDay.setTimeInMillis(diff);	//1년 이내로만 이 코드를 쓸 수 있음
    	System.out.println("남은 날 수 :"+(dDay.get(Calendar.DAY_OF_YEAR)-1));	//오늘이 포함되므로 -1
    	
    	diff=diff/(60*60*24*1000);	//60(분) * 60(1분) * 24(시간) * 1(초) = 하루 
    	System.out.println("남은 날 수 :"+diff);
    }

 

getTimeInMillis를 통해서 밀리초단위로 구할 수 있습니다. 밀리초 단위로 SimpleDateFormat에 설정하여 더 쉽게 볼 수도 있고, 두 시간 사이의 계산도 가능합니다. 

1970년 00시 00분부터 흐른 초 :1634727499
현재시간 오후 07:58:19
남은 날 수 :2
남은 날 수 :1

 

4. 특정 날짜가 현재 날짜보다 전날인지, 이후인지 확인

    public static void main(String[] args){
    	Calendar yesterday=Calendar.getInstance();
    	yesterday.set(Calendar.DATE, yesterday.get(Calendar.DATE)-1);	//현재 날짜 -1로 설정
    	Calendar today=Calendar.getInstance();
    	
    	Calendar tomorrow=Calendar.getInstance();
    	tomorrow.set(Calendar.DATE, tomorrow.get(Calendar.DATE)+1);	//현재 날짜 +1
    	
    	System.out.println("오늘이 어제보다 이전인가? "+today.before(yesterday));
    	System.out.println("오늘이 내일보다 이전인가? "+today.before(tomorrow));
    	
    	System.out.println("오늘이 어제보다 이후인가? "+today.after(yesterday));
    	System.out.println("오늘이 내일보다 이후인가? "+today.after(tomorrow));
    }

위 코드는 어제와 내일로 일단 날짜를 지정한 Calendar객체로 오늘이 앞날인지, 뒷날인지 비교하는 코드입니다. 

오늘이 어제보다 이전인가? false
오늘이 내일보다 이전인가? true
오늘이 어제보다 이후인가? true
오늘이 내일보다 이후인가? false

 

또한 위의 코드는 아래와 같이 add 메소드를 활용하는 방식으로 바꿀 수도 있습니다.

    	Calendar yesterday=Calendar.getInstance();
    	yesterday.set(Calendar.DATE, -1);
    	Calendar today=Calendar.getInstance();
    	
    	Calendar tomorrow=Calendar.getInstance();
    	tomorrow.add(Calendar.DATE,1);

 

이상으로 Calendar 클래스의 활용과 예제를 살펴보았습니다.

 

반응형
블로그 이미지

REAKWON

와나진짜

,