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
여러 사이트를 참조 했다.