- 스레드를 사용하면 한 프로세스가 한꺼번에 여러 가지 일을 할 수 있음
- 스레드는 경량 프로세스 임
- pthread(포직스 스레드)는 스레드 라이브러리
- pthrread_create()는 함수를 실행하는 스레드를 생성
- pthrread_join()은 스레드가 종료할 때가지 기다림
- 스레드는 전역 변수를 공유
- 두 스레드가 동시에 한 변수를 읽고 쓰면 결과를 예측 불가
- 뮤텍스는 공유 데이터를 보호하기 위한 락
- pthread_mutex_lock() 는 뮤텍스에 락을 검
- pthread_mutex_unlock() 는 뮤텍스에 건 락을 해제
2013년 1월 26일 토요일
C 언어 핵심 정리 -스레드 편-
C 언어 핵심 정리 -소켓과 네트워킹편-
- 텔넷은 간단한 네트워크 클라이언트
- socket() 함수로 소켓을 생성
- 서버 블랍(BLAB)
- B(bind) - 바인딩, L(listen) - 듣기, A(accept) - 접수, B(begin) - 대화시
- fork()로 한꺼번에 여러 클라이언트를 처리
- DNS = 도메인 이름 시스템
- getaddrinfo()는 도메인 이름에 대한 주소를 찾음
C언어 핵심 정리 -프로세스와 통신 편-
- system()은 터미널에서 명령을 입력한 것 처럼 문자열을 실행
- fork() 는 현재 프로세스를 복제
- fork() 와 exec()로 자식 프로세스를 생성
- 프로세스들은 파이프로 통신
- pipe()는 통신 파이프를 생성
- exit()는 프로그램을 바로 종료
- waitpid()는 프로세스가 종료할 때까지 가다림
- filno() 는 디스크립터 번호를 반환
- dup2()는 데이터 스트림을 복제
- 시그널은 운영체제가 보내는 메시지
- sigaction()은 시그널을 직접 처리
- 프로그램은 raise() 로 자신에게 시그널을 보낼 수 있음
- alarm() 은 지정한 시간 후에 SIGALRM 시그널을 보냄
- kill 명령은 시그널을 보냄
- 단순한 프로세스는 한번에 한 가지 일만 함
C언어 핵심 정리 -정적 라이브러리, 동적 라이브러리 편-
- #include<>는 /usr/include 와 같은 표준 디렉토리를 찾음
- -L <라이브러리 경로>는 디렉토리를 표준 라이브러리 디렉토리에 추가
- -I <라이브러리 이름> 은 /usr/lib와 같은 표준 디렉토리의 파일에 링크
- -I <인클루드 경로>는 디렉토리를 표준 인클루드 디렉토리에 추가
- ar 명령은 오브젝트 파일의 라이브러리 아카이브를 생성
- 라이브러리 아카이브는 lib<라이브러리 이름>.a 형태의 이름을 가짐
- 라이브러리 아카이브는 정적으로 링크
- gcc -shared 는 오브젝트 파일을 동적 라이브러리로 변환
- 동적 라이브러리는 실행시에 링크
- 동적 라이브러리는 운영체제에 따라 이름이 다름
- 동적 라이브러리는 .so, .dylib, .dll, .dll.a 확장자를 가질 수 있음
C언 핵심 정리 -고급 함수 편-
-고급 함수-
- 함수 포인터를 사용하면 함수를 데이터처럼 전달할 수 있음
- 함수 포인터는 *와 &연산자가 필요 없는 유일한 포인터지만, 원하면 사용가능
- 모든 함수의 이름은 함수에 대한 포인터
- qsort()는 배열을 정려
- 모든 정렬 함수는 비교 함수에 대한 포인터를 필요
- 비교 함수는 두 데이터의 순서를 결정
- 함수 포인터의 배열을 사용하면 데이터형에 따라 다른 함수를 실행
- 인자의 개수가 바뀔 수 있는 함수를 "가변 인자 함수"라고 함
- stdarg.h를 사용하면 가변 인자 함수를 만들 수 있음
C언어 핵심 정리 -데이터구조, 동적메모리 편-
-데이터 구조-
-동적 메모리-
- 동적 데이터 구조는 재귀적 구조체를 사용
- 재귀적 구조체는 자신과 비슷한 데이터에 대한 포인터를 한개 이상 갖고 있음
- 연결리스트는 동적 데이터 구조이
- 연결 리스트 안에 데이터를 쉽게 삽입할 수 있음
- 연결 리스트는 배열보다 확장하기 쉬움
-동적 메모리-
- 스택은 지역 변수를 저장하기 위해 사용
- 스택과 달리 힙 메모리는 자동으로 해체되지 않음
- malloc()는 힙에 메모리를 할당
- free()는 힙에 있는 메모리를 해제
- strdup()는 문자열을 힙에 복사
- 할당된 메모리에 접근할 수 없을 때 메모리 누수가 발생
- valgrind로 메모리 누수를 찾아낼 수 있음.
C언어 핵심 정리 -구조체,공용체,비트필드 편-
-구조체-
-공용체와 비트필드-
- 구조체는 데이터 형을 한데 묶습니다.
- 구조체 필드는 점(.)표기법으로 읽을 수 있습니다
- 배열과 같은 표기법으로 구조체를 초기화 할 수 있습니다
- -> 연산자를 사용하면 구조체 포인터의 필드를 쉽게 접근할 수 있음
- typedef로 데이터형의 별명을 만들 수 있음
- 구조체와 공용체의 필드이름을 지정해 초기화 할 수 있음
-공용체와 비트필드-
- 공용체는 여러형의 데이터를 한 곳에 보관할 수 있음
- 열거형으로 여러 기호를 만들 수 있음
- 비트 필드를 사용하면 구조체에 저장된 데이터를 비트 단위로 제어 가능
C언어 핵심 정리 -데이터형, 여러파일-
-데이터 형-
-여러 파일-
- char도 숫자형
- 아주 큰 정수에는 long형
- 작은 정수에는 short형
- 일반적인 정수에는 int형
- int형은 컴퓨터의 따라 크기가 달라짐
- 일반적인 실수에는 float형
- 정교한 실수에는 double형
-여러 파일-
- 함수 선언과 정의를 분리
- 선언은 헤더 파일에 넣기~
- #include<>는 라이브러리 헤더 파일에 사용
- #include " "는 지역 헤더 파일에 사용
- 오브젝트 코그를 파일에 저장하면 빨리 컴파일 할 수 있음
- make 로 컴파일 하긔~
C언어 핵심 정리 -데이터 스트림 편-
- printf()나 scanf()같은 C 함수는 표준 출력 및 표준 입력과 통신 합니다
- 표준 입력은 기본적으로 키보드를 읽습니다.
- 표준 에러는 에러 메시지를 출력하기 위해 따로 만든 출력 스트림입니다
- fopen("<파일이름>", mode)로 직접 데이터 스트림을 만들수 있음
- 표준 출력은 기본적으로 화면에 출려
- 리다이렉션으로 표준 입력, 출력, 에러가 연결된 스트림으로 바꿀수 있음
- fprintf(stderr, ...)으로 표준 에러에 출력할 수 있습니다.
- mode에서 쓸 때는 "w", 읽을 때는 "r", 추가할 때는 "a"로 열기 모드를 지정
- 명령행 인자는 문자열 포인터의 배열로 main()함수에 전달
- getopt() 함수를 사용하면 옵션을 쉽게 읽을 수 있음
C 언어 핵심 정리 -문자열-
- 문자열 상수는 읽기 전용 메모리에 저장됨
- 문자열의 배열은 배열의 배열
- strstr(a,b)는 문자열 a안에 있는 문자열 b의 위치를 반환
- strcat()는 두 문자열을 하나로 합침
- strcpy()는 문자열을 다른 배열에 복사
- string.h 헤더 파일은 유용한 문자열 함수들을 가지고 있음
- char string[..][..]로 배열의 배열을 만듬
- strcmp() 는 두 문자열을 비교
- strchr() 은 문자열 안에 있는 문자의 위치를 반환함
- strlen()은 문자열의 길이를 반환
C언어 핵심 정리 -포인터와 메모리
- scanf("%i", &x)로 사용자가 x에 직접 숫자를 입력할 수 있음
- 문자 포인터 변수x는 char* x로 선언
- 배열을 문자열로 초기화 하면, 문자열을 배열에 복사
- &x를 'x에 대한 포인터'라고 함
- &x는 x의 주소를 반환
- 배열 변수를 포인터로 사용할수 있음
- *a로 a번지에 있는 내용을 읽을 수 있음
- fgets(buf, size, stdin)로 간단히 문장을 입력
- 지역변수는 스택에 저장 됨
C언어 핵심 정리 -기초편-
- 단순문은 명령입니다
- 블럭문은 중괄호{}로 에워 쌉니다
- if문은 조건이 참일 때 실행됩니다.
- switch 문은 사용하면 한변수를 여러 값과 효율적으로 비교가능
- 논리 연산자(&&과 ||)로 조건을 결합
- 모든 프로그램에는 main()함수가 있어야 함
- #include는 입출력 코드와 같은 외부 코드를 연결
- 소스 파일은 .c 확장자를 가짐
- c 프로그램을 실행하려면 컴파일 해야함
- gcc는 가장 인기 있는 컴파일러
- 명령행에 && 연산자 사용하면 컴파일이 성공했을 때만 실행
- -o 는 컴파일된 파일 이름을 지정
- i++ 은 i에 1 더함, i--는 i에 1 뺌
- while는 조건이 참이면 루프를 반복
- do-while 는 코드를 적어도 한번은 실행
- for문을 사용하면 루프를 간략히 작성
부록 1 // 2. 전처리기, 3 static 키워드, 4.sizeof 키워드
2. 전처리기 지시자
#include <stdio.h> 이게 전처리기 지시자
전처리기 헤더 파일의 내용을 파일 안에 넣는거~
언제나 소스코드 제일 앞에 오고 #으로 시작
#define 는 매크로 만드는 지시자
매크로는 프로그램을 실행하는 동안 값이 바뀌지 않기 때문에 변수가 아니고
매크로는 프로그램이 컴파일 되기 전에 바뀜.
3. static 키워드
예제)
int count = 0;
int counter()
{
return ++count; //호출될 때 마다 값을 증가 시킴
}
이 코드의 문제는 count라는 전역 변수를 사용함.
어떤 함수도 사용해서 값을 바꿀 수가 있음.
전역 변수를 특정 함수나 파일만 사용할 수 있는 변수를 만드는 법~!
int counter()
{
static int count = 0;
return ++count;
}
static 키워드를 사용하면 변수를 전역 메모리에 보관하고 다른 함수가 접근하면 에러 발생
함수 앞에 static를 사용하면 같은 소스코드 안에 있는 코드만 이 함수를 사용가능
1줄 요약: static 키워드는 코드의 범위를 통제~
4. sizeof 키워드
어떤 범위를 알고 싶을 때 사용.
예제)
#include <stdio.h>
#include <limits.h>
int main()
{
printf("이 컴퓨터에서 int 형은 %lu바이트를 차지 합니다. \n", sizeof(int));
printf("그리고 int형은 %i에서 %i까지 저장할 수 있습니다. \n", INT_MIN, INT_MAX);
printf("그리고 short형은 %i에서 %i까지 저장할 수 있습니다. \n", SHRT_MIN, SHRT_MAX);
return 0;
}
매크로 이름은 INT(int), LONG(long), CHAR(char), FLT(float), DBL(double)
에 _MAX 나 _MIN을 붙이면 됨.
#include <stdio.h> 이게 전처리기 지시자
전처리기 헤더 파일의 내용을 파일 안에 넣는거~
언제나 소스코드 제일 앞에 오고 #으로 시작
#define 는 매크로 만드는 지시자
매크로는 프로그램을 실행하는 동안 값이 바뀌지 않기 때문에 변수가 아니고
매크로는 프로그램이 컴파일 되기 전에 바뀜.
3. static 키워드
예제)
int count = 0;
int counter()
{
return ++count; //호출될 때 마다 값을 증가 시킴
}
이 코드의 문제는 count라는 전역 변수를 사용함.
어떤 함수도 사용해서 값을 바꿀 수가 있음.
전역 변수를 특정 함수나 파일만 사용할 수 있는 변수를 만드는 법~!
int counter()
{
static int count = 0;
return ++count;
}
static 키워드를 사용하면 변수를 전역 메모리에 보관하고 다른 함수가 접근하면 에러 발생
함수 앞에 static를 사용하면 같은 소스코드 안에 있는 코드만 이 함수를 사용가능
1줄 요약: static 키워드는 코드의 범위를 통제~
4. sizeof 키워드
어떤 범위를 알고 싶을 때 사용.
예제)
#include <stdio.h>
#include <limits.h>
int main()
{
printf("이 컴퓨터에서 int 형은 %lu바이트를 차지 합니다. \n", sizeof(int));
printf("그리고 int형은 %i에서 %i까지 저장할 수 있습니다. \n", INT_MIN, INT_MAX);
printf("그리고 short형은 %i에서 %i까지 저장할 수 있습니다. \n", SHRT_MIN, SHRT_MAX);
return 0;
}
매크로 이름은 INT(int), LONG(long), CHAR(char), FLT(float), DBL(double)
에 _MAX 나 _MIN을 붙이면 됨.
부록 1 중요한 10가지 이야기 중 1. 연산자
1. 연산자
++i
i의 값을 1 증가시키고, 새 값을 반환
i++
i의 값을 1 증가시키고 이전 값을 반환
--i
i의 값을 1 감소시키고, 새 값을 반환
i--
i의 값을 1 감소시키고, 이전 값을 반환
1줄 요약 ++ -- 가 앞에 있으면 새 값을 반환하고, 뒤에 있으면 원래 값은 그대로
3항 연산자
어떤 값이 참일 때는 1번을 거짓일 때는 2번을 선택하고 싶다면??
(x == 1) ? 2 : 3
이라고 썼다면 x == 1이 참이면 2, 거짓이면 3
비트 조작
~a a에 있는 각 비트의 반대값을 반환합니다.
a&b a와 b의 각 비트의 논리곱을 반환합니다.
a|b a와 b의 각 비트의 논리합을 반환합니다.
a^b a와 b의 각 비트의 XOR을 반환합니다.
<< 모든 비트를 오른쪽 인자의 수만큼 왼쪽으로 이동
>> 모든 비트를 오른쪽 인자의 수만큼 오른쪽으로 이동
콤마를 사용해 수식을 분리
루프를 돌 때두 개 이상의 연산을 하는법
for(i = 0; i < 10; i++, j++) 콤마(,)로 구분~
12강 보충 - 스레드를 제어해 보장~
#include <stdio.h>
#include <pthread.h>
int beers = 2000000;
void* drink_lots(void* a)
{
int i;
for (i = 0; i < 100000; i++) {
beers = beers - 1;
}
return NULL;
}
int main()
{
pthread_t threads[20];
int t;
printf("벽에 있는 맥주병 개수: %i\n맥주병 개수: %i\n", beers, beers);
for (t = 0; t < 20; t++) {
pthread_create(&threads[t], NULL, drink_lots, NULL);
}
void* result;
for (t = 0; t < 20; t ++) {
pthread_join(threads[t], &result);
}
printf("벽에 %i 병의 맥주가 남아있습니다.\n", beers);
return 0;
}
200 만개의 병을 20개의 쓰레드를 이용하여 10만개씩 동시에 세는 프로그램임.
pthread_create 로 스레드 생성하고, pthread_join로 종료할 때가지 기다리고...
하지만 위의 소스를 실행하면 서로 공유 변수를 쓰기 때문에 계산이 개판이 됨.
고로 스레드를 통제하는 뮤텍스를 이용해 봅시당~
#include <stdio.h>
#include <pthread.h>
int beers = 2000000;
pthread_mutex_t beers_lock = PTHREAD_MUTEX_INITIALIZER;
void* drink_lots(void* a)
{
int i;
pthread_mutex_lock(&beers_lock);
for (i = 0; i < 100000; i++) {
beers = beers - 1;
}
pthread_mutex_unlock(&beers_lock);
printf("맥주 = %i\n", beers);
return NULL;
}
int main()
{
pthread_t threads[20];
int t;
printf("벽에 있는 맥주병 개수: %i\n맥주병 개수: %i\n", beers, beers);
for (t = 0; t < 20; t++) {
pthread_create(&threads[t], NULL, drink_lots, NULL);
}
void* result;
for (t = 0; t < 20; t ++) {
pthread_join(threads[t], &result);
}
printf("벽에 %i 병의 맥주가 남아있습니다.\n", beers);
return 0;
}
굵게 표시한 부분이 스레드를 통제하는 함수(라기보단 전처리기) 입니다.
lock 와 unlock 로 나뉘는데요~
이걸 반복문 전후로 사용법이 있고......
for (i = 0; i < 100000; i++)
{
pthread_mutex_lock(&beers_lock);
beers = beers - 1;
pthread_mutex_unlock(&beers_lock);
}
반복문 안에서 할수도 있습니다.
물론 둘의 과정은 틀리지만 결과는 같습니다.
어느 것이 좋다고는 말할 수는 없지만 최소한 잘못된 참조로 계산이 틀리지는 않습니다.
헤드 퍼스트 C 12강 (thread)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h> //포직스 스레드 라이브러리 사용
void error(char* msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(1);
}
void* does_not(void *a)
{
int i = 0;
for (i = 0; i < 5; i++) {
sleep(1);
puts("Does not!");
}
return NULL;
}
void* does_too(void *a)
{
int i = 0;
for (i = 0; i < 5; i++) {
sleep(1);
puts("Does too!");
}
return NULL;
}
int main()
{
pthread_t t0;
pthread_t t1;
if (pthread_create(&t0, NULL, does_not, NULL) == -1)
error("t0 스레드를 생성할 수 없습니다.");
if (pthread_create(&t1, NULL, does_too, NULL) == -1)
error("t1 스레드를 생성할 수 없습니다.");
void* result;
if (pthread_join(t0, &result) == -1)
error("t0 스레드를 종료할 수 없습니다.");
if (pthread_join(t1, &result) == -1)
error("t1 스레드를 종료할 수 없습니다.");
return 0;
}
------------------ 이 코드는 스레드가 충동하는 문제가 있음 -----------
2013년 1월 23일 수요일
헤드 퍼스트 C 11강 (노크노크 서버)
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
void error(char* msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(1);
}
int open_listener_socket()
{
int s = socket(PF_INET, SOCK_STREAM, 0);
if (s == -1)
error("소켓을 열 수 없습니다.");
return s;
}
void bind_to_port(int socket, int port)
{
struct sockaddr_in name;
name.sin_family = PF_INET;
name.sin_port = (in_port_t)htons(30000);
name.sin_addr.s_addr = htonl(INADDR_ANY);
int reuse = 1;
if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(int)) == -1)
error("소켓에 재사용 옵션을 설정할 수 없습니다.");
int c = bind(socket, (struct sockaddr*)&name, sizeof(name));
if (c == -1)
error("소켓에 바인딩할 수 없습니다.");
}
int catch_signal(int sig, void (*handler)(int))
{
struct sigaction action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
return sigaction(sig, &action, NULL);
}
int say(int socket, char* s)
{
int result = send(socket, s, strlen(s), 0);
if (result == -1)
fprintf(stderr, "%s: %s\n", "클라이언트에 메시지를 보낼 수 없습니다.", strerror(errno));
return result;
}
int listener_d;
void handle_shutdown(int sig)
{
if (listener_d)
close(listener_d);
fprintf(stderr, "안녕!\n");
exit(0);
}
int read_in(int socket, char* buf, int len)
{
char* s = buf;
int slen = len;
int c = recv(socket, s, slen, 0);
while ( (c > 0) && (s[c-1] != '\n')) {
s += c; slen -= c;
c = recv(socket, s, slen, 0);
}
if ( c < 0 )
return c;
else if (c == 0)
buf[0] = '\0';
else
s[c-1] = '\0';
return len - slen;
}
int main(int argc, char* argv[])
{
if (catch_signal(SIGINT, handle_shutdown) == -1)
error("인터럽트 처리기를 설정할 수 없습니다.");
listener_d = open_listener_socket();
bind_to_port(listener_d, 30000);
if (listen(listener_d, 10) == -1)
error("들을 수 없습니다.");
struct sockaddr_storage client_addr;
unsigned int address_size = sizeof(client_addr);
puts("연결을 기다립니다.");
char buf[255];
while(1) {
int connect_d = accept(listener_d, (struct sockaddr*)&client_addr, &address_size);
if (connect_d == -1)
error("두 번째 소켓을 열 수 없습니다.");
if (say(connect_d, "인터넷 노크노크 프로토콜 서버\r\n버전 1.0\r\nKnock Knock!\r\n") != -1) {
read_in(connect_d, buf, sizeof(buf));
if (strncasecmp("Who's there?", buf, 12))
say(connect_d, "'Who's there?'라고 질문했어야 합니다!");
else {
if (say(connect_d, "Oscar\r\n") != -1) {
read_in(connect_d, buf,sizeof(buf));
if (strncasecmp("Oscar who?", buf, 10))
say(connect_d, "'Oscar who?'라고 질문했어야 합니다!");
else
say(connect_d, "Oscar silly question, you get a silly answer.\r\n");
}
}
}
close(connect_d);
}
return 0;
}
헤드 퍼스트 C 11강 (socket)
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
void error(char* msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(1);
}
int main(int argc, char* argv[])
{
char* advice[] = {
"조금만 드세요.\r\n",
"꽉 끼는 청바지를 입으세요. 뚱뚱해 보이지는 않을 겁니다.\r\n",
"한 마디만 하겠습니다. 옳지 않아요.\r\n",
"오늘만이라도 솔직해지세요. 직장 상사에게 '진정' 생각하고 있는 걸 말하세요.\r\n",
"그 머리 모양은 아니지 싶습니다.\r\n"
};
int listener_d = socket(PF_INET, SOCK_STREAM, 0);
struct sockaddr_in name;
name.sin_family = PF_INET;
name.sin_port = (in_port_t)htons(30000);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if ( bind(listener_d, (struct sockaddr*)&name, sizeof(name)) == -1)
error("포트에 바인딩할 수 없습니다.");
listen(listener_d, 10);
puts("연결을 기다립니다.");
while (1) {
struct sockaddr_storage client_addr;
unsigned int address_size = sizeof(client_addr);
int connect_d = accept(listener_d, (struct sockaddr*)&client_addr, &address_size);
char* msg = advice[rand() % 5];
send(connect_d, msg, strlen(msg), 0);
close(connect_d);
}
return 0;
}
gcc 컴파일러로 해야함 vs로 할 시 헤더 파일이 틀려서 안됨.
헤드 퍼스트 C 10강
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
int score = 0;
void end_game(int sig)
{
printf("\n최종 점수: %i\n", score);
exit(0);
}
int catch_signal(int sig, void (*handler)(int))
{
struct sigaction action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
return sigaction(sig, &action, NULL);
}
void times_up(int sig)
{
puts("\n시간 초과!");
raise(SIGINT);
}
void error(char* msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(1);
}
int main()
{
catch_signal(SIGALRM, times_up);
catch_signal(SIGINT, end_game);
srandom(time(0));
while(1) {
int a = random() % 11;
int b = random() % 11;
char txt[4];
alarm(5);
printf("\n%i 곱하기 %i는? ", a, b);
fgets(txt, 4, stdin);
int answer = atoi(txt);
if (answer == a * b)
score++;
else
printf("\n틀렸습니다! 점수: %i\n", score);
}
return 0;
}
피드 구독하기:
덧글 (Atom)