把busybox移植到android下后想玩玩syslogd,于是先把busybox链接成一个syslogd以方便后续直接敲命令启动:
ln -s busybox syslogd
ls -l
lrwxrwxrwx root root 2013-01-06 19:38 syslogd -> busybox
OK,现在可以启动它了:
1|root@android:/data/data/test # ./syslogd
./syslogd
root@android:/data/data/test #
原以为执行成功了,但ps却发现并没有syslogd进程存在,怪了。于是加上-n参数让其在前台运行,看看会有什么提示:
root@android:/data/data/test # ./syslogd -n
./syslogd -n
syslogd: bind: No such file or directory
原来是绑定失败。接下来就只能通过查代码来进一步排查问题了。
busybox使用的是unix域socket通信的,默认使用的是/dev/log文件来做为socket文件,具体在create_socket函数中实现的:
static NOINLINE int create_socket(void) { 。。。 /* Unlink old /dev/log or object it points to. */ /* (if it exists, bind will fail) */ strcpy(sunx.sun_path, "/dev/log"); dev_log_name = xmalloc_follow_symlinks("/dev/log"); if (dev_log_name) { safe_strncpy(sunx.sun_path, dev_log_name, sizeof(sunx.sun_path)); free(dev_log_name); } 。。。
问题实际上就出在这里了。
因为在我的三星android手机上,本来就存在/dev/log,并且,这里log还是个文件夹,里面还有其它文件,导致导致上面的代码在调用bind时出错(代码的意思是/dev/log是一个socket文件)所以返回“Address already in use”错误。
问题原因清楚了就好改了。在Android下,只有/data目录是可读下的,例如,我的所有测试程序都被我放在/data/data/test目录下,所有的库放在/data/app目录下。
于是修改上面的目录为:/data/data/test/dev/log(要搜索代码,有好几个地方要改,但实际用到只有2处,其它都是打log的,但为了保持一致建议全部修改。
然后我在/data/data/test目录下创建dev目录后,再次执行syslogd成功。
root@android:/data/data/test # mkdir dev
mkdir dev
root@android:/data/data/test # ./syslogd
./syslogd
root@android:/data/data/test # ps|grep syslogd
ps|grep syslogd
root 27628 1 2196 240 c0c27c24 0000a86c S ./syslogd
root@android:/data/data/test #
我想,有二分之一的人安装opencv是上网找份资料,然后按照他们列出的步骤邯郸学步般地操作。我也有这么一个时期,在那个时期,总以为编程才是最主要的工作,至于这些安装系统、配置文件什么的,都是我所鄙视的,我觉得编程才是王道,就像前苏联着重发展重工业,就像朝鲜的先军政治。但是,安装系统、配置文件这些看似琐碎的活,都是你体现你计算机功底的地方,你要弄明白这些活中蕴涵的计算机知识。
说说VS安装openCV吧,确切地说,是在VS上配置openCV。首先你会先下一个openCV的可执行文件,就是那个三色的“品”字,虽然这个文件的后缀名是.exe,但是你却可以认为这是一个压缩文件,你双击之后得到的将是一份源代码,而不是安装一个软件。
好了,得到源代码了,涉及到第一个问题,有的文档让你用CMake编译这份源代码,有的没有,这其间的区别在哪里?本质原因是openCV是开源项目,它允许你改变openCV的源代码,所以既然有改动就一定涉及到编译,再者,一些第三方的文件,比如tbb如果想嵌入openCV使用,也是必须经过编译这一过程的,所以会有需不需要编译这码事。有的版本的openCV会直接发布编译好的版本,比如2.3版本之后,此时,你就直接在vs里面配置即可,有的发布了源代码,没有dll文件,这时你就要编译了。
解决了一个问题,下面解决第二个问题,配置VS。配置VS时,一大坨什么引入include目录,库目录什么的,有没有一条线在里面?捋一捋,我们编程的时候,总是要include一些头文件,这些头文件在哪里?如果没有IDE记录,这些事情都是要我们自己做的,我们要告诉程序这些库的路径。一切都不是理所当然,但是对于VS这个IDE来说,天生嵌入一些运行时库的路径是利索当然的,因为windows系统的一些库函数的位置都是固定的,比如文件夹c:/WINDOWS等,因此,当我们在程序中写入这样的语句“#include <stdio.h>”时,程序是不会报错的,因为IDE在我们电脑上生根的那天起,它已经知道了stdio.h的路径,同样也知道相应的实实在在的库函数可执行代码的路径。所以对于openCV这样一个“外来户”,并不是每一个电脑上都有,我们一定要让IDE知道openCV中头文件的路径,库函数可执行文件的路径,所以有了加入include目录和lib目录这样两个过程。
好了还剩下最后一个问题,就是真正一大坨的.lib的加入。一个可执行文件的生成总的来说分为两个过程,一是编译,一是链接,上文加入include为了编译的过程, 那么lib的出现就是为了链接的过程了,lib中是一些dll文件集合,dll是动态链接文件,说白了也是一些可执行文件,链接的过程,就是为了将你写的代码和这些库文件“结合”。在VS配置openCV过程中,你会有一个更新“附加依赖项”的过程,此时,你加入的这些**.lib就是链接时用到的库文件了,写过Makefile的同仁是否对这个过程似曾相识呢?
好了,没有疑惑了,这下我们可以毫无担忧地使用openCV的库了,就像标准C和标准C++的库一样。以后无论对编译器添加什么库的路径以及依赖关系,相信我们都能知其然,也知其所以然了。
最后附上我在VS2008上配置openCV 2.3.1时使用的一份文档,配置成功。
http://wenku.baidu.com/view/f8cfcf086c85ec3a86c2c501.html
linux内核中的copy_to_user和copy_from_user(一)
Kernel version:2.6.14
CPU architecture:ARM920T
Author:ce123(http://blog.csdn.net/ce123)
在学习Linux内核驱动的时候,经常会碰到copy_from_user和copy_to_user这两个函数,设备驱动程序中的ioctl函数就经常会用到。这两个函数负责在用户空间和内核空间传递数据。首先看看它们的定义(linux/include/asm-arm/uaccess.h),先看copy_from_user:
static inline unsigned long copy_from_user(void *to, const void __user *from, unsigned long n) { if (access_ok(VERIFY_READ, from, n)) n = __arch_copy_from_user(to, from, n); else /* security hole - plug it */ memzero(to, n); return n; }
先看函数的三个参数:*to是内核空间的指针,*from是用户空间指针,n表示从用户空间想内核空间拷贝数据的字节数。如果成功执行拷贝操作,则返回0,否则返回还没有完成拷贝的字节数。
这个函数从结构上来分析,其实都可以分为两个部分:access_ok用来对用户空间的地址指针from作某种有效性检验,这个宏和体系结构相关,在arm平台上为(linux/include/asm-arm/uaccess.h):
#define __range_ok(addr,size) ({ \ unsigned long flag, sum; \ __chk_user_ptr(addr); \ __asm__("adds %1, %2, %3; sbcccs %1, %1, %0; movcc %0, #0" \ : "=&r" (flag), "=&r" (sum) \ : "r" (addr), "Ir" (size), "0" (current_thread_info()->addr_limit) \ : "cc"); \ flag; }) #define access_ok(type,addr,size) (__range_ok(addr,size) == 0)可以看到access_ok中第一个参数type并没有用到,__range_ok的作用在于判断addr+size之后是否还在进程的用户空间范围之内。下面我们具体看一下。这段代码涉及到GCC内联汇编,不懂的朋友可以先看看这篇博客(http://blog.csdn.net/ce123/article/details/8209702)。
(1)unsigned long flag, sum;\\定义两个变量
- flag:保存结果的变量:非零代表地址无效,零代表地址可以访问。初始存放非零值(current_thread_info()->addr_limit),也就是当前进程的地址上限值。
- sum:保存要访问的地址范围末端,用于和当前进程地址空间限制数据做比较。
这个函数涉及到__CHECKER__宏的判断,__CHECKER__宏在通过Sparse(Semantic Parser for C)工具对内核代码进行检查时会定义的。在使用make C=1或C=2时便会调用该工具,这个工具可以检查在代码中声明了sparse所能检查到的相关属性的内核函数和变量。
- 如果定义了__CHECKER__,__chk_user_ptr和__chk_io_ptr在这里只声明函数,没有函数体,目的就是在编译过程中Sparse能够捕捉到编译错误,检查参数的类型。
- 如果没有定义__CHECKER__,这就是一个空函数。
请看具体的定义(linux/compiler.h):
#ifdef __CHECKER__ ... extern void __chk_user_ptr(void __user *); extern void __chk_io_ptr(void __iomem *); #else ... # define __chk_user_ptr(x) (void)0 # define __chk_io_ptr(x) (void)0 ... #endif(3)接下来是汇编:
adds %1, %2, %3
sum = addr + size 这个操作影响状态位(目的是影响是进位标志C),以下的两个指令都带有条件CC,也就是当C=0的时候才执行。
如果上面的加法指令进位了(C=1),则以下的指令都不执行,flag就为初始值current_thread_info()->addr_limit(非0),并返回。
如果没有进位(C=0),就执行下面的指令:
sbcccs %1, %1, %0
sum = sum - flag - 1,也就是(addr + size) - (current_thread_info()->addr_limit) - 1,操作影响符号位。
如果(addr + size) >= (current_thread_info()->addr_limit) - 1,则C=1
如果(addr + size) < (current_thread_info()->addr_limit) - 1,则C=0
当C=0的时候执行以下指令,否则跳过(flag非零)。
movcc %0, #0
flag = 0,给flag赋值0。
综上所述:__range_ok宏其实等价于:
- 如果(addr + size) >= (current_thread_info()->addr_limit) - 1,返回非零值
- 如果(addr + size) < (current_thread_info()->addr_limit),返回零
从这里再次可以认识到,copy_from_user的使用是结合进程上下文的,因为他们要访问“user”的内存空间,这个“user”必须是某个特定的进程。通过上面的源码就知道,其中使用了current_thread_info()来检查空间是否可以访问。如果在驱动中使用这两个函数,必须是在实现系统调用的函数中使用,不可在实现中断处理的函数中使用。如果在中断上下文中使用了,那代码就很可能操作了根本不相关的进程地址空间。其次由于操作的页面可能被换出,这两个函数可能会休眠,所以同样不可在中断上下文中使用。
1.2.__arch_copy_from_user
#define USER(x...) \ 9999: x;