針對查詢「timer」依關聯性排序顯示文章。依日期排序 顯示所有文章
針對查詢「timer」依關聯性排序顯示文章。依日期排序 顯示所有文章

2009年12月27日 星期日

Linux Kernel(7.1)- timer


有時候我們希望能在某個時間點執行某些動作,這時候便可以使用timer,在使用timer有些規矩必須被遵守。因為不是user-space來喚起,所以不允許存取user-space,current也就沒有意義。不能休眠,也不准schedule()或者任何有可能休眠的動作都不准。
struct timer_list {
 struct list_head entry;
 unsigned long expires;

 void (*function)(unsigned long);
 unsigned long data;

 struct tvec_base *base;
#ifdef CONFIG_TIMER_STATS
 void *start_site;
 char start_comm[16];
 int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
 struct lockdep_map lockdep_map;
#endif
};

timer_list必須初始化之後才能使用,您可以選擇init_timer()或TIMER_INITIALIZER(),接著就可以設定expires/callback function/data(參數),並且使用add_timer()將其加入timer中,或者使用del_timer()移除pending中的timer,也可以使用mod_timer()修改或者重新設定timer。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>

MODULE_LICENSE("GPL");

struct timer_list brook_timer;
static void callback(unsigned long);
struct data {
    int count;
};
static struct data data;

static void callback(unsigned long data)
{
    struct data *dp = (struct data*) data;
    printk("%s(): %d\n", __FUNCTION__, dp->count++);
    mod_timer(&brook_timer, jiffies + 5 * HZ);
}

static int __init init_modules(void)
{
    init_timer(&brook_timer);
    brook_timer.expires = jiffies + 5 * HZ;
    brook_timer.function = &callback;
    brook_timer.data = (unsigned long) &data;
    add_timer(&brook_timer);
    return 0;
}

static void __exit exit_modules(void)
{
    del_timer(&brook_timer);
}

module_init(init_modules);
module_exit(exit_modules);


kernel timer最短的間隔是1個jiffies,而且會受到硬體中斷,和其他非同步事件的干擾,所以不適合非常精密的應用。

Linux Kernel(7)- timing


kernel會定期產生timer interrupt,HZ定義每秒產生timer interrupt的次數,定義在linux/param.h,根據平台的不同從50~1200不等。
而jiffies每當發生一次timer interrupt就會遞增一次,jiffies定義於linux/jiffies.h,所以簡單的說,jiffies就等於1/HZ,不管在64bit或32bit上的機器,Linux kernel都使用64位元版的jiffies_64,而jiffies其實是jiffies_64的低32位元版,除了讀取外,我們都不應該直接修改jiffies/jiffies_64。
kernel提供幾組macro來比較時間的先後,time_after()/timer_before()/time_after_eq()/time_before_eq()。
/*
 * These inlines deal with timer wrapping correctly. You are 
 * strongly encouraged to use them
 * 1. Because people otherwise forget
 * 2. Because if the timer wrap changes in future you won't have to
 *    alter your driver code.
 *
 * time_after(a,b) returns true if the time a is after time b.
 *
 * Do this with "<0" and ">=0" to only test the sign of the result. A
 * good compiler would generate better code (and a really good compiler
 * wouldn't care). Gcc is currently neither.
 */
#define time_after(a,b)  \
 (typecheck(unsigned long, a) && \
  typecheck(unsigned long, b) && \
  ((long)(b) - (long)(a) < 0))
#define time_before(a,b) time_after(b,a)

#define time_after_eq(a,b) \
 (typecheck(unsigned long, a) && \
  typecheck(unsigned long, b) && \
  ((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a)

另外,kernel中有兩種時間的structure,struct timeval和struct timespec。
#ifndef _STRUCT_TIMESPEC
#define _STRUCT_TIMESPEC
struct timespec {
 __kernel_time_t tv_sec;   /* seconds */
 long  tv_nsec;  /* nanoseconds */
};
#endif

struct timeval {
 __kernel_time_t  tv_sec;  /* seconds */
 __kernel_suseconds_t tv_usec; /* microseconds */
};
早期以timeval為主,後來因為精密度的需求,有了timespec的誕生。kernel也提供了和jiffies的轉換函數。更多的轉換可以參考linux/jiffies.h
unsigned long timespec_to_jiffies(const struct timespec *value);
void jiffies_to_timespec(const unsigned long jiffies,
    struct timespec *value);
unsigned long timeval_to_jiffies(const struct timeval *value);
void jiffies_to_timeval(const unsigned long jiffies,
          struct timeval *value);


2018年3月17日 星期六

An Sample Code for threads control - A Wrap for service/thread start/stop/periodical run


multi-thread programming設計很常見, 寫法上與功能上也都很相似, 最基本的幾個操作就是, create/start/stop/periodical run/destroy等等. 最好還能monitor這些thread狀況, 比如被執行幾次, 執行時間多久, 是否有dead lock等等, 於是就寫了這個pattern, 提供這類功能.

service-reg.h

#ifndef SERVICE_REG_H
#define SERVICE_REG_H
#include <sys/time.h>

typedef void *(*srv_fp) (void *);

char * srv_dump(char *buf, int sz);

int _srv_reg(char *srv_name, srv_fp fp, char *fp_name, void *srv_data);
#define srv_reg(srv_name, fp, srv_data) _srv_reg(srv_name, fp, #fp, srv_data)
int srv_start(char *srv_name); // run forever without any delay
int srv_stop(char *srv_name);
int srv_unreg(char *srv_name);
int srv_start_periodical(char *srv_name, unsigned long sec, unsigned long nsec); // run forever with delay

#define SRV_NO_MEM -1

#endif


service-reg.c

#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <sys/timerfd.h>
#include <stdint.h>
#include <unistd.h>
#include "service-reg.h"
#include "lookup_table.h"

#define SEC2NSEC 1000000000.0

struct srv_info {
    void *srv_data;
    char *srv_name;
    srv_fp fp;
    int in_fp;
    char *fp_name;
    unsigned run_cnt;
    pthread_t thread_id; /* ID returned by pthread_create() */
    pthread_cond_t cond;
    pthread_mutex_t mutex;
    struct srv_info *next;
    volatile int running; /* 1 for running, 0 for stop */
    struct timespec active_time;
    struct timespec last_enter;
    struct timespec last_exit;
    int tm_fd; /* used to delay */
};

struct srv_tab {
    pthread_mutex_t mutex;
    struct srv_info *list;
    unsigned int num_srv;
} srv_tab = {
    .mutex = PTHREAD_MUTEX_INITIALIZER,
    .list = NULL,
    .num_srv = 0,
};

static void _srv_update_active_time(struct srv_info *s)
{
    clock_gettime(CLOCK_MONOTONIC, &(s->last_exit));

    s->active_time.tv_sec += s->last_exit.tv_sec - s->last_enter.tv_sec;
    if (s->last_exit.tv_nsec > s->last_enter.tv_nsec) {
        s->active_time.tv_nsec += s->last_exit.tv_nsec - s->last_enter.tv_nsec;
    } else {
        s->active_time.tv_sec--;
        s->active_time.tv_nsec += SEC2NSEC + s->last_exit.tv_nsec - s->last_enter.tv_nsec;
    }

    if (s->active_time.tv_nsec >= SEC2NSEC) {
        s->active_time.tv_nsec -= SEC2NSEC;
        s->active_time.tv_sec++;
    }
}

static void *_srv_wrap(void *v)
{
    struct srv_info *s = (struct srv_info *) v;
    while (1) {
recheck:
        if (s->running == 0) {
stoprun:
            pthread_cond_wait(&(s->cond), &(s->mutex));
            goto recheck;
        }

        /* if timer is enabled, than wait */
again:
        if (s->tm_fd != -1) {
            uint64_t exp;
            ssize_t sz;
            sz = read(s->tm_fd, &exp, sizeof(exp));
            if (sz != sizeof(exp)) {
                printf("timerfd_settime failed: s->tm_fd:%d, errno:%d/%s\n",
                            s->tm_fd, errno, strerror(errno));
                if (errno == EINTR) {
                    goto again;
                }
            }

            if (s->running == 0) {
                goto stoprun;
            }
        }

        clock_gettime(CLOCK_MONOTONIC, &(s->last_enter));
        s->in_fp = 1;
        s->fp(s->srv_data);
        s->in_fp = 0;
        _srv_update_active_time(s);
        s->run_cnt++;
    }
    return NULL;
}

int srv_init(void)
{
    return 0;
}

char * srv_dump(char *buf, int sz)
{
    struct srv_info *s;
    char *p = buf;
    int i = 0;
    struct timespec cur_clk;
    struct itimerspec cur_timer;

    clock_gettime(CLOCK_MONOTONIC, &cur_clk);

    if (buf == NULL) {
        return NULL;
    }

    memset(buf, 0, sz);
    pthread_mutex_lock(&(srv_tab.mutex));
    s = srv_tab.list;

    p += snprintf(p, sz - (buf - p), "cur-clk: %f\n", cur_clk.tv_sec + cur_clk.tv_nsec/SEC2NSEC);
    p += snprintf(p, sz - (buf - p), "id   srv_name    running/in_fp\t\tfp/fname/dp\n");
    p += snprintf(p, sz - (buf - p), "\t\t\trun-cnt/run-time\tenter-clk/leave-clk\n");
    p += snprintf(p, sz - (buf - p), "\t\t\ttm_fd/next_expir/perodic\n");
    while (s != NULL) {
        p += snprintf(p, sz - (buf - p), "%-8d%-16s%2d/%-2d\t\t%16p/%s/%-16p\t\t\t\n",
                        i++, s->srv_name, s->running, s->in_fp, s->fp, s->fp_name, s->srv_data);
        p += snprintf(p, sz - (buf - p), "\t\t   %10d/%-12f\t%12f/%-12f\n",
                        s->run_cnt,
                        s->active_time.tv_sec + s->active_time.tv_nsec/SEC2NSEC,
                        s->last_enter.tv_sec + s->last_enter.tv_nsec/SEC2NSEC,
                        s->last_exit.tv_sec + s->last_exit.tv_nsec/SEC2NSEC);
        if (s->tm_fd != -1) {
            if (timerfd_gettime(s->tm_fd, &cur_timer) < 0) {
                printf("timerfd_get failed: errno:%d/%s\n", errno, strerror(errno));
                p += snprintf(p, sz - (buf - p), "\t\t   %6d\n", s->tm_fd);
            } else {
                p += snprintf(p, sz - (buf - p), "\t\t   %6d/%f/%-16f\n",
                                s->tm_fd, cur_timer.it_value.tv_sec + cur_timer.it_value.tv_nsec/SEC2NSEC,
                                cur_timer.it_interval.tv_sec + cur_timer.it_interval.tv_nsec/SEC2NSEC);
            }
        }
        s = s->next;
    }
    pthread_mutex_unlock(&(srv_tab.mutex));
    return buf;
}

int _srv_reg(char *srv_name, srv_fp fp, char *fp_name, void *srv_data)
{
    struct srv_info *s = (struct srv_info *) malloc(sizeof(struct srv_info));
    if (s == NULL) {
        return SRV_NO_MEM;
    }
    memset(s, 0, sizeof(struct srv_info));
    s->fp = fp;
    s->srv_data = srv_data;
    s->srv_name = strdup(srv_name);
    s->fp_name = strdup(fp_name);
    pthread_cond_init(&s->cond, NULL);
    pthread_mutex_init(&s->mutex, NULL);
    s->tm_fd = -1;

    if (!s->srv_name) {
        free(s);
        return SRV_NO_MEM;
    }

    /* insert into tab */
    pthread_mutex_lock(&(srv_tab.mutex));
    s->next = srv_tab.list;
    srv_tab.list = s;
    srv_tab.num_srv++;
    pthread_mutex_unlock(&(srv_tab.mutex));

    /* create the thread */
    pthread_create(&s->thread_id, NULL , _srv_wrap, s);
    return 0;
}

static int _srv_update_timer(struct srv_info *s, unsigned long sec, unsigned long nsec)
{
    struct timespec now_tm;
    struct itimerspec new_tm;

    if (s->tm_fd < 0) {
        s->tm_fd = timerfd_create(CLOCK_MONOTONIC, 0);
        if (s->tm_fd < 0) {
            printf("timerfd_create failed: errno:%d/%s\n", errno, strerror(errno));
            return -1;
        }
    }

    if (clock_gettime(CLOCK_MONOTONIC, &now_tm) == -1) {
        printf("timerfd_create failed: errno:%d/%s\n", errno, strerror(errno));
        return -1;
    }

    new_tm.it_value.tv_sec = now_tm.tv_sec + sec;
    new_tm.it_value.tv_nsec = now_tm.tv_nsec + nsec;

    new_tm.it_interval.tv_sec = sec;
    new_tm.it_interval.tv_nsec = nsec;

    if (timerfd_settime(s->tm_fd, TFD_TIMER_ABSTIME, &new_tm, NULL) < 0) {
        printf("timerfd_settime failed: errno:%d/%s\n", errno, strerror(errno));
        return -1;
    }

    return 0;
}

inline static int _srv_name_equal(struct srv_info *s, char *srv_name)
{
    return strlen(s->srv_name) == strlen(srv_name) && !strcmp(s->srv_name, srv_name);
}

static int _srv_set_running(char *srv_name, int running, unsigned long sec, unsigned long nsec)
{
    struct srv_info *s;
    int ret = 0;

    pthread_mutex_lock(&(srv_tab.mutex));
    s = srv_tab.list;
    while (s != NULL) {
        if (_srv_name_equal(s, srv_name)) {
            s->running = running;
            if (sec || nsec) {
                ret = _srv_update_timer(s, sec, nsec);
            } else {
                /* reset timer to zero */
                _srv_update_timer(s, 0, 0);
                /* clear timer */
                close(s->tm_fd);
                s->tm_fd = -1;
            }
            pthread_cond_signal(&s->cond);
            pthread_mutex_unlock(&(srv_tab.mutex));
            return ret;
        }
        s = s->next;
    }
    pthread_mutex_unlock(&(srv_tab.mutex));

    return -1;
}

int srv_start(char *srv_name)
{
    return _srv_set_running(srv_name, 1 /* running */, 0 /* delay.sec */, 0 /* delay.nsec */);
}

int srv_stop(char *srv_name)
{
    return _srv_set_running(srv_name, 0 /* running */, 0 /* delay.sec */, 0 /* delay.nsec */);
}

static int _srv_free(struct srv_info *s)
{
    int ret;
    s->running = 0;

    /* cancel the thread */
    ret = pthread_cancel(s->thread_id);
    if (ret != 0) {
        printf("pthread_cancel failed: %d/%d\n", ret, errno);
        /* FIXME */
    }

    ret = pthread_join(s->thread_id, NULL);
    if (ret != 0) {
        printf("pthread_join failed: %d/%d\n", ret, errno);
        /* FIXME */
    }

    /* free resouce */
    pthread_cond_destroy(&(s->cond));
    pthread_mutex_destroy(&(s->mutex));
    free(s->srv_name);
    free(s->fp_name);
    if (s->tm_fd != -1) {
        close(s->tm_fd);
    }
    free(s);
    return ret;
}

int srv_unreg(char *srv_name)
{
    struct srv_info **s, *sn = NULL;
    int ret;
    s = &(srv_tab.list);
    pthread_mutex_lock(&(srv_tab.mutex));
    while ((*s) != NULL) {
        if (_srv_name_equal(*s, srv_name)) {
            sn = *s;
            *s = (*s)->next;
            srv_tab.num_srv--;
            break;
        }
        s = &((*s)->next);
    }
    pthread_mutex_unlock(&(srv_tab.mutex));
    if (sn) {
        ret = _srv_free(sn);
    }

    return 0;
}

int srv_start_periodical(char *srv_name, unsigned long sec, unsigned long nsec)
{
    return _srv_set_running(srv_name, 1 /* running */, sec /* delay.sec */,  nsec /* delay.nsec */);
}




main.c (test program)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <stdarg.h>
#include "service-reg.h"

int pr(const char *fmt, ...)
{
    va_list ap;
    int ret;
    struct timespec cur_clk;
    clock_gettime(CLOCK_MONOTONIC, &cur_clk);

    printf("***CLK: %f\n", cur_clk.tv_sec + cur_clk.tv_nsec/1000000000.0);
    va_start(ap, fmt);
    ret = vprintf(fmt, ap);
    va_end(ap);
    return ret;
}

void *sms_service(void *v)
{
    int *i = (int *) v;
    pr("   ->hello, %p/%d, srv_start_periodical %d\n", v, (*i)++, 2);
    pr("   ->%d\n", srv_start_periodical("brook", 2, 0));
}

int main(int argc, char *argv[])
{
    int i = 0, data = 0, ret;
    char name[] = "brook", buf[1024];

    pr("srv_reg %s\n", name);
    srv_reg(name, sms_service, (void*)&data);

    pr("srv_start %s\n", name);
    srv_start(name);

    pr("sleep 3\n");
    sleep(3);
    sleep(3);

    pr("srv_stop %s\n", name);
    srv_stop(name);

    pr("srv_start_periodical %s with %dsec\n", name, 1);
    srv_start_periodical(name, 1, 0);

    pr("srv_dump %s\n", name);
    pr("%s\n", srv_dump(buf, sizeof(buf)));

    pr("sleep 2\n");
    sleep(2);

    pr("srv_unreg %s\n", name);
    srv_unreg(name);

    pr("over\n");
    return 0;
}



執行結果

brook@vista:~$ ./service-reg
***CLK: 7914669.498393
srv_reg brook
***CLK: 7914669.498469
srv_start brook
***CLK: 7914669.498481
sleep 3
***CLK: 7914669.498531
        ->hello, 0x7ffc24e80df8/0, srv_start_periodical 2
***CLK: 7914669.498541
        ->0
***CLK: 7914671.498595
        ->hello, 0x7ffc24e80df8/1, srv_start_periodical 2
***CLK: 7914671.498647
        ->0
***CLK: 7914672.498576
srv_stop brook
***CLK: 7914672.498599
srv_start_periodical brook with 1sec
***CLK: 7914672.498606
srv_dump brook
***CLK: 7914672.498621
        ->hello, 0x7ffc24e80df8/2, srv_start_periodical 2
***CLK: 7914672.498662
        ->0
***CLK: 7914672.498625
cur-clk: 7914672.498611
id      srv_name        running/in_fp           fp/fname/dp
                        run-cnt/run-time        enter-clk/leave-clk
                        tm_fd/next_expir/perodic
0       brook            1/0                    0x40106f/sms_service/0x7ffc24e80df8
                            2/0.000082          7914671.498594/7914671.498656
                        3/0.999982/1.000000

***CLK: 7914672.498680
sleep 2
***CLK: 7914674.498698
        ->hello, 0x7ffc24e80df8/3, srv_start_periodical 2
***CLK: 7914674.498730
        ->0
***CLK: 7914674.498742
srv_unreg brook
***CLK: 7914674.498997
over




2019年8月18日 星期日

Linux Kernel(17.1)- Basic Device Tree syntax


這篇會介紹一下Device Tree的基本資料型態,並透過觀察/sys/firmware/devicetree/讓你更貼近一下DT的資料結構,下面範例是在vexpress-v2p-ca9.dts中include "brook.dtsi",然後在"brook.dtsi"撰寫DT的基本語法。DT的每個node,可包含零個以上的properties或child node。
brook@vista:~/qemu/linux-arm$ vim arch/arm/boot/dts/vexpress-v2p-ca9.dts
 #include "vexpress-v2m.dtsi"
 #include "brook.dtsi" /* add this line */

brook@vista:~/qemu/linux-arm$ vim arch/arm/boot/dts/brook.dtsi 

/ {
    node1 {
        a-string-property = "A string";
        a-string-list-property = "first string", "second string";
        // hex is implied in byte arrays. no '0x' prefix is required
        a-byte-data-property = [01 23 34 56];
        child-node1 {
            first-child-property;
            second-child-property = <1>;
            a-string-property = "Hello, world";
        };
        child-node2 {
        };
    };
    node2 {
        an-empty-property;
        a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
        // [@], 
        //  is a simple ascii string and can be up to 31 characters in length.
        child-node@1 {
        };
        child-node@2 {
        };
    };
};


brook@vista:~/qemu/linux-arm$ export ARCH=arm
brook@vista:~/qemu/linux-arm$ export CROSS_COMPILE=arm-linux-gnueabihf-
brook@vista:~/qemu/linux-arm$ export PATH=/opt/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf/bin:$PATH
brook@vista:~/qemu/linux-arm$ make dtbs


properties的value可以是empty或是以下資料型態:
  • Text strings (null terminated) are represented with double quotes: string-property = "a string";
  • 'Cells' are 32 bit unsigned integers delimited by angle brackets: cell-property = <0xbeef 123 0xabcd1234>;
  • Binary data is delimited with square brackets: binary-property = [0x01 0x23 0x45 0x67];
  • Data of differing representations can be concatenated together using a comma: mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
  • Commas are also used to create lists of strings: string-list = "red fish", "blue fish";


brook@vista:~/qemu/linux-arm# cd ..
brook@vista:~/qemu$ qemu-system-arm -M vexpress-a9 -m 512M -kernel ./linux-arm/arch/arm/boot/zImage -dtb ./linux-arm/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -initrd ./initrd-arm.img -nographic -append "console=ttyAMA0"

----- boot to VM -----

Please press Enter to activate this console.
/ # ls /sys/firmware/
devicetree  fdt
/ # ls /sys/firmware/devicetree/
base
/ # ls /sys/firmware/devicetree/base/ #根目錄(/sys/firmware/devicetree/base/) 多了node1與node2
/ # ls /sys/firmware/devicetree/base/
#address-cells                 model
#size-cells                    name
aliases                        node1
arm,hbi                        node2
arm,vexpress,site              pmu
cache-controller@1e00a000      reserved-memory
chosen                         scu@1e000000
clcd@10020000                  smb@4000000
compatible                     timer@100e4000
cpus                           timer@1e000600
dcc                            virtio_mmio@10013000
hsb@e0000000                   virtio_mmio@10013200
interrupt-controller@1e001000  virtio_mmio@10013400
interrupt-parent               virtio_mmio@10013600
memory-controller@100e0000     watchdog@100e5000
memory-controller@100e1000     watchdog@1e000620
memory@60000000

/ # find /sys/firmware/devicetree/base/node1
/sys/firmware/devicetree/base/node1
/sys/firmware/devicetree/base/node1/child-node2
/sys/firmware/devicetree/base/node1/child-node2/name
/sys/firmware/devicetree/base/node1/a-string-property
/sys/firmware/devicetree/base/node1/a-string-list-property
/sys/firmware/devicetree/base/node1/a-byte-data-property
/sys/firmware/devicetree/base/node1/name
/sys/firmware/devicetree/base/node1/child-node1
/sys/firmware/devicetree/base/node1/child-node1/first-child-property
/sys/firmware/devicetree/base/node1/child-node1/second-child-property
/sys/firmware/devicetree/base/node1/child-node1/a-string-property
/sys/firmware/devicetree/base/node1/child-node1/name


/ # find /sys/firmware/devicetree/base/node2
/sys/firmware/devicetree/base/node2
/sys/firmware/devicetree/base/node2/child-node@1
/sys/firmware/devicetree/base/node2/child-node@1/name
/sys/firmware/devicetree/base/node2/child-node@2
/sys/firmware/devicetree/base/node2/child-node@2/name
/sys/firmware/devicetree/base/node2/a-cell-property
/sys/firmware/devicetree/base/node2/name
/sys/firmware/devicetree/base/node2/an-empty-property


/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/a-string-property
41 20 73 74 72 69 6E 67  A string
00

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/a-string-list-property
66 69 72 73 74 20 73 74  first st
72 69 6E 67 00 73 65 63  ringsec
6F 6E 64 20 73 74 72 69  ond stri
6E 67 00                 ng
strings lists中的element是"0"分隔

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/a-byte-data-property
01 23 34 56              #4V
byte-data如其名,每個值大小就是一個byte

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/name
6E 6F 64 65 31 00        node1

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/child-node1/first-child-property
empty

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/child-node1/second-child-property
00 00 00 01

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/child-node1/a-string-property
48 65 6C 6C 6F 2C 20 77  Hello, w
6F 72 6C 64 00           orld

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node2/a-cell-property
00 00 00 01 00 00 00 02
00 00 00 03 00 00 00 04
cell每個value大小為32byte

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node2/child-node@1/name
63 68 69 6C 64 2D 6E 6F  child-no
64 65 00                 de

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node2/child-node@2/name
63 68 69 6C 64 2D 6E 6F  child-no
64 65 00                 de


    參考資料:
  • https://elinux.org/Device_Tree_Usage, Device Tree Usage
  • https://elinux.org/images/f/f9/Petazzoni-device-tree-dummies_0.pdf, device tree dumies
  • https://blog.csdn.net/RadianceBlau/article/details/70800076, Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇)




2009年12月27日 星期日

Linux Modules(7.2)- tasklet


Tasklet和timer類似(基本上都是運作在Softirqs上面),但是不同於timer會在特定時間執行,tasklet會在下一次interrupt來臨時執行。Tasklet有兩種implement,分別為TASKLET_SOFTIRQ和HI_SOFTIRQ,這兩種的差別在於HI_SOFTIRQ筆TASKLET_SOFTIRQ早執行。另外Tasklet只在註冊的CPU上面執行,而且註冊的tasklet同一時間只會被某個CPU執行。

您可以dynamically或statically的建立tasklet,
DECLARE_TASKLET(task, func, data);
DECLARE_TASKLET_DISABLED(task, func, data);

tasklet_init(task, func, data);

宣告後,還必須呼叫tasklet_schedule(task)才會被執行,但如果是用
DECLARE_TASKLET_DISABLED()宣告成disabled狀態,那就還必須用tasklet_enable()將其狀態設成enabled才能被執行。您也可以透過tasklet_disabled() disabled某個tasklet。tasklet_kill()可以保證tasklet不會被schedule,如果已經在執行,就會等它執行結束。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>

MODULE_LICENSE("GPL");

static void f(unsigned long name);

// create tasklet statically
static DECLARE_TASKLET(t1, f, (unsigned long)"t1");
static DECLARE_TASKLET_DISABLED(t2, f, (unsigned long)"t2");

static struct tasklet_struct *t3;

static void f(unsigned long name)
{
    printk("%s(): on cpu %d\n", (char*)name, smp_processor_id());
}

static void f3(unsigned long name)
{
    static u32 c = 0;
    tasklet_schedule(t3);
    if (!(c++ % 2000000)) { // 每隔2000000次呼叫就印出訊息
        printk("%s(): on cpu %d\n", (char*)name, smp_processor_id());
    }
}

static int __init init_modules(void)
{
    // create tasklet dynamically
    t3 = kzalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
    tasklet_init(t3, f3, (unsigned long)"t3");

    tasklet_schedule(&t1);
    tasklet_schedule(&t2);
    tasklet_schedule(t3);
    tasklet_enable(&t2); // 沒有enable就不會被啟動
    return 0;
}

static void __exit exit_modules(void)
{
    // remove module就應該要確保tasklet有被移除
    tasklet_kill(&t1);
    tasklet_kill(&t2);
    tasklet_kill(t3);
}

module_init(init_modules);
module_exit(exit_modules);


Based on Kernel Version:2.6.35

參考資料:
Linux Kernel Development 3rd.
Linux Device Driver 3rd, http://www.makelinux.net/ldd3/chp-7-sect-5.shtml



2016年10月9日 星期日

Install Node.js on Openembedded


基本上Node.js在Openembedded上的recipes都已經寫好了,只要clone下來,並且加入IMAGE_INSTALL列表即可
brook@vista:~/projects/poky$ git clone https://github.com/imyller/meta-nodejs.git
Cloning into 'meta-nodejs'...
remote: Counting objects: 1575, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 1575 (delta 9), reused 0 (delta 0), pack-reused 1561
Receiving objects: 100% (1575/1575), 241.91 KiB | 368.00 KiB/s, done.
Resolving deltas: 100% (846/846), done.
Checking connectivity... done.
brook@vista:~/projects/poky$ . oe-init-build-env
You had no conf/local.conf file. This configuration file has therefore been
created for you with some default values. You may wish to edit it to, for
example, select a different MACHINE (target hardware). See conf/local.conf
for more information as common configuration options are commented.

You had no conf/bblayers.conf file. This configuration file has therefore been
created for you with some default values. To add additional metadata layers
into your configuration please add entries to conf/bblayers.conf.

The Yocto Project has extensive documentation about OE including a reference
manual which can be found at:
    http://yoctoproject.org/documentation

For more information about OpenEmbedded see their website:
    http://www.openembedded.org/


### Shell environment set up for builds. ###

You can now run 'bitbake <target>'

Common targets are:
    core-image-minimal
    core-image-sato
    meta-toolchain
    meta-ide-support

You can also run generated qemu images with a command like 'runqemu qemux86'

brook@vista:~/projects/poky/build$ vim conf/bblayers.conf
...下段說明
brook@vista:~/projects/poky/build$ vim conf/local.conf
...下段說明
brook@vista:~/projects/poky/build$ bitbake core-image-minimal
WARNING: Host distribution "Ubuntu-16.04" has not been validated with this version of the build system; you may possibly experience unexpected failures. It is recommended that you use a tested distribution.
Parsing recipes: 100% |#############################################################################################| Time: 00:00:28
Parsing of 876 .bb files complete (0 cached, 876 parsed). 1316 targets, 49 skipped, 0 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies

Build Configuration:
BB_VERSION        = "1.30.0"
BUILD_SYS         = "x86_64-linux"
NATIVELSBSTRING   = "Ubuntu-16.04"
TARGET_SYS        = "i586-poky-linux"
MACHINE           = "qemux86"
DISTRO            = "poky"
DISTRO_VERSION    = "2.1.1"
TUNE_FEATURES     = "m32 i586"
TARGET_FPU        = ""
meta
meta-poky
meta-yocto-bsp    = "krogoth:8c69f7d56cbd496aa01ba0738675a170826a536b"
meta-nodejs       = "master:848b0defe8eba6e7ffa97b66e4316c17c92be9d4"
...
brook@vista:~/projects/poky/build$ ./tmp/sysroots/x86_64-linux/usr/bin/qemu-system-i386 -nographic -kernel ./tmp/deploy/images/qemux86/bzImage-qemux86.bin -cpu qemu32 -drive file=./tmp/deploy/images/qemux86/core-image-minimal-qemux86.ext4,if=virtio,format=raw -show-cursor -usb -usbdevice tablet -vga vmware -no-reboot -m 256 -append "vga=0 uvesafb.mode_option=640x480-32 root=/dev/vda rw mem=256M ip=192.168.7.2::192.168.7.1:255.255.255.0 oprofile.timer=1 rootfstype=ext4 "

Poky (Yocto Project Reference Distro) 2.1.1 qemux86 /dev/ttyS0

qemux86 login: root
root@qemux86:~# uname -a
Linux qemux86 4.4.11-yocto-standard #1 SMP PREEMPT Sun Oct 9 19:29:24 CST 2016 i686 GNU/Linux
root@qemux86:~# echo 'console.log("hello world");' > node.js
root@qemux86:~# node node.js
hello world


conf/bblayers.conf修改的內容
BBLAYERS ?= " \
  /home/brook/projects/poky/meta \
  /home/brook/projects/poky/meta-poky \
  /home/brook/projects/poky/meta-yocto-bsp \
  ${TOPDIR}/../meta-nodejs \
  "


conf/local.conf修改的內容
# This sets the default machine to be qemux86 if no other machine is selected:
MACHINE ??= "qemux86"
CORE_IMAGE_EXTRA_INSTALL += "nodejs"


    參考資料:
  1. meta-nodejs, Node.js的openembedded recipes
  2. JavaScript for IoT: Blinking LED on Raspberry Pi with Node.js , Node.js的demo影片。
  3. 「Node.js & IoT: Zero to One」 是一本 Node.js 的入門電子書,內容定位為基礎教學,目標是介紹 Node.js 以及 Node.js + IoT 相關技術主題,每個主題都從基本觀念(Zero)開始,介紹到能撰寫簡單的程式為止(One)。不過目前只有介紹JS的基本語法。
  4. Addons教你如何擴充JS,用C/C++寫一個Node.js module。




2014年6月14日 星期六

Table Of Content for tag "Linux - kernel"


Linux Kernel(1)- Linux Module簡介
Linux Modules(1.1)module parameters

Linux Kernel(2)- register char device

Linux Kernel(3)- procfs
Linux Kernel(3.1)- procfs之vector方式寫入
Linux Kernel(3.2)- procfs之symlink與mkdir

Linux Kernel(4)- seq_file
Linux Kernel(4.1)- seq_file之範例(fp/proc/devices.c)
Linux Kernel(4.2)- seq_file之single page

Linux Kernel(5)- ioctl

Linux Kernel(6)- miscdev

Linux Kernel(7)- timing
Linux Kernel(7.1)- timer
Linux Modules(7.2)- tasklet
Linux Modules(7.3)- work queue

Linux Kernel(8)- Notification
Linux Kernel(8.1)- Notifier機制剖析

Linux Kernel(9)- Kthread

Linux Kernel(10.1)- drivers/mtd/devices/mtdram.c
Linux Kernel(10.2)- mtd partitions
Linux Kernel(10.3)- Command line partition table parsing

Linux Kernel(11)- sysfs and device node
Linux Kernel(11.1)- sysfs and hotplug

Linux Kernel(12)- netfilter
Linux Kernel(12.1)- netfilter機制之初探

Linux Kernel(13)- syscall

Linux Kernel(14)- Kernel Synchronization
Linux Modules(14.1)- Read Copy Update

Linux Kernel(15)- Platform Devices
Linux Kernel(15.1)- platform_driver_register()之如何调用driver.probe()
Linux Kernel(15.2)- platform_device_register()之如何调用driver.probe()
Linux Kernel(15.3)- The Linux usage model for device tree data

Linux Kernel(16.1)- Network Device Driver, simple snull

Linux Kernel(17)- Device Tree
Linux Kernel(17.1)- Basic Device Tree syntax





熱門文章