콘텐츠로 바로가기

now0930 일지

이런저런 생각

  • 홈
  • 비공개
  • 강좌
  • 잔여 작업 조회
  • 위치

linux kernel 중 container_of 이해와 사용

container_of를 사용하여 double linked list로 연결된 task_struct가 가진 다른 멤버에 접근할 수 있다. 자세한 원리는 모르겠지만, 컴파일러가 offset을 고정시키는데, 구조체를 미리 알고 있기때문에, 거꾸로 세어 나가는 원리인 듯 하다. 이것은 기술인가? 잔머리인가? 두 번 탐색하지 않아도 되기 때문에 많은 부분에서 사용된다 한다.

유저 프로세스 raspbian_proc가 printf로 글자를 출력하고, sleep으로 3초동안 대기한다. 프린트 될 때 wake_up_process 함수를 실행한다. 이 부분에서 list_head로 연결된 next, prev task_struct를 보기로 했다. 잘 안된다.

싱글 포인터를 사용하면 next 구조체로 접근할 수 없다. double 포인터로 어떻게 어떻게 잘 구해내야 한다. 커널 패치를 하다하다 안되어, 임시로 프로그램을 만들어 gdb로 파 보았다.

#include <stdio.h>
#include <stddef.h>

// Copied from linux/kernel.h
#define container_of(ptr, type, member) ({                      \
		const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
		(type *)( (char *)__mptr - offsetof(type,member) );})


struct myStruct { int a, b; struct myStruct *next; struct myStruct *prev;char name[10];};



//struct myStruct { int a, b; };

int main()
{

	//.은 구조체 내부 멤버를 지칭.
	struct myStruct var = {.a = 0, .b = 0, .next=NULL, .prev=NULL, .name="first"}; 
	struct myStruct var2 = {.a = 2, .b = 2, .next=NULL, .prev=NULL, .name="second"}; 
	struct myStruct var3 = {.a = 3, .b = 3, .next=NULL, .prev=NULL, .name="third"}; 


	// next로 기본 값 되는 듯.
	/*
	var.ptr = (struct list_head*)&(var2.ptr);
	var2.ptr = (struct list_head*)&(var3.ptr);
	var3.ptr = (struct list_head*)&(var.ptr);
	*/

	var.next = &(var2);
	var2.next = &(var3);
	var3.next = &(var);

	var.prev = &(var3);
	var2.prev = &(var);
	var3.prev = &(var2);


	//var.ptr->prev = (struct list_head*)&(var3.ptr);
	//var2.ptr->prev = (struct list_head*)&(var.ptr);
	//var3.ptr->prev = (struct list_head*)&(var2.ptr);


	//var2.ptr->prev= var3.ptr;
	//
	struct myStruct* target;
	struct myStruct** tmp;
	tmp = &(var2.next);
	tmp = &(*tmp)->next;
	printf("test name: %s, var address %p, a value = %d\n", (*tmp)->name, *tmp, (*tmp)->a);
	//var2.next를 가리키는  var3
	target=container_of((struct myStruct**)tmp, struct myStruct, next); 
	printf("test2 name: %s, var address %p, a value = %d\n", target->name, target, target->a);


	target=container_of(&var.next, struct myStruct, next); 
	printf("name: %s, var address %p, a value = %d\n", target->name, target, target->a);

	target=container_of(&var2.next, struct myStruct, next); 
	printf("name: %s, var address %p, a value = %d\n", target->name, target, target->a);

	target=container_of(&var3.next, struct myStruct, next); 
	printf("name: %s, var address %p, a value = %d\n", target->name, target, target->a);


	//prev로 검색.
	target=container_of(&var.prev, struct myStruct, prev); 
	printf("name: %s, var address %p, a value = %d\n", target->name, target, target->a);

	target=container_of(&var2.prev, struct myStruct, prev); 
	printf("name: %s, var address %p, a value = %d\n", target->name, target, target->a);

	target=container_of(&var3.prev, struct myStruct, prev); 
	printf("name: %s, var address %p, a value = %d\n", target->name, target, target->a);


	//int *memberPointer = &var.b;
//
//	int *memberPointer = &var.a;
//
//	printf("Struct addr=%p\n", &var);
//
//	struct myStruct *newSp = container_of(memberPointer, struct myStruct, b); 
//
//	printf("Struct addr new=%p\n", newSp);
//
//	if(newSp == &var)
//	{   
//		printf("It's equal.\n");
//	}   
//
	return 0;
}
pi@raspberrypi:~/RaspberryDebug/taskAddress $ ./a.out 
test name: first, var address 0xbef413e0, a value = 0
test2 name: third, var address 0xbef413a8, a value = 3
name: first, var address 0xbef413e0, a value = 0
name: second, var address 0xbef413c4, a value = 2
name: third, var address 0xbef413a8, a value = 3
name: first, var address 0xbef413e0, a value = 0
name: second, var address 0xbef413c4, a value = 2
name: third, var address 0xbef413a8, a value = 3

struct myStruct** tmp;
tmp = &(var2.next);
tmp = &(*tmp)->next;

tmp같이 이중 포인터로 접근해야 var2.next에 해당하는 var3을 target으로 받을 수 있다. 포인터 연산자 우선순위도 좀 신경써야 해서, (*tmp)를 먼저 쓰고 -> 로 next에 접근해야 한다. 너무 헷갈린다. list_head* next, prev도 더블 포인터다. 이를 gdb로 겨우 알아내고, 커널 core.c 코드를 좀 수정했다. 커널을 디버그하면 좋으나, 그런 방법은 없고, trace32나 kgdb등 방법을 찾아야 된다. 비싸거나 힘들다.

[21:52:04]>cat linux/kernel/sched/core.c  | head -2180 | tail -50
		ttwu_activate(rq, p, ENQUEUE_WAKEUP | ENQUEUE_NOCLOCK);
	}

	ttwu_do_wakeup(rq, p, 0, rf);
	ttwu_stat(p, smp_processor_id(), 0);
out:
	raw_spin_unlock(&p->pi_lock);
}

/**
 * wake_up_process - Wake up a specific process
 * @p: The process to be woken up.
 *
 * Attempt to wake up the nominated process and move it to the set of runnable
 * processes.
 *
 * Return: 1 if the process was woken up, 0 if it was already running.
 *
 * This function executes a full memory barrier before accessing the task state.
 */
int wake_up_process(struct task_struct *p)
{
	//2021. 5. 23추가
	struct task_struct* tmp;
	struct list_head **tmp2;
	if ( !strcmp (p->comm, "raspbian_proc")) {
		//printk("[+]test activated\n");
		tmp2 = (struct list_head**)(p->tasks.next);
		tmp = container_of(tmp2, struct task_struct, tasks.next);
		printk("[=]pid[%d], [%s] point %p at %p \n",
				p->pid, p->comm, p->tasks.next, &p->tasks.next);
		printk("[+]%s point %p at %p\n", tmp->comm, tmp->tasks.next, &tmp->tasks.next);
		printk("[-]%s point %p at %p\n", tmp->comm, tmp->tasks.prev, &tmp->tasks.prev);
		//printk("[==]p->task.next: %p, p: %p\n", &p->tasks.next, &p);
		//

	}

	//2021. 5. 16추가


	return try_to_wake_up(p, TASK_NORMAL, 0);
}
EXPORT_SYMBOL(wake_up_process);

int wake_up_state(struct task_struct *p, unsigned int state)
{
	return try_to_wake_up(p, state, 0);
}
pi@raspberrypi:~/RaspberryDebug/process $ ./raspbian_proc 
raspbian tracing 
raspbian tracing 
raspbian tracing 
[   12.482776] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
[   12.482790] Bluetooth: BNEP filters: protocol multicast
[   12.482811] Bluetooth: BNEP socket layer initialized
[   12.602761] Bluetooth: hci0: unexpected event for opcode 0x0c52
[   43.549185] [=]pid[659], [raspbian_proc] point fb675b81 at e45574a0 
[   43.549191] [+]kworker/2:4 point 1d830555 at fb675b81
[   43.549195] [-]kworker/2:4 point e45574a0 at 71182716
[   46.549300] [=]pid[659], [raspbian_proc] point fb675b81 at e45574a0 
[   46.549309] [+]kworker/2:4 point 1d830555 at fb675b81
[   46.549317] [-]kworker/2:4 point e45574a0 at 71182716
[   49.549442] [=]pid[659], [raspbian_proc] point fb675b81 at e45574a0 
[   49.549452] [+]kworker/2:4 point 1d830555 at fb675b81
[   49.549460] [-]kworker/2:4 point e45574a0 at 71182716

여러 사이트를 참조 했다.

https://kamang-it.tistory.com/entry/Cclangcontainerof%EC%99%80-offsetof%EB%A1%9C-%EB%A9%A4%EB%B2%84-%EB%B3%80%EC%88%98%EB%A1%9C-%ED%95%B4%EB%8B%B9-%EA%B5%AC%EC%A1%B0%EC%B2%B4-%EB%B0%98%ED%99%98%ED%95%98%EA%B8%B0
https://digiconfactory.tistory.com/entry/C%EC%96%B8%EC%96%B4-%EC%9D%B4%EC%A4%91-%EC%97%B0%EA%B2%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8-Doubly-Linked-List-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0
https://stackoverflow.com/questions/21398105/accessing-structure-elements-via-double-pointers-in-c
https://doomsodradek.blogspot.com/2017/06/c-struct-warning-assignment-from.html

이 글 공유하기:

  • Tweet
발행일 2021-05-23글쓴이 이대원
카테고리 생활코딩 태그 container_of, linux, raspberry, 임베디드 리눅스

댓글 남기기응답 취소

이 사이트는 Akismet을 사용하여 스팸을 줄입니다. 댓글 데이터가 어떻게 처리되는지 알아보세요.

글 내비게이션

이전 글

wifi 동글(ew-7811)로 내부 망 연결

다음 글

과학하고 앉아있네 podcast 다운로드 + plex 관리 스크립트

2025 5월
일 월 화 수 목 금 토
 123
45678910
11121314151617
18192021222324
25262728293031
4월    

최신 글

  • common mode, differential mode 2025-05-11
  • signal conditioner, 신호 처리기 2025-05-10
  • strain gage 2025-05-09
  • 칼만 필터 2025-05-01
  • positioner(I/P) 2025-04-26

카테고리

  • 산업계측제어기술사
  • 삶 자국
    • 책과 영화
    • 투자
  • 생활코딩
    • LEGO
    • ROS
    • tensorflow
  • 전기기사
  • 피아노 악보

메타

  • 로그인
  • 엔트리 피드
  • 댓글 피드
  • WordPress.org

페이지

  • 소개
  • 잔여 작업 조회
    • 작업 추가
    • 작업의 사진 조회
    • 작업 수정 페이지
  • 사진
    • GPS 입력된 사진
    • 사진 조회
  • 위치
    • 하기 휴가 방문지
    • 해외 출장

태그

android bash c docker driver FSM gps java kernel LEGO linux mysql network program opcua open62541 plc programmers python raspberry reinforcementLearning ros state space system program tensorflow transfer function 경제 미국 민수 삼국지 세계사 실기 에너지 역사 유전자 일본 임베디드 리눅스 전기기사 조선 중국 채윤 코딩 테스트 통계 한국사 한국어

팔로우하세요

  • Facebook
now0930 일지
WordPress로 제작.