본문 바로가기

Operating System/Linux

Ⅹ 메모리 관리

10장 메모리 관리

10장 강의 목표

변수와 메모리 이해
동적 메모리 할당
동적 할당과 연결 리스트
공유 메모리
메모리 관리 함수
 
기말고사: 8,9,10장 시험
20점 - 코드 괄호 
15점 - 프로그램 관련, 프로그램 실행 시켰을 때 결과가 뭐가 나오는지 (최근 과제보다 훨씬 쉬운 형태)
10장 프로세스 이미지와 관련된 내용, 그림 작성하라는 것은 아니고 그게 무슨뜻인지 이해할 수 있으면 된다. 
지난 중간고사 평균 15점인데 이번엔 20점이 충분히 나올 것으로 예상
가능한한 쉬운 내용만 보고 복잡한 프로그램은 하지 마세요.
 

10.1 변수와 메모리

프로세스

프로세스는 실행중인 프로그램이다.
프로그램 실행을 위해서는
  - 프로그램의 코드, 데이터, 스택, 힙, U-영역 등이 필요하다.
프로세스 이미지(구조)는 메모리 내의 프로세스 레이아웃
프로그램 자체가 프로세스는 아니다 !
 

프로세스 구조

프로세스 구조

 
코드 세그먼트(code segment)
  - 기계어 명령어
 
데이터 세그먼트(data segment)
  - e.g. int maxcount = 99; (initialized)
  - e.g. long sum[1000];  (uninitialized)
스택(stack)
  - 지역 변수, 매개 변수, 반환주소, 반환값, 등
 
힙(heap)
  - 동적 메모리 할당
  - malloc() in C,
  - new class() in Java
 
 
 

vars.c

#include <stdio.h>
#include <stdlib.h>
int a = 1;
static int b = 2;
 
int main() {
    int c = 3;
    static int d = 4;
    char *p;
    
    p = (char *) malloc(40);
    fun(5);
}
 
void fun(int n)
{
    int m = 6;
     ...
}
 

정적 변수와 데이터 영역

 
 
#include <stdio.h>
#include <stdlib.h>
int a = 1;
static int b = 2;
 
int main() {
    int c = 3;
    static int d = 4;
    char *p;
    
    p = (char *) malloc(40);
    fun(5);
}
 
void fun(int n)
{
    int m = 6;
     ...
}
 
 

지역 변수와 실행시간 스택: main() 호출

 
#include <stdio.h>
#include <stdlib.h>
int a = 1;
static int b = 2;
 
int main() {
    int c = 3;
    static int d = 4;
    char *p;
    
    p = (char *) malloc(40);
    fun(5);
}
 
void fun(int n)
{
    int m = 6;
     ...
}
 

지역 변수와 실행시간 스택: fun() 호출

 
#include <stdio.h>
#include <stdlib.h>
int a = 1;
static int b = 2;
 
int main() {
    int c = 3;
    static int d = 4;
    char *p;
    
    p = (char *) malloc(40);
    fun(5);
}
 
void fun(int n)
{
    int m = 6;
     ...
}
 
 

할당 방법에 따른 변수들의 분류

변수 구분 변수 종류
정적 변수 전역변수, static 변수
자동 변수 지역변수, 매개변수
동적 변수 힙 할당 변수

 
 
 

10.2 동적 메모리 할당

동적 메모리 할당

동적 할당을 사용하는 이유
  - 필요할 때 필요한 만큼만 메모리를 요청해서 사용하여
  - 메모리를 절약한다.
 
malloc( )
calloc( )
realloc( )
 
free( )
 

 

메모리 할당

#include <stdlib.h>
void *malloc(size_t size);
size 크기의 메모리를 할당하며 그 시작주소를 void* 형으로 반환한다.
void free(void *p);
포인터 p가 가리키는 메모리 공간을 해제한다.
힙에 동적 메모리 할당
라이브러리가 메모리 풀을 관리한다
 
malloc() 함수는 메모리를 할당할 때 사용하고 free()는 할당한 메모리를 해제할 때 사용한다.
 

 

메모리 할당 예

char *p;
p = (char *) malloc(40);
 

 
 
int *p;
p = (int *) malloc(10 * sizeof(int));

 
 

구조체를 위한 메모리 할당 예

struct student {
  int id;
  char name[10];
};
struct student *p;
p = (struct student *) malloc(sizeof(struct student));

 

 
 

구조체 배열을 위한 메모리 할당 예

struct student *p;
p = (struct student *) malloc(n * sizeof(struct student));

 

 
 

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

void main(void){
    void *p1, *p2, *p3;
    
    p1 = malloc(16);
//  free(p1);
    p2 = malloc(32);
    p3 = malloc(16); 
    printf("p1 = %ld\n", (long int)p1);
    printf("p2 = %ld\n", (long int)p2);
    printf("p3 = %ld\n", (long int)p3);

 
./ppp
p1 = 110748166330016
p2 = 110748166330048
p3 = 110748166330096
 
free(p1); (O),,
p1 = 1065943311384960
p2 = 1065943311384992
p3 = 1065943311384960

 

더보기

이 코드는 메모리 할당을 사용하여 포인터 변수를 초기화하고, 각 포인터가 가리키는 메모리 주소를 출력하는 프로그램입니다. 각 줄을 한 줄씩 해석해 보겠습니다.

1. `#include <stdio.h>`
    - 표준 입출력 함수들을 사용하기 위해 `stdio.h` 헤더 파일을 포함시킵니다.

2. `#include <stdlib.h>`
    - 메모리 할당 함수들을 사용하기 위해 `stdlib.h` 헤더 파일을 포함시킵니다.

3. 빈 줄
    - 가독성을 위한 빈 줄입니다.

4. `void main(void)`
    - 메인 함수의 정의를 시작합니다. 여기서 `void`는 반환 값이 없음을 나타내며, `void` 인자는 인수가 없음을 나타냅니다.

5. `{`
    - 메인 함수의 시작을 나타냅니다.

6. `void *p1, *p2, *p3;`
    - 세 개의 포인터 변수를 선언합니다. 
    - `void*` 타입은 모든 타입의 포인터를 저장할 수 있는 일반 포인터 타입입니다.
    - `p1`, `p2`, `p3`는 각각 할당된 메모리의 주소를 저장합니다.

7. 빈 줄
    - 가독성을 위한 빈 줄입니다.

8. `p1 = malloc(16);`
    - `malloc` 함수를 호출하여 16바이트의 메모리를 할당하고, 할당된 메모리의 시작 주소를 `p1`에 저장합니다.
    - `malloc` 함수는 성공 시 할당된 메모리 블록의 시작 주소를 반환하고, 실패 시 `NULL`을 반환합니다.

9. `// free(p1);`
    - 이 줄은 주석 처리되어 있으며, `p1`에 할당된 메모리를 해제하는 코드입니다.
    - 주석 처리가 되어 있으므로 이 코드는 실행되지 않습니다.

10. `p2 = malloc(32);`
    - `malloc` 함수를 호출하여 32바이트의 메모리를 할당하고, 할당된 메모리의 시작 주소를 `p2`에 저장합니다.

11. `p3 = malloc(16);`
    - `malloc` 함수를 호출하여 16바이트의 메모리를 할당하고, 할당된 메모리의 시작 주소를 `p3`에 저장합니다.

12. `printf("p1 = %ld\n", (long int)p1);`
    - `printf` 함수를 사용하여 `p1`이 가리키는 메모리 주소를 출력합니다.
    - `(long int)p1`은 `p1`의 값을 `long int` 타입으로 형변환하여 출력합니다.

13. `printf("p2 = %ld\n", (long int)p2);`
    - `printf` 함수를 사용하여 `p2`가 가리키는 메모리 주소를 출력합니다.
    - `(long int)p2`은 `p2`의 값을 `long int` 타입으로 형변환하여 출력합니다.

14. `printf("p3 = %ld\n", (long int)p3);`
    - `printf` 함수를 사용하여 `p3`가 가리키는 메모리 주소를 출력합니다.
    - `(long int)p3`은 `p3`의 값을 `long int` 타입으로 형변환하여 출력합니다.

### 출력 예시

프로그램이 실행되면 할당된 메모리의 주소가 출력됩니다. 예를 들어:

```
p1 = 12345678
p2 = 12345698
p3 = 123456b8
```

이 결과는 다음을 보여줍니다:
- `p1`은 16바이트의 메모리를 할당받아 그 시작 주소를 가리킵니다.
- `p2`는 32바이트의 메모리를 할당받아 그 시작 주소를 가리킵니다.
- `p3`은 16바이트의 메모리를 할당받아 그 시작 주소를 가리킵니다.

주석 처리가 된 `free(p1);` 줄은 할당된 메모리를 해제하는 기능을 합니다. 이를 주석 처리하지 않고 실행하면, `p1`에 할당된 메모리가 해제되어 `p2`와 `p3`의 메모리 할당 위치가 달라질 수 있습니다.

 

동적 할당: stud1.c

#include <stdio.h>  
#include <stdlib.h>
struct student {
    int id;
    char name[20];
};
/* 입력받을 학생 수를 미리 입력받고 이어서 학생 정보를 입력받은 후,
   이들 학생 정보를 역순으로 출력하는 프로그램 */
int main()
{
    struct student *p; // 동적 할당된 블록을 가리킬 포인터
    int n, i;
    printf("몇 명의 학생을 입력하겠습니까? ");
    scanf("%d", &n);
    if (n <= 0) {
        fprintf(stderr, "오류: 학생 수를 잘못 입력했습니다.\n");
        fprintf(stderr, "프로그램을 종료합니다.\n");
        exit(1);
    }
   p = (struct student *) malloc(n * sizeof(struct student));
   if (p == NULL) {
       perror("malloc");
                  exit(2);
   }
 
   printf("%d 명의 학번과 이름을 입력하세요.\n", n);
   for (i = 0; i < n; i++)
                 scanf("%d %s\n", &p[i].id, p[i].name);
 
   printf("\n* 학생 정보(역순) *\n");
   for (i = n-1; i >= 0; i--)
        printf("%d %s\n", p[i].id, p[i].name);
 
   printf("\n");
   exit(0);
      }
 

더보기

이 코드는 사용자가 입력한 학생 수만큼 동적으로 메모리를 할당하여 학생 정보를 입력받고, 입력받은 학생 정보를 역순으로 출력하는 프로그램입니다. 각 줄을 한 줄씩 해석해 보겠습니다.

1. `#include <stdio.h>`
    - 표준 입출력 함수들을 사용하기 위해 `stdio.h` 헤더 파일을 포함시킵니다.

2. `#include <stdlib.h>`
    - 메모리 할당 함수와 기타 유틸리티 함수들을 사용하기 위해 `stdlib.h` 헤더 파일을 포함시킵니다.

3. `struct student {`
    - 학생 정보를 저장할 구조체 `student`를 정의합니다.

4. `    int id;`
    - 학생의 학번을 저장할 정수형 변수를 정의합니다.

5. `    char name[20];`
    - 학생의 이름을 저장할 20바이트 크기의 문자열 배열을 정의합니다.

6. `};`
    - 구조체 정의의 끝을 나타냅니다.

7. `/* 입력받을 학생 수를 미리 입력받고 이어서 학생 정보를 입력받은 후,`
8. `   이들 학생 정보를 역순으로 출력하는 프로그램 */`
    - 프로그램의 동작을 설명하는 주석입니다.

9. `int main()`
    - 메인 함수의 정의를 시작합니다.

10. `{`
    - 메인 함수의 시작을 나타냅니다.

11. `    struct student *p;`
    - 동적으로 할당된 학생 구조체 배열을 가리킬 포인터 `p`를 선언합니다.

12. `    int n, i;`
    - 학생 수를 저장할 정수형 변수 `n`과 반복문에서 사용할 정수형 변수 `i`를 선언합니다.

13. `    printf("몇 명의 학생을 입력하겠습니까? ");`
    - 사용자에게 몇 명의 학생 정보를 입력할 것인지 묻는 메시지를 출력합니다.

14. `    scanf("%d", &n);`
    - 사용자가 입력한 학생 수를 `n` 변수에 저장합니다.

15. `    if (n <= 0) {`
    - 학생 수 `n`이 0 이하인 경우를 확인하는 조건문입니다.

16. `        fprintf(stderr, "오류: 학생 수를 잘못 입력했습니다.\n");`
    - 표준 오류 스트림에 오류 메시지를 출력합니다.

17. `        fprintf(stderr, "프로그램을 종료합니다.\n");`
    - 표준 오류 스트림에 프로그램 종료 메시지를 출력합니다.

18. `        exit(1);`
    - 프로그램을 종료합니다. 종료 코드 1은 오류를 나타냅니다.

19. `    }`
    - `if` 조건문의 끝을 나타냅니다.

20. `    p = (struct student *) malloc(n * sizeof(struct student));`
    - `malloc` 함수를 호출하여 `n`명의 학생 구조체 배열에 필요한 메모리를 동적으로 할당하고, 그 시작 주소를 `p` 포인터에 저장합니다.
    - `sizeof(struct student)`는 학생 구조체의 크기를 계산합니다.

21. `    if (p == NULL) {`
    - `malloc` 함수가 메모리 할당에 실패했는지 확인하는 조건문입니다.

22. `        perror("malloc");`
    - 표준 오류 스트림에 메모리 할당 실패에 대한 오류 메시지를 출력합니다.

23. `        exit(2);`
    - 프로그램을 종료합니다. 종료 코드 2는 메모리 할당 실패를 나타냅니다.

24. `    }`
    - `if` 조건문의 끝을 나타냅니다.

25. 빈 줄
    - 가독성을 위한 빈 줄입니다.

26. `    printf("%d 명의 학번과 이름을 입력하세요.\n", n);`
    - 사용자에게 `n`명의 학생 학번과 이름을 입력하라고 안내 메시지를 출력합니다.

27. `    for (i = 0; i < n; i++)`
    - 학생 수 `n`만큼 반복문을 실행합니다. `i`는 0부터 `n-1`까지 증가합니다.

28. `        scanf("%d %s\n", &p[i].id, p[i].name);`
    - 각 학생의 학번과 이름을 입력받아 동적 할당된 학생 구조체 배열에 저장합니다.
    - `p[i].id`에 학번을 저장하고, `p[i].name`에 이름을 저장합니다.

29. 빈 줄
    - 가독성을 위한 빈 줄입니다.

30. `    printf("\n* 학생 정보(역순) *\n");`
    - 역순으로 학생 정보를 출력하기 전에 제목을 출력합니다.

31. `    for (i = n-1; i >= 0; i--)`
    - 학생 수 `n`만큼 반복문을 역순으로 실행합니다. `i`는 `n-1`부터 0까지 감소합니다.

32. `        printf("%d %s\n", p[i].id, p[i].name);`
    - 역순으로 각 학생의 학번과 이름을 출력합니다.

33. 빈 줄
    - 가독성을 위한 빈 줄입니다.

34. `    printf("\n");`
    - 출력 형식을 위해 빈 줄을 출력합니다.

35. `    exit(0);`
    - 프로그램을 종료합니다. 종료 코드 0은 정상 종료를 나타냅니다.

36. `}`
    - 메인 함수의 끝을 나타냅니다.

### 요약

- 사용자로부터 입력받을 학생 수를 입력받고, 그 수만큼 메모리를 동적으로 할당합니다.
- 각 학생의 학번과 이름을 입력받아 동적 할당된 메모리에 저장합니다.
- 입력받은 학생 정보를 역순으로 출력합니다.
- 입력받은 학생 수가 0 이하이거나 메모리 할당에 실패한 경우, 적절한 오류 메시지를 출력하고 프로그램을 종료합니다.

 

동적 할당: stud1.c

$ stud1
몇 명의 학생을 입력하겠습니까? 5
명의 학번과 이름을 입력하세요.
1001001 박연아
1001003 김태환
1001006 김현진
1001009 장샛별
1001011 홍길동
^D
* 학생 정보(역순) *
1001011 홍길동
1001009 장샛별
1001006 김현진
1001003 김태환
1001001 박연아

 
 
밑에 힙에서 계속 쌓이고
스택과 겹치지 않도록 해야하므로
항상 malloc = null 확인해야 한다.
 
핵심은 malloc해서 필요한 사이즈만큼 메모리를 할당하고
할당 후 그 포인터를 캐스팅해서 써야 한다.
할당했다면 그 포인터가 널인가 아닌가 검사를 해야한다.
그게 핵심입니다.
 
 

배열 할당 calloc()

같은 크기의 메모리를 여러 개를 할당할 경우
#include <stdlib.h>
void *calloc(size_t n, size_t size);
size 크기의 메모리를 n개 할당한다. 값을 모두 0으로 초기화한다.
실패하면 NULL를 반환한다.
 
size는 배열 요소 하나의 크기, n은 배열의 크기 


 
이미 할당된 메모리의 크기 변경
#include <stdlib.h>
void *realloc(void *p, size_t newsize);
p이 가리키는 이미 할당된 메모리의 크기를 newsize로 변경한다.
 
 메모리를 할당하여 사용하던 중 부족하면 더 늘려서 사용할 때 사용하는 realloc
사이즈를 줄일 수도 있고 늘릴 수도 있다. 첫 번째 인수는 사용 중인 메모리, 두 번째 인수는 원하는 사이즈의 메모리
 

calloc() 

int *p,*q;
p = malloc(10*sizeof(int));
if (p == NULL)
  perror(“malloc”);
q = calloc(10, sizeof(int));
if (q == NULL)
  perror(“calloc”);

 

더보기

 이 코드는 동적 메모리 할당 함수인 `malloc`과 `calloc`을 사용하여 메모리를 할당하고, 할당 실패 시 오류 메시지를 출력하는 예제입니다. 각 줄을 한 줄씩 해석해 보겠습니다.

1. `int *p, *q;`
    - 두 개의 정수형 포인터 변수 `p`와 `q`를 선언합니다. 이 포인터들은 각각 `malloc`과 `calloc` 함수를 사용하여 할당된 메모리 블록을 가리키게 됩니다.

2. `p = malloc(10 * sizeof(int));`
    - `malloc` 함수를 사용하여 10개의 `int` 크기만큼의 메모리를 할당합니다.
    - `10 * sizeof(int)`는 `int` 타입의 크기(`sizeof(int)`)에 10을 곱한 값으로, 10개의 정수를 저장할 수 있는 메모리 크기를 나타냅니다.
    - 할당된 메모리 블록의 시작 주소는 포인터 `p`에 저장됩니다.

3. `if (p == NULL)`
    - `malloc` 함수가 메모리 할당에 실패한 경우를 확인하는 조건문입니다.
    - `malloc`이 실패하면 `NULL`을 반환합니다. `p`가 `NULL`이면 할당이 실패한 것입니다.

4. `  perror("malloc");`
    - `malloc` 함수가 실패했을 때, 표준 오류 스트림에 오류 메시지를 출력합니다.
    - `perror` 함수는 지정된 문자열("malloc")과 함께 최근에 발생한 시스템 오류 메시지를 출력합니다.

5. `q = calloc(10, sizeof(int));`
    - `calloc` 함수를 사용하여 10개의 `int` 크기만큼의 메모리를 할당합니다.
    - `calloc` 함수는 두 개의 인수를 받으며, 첫 번째 인수는 할당할 요소의 개수(10), 두 번째 인수는 각 요소의 크기(`sizeof(int)`)입니다.
    - `calloc`은 할당된 메모리를 0으로 초기화합니다.
    - 할당된 메모리 블록의 시작 주소는 포인터 `q`에 저장됩니다.

6. `if (q == NULL)`
    - `calloc` 함수가 메모리 할당에 실패한 경우를 확인하는 조건문입니다.
    - `calloc`이 실패하면 `NULL`을 반환합니다. `q`가 `NULL`이면 할당이 실패한 것입니다.

7. `  perror("calloc");`
    - `calloc` 함수가 실패했을 때, 표준 오류 스트림에 오류 메시지를 출력합니다.
    - `perror` 함수는 지정된 문자열("calloc")과 함께 최근에 발생한 시스템 오류 메시지를 출력합니다.

### 요약

- `int *p, *q;`: 두 개의 정수형 포인터 변수 `p`와 `q`를 선언합니다.
- `p = malloc(10 * sizeof(int));`: `malloc` 함수를 사용하여 10개의 `int` 크기만큼의 메모리를 할당하고, 포인터 `p`에 저장합니다. 할당 실패 시 `NULL`을 반환합니다.
- `if (p == NULL) perror("malloc");`: `malloc` 함수가 실패했는지 확인하고, 실패했을 경우 오류 메시지를 출력합니다.
- `q = calloc(10, sizeof(int));`: `calloc` 함수를 사용하여 10개의 `int` 크기만큼의 메모리를 할당하고, 포인터 `q`에 저장합니다. 할당된 메모리는 0으로 초기화됩니다. 할당 실패 시 `NULL`을 반환합니다.
- `if (q == NULL) perror("calloc");`: `calloc` 함수가 실패했는지 확인하고, 실패했을 경우 오류 메시지를 출력합니다.

이 예제는 `malloc`과 `calloc` 함수를 사용하여 메모리를 할당하고, 할당 실패 시 이를 처리하는 방법을 보여줍니다.

 

 

10.3 동적 할당과 연결 리스트

연결 리스트의 필요성

• 예: 여러 학생들의 데이터를 저장해야 한다고 생각해보자.
  - 가장 간단한 방법은 구조체 배열을 선언하여 사용하는 것이다.
  - 이 방법은 배열의 크기를 미리 결정해야 하는 문제점이 있다.
  - 배열의 크기보다 많은 학생들은 처리할 수 없으며 이보다 적은 학생들의 경우에는 배열의 기억공간은 낭비된다.
 
동적 메모리 할당
  - 필요할 때마다 동적으로 메모리를 할당하여
  - 연결리스트(linked list)로 관리한다

 
 

자기 참조 구조체를 위한 메모리 할당

struct student {
  int id;
  char name[20];
  struct student *next;
};
struct student *p;
p = (struct student *) malloc(sizeof(struct student));

더보기

이 코드는 `student`라는 자기 참조 구조체를 정의하고, `student` 구조체를 위한 메모리를 동적으로 할당하는 예제입니다. 각 줄을 한 줄씩 해석해 보겠습니다.

1. `struct student {`
    - `student`라는 구조체를 정의합니다. 이 구조체는 학생 정보를 저장할 것입니다.

2. `  int id;`
    - 학생의 학번을 저장할 정수형 변수 `id`를 선언합니다.

3. `  char name[20];`
    - 학생의 이름을 저장할 20바이트 크기의 문자열 배열 `name`을 선언합니다.

4. `  struct student *next;`
    - 다음 학생 구조체를 가리킬 포인터 `next`를 선언합니다. 
    - 자기 참조 구조체(self-referential structure)로, 이 구조체는 다른 `student` 구조체를 가리키는 포인터를 포함합니다.
    - 이를 통해 학생 구조체들을 연결 리스트 형태로 만들 수 있습니다.

5. `};`
    - 구조체 정의의 끝을 나타냅니다.

6. `struct student *p;`
    - `student` 구조체를 가리킬 포인터 변수 `p`를 선언합니다.
    - 이 포인터는 동적으로 할당된 `student` 구조체를 가리키게 됩니다.

7. `p = (struct student *) malloc(sizeof(struct student));`
    - `malloc` 함수를 사용하여 `student` 구조체 크기만큼의 메모리를 동적으로 할당하고, 그 시작 주소를 포인터 `p`에 저장합니다.
    - `sizeof(struct student)`는 `student` 구조체의 크기를 계산합니다.
    - `malloc` 함수는 성공 시 할당된 메모리 블록의 시작 주소를 반환하고, 실패 시 `NULL`을 반환합니다.
    - `(struct student *)`는 `malloc` 함수가 반환한 `void*` 포인터를 `struct student*` 타입으로 캐스팅합니다.

### 요약

- `struct student { int id; char name[20]; struct student *next; };`
    - `student`라는 구조체를 정의합니다. 이 구조체는 학생의 학번(`id`), 이름(`name`), 그리고 다음 학생을 가리키는 포인터(`next`)를 포함합니다.
    - `next` 포인터를 통해 여러 `student` 구조체를 연결 리스트로 연결할 수 있습니다.

- `struct student *p;`
    - `student` 구조체를 가리킬 포인터 변수 `p`를 선언합니다.

- `p = (struct student *) malloc(sizeof(struct student));`
    - `malloc` 함수를 사용하여 `student` 구조체 크기만큼의 메모리를 동적으로 할당하고, 그 시작 주소를 포인터 `p`에 저장합니다.
    - 할당된 메모리는 `p`를 통해 접근할 수 있으며, 연결 리스트의 첫 번째 노드로 사용할 수 있습니다.

이 코드는 기본적인 동적 메모리 할당과 자기 참조 구조체의 개념을 보여줍니다. `malloc` 함수를 통해 메모리를 할당하고, 포인터를 사용하여 이 메모리에 접근할 수 있습니다.

 

 

동적 할당: stud2.c

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

/* 학생 정보를 입력받아 연결 리스트에 저장하고 학생 정보를 역순으로
   출력한다.  */
int main()
{
    int count = 0, id;
    char name[20];
    struct student *p, *head = NULL;
 
    printf("학번과  이름을 입력하세요\n");  
  
    while (scanf("%d %s", &id, name) == 2)  {
        p = (struct student *) malloc(sizeof(struct student));
        if (p == NULL) {
            perror("malloc");
            exit(1);
        }
        p->id = id; 
        strcpy(p->name, name);
        p->next = head; 
        head = p;
    }
  

  
    printf("\n* 학생 정보(역순) *\n");
    p = head;
    while (p != NULL)  {
        count++;
        printf("학번: %d 이름: %s \n", p->id,  p->name);
        p = p->next;
    }
    printf("총 %d 명입니다.\n", count);
    exit(0);
}
 

더보기

이 코드는 학생 정보를 입력받아 연결 리스트에 저장하고, 입력된 학생 정보를 역순으로 출력하는 프로그램입니다. 각 줄을 한 줄씩 해석해 보겠습니다.

1. `#include <stdio.h>`
    - 표준 입출력 함수들을 사용하기 위해 `stdio.h` 헤더 파일을 포함시킵니다.

2. `#include <stdlib.h>`
    - 메모리 할당 함수와 기타 유틸리티 함수들을 사용하기 위해 `stdlib.h` 헤더 파일을 포함시킵니다.

3. `#include <string.h>`
    - 문자열 처리 함수들을 사용하기 위해 `string.h` 헤더 파일을 포함시킵니다.

4. `…`
    - 필요하지 않은 부분이 생략되었음을 나타냅니다.

5. `/* 학생 정보를 입력받아 연결 리스트에 저장하고 학생 정보를 역순으로 출력한다.  */`
    - 프로그램의 동작을 설명하는 주석입니다.

6. `int main()`
    - 메인 함수의 정의를 시작합니다.

7. `{`
    - 메인 함수의 시작을 나타냅니다.

8. `int count = 0, id;`
    - 정수형 변수 `count`와 `id`를 선언합니다.
    - `count`: 학생 수를 세기 위해 사용됩니다.
    - `id`: 입력받은 학생의 학번을 저장할 변수입니다.

9. `char name[20];`
    - 학생의 이름을 저장할 20바이트 크기의 문자열 배열 `name`을 선언합니다.

10. `struct student *p, *head = NULL;`
    - 두 개의 `student` 구조체 포인터 변수 `p`와 `head`를 선언합니다.
    - `p`: 새로 할당된 학생 구조체를 가리킬 포인터입니다.
    - `head`: 연결 리스트의 시작(헤드)을 가리키는 포인터입니다. 초기값은 `NULL`입니다.

11. `printf("학번과  이름을 입력하세요\n");`
    - 사용자에게 학번과 이름을 입력하라고 안내하는 메시지를 출력합니다.

12. `while (scanf("%d %s", &id, name) == 2)  {`
    - 학번과 이름을 입력받아 각각 `id`와 `name` 변수에 저장합니다.
    - `scanf` 함수는 두 개의 값을 성공적으로 읽으면 2를 반환합니다.
    - 입력이 성공적으로 이루어졌을 때만 반복문이 실행됩니다.

13. `    p = (struct student *) malloc(sizeof(struct student));`
    - `malloc` 함수를 사용하여 `student` 구조체 크기만큼의 메모리를 동적으로 할당하고, 그 시작 주소를 포인터 `p`에 저장합니다.
    - `sizeof(struct student)`는 `student` 구조체의 크기를 계산합니다.

14. `    if (p == NULL) {`
    - `malloc` 함수가 메모리 할당에 실패했는지 확인하는 조건문입니다.
    - `malloc`이 실패하면 `NULL`을 반환합니다. `p`가 `NULL`이면 할당이 실패한 것입니다.

15. `        perror("malloc");`
    - `malloc` 함수가 실패했을 때, 표준 오류 스트림에 오류 메시지를 출력합니다.

16. `        exit(1);`
    - 프로그램을 종료합니다. 종료 코드 1은 오류를 나타냅니다.

17. `    }`
    - `if` 조건문의 끝을 나타냅니다.

18. `    p->id = id;`
    - 새로 할당된 `student` 구조체의 `id` 필드에 입력받은 학번을 저장합니다.

19. `    strcpy(p->name, name);`
    - `strcpy` 함수를 사용하여 입력받은 이름을 `student` 구조체의 `name` 필드에 복사합니다.

20. `    p->next = head;`
    - 새로 할당된 `student` 구조체의 `next` 필드에 현재 리스트의 헤드 포인터를 저장합니다.
    - 이는 새로 입력된 학생이 리스트의 첫 번째 요소가 되도록 설정합니다.

21. `    head = p;`
    - `head` 포인터를 새로 할당된 `student` 구조체를 가리키도록 업데이트합니다.
    - 이는 새로 입력된 학생이 리스트의 첫 번째 요소가 되도록 합니다.

22. `}`
    - `while` 반복문의 끝을 나타냅니다.

23. 빈 줄
    - 가독성을 위한 빈 줄입니다.

24. `printf("\n* 학생 정보(역순) *\n");`
    - 역순으로 학생 정보를 출력하기 전에 제목을 출력합니다.

25. `p = head;`
    - `p` 포인터를 리스트의 시작을 가리키는 `head`로 초기화합니다.

26. `while (p != NULL)  {`
    - 리스트를 순회하기 위한 반복문을 시작합니다. `p`가 `NULL`이 아닐 때까지 실행됩니다.

27. `    count++;`
    - 학생 수를 세기 위해 `count`를 1 증가시킵니다.

28. `    printf("학번: %d 이름: %s \n", p->id,  p->name);`
    - 현재 학생의 학번과 이름을 출력합니다.

29. `    p = p->next;`
    - `p` 포인터를 다음 학생을 가리키도록 업데이트합니다.

30. `}`
    - `while` 반복문의 끝을 나타냅니다.

31. `printf("총 %d 명입니다.\n", count);`
    - 총 학생 수를 출력합니다.

32. `exit(0);`
    - 프로그램을 정상적으로 종료합니다. 종료 코드 0은 정상 종료를 나타냅니다.

33. `}`
    - 메인 함수의 끝을 나타냅니다.

### 요약

- 사용자로부터 학번과 이름을 입력받아 동적으로 메모리를 할당하고, 할당된 메모리에 학생 정보를 저장합니다.
- 입력된 학생 정보는 연결 리스트에 저장되며, 새로운 학생 정보는 리스트의 앞에 추가됩니다.
- 입력이 완료되면 저장된 학생 정보를 역순으로 출력합니다.
- 입력된 총 학생 수를 출력합니다.

이 프로그램은 연결 리스트를 사용하여 학생 정보를 동적으로 저장하고 관리합니다. 사용자는 학번과 이름을 입력하고, 프로그램은 이를 리스트에 저장하여 나중에 역순으로 출력할 수 있도록 합니다.


----- End (시험범위)
 

10.4 공유 메모리

공유 메모리의 필요성

공유 메모리
  - 프로세스 사이에 메모리 영역을 공유해서 사용할 수 있도록 해준다.
 

 
 

공유 메모리 관련 함수

공유 메모리 생성 shmget()

 
#include <sys/shm.h>
#include <sys/ipc.h>
int shmget(key_t key, size_t size, int shmflg);
key를 사용하여 size 크기의 공유 메모리를 생성하고 생성된 공유 메모리의 ID를 반환한다.

 
공유 메모리 연결 shmat()

 
#include <sys/shm.h>
#include <sys/ipc.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid 공유 메모리를 이 프로세스의 메모리 위치 shmaddr에 연결하고 그 주소를 반환한다.

 
공유 메모리 연결 해제 shmdt()

#include <sys/shm.h>
#include <sys/ipc.h>
int shmdt(const void *shmaddr);
공유 메모리에 대한 연결 주소 shmaddr를 연결해제 한다.
성공 시 0, 실패 시 –1을 반환한다.

 
공유 메모리 제어 shmctl()

#include <sys/shm.h>
#include <sys/ipc.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid 공유메모리를 cmd 명령어에 따라 제어한다.


cmd
  - IPC_RMID: 공유 메모리 제거
  - IPC_SET: buf의 지정된 값을 공유 메모리 정보(사용자 ID, 그룹 ID, 접근권한) 설정
  - IPC_STAT: 공유 메모리 정보를 buf에 저장
  - SHM_ OCK: 공유 메모리 잠금
  - SHM_UNLOCK: 공유 메모리 잠금 해제
 
 
공유 메모리: shm1.c

 1 #include <sys/ipc.h>
 …
 7 int main()
 8 {
 9     int shmid;
10     key_t key;
11     char *shmaddr;
12
13     key = ftok("helloshm", 1);
14     shmid = shmget(key, 1024, IPC_CREAT|0644);
15     if (shmid == -1) {
16         perror("shmget");
17         exit(1);
18     }
19
20     printf("shmid : %d", shmid);
21     shmaddr = (char *) shmat(shmid, NULL, 0);
22     strcpy(shmaddr, "hello shared memory");
23     return(0);
24 }

 
실행 결과

$shm1
shmid : 17
$ ipcs -m
------ Shared Memory Segments --------
key            shmid  owner perms bytes nattch status
0x00000000  4       chang 600    16384 1       dest
0xffffffff      17       chang 644    1024  0
...

 

공유 메모리: shm2.c

 1 #include <sys/ipc.h>
 …
 7 int main()
 8 {
 9     int shmid;
10    key_t key;
11    char *shmaddr;
12
13    key = ftok("helloshm", 1);
14    shmid = shmget(key, 1024, 0);
15    if (shmid == -1) {
16        perror("shmget");
17        exit(1);
18    }
19
20    printf("shmid : %d\n", shmid);
21    shmaddr = (char *) shmat(shmid, NULL, 0);
22    printf("%s\n", shmaddr);
24    return(0);
25 }

 
실행 결과

shmid : 17
hello shared memory

 

부모-자식 프로세스 사이의 메모리 공유: shm3.c

 1 #include <sys/ipc.h>
 2 #include <sys/shm.h>
 3 #include <sys/types.h>
 4 #include <sys/wait.h>
 5 #include <unistd.h>
 6 #include <stdlib.h>
 7 #include <stdio.h>
 8
 9  int main()
10 {
11    int shmid;
12    char *shmp1, *shmp2;
13
14    shmid = shmget(IPC_PRIVATE,
           10*sizeof(char),IPC_CREAT|0666);
15    if (shmid == -1) {
16       printf("shmget failed\n");
17       exit(0);
18    }
20  if (fork() == 0) {
21     shmp1 = (char *) shmat(shmid, NULL, 0);
22     for (int i=0; i<10; i++)
23        shmp1[i] = i*10;
24     shmdt(shmp1);
25     exit(0);
26  } else {
27     wait(NULL);
28     shmp2 = (char *) shmat(shmid, NULL, 0);
29     for (int i=0; i<10; i++)
30  printf("%d ", shmp2[i]);
31     shmdt(shmp2);
32     if (shmctl(shmid,IPC_RMID,NULL)==-1)
33        printf("shmctl failed\n");
34   }
35   return 0;
36 }

실행 결과

0 10 20 30 40 50 60 70 80 90

 
 

10.5 메모리 관리 함수

메모리 관리 함수

# include <string.h>
 void *memset(void *s, int c, size_t n);
s에서 시작하여 n 바이트만큼 문자 c로 설정한 다음에 s를 반환한다.
 int memcmp(const void *s1, const void *s2, size_t n);
s1과 s2에서 첫 n 바이트를 비교해서, 메모리 블록 내용이 동일하면 0을 반환하고 s1이 s2보다 작으면 음수를 반환하고, s1이 s2보다 크다면 양수를 반환한다.
 void *memchr(const void *s, int c, size_t n);
s가 가리키는 메모리의 n 바이트 범위에서 문자 c를 탐색한다.
c와 일치하는 첫 바이트에 대한 포인터를 반환하거나,
c를 찾지 못하면 NULL을 반환한다.
# include <string.h>
 void *memmove(void *dst, const void *src, size_t n);
   src에서 dst로 n 바이트를 복사하고, dst를 반환한다.
 
 void *memcpy(void *dst, const void *src, size_t n);
src에서 dst로 n 바이트를 복사한다. 두 메모리 영역은 겹쳐지지 않는다.
만일 메모리 영역을 겹쳐서 쓰길 원한다면 memmove() 함수를 사용해라.
dst를 반환한다.
참고 char *strncpy(char *dst, const char *src, size_t n);

 
 
mem.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{
    char str[32]="Do you ike inux?";
    char *p,*q;
    p = (char *) malloc(32);
    memcpy(p, str, strlen(str)); 
    puts(p);
    memset(p+12,'l',1);
    puts(p);
    q = (char *) memchr(p,'l',18);
    puts(q);
    memmove(str+12,str+7,10);
    puts(str);
}
 
 
$ mem
Do you ike inux?
Do you ike  inux?
ike  inux?
Do you ike  ike  inux
 
 

메모리 맵핑

메모리 맵핑
  - 파일의 일부 영역에 메모리 주소를 부여할 수 있다.
  - 마치 변수를 사용하는 것처럼 파일을 사용할 수 있다.
 

 
 

메모리 매핑 시스템 호출

#include <sys/types.h>
#include <sys/mman.h>
caddr_t mmap(caddr_t addr, size_t  en, int prot, int flag, int fd, off_t off);
fd가 나타내는 파일의 일부 영역(off부터  en 크기)에 메모리 주소를 부여하고 메모리 맵핑된 영역의 시작 주소(addr)를 반환한다.

 
매개 변수
  - addr: 메모리 맵핑에 부여할 메모리 시작 주소,
           이 값이 NULL이면 시스템이 적당한 시작 주소를 선택한다.
  - en: 맵핑할 파일 영역의 크기로 메모리 맵핑의 크기와 같다.
  - prot: 매핑된 메모리 영역에 대한 보호 정책을 나타낸다.
    PROT_READ(읽기), PROT_WRITE(쓰기), PROT_EXEC(실행), PROT_NONE(접근 불가)
  - fd: 대상 파일의 파일 디스크립터
  - off: 맵핑할 파일 영역의 시작 위치
 
 
 

메모리 매핑을 사용한 cat 명령어 구현:mmap.c

 1 #include <stdio.h>
 …
 8
 9 int main(int argc, char *argv[])
10 {
11     struct stat sbuf;
12     char *p;
13     int fd;
14
15     if (argc < 2) {
16         fprintf(stderr, "사용법: %s 파일이름\n", argv[0]);
17     exit(1);
18     }
19
20     fd = open(argv[1], O_RDONLY);
21     if (fd == -1) {
22         perror("open");
23     exit(1);
24     }
26     if (fstat(fd, &sbuf) == -1) {
27         perror("fstat");
28     exit(1);
29     }
30
31     p = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
32     if (p == MAP_FAILED) {
33         perror("mmap");
34         exit(1);
35     }
36
37     for ( ong  = 0;  < sbuf.st_size; ++)
38         putchar(p[ ]);
39
40     close(fd);
41     munmap(p, sbuf.st_size);   // 메모리 매핑 해제
42     return 0;
43 }

 
 
 
 

핵심개념

지역변수와 매개변수에 대한 메모리 공간은 실행시간 스택에 자동적으로 할당되며 동적 변수는 힙에 할당된다.
동적 할당을 사용하는 이유는 필요할 때 필요한 만큼만 메모리를 요청해서 사용하여 메모리를 절약하기 위해서이다.
malloc 함수는 메모리를 할당할 때 사용하고 free는 할당한 메모리를 해제할 때 사용한다.
malloc() 함수는 메모리 할당에 성공하면 할당된 메모리의 시작주소를 반환하고 실패하면 NULL을 반환한다.
공유 메모리는 프로세스간 사이에 메모리 영역을 공유해서 사용할 수 있도록 해준다.
메모리 맵핑을 이용하면 파일의 일부 영역에 메모리 주소를 부여할 수 있다.
 
 
 
 
 
 
 
 

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

'Operating System > Linux' 카테고리의 다른 글

Ⅸ 프로세스 제어  (0) 2024.05.22
Ⅷ 프로세스  (0) 2024.05.08
Ⅶ 파일 및 레코드 잠금  (0) 2024.05.08
Ⅵ 파일 시스템  (0) 2024.05.08
Ⅴ 파일 입출력, 중간고사  (0) 2024.04.15