위험한 포인터---문자 포인터 및 문자 배열 포인터로 인한 분할 오류

머리말

(1) 오늘 플랫폼 버스 모델을 사용하여 LED 조명 프로그램을 작성할 때 드라이버가 로드될 때마다 다음 오류가 보고되고 마지막 줄에 세그먼트 오류가 보고되는 것을 발견했습니다.
(2) 처음에는 플랫폼 버스 모델이 잘못 쓰여서 그런 줄 알고 한참을 비교한 결과 아무 문제가 없어 결국 printk를 사용하여 문제를 찾았습니다.

여기에 이미지 설명 삽입

문제 코드 섹션

(1) 당시 인쇄 디버깅을 위해 printk를 사용했는데, 결국 이 부분에서 문제를 찾았다.
(2) 그래서 저는 이 세 줄의 코드를 공부하기 시작했습니다. 아주 흥미로운 곳을 발견했고,이 곳에서 보고된 오류가 가끔 나타나지 않습니다.! 그는 위아래로 움직였고 문제를 찾는 데 오랜 시간이 걸렸습니다.
(3) 마지막으로 내가 전달한 gpios[i].name은 문자 포인터이고 다른 사람의 코드에서 전달한 gpios[i].name은 문자 배열 포인터라는 것을 알았습니다.

여기에 이미지 설명 삽입

(4) 마지막으로 다음과 같이 구조를 변경했더니 문제가 해결되었습니다.

/**  原来的结构体  **/
struct gpio_desc{
    
    
	int gpio;   //引脚编号
    char *name; //名字
};

/**  更改后的结构体  **/
struct gpio_desc{
    
    
	int gpio;   //引脚编号
    char name[128]; //名字
};

문자 포인터와 문자 배열 포인터의 차이점

구문을 준수하는지 여부에 관계없이 문자 포인터를 전달합니다.

(1) 먼저 sprintf()의 함수 선언을 보면 첫 번째 매개변수가 char* 유형의 데이터임을 알 수 있습니다.
(2) 그런 다음 문자 포인터를 전달하면 실제로 문법 규칙을 준수합니다.

int sprintf(char *str, const char *format, ...);

오류가 있는 이유

(1) 문법에 맞는데 왜 에러가 나나요? 이것은 문자 포인터와 문자 배열 포인터의 차이점에 관한 것입니다.
(2) 가장 간단하고 대중적인 용어로 문자 배열 포인터는 실제로 문자 포인터의 업그레이드된 버전입니다. 왜 그런 말을 해? 문자 배열 포인터와 문자 포인터는 모두 포인터이지만 가리키는 영역이 다르기 때문입니다.
(3) 문자 배열 포인터
<1>은 먼저 시스템에서 사용하지 않는 메모리 영역을 적용해야 합니다. 예를 들어 여기서 설정한 것은 char name[128]입니다. 사용되지 않은 메모리 영역 메모리가 사용되었습니다.
<2> 이 메모리 조각을 적용한 후 시스템은 이 메모리 조각의 첫 번째 주소를 반환합니다.
(4) 문자 포인터 . 문자포인터를 신청하면 어느 곳이든 가리킬 수 있으며, 그 곳은 적용된 공간이 될 수도 있고 적용되지 않은 공간이 될 수도 있습니다. 그가 가리키는 영역은 알 수 없으며, 그에게 가치를 할당하기 위해 주도권을 행사하지 않으면 그는 전설적인 와일드 포인터입니다 .
(5) 주석 구분 기호 위와 아래의 두 코드는 동일합니다.

char name[128];
/***  上下两个代码等价  ***/
char *name;
name = (char*)malloc(128*sizeof(char));

이 문제가 모든 칩에서 보고됩니까?

MMU의 역할

(1) 이 문제를 제기하기 전에 우리는 한 가지 질문을 생각해 볼 필요가 있습니다. 성공적인 컴파일, 오류 코드 없음, 정말 문제가 없습니까?
(2) 대답은 '아니오'여야 합니다. 그렇지 않으면 일부 형이상학적 질문이 항상 발생하지 않을 것입니다. 여기도 마찬가지고, 문법의 관점에서 볼 때 실제로 우리 코드에는 문제가 없으며 컴파일러는 원활하게 통과할 수 있습니다. 따라서 이 코드 문자열을 컴파일할 때 아무런 문제가 없었습니다.
(3) 그런데 드라이버를 로드하면 왜 내 PC에서 오류가 발생합니까? 운영 체제에 메모리 관리 장치 MMU를 도입해야 합니다.
(4)
<1>MMU는 가상 주소를 물리 주소로 변환하고 메모리에 대한 액세스 권한을 제어하는 ​​컴퓨터 하드웨어의 구성 요소입니다.
<2> 프로그램이 실행되면 malloc() 함수를 사용하여 힙에 메모리를 할당하거나 로컬 변수를 정의하여 스택에 메모리를 할당하는 등 메모리 할당을 요청합니다. MMU는 이러한 메모리 할당을 추적하고 프로세스에 할당된 메모리 블록을 추적합니다.
<3>메모리 관리는 일반적으로 메모리 매핑을 통해 구현됩니다. 각 프로세스에는 코드 세그먼트, 데이터 세그먼트, 힙, 스택 등과 같은 여러 영역으로 나누어지는 자체 가상 주소 공간이 있습니다. 프로그램이 메모리를 요청하면 운영 체제는 프로그램에 가상 주소 공간의 적절한 가상 메모리 주소를 할당합니다. 그런 다음 MMU는 이러한 가상 주소를 물리적 메모리 주소에 매핑합니다.
<4> 프로그램이 가상 주소에 액세스하려고 하면 MMU는 가상 주소를 해당 물리적 ​​주소로 변환합니다. 이 과정에서 MMU는 가상 주소가 유효한 할당 메모리 범위 내에 있고 액세스 권한이 올바른지 확인합니다. 가상 주소가 유효하지 않거나 권한이 허용되지 않으면 MMU는 예외(예: 분할 오류 또는 액세스 위반)를 트리거하고 운영 체제가 개입하여 프로그램 실행을 종료합니다.
<5> 따라서 운영 체제는 MMU를 사용하여 공간이 적용되었는지 여부와 액세스 권한을 알 수 있습니다.적용되지 않은 메모리나 액세스 권한이 제한된 메모리에 액세스하려는 시도는 운영 체제가 개입하여 프로그램이 계속 실행되는 것을 방지하여 시스템 안정성과 보안을 보장합니다.
(5) MMU에 대한 위의 소개를 읽은 후, 우리는 MMU가 미적용 메모리에 대한 액세스 또는 액세스가 제한된 메모리 동작을 방지할 수 있음을 알고 있습니다. 이것은 좋은 설명입니다. 이 버그는 때때로 나타나고 때로는 사라집니다.
(6) 우리 모두는 gpios[i].name이 문자 포인터로 정의될 때 본질적으로 와일드 포인터이므로 그것이 가리키는 공간이 유효한지 여부를 알 수 없다는 것을 알고 있습니다. 적용되지 않은 메모리를 가리키는 경우. 세분화 오류가 표시됩니다.
(7) 그러나 gpios[i].name이 문자 포인터로 사용되고 와일드 포인터로 적용된 메모리를 가리키면 분할 오류가 발생하지 않습니다. 그러나 MMU의 역할은 그다지 크지 않아 보인다. 정말 이럴까?
(8) MMU는 와일드 포인터가 적용된 메모리를 가리키고 있음을 발견하지만 와일드 포인터가 액세스 권한을 가지고 있는지 여부를 식별할 수 있습니다. 액세스 권한이 없으면 분할 오류가 보고되지 않더라도 다른 오류가 계속 나타납니다.

MMU가 없으면 어떻게 될까요?

(1) MMU가 시스템의 보안을 크게 보호하고 있음을 알 수 있다. 그러나 위에서 말했듯이 MMU는 컴퓨터 하드웨어의 구성 요소입니다.현재 사용하는 칩에 MMU가 없으면 STM32F103과 같은 일반 마이크로 컨트롤러에 불과합니다.
(2) MMU가 없기 때문에 기존의 MCU는 하드웨어 관점에서 메모리 상태를 감지할 수 없습니다.
(3) 그런데 정말 구원이 없느냐? 분명히 그렇지는 않지만 Linux는 이제 MMU가 없는 칩을 지원합니다. 따라서 이에 대처할 수 있는 방법이 있어야 합니다.
(4) 일반적인 소프트웨어 메모리 관리 방법에는 비트맵 관리 , 연결 목록 관리메모리 풀이 포함됩니다 .
(5) 소프트웨어 메모리 관리가 있지만 여전히 결함이 있습니다. 하드웨어 수준의 주소 변환 및 권한 검사가 없기 때문에 이러한 방법은 버그, 범위를 벗어난 액세스 또는 리소스 경합에 더 취약할 수 있습니다 . MMU가 있는 시스템은 서로 다른 프로세스의 메모리 공간을 보다 효과적으로 분리하여 더 나은 보안과 안정성을 제공할 수 있습니다.
(6) 리소스 경합이 발생할 가능성이 더 높은 이유는 무엇입니까? 위의 오류를 보고한 코드를 예로 들어 보겠습니다.
<1> 우선 gpios[i].name이 문자 포인터로 정의될 때 가리키는 위치를 알 수 없다는 것을 알고 있습니다. 그런 다음 문제가 있습니다. gpios[i].name이 다른 변수에 적용되고 점유된 메모리 조각을 가리키기만 하면 어떻게 됩니까?
<2> 이와 같이 gpios[i].name의 데이터를 조정하면 그에 따라 해당 변수가 변경됩니다. 정상적인 논리에 따르면 어떤 변수가 변경되지 않아야 합니다. 그러면 알 수 없는 오류가 발생합니다.
<3> 이 와일드 포인터는 자원 경합인 다른 가변 저장 영역을 가리킵니다.

요약하다

(1) 문자 포인터와 문자 배열 포인터의 유사점과 차이점을 명확하게 이해해야 합니다. 문자 포인터가 초기화되지 않으면 그는 위험한 와일드 포인터입니다.
(2) MMU 메커니즘을 통해 칩은 우수한 메모리 관리를 수행하여 임의 포인터 문제를 감지할 수 있습니다.
(3) MMU 메커니즘이 없다면 비트맵 관리 , 링크드 리스트 관리 , 메모리 풀 등의 방법을 통해서도 메모리 관리가 가능하지만 MMU에 비하면 여전히 부족하다.

추천

출처blog.csdn.net/qq_63922192/article/details/131902977