출처가 설명을 제대로 하지 않아, 적용하기 어려웠다. PowerShell에 최대한 짧게 입력했다. scroll lock키를 무한 동작
$wshell=New-Object -ComObject wscript.shell; //이거 없으면 키 입력을 만들 수 없음.
while($true)
{
$wshell.sendkeys("{SCROLLLOCK}"); //키 값 확인.
Start-Sleep -Seconds 100;
}
책이 dwc_otg_driver.c의 request_irq로 인터럽트를 등록을 설명했다. 그러나, 라즈베리 파이4가 dwc-otg_driver.c 함수를 사용하지 않는다. 있긴 있는데, 4로 오면서 의도적으로 사용하지 않는 듯 하다. 아무리 찾아봐도 request_irq를 dump_stack 메세지에서 찾을 수 없다. 내가 본 메세지는 다음과 같다.
Jun 14 07:38:24 raspberrypi kernel: [ 0.547246] xhci_hcd 0000:01:00.0: hcc params 0x002841eb hci version 0x100 quirks 0x0000001000000890
Jun 14 07:38:24 raspberrypi kernel: [ 0.550250] [+][irq_debug] irq_num: 53, func: request_threaded_irq, line: 1888, caller: xhci_run+0x240/0x670
Jun 14 07:38:24 raspberrypi kernel: [ 0.552941] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.19.127-v7l+ #78
Jun 14 07:38:24 raspberrypi kernel: [ 0.555588] Hardware name: BCM2835
Jun 14 07:38:24 raspberrypi kernel: [ 0.558290] [<c0212dd8>] (unwind_backtrace) from [<c020d3ec>] (show_stack+0x20/0x24)
Jun 14 07:38:24 raspberrypi kernel: [ 0.561023] [<c020d3ec>] (show_stack) from [<c09c01dc>] (dump_stack+0xd8/0x11c)
Jun 14 07:38:24 raspberrypi kernel: [ 0.563706] [<c09c01dc>] (dump_stack) from [<c0285ae0>] (request_threaded_irq+0x1b4/0x1b8)
Jun 14 07:38:24 raspberrypi kernel: [ 0.566425] [<c0285ae0>] (request_threaded_irq) from [<c07b4530>] (xhci_run+0x240/0x670)
Jun 14 07:38:24 raspberrypi kernel: [ 0.569178] [<c07b4530>] (xhci_run) from [<c079bd6c>] (usb_add_hcd+0x324/0x7ac)
Jun 14 07:38:24 raspberrypi kernel: [ 0.571892] [<c079bd6c>] (usb_add_hcd) from [<c07afa84>] (usb_hcd_pci_probe+0x278/0x398)
Jun 14 07:38:24 raspberrypi kernel: [ 0.574625] [<c07afa84>] (usb_hcd_pci_probe) from [<c07cc760>] (xhci_pci_probe+0x3c/0x180)
Jun 14 07:38:24 raspberrypi kernel: [ 0.577382] [<c07cc760>] (xhci_pci_probe) from [<c067d008>] (pci_device_probe+0xb0/0x138)
Jun 14 07:38:24 raspberrypi kernel: [ 0.580118] [<c067d008>] (pci_device_probe) from [<c0718f3c>] (really_probe+0x20c/0x2cc)
Jun 14 07:38:24 raspberrypi kernel: [ 0.582911] [<c0718f3c>] (really_probe) from [<c07191d0>] (driver_probe_device+0x70/0x188)
Jun 14 07:38:24 raspberrypi kernel: [ 0.585734] [<c07191d0>] (driver_probe_device) from [<c07193e4>] (__driver_attach+0xfc/0x100)
Jun 14 07:38:24 raspberrypi kernel: [ 0.588587] [<c07193e4>] (__driver_attach) from [<c0716e48>] (bus_for_each_dev+0x84/0xc4)
Jun 14 07:38:24 raspberrypi kernel: [ 0.591438] [<c0716e48>] (bus_for_each_dev) from [<c07187c8>] (driver_attach+0x2c/0x30)
Jun 14 07:38:24 raspberrypi kernel: [ 0.594277] [<c07187c8>] (driver_attach) from [<c0718248>] (bus_add_driver+0x1d0/0x214)
Jun 14 07:38:24 raspberrypi kernel: [ 0.597086] [<c0718248>] (bus_add_driver) from [<c0719bc4>] (driver_register+0x84/0x118)
Jun 14 07:38:24 raspberrypi kernel: [ 0.599945] [<c0719bc4>] (driver_register) from [<c067c2e0>] (__pci_register_driver+0x58/0x5c)
Jun 14 07:38:24 raspberrypi kernel: [ 0.602852] [<c067c2e0>] (__pci_register_driver) from [<c0e3f6cc>] (xhci_pci_init+0x68/0x6c)
Jun 14 07:38:24 raspberrypi kernel: [ 0.605785] [<c0e3f6cc>] (xhci_pci_init) from [<c0203004>] (do_one_initcall+0x50/0x220)
Jun 14 07:38:24 raspberrypi kernel: [ 0.608748] [<c0203004>] (do_one_initcall) from [<c0e01374>] (kernel_init_freeable+0x340/0x3e0)
Jun 14 07:38:24 raspberrypi kernel: [ 0.611723] [<c0e01374>] (kernel_init_freeable) from [<c09d5590>] (kernel_init+0x18/0x128)
Jun 14 07:38:24 raspberrypi kernel: [ 0.614684] [<c09d5590>] (kernel_init) from [<c02010ac>] (ret_from_fork+0x14/0x28)
Jun 14 07:38:24 raspberrypi kernel: [ 0.617678] Exception stack(0xefa61fb0 to 0xefa61ff8)
일단 request_threaded_irq가 request_irq임은 확실하다.
xhci_run이 request_threaded_irq를 호출한다.
usb_add_hcd가 xhci_run을 호출한다.
… 이런 식으로 kernel_init로 간다.
결국 시스템 시작할 때 인터럽트를 등록한다.
문제가 책 예시와 다르게, 누가 request_irq를 실행하는지 모르는 점이다. dump_stack이 미리 정의되지 않은 함수는 보여주지 않는 듯 하다. 찾아보면 알겠지만 msi가 드라이버를 만들었다. xhci_run, usb_add_hcd, usb_hcd_pci_probe 등 여러 함수 중 request_irq를 콜하는 부분을 쉽게 찾을 수 없었다. 그러나 시간이 많은 나는 결국 xhci_run의 xhci_setup_msi가 requset_irq를 함을 찾아냈다.
/*
* Set up MSI
*/
static int xhci_setup_msi(struct xhci_hcd *xhci)
{
int ret;
/*
* TODO:Check with MSI Soc for sysdev
*/
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
if (ret < 0) {
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"failed to allocate MSI entry");
return ret;
}
ret = request_irq(pdev->irq, xhci_msi_irq,
0, "xhci_hcd", xhci_to_hcd(xhci));
//210611 irq 추가...
interrupt_debug_irq_desc(pdev->irq);
if (ret) {
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"disable MSI interrupt");
pci_free_irq_vectors(pdev);
}
return ret;
}
이렇게 설정 후 컴파일 하면, 메세지가 제대로 출력된다. 교재와 다르게 인터럽트 53번으로 설정한다.
Jun 14 07:38:24 raspberrypi kernel: [ 0.626769] 1fe0: 00000000 00000000 00000000 00000000 00000013 00000000
Jun 14 07:38:24 raspberrypi kernel: [ 0.629855] genirq: irq_chip Brcm_MSI did not update eff. affinity mask of irq 53
Jun 14 07:38:24 raspberrypi kernel: [ 0.632884] [+] irq_desc debug start
Jun 14 07:38:24 raspberrypi kernel: [ 0.635920] irq num: 53 name: xhci_hcd
Jun 14 07:38:24 raspberrypi kernel: [ 0.638938] dev_id:0xefa55000
Jun 14 07:38:24 raspberrypi kernel: [ 0.641924] interrupt handler: xhci_msi_irq+0x0/0x20
Jun 14 07:38:24 raspberrypi kernel: [ 0.644914] [-] irq_desc debug end
몇 번 해보면서 느꼈는데, 꼬리에 꼬리를 물어 원하는 부분을 찾아내는 능력이 필요해 보인다. 그래야 그 부분을 수정하여 원하는 기능을 구현할 수 있으니까.
과거 쎈호스팅에서 mp3를 다운로드를 제공했었는데, 팟빵 앱으로 접근하지 않는 사용자들을 제약하려고 주소를 옮겼다. 겨우 찾았는데, 쎈호스팅과 과학과사람들이 어떤 일을 했는지 이후 다운로드 서비스도 하고 있지 않았다. 귀찮아 방치했는데, 하드디스크로 바꾼 기념으로 찾아보니, 홈페이지가 rss를 제공하고 있었다.
bash로 xml을 parsing하기 힘들어 xpath 도움을 받았다. xpath를 쓰지 않았을 때 정규표현식으로 억지로 뽑아냈다. xmllint도 있는데, 맨 처음 한 개만 제대로 출력하고 이 후 정보를 무시해버려 시간만 날렸다. 몇 시간 노가다 끝에 다음 방식으로 정리했다. 과거 노가다보다 훨씬 정확하고 보람차다.
#!/bin/bash
#cron을 위한 cd
cd ???
echo "TEDTalks Downloader v0.1"
echo "by Denver Gingerich (http://ossguy.com/)"
which wget
if [ $? -eq 0 ]; then
echo "Using wget..."
GET_CMD="wget --quiet -O"
else
which curl
if [ $? -eq 0 ]; then
echo "Using curl..."
GET_CMD="curl -L -o"
else
echo "Could not find wget or curl"
exit 2
fi
fi
FEED=feed_podcast.xml
URLS=urls_podcast
DIRNAME=파토의과학
rm -f ${FEED}
rm -f ${URLS}
#podcast_address=http://www.podbbang.com/ch/6205
podcast_address=https://feeds.feedburner.com/sciencewithpeople
${GET_CMD} ${FEED} ${podcast_address}
#xpath
#get nth item
#xpath -e "//rss/channel/item[position()=2]/title" temp2.txt
patoCouter=10;
#과학과사람들fileInfo.txt 만듦.
#filename, path, title, 업데이트일 순으로 정리
for i in $(seq 1 $patoCouter);
do
#echo $i
title=$(xpath -q -e "//rss/channel/item[position()=$i]/title" ${FEED})
title2=$(echo $title | sed 's/<title>//g' | sed 's/<\/title>//')
title3=$(echo $title2 | sed 's/ / /g' | sed 's/,/_/g')
#echo $title3
fileTmp=$(xpath -q -e "//rss/channel/item[position()=$i]/enclosure" ${FEED})
file=$(echo $fileTmp | cut -d'"' -f2)
#echo $file
pubTmp=$(xpath -q -e "//rss/channel/item[position()=$i]/pubDate" ${FEED})
pub=$(echo $pubTmp| sed 's/<pubDate>//g' | sed 's/<\/pubDate>//'| cut -d',' -f2 | cut -d' ' -f1-4 | sed 's/^ //g')
#monthtmp=$(echo $pub | cut -d'-' -f2)
#daytmp=$(echo $pub | cut -d'-' -f1)
#yeartmp=$(echo $pub | cut -d'-' -f3)
#echo $pubTmp
echo $pub
#echo $monthtmp
#from
#https://www.unix.com/shell-programming-and-scripting/191527-converting-month-into-integer.html
#M=1
#for X in Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
#do
# [ "$monthtmp" = "$X" ] && break
# M=`expr $M + 1`
#done
#myeardate=$(date --date="$(printf "%s" $yeartmp-$M-$daytmp)" +"%Y-%m-%d")
#echo $myeardate
printf "$title3" >> ${URLS}
printf "," >> ${URLS}
printf "$file" >> ${URLS}
printf "," >> ${URLS}
printf "$pub" >> ${URLS}
printf "\n" >> ${URLS}
done
cp ${URLS} ${DIRNAME}/파토가과학하고앉아있네FileAndTitle.txt
mkdir -p ${DIRNAME}
cd ${DIRNAME}
for line in $(cat ../${URLS} | cut -d',' -f2);
do
URL=$(echo ${line} | cut -d',' -f2)
FILENAME=`basename ${URL}|tr -d '\r'`
if [ ! -f "${FILENAME}" ]; then
# || [ ${SIZE} -ne `ls -l "${FILENAME}" | awk '{print $5}'` ]; then
rm -f "${FILENAME}"
${GET_CMD} "${FILENAME}" "${URL}"
fi
done
id3를 업데이트 하는 스크립트를 실행하면 각 폴더에 설정한 details.txt를 읽어 업데이트 한다. 이전에 이 내용을 포스트했다. 다 하고나니 이렇게까지 해야 하나 싶다. 관리하기 참 까다롭다. plex media server 스캐너까지 관리하고 싶은데, 어떻게 하는지 모르겠다. 모두 하면 다음과 같이 관리할 수 있다.
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
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);
}
[ 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
실습 라즈베리 파이를 전원만 붙여 책상에 올렸다. 처음에는 이미지를 잘못 뜨면 부팅되지 않을까 키보드, 마우스, 모니터가 필요하다 생각했다. 그러나 크로스 컴파일 삽질을 몇 번 해보니 부팅 이미지를 쉽게 만들 수 있었다. 공간(공간 = 돈)을 절약하고자 라즈베리 파이에 전원만 넣고 나머지를 싹 치웠다.
커널 패닉을 몇 번이나 보겠어? 데스크탑에서 ssh로 라즈베리 파이에 접속하여 사용하기로 했다. 유선으로 연결하면 간단한데, 공유기는 거실에 있고, 데스크탑은 다른 방에 있어 지저분하다. 게다가 데스크탑에 랜포트가 하나라 인터넷용으로 할당해야 한다. usb용 와이파이 모듈로 붙이면 간단한데, 지금 사기 아깝다. 포기하려 했으나, 과거 레고 ev3에 연결했던 와이파이 동글을 찾아냈다. 당장 레고에서 적출하여 데스크탑에 꼽았다.
[09:59:09]>lsusb
...
Bus 001 Device 002: ID 7392:7811 Edimax Technology Co., Ltd EW-7811Un 802.11n Wireless Adapter [Realtek RTL8188CUS]
realtek rtl8188cus 드라이버를 찾아야 한다. 인터넷에서 찾았지만 너무 오래되어 kernel 2.x대로 컴파일 해야한다. 무슨 헤더파일이 커널 업데이트 되면서 사라졌는데, 이 부분을 수정해야 컴파일 할 수 있을 것 같다. 누가??? 포기하고, rtl8188이 rtl8192 드라이버를 사용하는 듯 하여 이 드라이버를 찾아봤다.