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

댓글 남기기

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