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