D-Bus

D-Bus 也是一个用于进程间通信的框架。

简单介绍一下如何编写一个helloworld通信例子。

0. 下载源码

http://www.freedesktop.org/wiki/Software/dbus#Download.

1. ./configure

这时候可能会出现问题:

configure: error: Could not find expat.h, check config.log for failed attempts

可以通过

sudo apt-get install libexpat1-dev

来解决

2. make

3. make install

其中可能遇到的编译问题

1.  fatal error: dbus/dbus.h: No such file or directory

    把/usr/local/include/dbus-1.0/dbus/拷贝到/usr/include/dbus

2. fatal error: dbus/dbus-arch-deps.h: No such file or directory

  /usr/local/lib/dbus-1.0/include/dbus/dbus-arch-deps.h拷贝到/usr/include/dbus

3. 链接错误 collect2: ld returned 1 exit status

 添加链接选项 -l dbus-1

测试代码

服务端接收消息

/* server.c */

#include <dbus/dbus.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

static DBusHandlerResult
filter_func(DBusConnection *connection, DBusMessage *message, void *usr_data)
{
    dbus_bool_t handled = false;
    char *word = NULL;
    DBusError dberr;

    if (dbus_message_is_signal(message, "client.signal.Type", "Test")) {
        dbus_error_init(&dberr);
        dbus_message_get_args(message, &dberr, DBUS_TYPE_STRING,
            &word, DBUS_TYPE_INVALID);
        if (dbus_error_is_set(&dberr)) {
            dbus_error_free(&dberr);
        } else {
            printf("receive message %s\n", word);
            handled = true;
        }
    }
    return (handled ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
}

int main(int argc, char *argv[])
{
    DBusError dberr;
    DBusConnection *dbconn;

    dbus_error_init(&dberr);

    dbconn = dbus_bus_get(DBUS_BUS_SESSION, &dberr);
    if (dbus_error_is_set(&dberr)) {
        dbus_error_free(&dberr);
        return -1;
    }

    if (!dbus_connection_add_filter(dbconn, filter_func, NULL, NULL)) {
        return -1;
    }

    dbus_bus_add_match(dbconn, "type='signal',interface='client.signal.Type'", &dberr);
    if (dbus_error_is_set(&dberr)) {
        dbus_error_free(&dberr);
        return -1;
    }

    while(dbus_connection_read_write_dispatch(dbconn, -1)) {
        /* loop */
    }
    return 0;
}
 
客户端发送消息
#include <dbus/dbus.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int db_send(DBusConnection *dbconn)
{
    DBusMessage *dbmsg;
    char *word;
    int i;

    dbmsg = dbus_message_new_signal("/client/signal/Object", // object name of the signal
                                  "client.signal.Type",      // interface name of the signal
                                  "Test");                   // name of the signal
    if (!dbmsg) {
        return -1;
    }

    word = "hello world";
    if (!dbus_message_append_args(dbmsg, DBUS_TYPE_STRING, &word, DBUS_TYPE_INVALID)) {
        return -1;
    }

    if (!dbus_connection_send(dbconn, dbmsg, NULL)) {
        return -1;
    }
    dbus_connection_flush(dbconn);
    printf("send message %s\n", word);

    dbus_message_unref(dbmsg);
    return 0;
}

int main(int argc, char *argv[])
{
    DBusError dberr;
    DBusConnection *dbconn;
    int i;

    dbus_error_init(&dberr);

    dbconn = dbus_bus_get(DBUS_BUS_SESSION, &dberr);
    if (dbus_error_is_set(&dberr)) {
        return -1;
    }

    db_send(dbconn);

    dbus_connection_unref(dbconn);

    return 0;
}
 
测试结果如下
root@ubuntu:~/dbus# gcc -o client client.c -l dbus-1
root@ubuntu:~/dbus# gcc -o server server.c -l dbus-1
 
root@ubuntu:~/dbus# ./listen
receive message hello world
 
root@ubuntu:~/dbus# ./send
send message hello world
 
很多API和具体实现还没搞清楚,有待深入研究。
D-Bus的性能会比linux自带的send,recv高么?
http://pvanhoof.be/blog/index.php/2010/05/13/ipc-performance-the-report

内核模块编译

Makefile文件

# 内核文件名称
obj-m := hello.o
# 内核源码地址     
KDIR := /lib/modules/$(shell uname -r)/build
# 当前路径
PWD = $(shell pwd)

default:
     make -C $(KDIR) SUBDIRS=$(PWD)  modules
clean:
     rm rf Module.symvers *.o *.mod.c *.cmd .tmp_versions

源文件hello.c

#include <linux/module.h>
#include <linux/init.h>

static int hello_init(void)
{
        printk("helloworld init\n");
        return 0;
}

static void hello_exit(void)
{
        printk("helloworld exit\n");
        return;
}

module_init(hello_init);
module_exit(hello_exit);    

然后执行make 生成内核模块hello.ko

加载模块 insmod hello.ko

卸载模块 rmmod hello.ko

查看模块 lsmod 

Module                  Size  Used by
hello                  12496  0 

Used by表示这个模块被引用的次数,只有为0的时候才可以卸载模块

内核使用printk打印信息,可以用命令dmesg查看log

[  412.605328] helloworld init
[  433.266742] helloworld exit

找到一个很详细的内核模块加载的文章

/user_files/lazycat/File/《深入Linux设备驱动程序内核机制》第1章 内核模块.pdf

 

unix domain socket相关

1.关于SIGPIPE信号

今天测试代码发现,当已经建立了Stream连接的DomainSocket的服务端进程终止后,客户端进程调用send函数导致客户端进程也退出了的情况。

上网搜了一下,发现是客户端进程收到了一个SIGPIPE信号,信号默认的执行动作是终止退出。

解决办法应该是使用

 

signal(SIGPIPE, SIG_IGN);

忽略SIGPIPE信号。

这样在断开连接后,还继续调用send的情况下,会返回-1,错误号为EPIPE(Broken pipe)。

PS:在recv(sock, ...) 得到返回值为0的时候表示连接断开,这时候应该调用close(sock)来保证后续不会出现问题。

相关链接:

http://www.cppblog.com/elva/archive/2008/09/10/61544.html

http://stackoverflow.com/questions/108183/how-to-prevent-sigpipes-or-handle-them-properly

gdb结合coredump定位崩溃进程

Linux环境下经常遇到某个进程挂掉而找不到原因,我们可以通过生成core file文件加上gdb来定位。

 
如何产生core file?
我们可以使用ulimit这条命令对core file文件的大小进行设定。
一般默认情况下,core file的大小被设置为了0,这样系统就不dump出core file了。
这时用如下命令进行设置:
ulimit -c unlimited
这样便把core file的大小设置为了无限大,同时也可以使用数字来替代unlimited,对core file的上限值做更精确的设定。
 
生成的core file在哪里?
core file生成的地方是在/proc/sys/kernel/core_pattern文件定义的。
改动到生成到自己定义的目录的方法是:
echo "pattern" > /proc/sys/kernel/core_pattern
并且只有超级用户可以修改这两个文件。
"pattern"类似我们C语言打印字符串的格式,相关标识如下:
%%: 相当于%
%p: 相当于<pid>
%u: 相当于<uid>
%g: 相当于<gid>
%s: 相当于导致dump的信号的数字
%t: 相当于dump的时间
%h: 相当于hostname
%e: 相当于执行文件的名称
这时用如下命令设置生成的core file到系统/tmp目录下,并记录pid以及执行文件名
echo "/tmp/core-%e-%p" > /proc/sys/kernel/core_pattern
 
测试如下代码
#include <stdio.h>

int func(int *p)
{
        *p = 0;
}

int main()
{
        func(NULL);
        return 0;
}
 
生成可执行文件并运行
gcc -o main a.c
root@ubuntu:~# ./main
Segmentation fault (core dumped) 
<-----这里出现段错误并生成core文件了。
在/tmp目录下发现文件core-main-10815 
 
如何查看进程挂在哪里了?
我们可以用
gdb main /tmp/core-main-10815 
查看信息,发现能定位到函数了
Program terminated with signal 11, Segmentation fault.
#0  0x080483ba in func ()
 

如何定位到行?

在编译的时候开启-g调试开关就可以了
gcc -o main -g a.c
gdb main /tmp/core-main-10815 
最终看到的结果如下,好棒。
Program terminated with signal 11, Segmentation fault.
#0  0x080483ba in func (p=0x0) at a.c:5
5          *p = 0;
 
总结一下,需要定位进程挂在哪一行我们只需要4个操作,
ulimit -c unlimited
echo "/tmp/core-%e-%p" > /proc/sys/kernel/core_pattern
gcc -o main -g a.c
gdb main /tmp/core-main-10815 
就可以啦。
 
补充说明:
相关常用gdb命令
1,(gdb) backtrace /* 查看当前线程函数栈回溯 */
以上面的例子为例
Program terminated with signal 11, Segmentation fault.
#0  0x080483ba in func (p=0x0) at main.c:5
5 *p = 0;
(gdb) backtrace
#0  0x080483ba in func (p=0x0) at main.c:5
#1  0x080483d4 in main () at main.c:10
如果是多线程环境下(gdb) thread apply all backtrace /* 显示所有线程栈回溯 */
 
2,(gdb) print [var] /* 查看变量值 */
(gdb) print p
$1 = (int *) 0x0
(gdb) print &p
$2 = (int **) 0xbf96d4d4
 
3,(gdb) x/FMT [Address] /* 根据格式查看地址指向的值 */
其中
FMT is a repeat count followed by a format letter and a size letter.
Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),
  t(binary), f(float), a(address), i(instruction), c(char) and s(string).
Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).
The specified number of objects of the specified size are printed
according to the format.
 
(gdb) x/d 0xbf96d4d4
0xbf96d4d4: 0
(gdb) x/c 0xbf96d4d4
0xbf96d4d4: 0 '\000'
 
另外能导致产生core file文件的信号有以下10种

SIGQUIT:终端退出符

SIGILL:非法硬件指令

SIGTRAP:平台相关的硬件错误,现在多用在实现调试时的断点

SIGBUS:与平台相关的硬件错误,一般是内存错误

SIGABRT:调用abort函数时产生此信号,进程异常终止

SIGFPE:算术异常

SIGSEGV:segment violation,无效内存引用

SIGXCPU:超过了cpu使用资源限制(setrlimit)

SIGXFSZ:超过了文件长度限制(setrlimit)

SIGSYS:无效的系统调用