ラベル udev の投稿を表示しています。 すべての投稿を表示
ラベル udev の投稿を表示しています。 すべての投稿を表示

2007年4月28日土曜日

udevd init_uevent_netlink_sock

udevdのinit_uevent_netlink_sock関数の解析メモ

static int init_uevent_netlink_sock(void)
{
    struct sockaddr_nl snl;
    const int buffersize = 16 * 1024 * 1024;
    int retval;

    memset(&snl, 0x00, sizeof(struct sockaddr_nl));
    netlinkソケットはカーネルとユーザ空間の通信の通信に使用。
    アドレスはstruct sockaddr_nlを使う。

    snl.nl_family = AF_NETLINK;
    snl.nl_pid = getpid();
    snl.nl_groups = 1;

    netlinkソケットを作成する
    uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if (uevent_netlink_sock == -1) {
        err("error getting socket: %s", strerror(errno));
        return -1;
    }

    SO_RCVBUFFORCEは、特権プロセスがカーネルパラメータであるrmem_maxを
    上書きして受信バッファの最大サイズを設定することができる。
    rmem_maxは、/proc/sys/net/coreに存在する。

    /* set receive buffersize */
    setsockopt(uevent_netlink_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));

    retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl));
    if (retval < 0) {
        err("bind failed: %s", strerror(errno));
        close(uevent_netlink_sock);
        uevent_netlink_sock = -1;
        return -1;
    }
    return 0;
}

■ 参考資料

JM Manpage of NETLINK

2007年4月26日木曜日

OOM Killer

● 概要

 Out Of Memory Killerのこと。Linuxのデフォルトの動作では、プロセスがメモリを要求した場合、総メモリ使用量が実メモリ+swap以上であっても、ある程度許可するようになっている。これは、各プロセスが要求したメモリをすべて使うわけではないという経験的な法則により、できるだけ多くのプロセスを起動するためにそのように動作になっているようである。そのため、あるプロセスが確保できたはずのメモリを使おうとし、実際にメモリが足りない場合カーネルが適当なプロセスを選択し、そのプロセスをkillしてしまうことをOut Of Memory Killerという。

● 特定プロセス除外

このOOM Killerで重要なプロセスを除外するためには、/proc/(プロセスID)/oom_adjに-17を書き込む。

$ echo -17 > /proc/(プロセスID)/oom_adj

● OOM Killer停止

 また、/proc/sys/vm/overcommit_memoryの値でLinuxの動作そのものを変更することもできる。

値説 明
0メモリ確保要求があった場合、カーネルが現在未使用のメモリ量から経験的手法で判断する。(デフォルト)
1メモリ確保要求は全て許可する。
2メモリ確保要急がswapメモリ + (物理メモリ * /proc/sys/vm/overcommit_ratio / 100)まで許可する。

 2に設定することで実メモリ以上のメモリ確保要求は失敗するため、OOM Killerは動作しない。

● 参考 カーネルソース

overcommit_memory定義値

(include/linux/mman.h )
#define OVERCOMMIT_GUESS 0
#define OVERCOMMIT_ALWAYS 1
#define OVERCOMMIT_NEVER 2


int __vm_enough_memory(long pages, int cap_sys_admin)
mm/mmap.c
OOM Killerを実行するか判定を行う。

unsigned long badness(struct task_struct *p, unsigned long uptime)
mm/oom_kill.c
OOM Killerの対象プロセスを判定する

■ 参考資料

OOM Killer とは:IPpro
路地裏 ソース解読研究所: Number -17
OOM Killer によるプロセスの強制終了を防ぐ
JF: Linux Kernel 2.6 Documentation: vm.txt
memologue Linux Memory Overcommit

udevd init_udevd_socket

udevdのinit_udevd_socket関数の解析メモ

static int init_udevd_socket(void)
{
    struct sockaddr_un saddr;
    socklen_t addrlen;
    const int feature_on = 1;
    int retval;

    memset(&saddr, 0x00, sizeof(saddr));
    AF_LOCALはAF_UNIXと同じ
    Windowsでも使うため一般的な名前としてAF_LOCALを使う

    saddr.sun_family = AF_LOCAL;
    先頭をNULL文字にすると抽象名前空間となる。
    AF_UNIX(AF_LOCAL)ソケットでのみ使用できる。
    抽象名前空間の実装はLinux2.2以降から。
    JM Manpage of UNIXを参照のこと

    /* use abstract namespace for socket path */
    strcpy(&saddr.sun_path[1], UDEVD_CTRL_SOCK_PATH);
    addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1;

    udevd_sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
    if (udevd_sock == -1) {
        err("error getting socket: %s", strerror(errno));
        return -1;
    }

    /* the bind takes care of ensuring only one copy running */
    retval = bind(udevd_sock, (struct sockaddr *) &saddr, addrlen);
    if (retval < 0) {
        err("bind failed: %s", strerror(errno));
        close(udevd_sock);
        udevd_sock = -1;
        return -1;
    }
    SO_PASSCREDはmsgの補助メッセージとして信任状を受信できるようにする。
    信任状は以下の型
    struct ucred {
        pid_t  pid;  /* process id of the sending process */
        uid_t  uid;  /* user id of the sending process */
        gid_t  gid;  /* group id of the sending process */
    };
    補助メッセージはsendmsg()とrecvmsg()で送受信する。
    JM Manpage of UNIXを参照のこと

    /* enable receiving of the sender credentials */
    setsockopt(udevd_sock, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on));

    return 0;
}

■ 参考資料

JM Manpage of UNIX
JM Manpage of SEND
JM Manpage of RECV

2007年4月24日火曜日

udevd main

udevdのmain関数の解析メモ

int main(int argc, char *argv[], char *envp[])
{
    int retval;
    int fd;
    struct sigaction act;
    fd_set readfds;
    const char *value;
    int daemonize = 0;
    int option;
    static const struct option options[] = {
        { "daemon", 0, NULL, 'd' },
        { "debug-trace", 0, NULL, 't' },
        { "verbose", 0, NULL, 'v' },
        { "help", 0, NULL, 'h' },
        {}
    };
    int rc = 1;
    int maxfd;

    logging_init("udevd");
    udev_config_init();  環境変数からconfig file名などを取得。
    selinux_init();  何もしない
    dbg("version %s", UDEV_VERSION);

    /* parse commandline options */
    while (1) {
        コマンドオプションの解析(省略)
    }

    rootで実行されているかチェック
    if (getuid() != 0) {
        fprintf(stderr, "root privileges required\n");
        err("root privileges required");
        goto exit;
    }

    /* init sockets to receive events */
    制御用のUNIXドメインソケットを作成
    if (init_udevd_socket() < 0) {
        if (errno == EADDRINUSE) {
            fprintf(stderr, "another udev daemon already running\n");
            err("another udev daemon already running");
            rc = 1;
        } else {
            fprintf(stderr, "error initializing udevd socket\n");
            err("error initializing udevd socket");
            rc = 2;
        }
        goto exit;
    }
    カーネルイベント用のソケット作成
    if (init_uevent_netlink_sock() < 0) {
        fprintf(stderr, "error initializing netlink socket\n");
        err("error initializing netlink socket");
        rc = 3;
        goto exit;
    }

    /* setup signal handler pipe */
    シグナルハンドラーからの通知に使用するパイプを作成
    retval = pipe(signal_pipe);
    if (retval < 0) {
        err("error getting pipes: %s", strerror(errno));
        goto exit;
    }

    作成したpipeに対してfcntlが可能かどうかチェックしNONBLOCKに設定する
    retval = fcntl(signal_pipe[READ_END], F_GETFL, 0);
    if (retval < 0) {
        err("error fcntl on read pipe: %s", strerror(errno));
        goto exit;
    }
    retval = fcntl(signal_pipe[READ_END], F_SETFL, retval | O_NONBLOCK);
    if (retval < 0) {
        err("error fcntl on read pipe: %s", strerror(errno));
        goto exit;
    }

    retval = fcntl(signal_pipe[WRITE_END], F_GETFL, 0);
    if (retval < 0) {
        err("error fcntl on write pipe: %s", strerror(errno));
        goto exit;
    }
    retval = fcntl(signal_pipe[WRITE_END], F_SETFL, retval | O_NONBLOCK);
    if (retval < 0) {
        err("error fcntl on write pipe: %s", strerror(errno));
        goto exit;
    }

    ルールファイルをパースしてメモリに展開する。
    /* parse the rules and keep them in memory */
    sysfs_init();
    udev_rules_init(&rules, 1);

    export_initial_seqnum();

    デーモン化するために子プロセスを作成し親は終了する
    if (daemonize) {
        pid_t pid;

        pid = fork();
        switch (pid) {
        case 0:
            dbg("daemonized fork running");
            break;
        case -1:
            err("fork of daemon failed: %s", strerror(errno));
            rc = 4;
            goto exit;
        default:
            dbg("child [%u] running, parent exits", pid);
            rc = 0;
            goto exit;
        }
    }
    /dev/nullを標準入力と標準出力に設定
    /* redirect std fd's */
    fd = open("/dev/null", O_RDWR);
    if (fd >= 0) {
        dup2(fd, STDIN_FILENO);
        if (!verbose)
            dup2(fd, STDOUT_FILENO);
        dup2(fd, STDERR_FILENO);
        if (fd > STDERR_FILENO)
            close(fd);
    } else
        err("error opening /dev/null: %s", strerror(errno));

    /* set scheduling priority for the daemon */
    setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY);

    chdir("/");
    umask(022);

    /* become session leader */
    セッションリーダーになる
    sid = setsid();
    dbg("our session is %d", sid);

    /* OOM_DISABLE == -17 */
    Out Of Memory Killerを無効にするために/proc/self/oom_adに-17を書く
    OOM Killerについては詳細記事を参照のこと

    fd = open("/proc/self/oom_adj", O_RDWR);
    if (fd < 0)
        err("error disabling OOM: %s", strerror(errno));
    else {
        write(fd, "-17", 3);
        close(fd);
    }

    シグナルハンドラーを設定する
    /* set signal handlers */
    memset(&act, 0x00, sizeof(struct sigaction));
    act.sa_handler = (void (*)(int)) sig_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_RESTART;
    sigaction(SIGINT, &act, NULL);
    sigaction(SIGTERM, &act, NULL);
    sigaction(SIGCHLD, &act, NULL);
    sigaction(SIGHUP, &act, NULL);

    /* watch rules directory */
    ルールディレクトリィの監視を行う。glibc経由ではなくsyscallで直接呼ぶ。
    syscall(__NR_inotify_init),syscall(__NR_inotify_add_watch,・・・など

    inotify_fd = inotify_init();
    if (inotify_fd >= 0)
        inotify_add_watch(inotify_fd, udev_rules_dir, IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
    else if (errno == ENOSYS)
        err("the kernel does not support inotify, udevd can't monitor configuration file changes");
    else
        err("inotify_init failed: %s", strerror(errno));

    /* maximum limit of forked childs */
    子プロセス数の設定値を取得(省略)

    /* export log_priority , as called programs may want to follow that setting */
    sprintf(udev_log, "UDEV_LOG=%i", udev_log_priority);
    putenv(udev_log);
    if (debug_trace)
        putenv("DEBUG=1");

    select用ファイルディスクリプタの最大値を取得
    maxfd = udevd_sock;
    maxfd = UDEV_MAX(maxfd, uevent_netlink_sock);
    maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]);
    maxfd = UDEV_MAX(maxfd, inotify_fd);

    selectでイベント受信待ち
    while (!udev_exit) {
        struct udevd_uevent_msg *msg;
        int fdcount;

        FD_ZERO(&readfds);
        FD_SET(signal_pipe[READ_END], &readfds);
        FD_SET(udevd_sock, &readfds);
        FD_SET(uevent_netlink_sock, &readfds);
        if (inotify_fd >= 0)
            FD_SET(inotify_fd, &readfds);

        fdcount = select(maxfd+1, &readfds, NULL, NULL, NULL);
        if (fdcount < 0) {
            if (errno != EINTR)
                err("error in select: %s", strerror(errno));
            continue;
        }

            イベントごとの処理を行う。(省略)

    }
    rc = 0;

    後始末を行う
exit:
    udev_rules_cleanup(&rules);
    sysfs_cleanup();

    if (signal_pipe[READ_END] >= 0)
        close(signal_pipe[READ_END]);
    if (signal_pipe[WRITE_END] >= 0)
        close(signal_pipe[WRITE_END]);

    if (udevd_sock >= 0)
        close(udevd_sock);
    if (inotify_fd >= 0)
        close(inotify_fd);
    if (uevent_netlink_sock >= 0)
        close(uevent_netlink_sock);

    logging_close();

    return rc;
}

■ 参考資料

JM Manpage of PIPE
JM Manpage of FCNTL
JM Manpage of SETSID
JM Manpage of SIGACTION

2007年4月23日月曜日

Debian ネットワーク起動

 Debian(etch)のネットワーク起動について調査した。Debianの起動スクリプトは/etc/init.d/にあり、ネットワーク関係は/etc/init.d/networkになる。

start)
        process_options
        log_action_begin_msg "Configuring network interfaces"
        if ifup -v -a; then
            log_action_end_msg $?
        else
            log_action_end_msg $?
        fi
        ;;

 'ifup -v -a'を実行し、ネットワークインターフェースを起動している。ifupはDebian独自のユーティリティのようで、manページによると'-a'オプションは、/etc/network/interfacesの中でautoに指定されいているものを起動する。/etc/network/interfacesは以下のようになっていた。

# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

# This is a list of hotpluggable network interfaces.
# They will be activated automatically by the hotplug subsystem.
mapping eth0
        script grep
        map eth0

# The primary network interface
allow-hotplug eth0
iface eth0 inet dhcp

 autoになっているのはループバックデバイスloのみ。試しに、/etc/init.d/networking stopを実行すると、eth0, loともに停止する。しかし、/etc/init.d/networking startを実行しても、loしか起動しない。いろいろ試したところ、/etc/init.d/udev startを実行すると、eth0が起動した。これは、udevdを起動するスクリプトである。

 udevdが何かを調べてみたらSUSE Linuxのマニュアルにいろいろ書いてあった。SUSE Linux Enterprise Server Manual (PDF)の25章udevを使用した動的カーネルデバイス管理によると、udevdは動的にデバイスを管理するためのものであり、それまでのhotplugを置き換えるもののようだ。 udevdは、/etc/udev/rules.d/*.rulesディレクトリィに記述されルールに従って処理を行う。この場所にz55_hotplug.rulesがありここにnetwork関連の記述があった。

SUBSYSTEM=="net",                               RUN+="net.agent"

 net.agentを実行するようになっており、/lib/udev/net.agentがそれに相当するようだ。これはシェルスクリプトであり、net_ifup()関数でifupを実行している。

net_ifup() {
    check_program /sbin/ifup

    if grep -q '^auto[[:space:]].*\<'"$INTERFACE"'\>' \
            /etc/network/interfaces; then
        # this $INTERFACE is marked as "auto"
        IFUPARG='\('$INTERFACE'\|-a\|--all\)'
    else
        IFUPARG=$INTERFACE
    fi

    if ps -C ifup ho args | grep -q "$IFUPARG"; then
        debug_mesg "Already ifup-ing interface $INTERFACE"
        exit 0
    fi

    wait_for_interface lo

    exec ifup --allow=hotplug $INTERFACE
}

 ついでに上記ででてくるwait_for_interfaceは、sysファイルシステムを使ってネットワークインターフェースの状態を判定している。

wait_for_interface() {
    local interface=$1

    while :; do
        local state="$(cat /sys/class/net/$interface/operstate 2>/dev/null || true)"
        if [ "$state" != down ]; then
                return 0
        fi
        sleep 1
    done
}

■ 参考資料

SUSE Linux Enterprise Serverマニュアル(PDF)
ifupのmanページ