年底要去老婆老家,准备开车去,一路近800公里,又是第一次开,有一个导航当然是最好不过了~作为导航业内人士,有现成的GPS设备可以用,当然不屑用市面上的GPS导航仪(好吧,我承认没有钱也是一个重要的原因~),DIY自然成了我的首选! 导航其实就是能在地图上显示出你当前的位置、方向、速度等信息,所需的硬件就是GPS接收机和显示地图的设备,所需的软件就是接收GPS接收机的数据并解析显示于地图上。下面就来一个个的分析。
先说说硬件吧~
GPS接收机有现成的,无非是精度好点差点,都属于单点定位,精度在5-15米之间,这个问题不大~
显示地图的设备无非是嵌入式系统或者本本。嵌入式系统的优点是小巧方便,缺点是功能比较弱,需要专门定制软件,作为DIY的话不方便利用已有的成果,且屏 幕一般都比本本小得多,另外一个放弃的重要原因是我手头上虽然有一个嵌入式系统,还带触摸屏,但是这个东西关系着毕业大计,金贵着呢,可不敢随便拿出来折腾!因此这里我选用本本作为显示地图的载体。它功能强,屏幕大,有丰富的软件资源可供利用,唯一的缺点就是有点大,不方便直接放在前车窗,会挡住视线,估计要放在副驾驶位子上了~
至于GPS接收机与本本的连接当然用经典的串口了(其实我也知道蓝牙更好~),本本没有物理串口不要紧,用USB-串口转接线,还省去了不能带电插拔的麻烦!
再来说说软件。
以前也查过,windows下的免费的导航软件很少,OZI的demo版据说功能限制很多,GooPs试用过,反正是不咋的,需要GE开着,没法联网的时候要事先在缓存里有地图才行,比较麻烦,不实用!再来看Linux下的GPS导航软件,其他的我不知道,反正是一眼就看中gpsDrive[1]了!瞧人家主页上说的,支持Linux、Mac OSX和FreeBSD,就是不支持Windows!我喜欢^_^装了一看,界面还蛮好看的,估计接上GPS就能工作了!就选它了~
选好了gpsDrive,下面就是找地图下载了~在gpsDrive的主页里它主要是从OpenStreetMap项目里下载地图,顺链接过去看了一下, 这玩意靠的是大家都来贡献,在中国地图基本上没法用!还是要另想办法啊~在谷歌里搜索gpsdrive,还真找到一个台湾的哥们在eeePC上用过[2],给出了使用指南,最关键的是给出了一个地图下载的网址[3]!赶紧顺着网址看过去,嘿,还真是专门为qpeGPS和 gpsDrive设计的!里面可以设定要下载的范围、缩放等级、重叠率等,然后记着要点击“Generate wGet”,不然非等死你不可!即使这样,生成一个包含690个小图的下载链接及配套经纬度信息的页面也要让我的Firefox假死几分钟~
有了下载链接和经纬度信息下面怎么办呢?很简单,把下载链接都拷到一个文件里,保存为sh文件,然后执行即可。需要注意的是,网络不好的时候,经常会停在某个wget半天也不动弹,是可忍,孰不可忍!我的经验是肉眼看看不行了就Ctrl+C中断sh的执行,将已经保存下来的图片对应的wget命令注释掉(我用kate里的Ctrl+D来干这事,忒方便),然后再次执行。这样的过程根据网络的好坏不同,可能需要执行N次(在我这里N>10),这也是 整个地图下载过程中最ugly的部分(应该也可以编程来自动处理这种情况的,但宽恕我偷懒了~)……下载完的小地图放在~/.gpsdrive/maps/里,还需要将配套的经纬度信息也存到一个文本文件里,以备后用~
这时下载好的小地图尺寸是固定的(1280*1024),满足gpsDrive的要求,相对精度也是没有问题的,但由于万恶的GOV人为增加偏移量,直接使用配套的经纬度信息来显示地图那误差是相当的大,目测大约有600-1000米,当然是没法用了!也许你要问市面上那些GPS导航仪怎么就能定位到路上呢?除了他们一定使用了道路匹配算法外,最主要原因应该还是他们手头上有相应的加密解密算法,这玩意我们自然搞不到,所以只有另想办法~
要不怎么说我人品好呢,就在2009年的下半年,有人刚刚发现了谷歌地图的偏移值可以通过网络查询的方式获得,更为可贵的是,发现者无私的奉献出了全部知识[4-5],让我由衷的敬佩!按照发现者的指引,我成功的解决了谷歌地图偏移的问题[6],根据前面下载的与小地图匹配的经纬度信息来查询偏移值,然后进行修正,最后统一放在了~/.gpsdrive/maps/map_koord.txt文件中~
说到这里技术上的难题基本上都解决了,设定GPS接收机送出NMEA中的RMC语句就可以得到时间、经纬度、方向、速度等信息,配合gpsd,就可以正常使用gpsDrive了!不过根据人品守恒定律,我也该碰到点麻烦了~果然,本来准备用的Garmin 25-LVS的天线好像有问题,导致接收机始终无法输出任何东西,没办法,只好用JNS100来代替。设置输出RMC语句后,用串口调试助手可以看到正确 的输出了GNRMC语句,但是用gpsd+xgps来观察,状态却始终是Unkown,这是怎么回事呢?将输出语句改为GGA,输出的是GPGGA语句,这就可以定位了!但是由于GGA语句的信息不全,还要另外加VTG语句才完整。再设定添加VTG,输出GNVTG,在xgps中依然看不到速度、方向信息!仔细观察了一下,发现问题可能就出在开头的标识符上,凡是GP***开头的好像gpsd就认,以GN***开头的就不认!那就自己加个小程序[7],从/dev/ttyUSB0接收GN***的语句,然后改为GP***后再输出到一个伪终端(/dev/pts/*),让gpsd从那个伪终端接收即可~嘿嘿,幸亏前一阵要离线调试监控系统,逼着自己搞通了虚拟串口和伪终端,现在这样的工作就是小case了~中间也出了一点小波折,改完后的GP***语句gpsd还是不认,观察 gpsd的调试信息发现是校验码错误,这才想起来光改了内容,没有生成新的校验码,不错才有鬼了!NMEA的校验码很简单,就是字节的XOR操作,很快就搞定了,这时xgps里已经能够正确的显示所有需要的信息了~
最后连上lab的GPS天线测试了一下,肉眼观察误差在5米之内,perfect!至此,所有的技术问题都解决了,剩下的就是找个盒子把这些零碎装起来, 只留三个接口出来,加上我的本本就可以GPS车载导航了~
具体的程序我依然都放在了代码发芽网上,这里就给个链接,因为内容仍会时不时的更新一下,不方便直接放在这里。
[1] GpsDrive - a free car (bike, ship, plane) navigation system:http://www.gpsdrive.de/
[2] EeePC上用GPSDrive玩GPS和Google Map:http://www.terewong.com/mytoys0ab27.htm
[3] Google maps to qpeGPS and gpsDrive:http://gtm.tel.uva.es/ztep/maps/dmap.htm
[4] 中国地图经纬度偏移修正算法完美解决方案:http://blog.csdn.net/dongmeng110/archive/2009/10/31/4750380.aspx
[5] Google地图经纬度到像素索引值的转换公式:http://blog.csdn.net/dongmeng110/archive/2009/12/13/4997634.aspx
[6] 谷歌地图校准程序:http://fayaa.com/code/view/8649/
[7] 串口接收GNRMC转发GPRMC到伪终端:http://fayaa.com/code/view/8643/
ps,后来发现25LVS不是天线的问题,而是串口的线断了!重新焊了一下,又用上了!这下子原来写的“串口接收GNRMC转发GPRMC到伪终端”程序就用不上了,开了GPSD直接就可以收到GPRMC语句,更加的方便了~~定位精度还是很高的,显示在地图上能有5米左右。但是开始纸盒子的安装设计不太好,受力点集中在串口连接头上,因为没有螺丝固定,很容易就掉下来了,另外电源的连接头也不牢靠。干脆换了一个扁扁的硬纸盒子,把各种连接头都放在了盒子里面,只将又粗又结实的USB线和电源线引出来,这下就牢靠多了!当然天线也是要引出来~
多讲一句,有了淘宝就是好啊,本来以为一个磁性天线要200多,在淘宝上一查,20块就可以搞定!买了两根,试了一下,一点问题也没有!现在是已经停产的25LVS+20块钱的磁性天线+自己的本本,嘿嘿,非常便宜,心里没有负担啦!^_^
在校园里跑车试了一下,在一条仅能通过一辆车的路上,GPS也能精确的定位,感觉精度已经差不多1-2米了!真是意外啊!现在一切准备就绪,就等出发啦~~
2010年1月30日 星期六
2009年4月27日 星期一
还是minicom好用
这几天在重新熟悉Ubuntu下用2410的开发板进行编程。
用下来的感受是windows下可以做的事情Ubuntu下完全可以做到,加上我们是在用嵌入式linux进行开发,所以完全应该在Ubuntu下进行,忘掉Windows吧~
回到主题,说说minicom。前面写过一篇文章,里面说minicom用不惯,用了它的一个前端——cutecom。这几天使用下来,发现cutecom的一个重要缺点——显示时乱码。这个问题其实不是它所独有的,在Windows下的DNW和一般的串口调试助手都会出现这个问题。在Windows下的解决方法是使用自带的超级终端,它不仅不会显示乱码,还可以彩色显示,带tab自动补全,甚至可以利用zmodem协议来传输小文件!
那么Ubuntu下有没有这样的好东西呢?当然有了,那就是minicom!minicom的使用其实很简单,先用"minicom -s"启动设置界面,将串口名,波特率,握手协议等设定好,保存为默认的df1,以后只要用"minicom -m -c on"打开就可以了,不用再进行串口设置。直接使用minicom和超级终端一样自带tab补全,也可以利用zmodem协议传输文件,只要在输入rc后按Alt+S,就会出现选择要传输文件的提示窗口,在其中用空格选择要传输的文件,最后按回车就可以开始传输了。传输过程中有进度提示,完毕后会自动回到minicom的命令行。唯一让我不爽的是对中文支持不好,不过暂时用的都是英文,按理说minicom应该也能支持中文的,好像是-8选项,但我还没倒腾出来,就先不说了,以后再来研究~
用下来的感受是windows下可以做的事情Ubuntu下完全可以做到,加上我们是在用嵌入式linux进行开发,所以完全应该在Ubuntu下进行,忘掉Windows吧~
回到主题,说说minicom。前面写过一篇文章,里面说minicom用不惯,用了它的一个前端——cutecom。这几天使用下来,发现cutecom的一个重要缺点——显示时乱码。这个问题其实不是它所独有的,在Windows下的DNW和一般的串口调试助手都会出现这个问题。在Windows下的解决方法是使用自带的超级终端,它不仅不会显示乱码,还可以彩色显示,带tab自动补全,甚至可以利用zmodem协议来传输小文件!
那么Ubuntu下有没有这样的好东西呢?当然有了,那就是minicom!minicom的使用其实很简单,先用"minicom -s"启动设置界面,将串口名,波特率,握手协议等设定好,保存为默认的df1,以后只要用"minicom -m -c on"打开就可以了,不用再进行串口设置。直接使用minicom和超级终端一样自带tab补全,也可以利用zmodem协议传输文件,只要在输入rc后按Alt+S,就会出现选择要传输文件的提示窗口,在其中用空格选择要传输的文件,最后按回车就可以开始传输了。传输过程中有进度提示,完毕后会自动回到minicom的命令行。唯一让我不爽的是对中文支持不好,不过暂时用的都是英文,按理说minicom应该也能支持中文的,好像是-8选项,但我还没倒腾出来,就先不说了,以后再来研究~
2009年4月24日 星期五
在嵌入式开发中使用Qt
1. 编译PC上的Qt。这里的configure参数不是很重要,用默认的参数即可。这里为了调试方便加入-qvfb参数。
gunzip qt-x11-opensource-src-4.3.2.tar.gz
tar xvf qt-x11-opensource-src-4.3.2.tar
./configure -qvfb
make
make install
2. 交叉编译嵌入式Qtopia-core。这里的configure参数对于最终库文件的大小和支持的功能至关重要,一定要确保加入了足够的功能,同时不需要的功能一个也不要!
gunzip qtopia-core-opensource-src-4.3.2.tar.gz
tar xvf qtopia-core-opensource-src-4.3.2.tar
cd /tmp/qtopia-core-opensource-src-4.3.2
在qtopia-core-opensource-src-4.2.2/src/gui/embedded/qmouselinuxtp_qws.h加入
#define QT_QWS_IPAQ
#define QT_QWS_IPAQ_RAW
以上两个定义是为了支持触摸屏
在下面的configure参数中,-prefix指定了最后install的目录,
./configure -prefix /home/ace/Qtopiacore-4.3.2 -no-largefile -xplatform qws/linux-arm-g++ -no-libtiff -no-gif -no-libmng -no-openssl -no-cups -no-qdbus -embedded arm -little-endian -no-freetype -qt-gfx-linuxfb -no-qt3support -depths 8,16
make
make install
将编译好Qtopiacore-4.3.2下lib库放到根文件系统的Qtopiacore下 (注意根文件系统中的目录要和-prefix /home/ace/Qtopiacore-4.3.2一样否则下到arm板上运行程序的时候会报错说找不到字体)。
如果报错说找不到libstc++.so.6文件,可以将usr/local/arm/3.4.1/arm-linux/lib/libstc++.so.6复制到/home/ace/Qtopiacore-4.3.2/lib下(或者软链接)。
为了实现触摸屏的校准将/home/ace/Qtopiacore-4.3.2/examples/qtopiacore/mousecalibration/mousecalibration拷贝到根文件系统中,在文件系统的.bashrc文件中加入环境变量,并且export QWS_MOUSE_PROTO="LinuxTP:/dev/h3600_tsraw"。
3. 制作根文件系统及进行触摸屏校准。
将根文件系统下到arm板子上去,启动板子运行./mousecalibration -qws 进行触摸屏的校准。
校准完后 cat /etc/pointercal,将显示的数据记录下来, 然后新建一个空白文档将其命名为pointercal ,然后将记录的数据放到pointercal文档中。
rm -f /etc/pointercal
sudo cp -f 新建的pointercal /etc
sudo chown 500:244 /etc/pointercal
制作根文件系统,烧到arm板子。
4. Done!
gunzip qt-x11-opensource-src-4.3.2.tar.gz
tar xvf qt-x11-opensource-src-4.3.2.tar
./configure -qvfb
make
make install
2. 交叉编译嵌入式Qtopia-core。这里的configure参数对于最终库文件的大小和支持的功能至关重要,一定要确保加入了足够的功能,同时不需要的功能一个也不要!
gunzip qtopia-core-opensource-src-4.3.2.tar.gz
tar xvf qtopia-core-opensource-src-4.3.2.tar
cd /tmp/qtopia-core-opensource-src-4.3.2
在qtopia-core-opensource-src-4.2.2/src/gui/embedded/qmouselinuxtp_qws.h加入
#define QT_QWS_IPAQ
#define QT_QWS_IPAQ_RAW
以上两个定义是为了支持触摸屏
在下面的configure参数中,-prefix指定了最后install的目录,
./configure -prefix /home/ace/Qtopiacore-4.3.2 -no-largefile -xplatform qws/linux-arm-g++ -no-libtiff -no-gif -no-libmng -no-openssl -no-cups -no-qdbus -embedded arm -little-endian -no-freetype -qt-gfx-linuxfb -no-qt3support -depths 8,16
make
make install
将编译好Qtopiacore-4.3.2下lib库放到根文件系统的Qtopiacore下 (注意根文件系统中的目录要和-prefix /home/ace/Qtopiacore-4.3.2一样否则下到arm板上运行程序的时候会报错说找不到字体)。
如果报错说找不到libstc++.so.6文件,可以将usr/local/arm/3.4.1/arm-linux/lib/libstc++.so.6复制到/home/ace/Qtopiacore-4.3.2/lib下(或者软链接)。
为了实现触摸屏的校准将/home/ace/Qtopiacore-4.3.2/examples/qtopiacore/mousecalibration/mousecalibration拷贝到根文件系统中,在文件系统的.bashrc文件中加入环境变量,并且export QWS_MOUSE_PROTO="LinuxTP:/dev/h3600_tsraw"。
3. 制作根文件系统及进行触摸屏校准。
将根文件系统下到arm板子上去,启动板子运行./mousecalibration -qws 进行触摸屏的校准。
校准完后 cat /etc/pointercal,将显示的数据记录下来, 然后新建一个空白文档将其命名为pointercal ,然后将记录的数据放到pointercal文档中。
rm -f /etc/pointercal
sudo cp -f 新建的pointercal /etc
sudo chown 500:244 /etc/pointercal
制作根文件系统,烧到arm板子。
4. Done!
2009年4月15日 星期三
基于svm的驾驶决策建模
在前文的基础上,将该方法进一步推广到整个驾驶过程的决策建模,除了解决了驾驶决策的问题,还能避免其他方法参数校准的难题。
对于整个驾驶过程决策建模,要考虑在不同的驾驶阶段,驾驶者关注的决策变量是不同的。如果对各种不同的驾驶阶段分别进行基于svm的建模,当然可以,但需要另外对不同阶段间的转换进行判断,而各个阶段之间的过渡往往是模糊的,不同阶段转换的会很复杂,工作量甚至可能超过svm建模!为避免这个问题,我们用一个包含所有阶段的决策变量的状态空间来进行建模。以基本的跟驰阶段为例,用四个决策变量就可以进行驾驶决策了,但这时要给出所有决策变量在该时刻的值,如果没有的话就设为某一固定值(一般取为0即可),这样驾驶者在不同驾驶阶段的转换逻辑也包含在了状态空间模型中,不用另外做判断的工作,只需在每一时刻给出所有决策变量的值,大大简化了驾驶决策的建模。
本方法的优点很明显,但是缺点和前文类似,都需要包含各种类型决策变量的数据来进行训练,数据质量的好坏直接影响了模型的有效性和完备性。虚拟驾驶数据采集系统是解决这一问题的最佳方案!
对于整个驾驶过程决策建模,要考虑在不同的驾驶阶段,驾驶者关注的决策变量是不同的。如果对各种不同的驾驶阶段分别进行基于svm的建模,当然可以,但需要另外对不同阶段间的转换进行判断,而各个阶段之间的过渡往往是模糊的,不同阶段转换的会很复杂,工作量甚至可能超过svm建模!为避免这个问题,我们用一个包含所有阶段的决策变量的状态空间来进行建模。以基本的跟驰阶段为例,用四个决策变量就可以进行驾驶决策了,但这时要给出所有决策变量在该时刻的值,如果没有的话就设为某一固定值(一般取为0即可),这样驾驶者在不同驾驶阶段的转换逻辑也包含在了状态空间模型中,不用另外做判断的工作,只需在每一时刻给出所有决策变量的值,大大简化了驾驶决策的建模。
本方法的优点很明显,但是缺点和前文类似,都需要包含各种类型决策变量的数据来进行训练,数据质量的好坏直接影响了模型的有效性和完备性。虚拟驾驶数据采集系统是解决这一问题的最佳方案!
知道如何在blogger里发帖子了
最近在笔记本上的xp系统里往blogger发帖子老是连接不上,这就直接导致这个月都没在这里写什么东西!当然在本地还是写了一些的,CintaNote确实蛮方便的~
现在是在Ubuntu下,刚才偶尔打开一个blogger上自己以前写的帖子,发现速度还可以,就尝试着进入控制台发个帖子,发现速度还不错!以后就在Ubuntu下发blogger的帖子了~但是在本地写的东西放在CintaNote里,在xp系统下,拷贝到Ubuntu下不是很方便,要想个办法~
现在是在Ubuntu下,刚才偶尔打开一个blogger上自己以前写的帖子,发现速度还可以,就尝试着进入控制台发个帖子,发现速度还不错!以后就在Ubuntu下发blogger的帖子了~但是在本地写的东西放在CintaNote里,在xp系统下,拷贝到Ubuntu下不是很方便,要想个办法~
2009年3月23日 星期一
2009年3月19日 星期四
[转]利用消息队列实现进程间通信
UNIX系统程序设计-10-编程语言-电脑网络-搜狐社区: "第十章:利用消息队列实现进程间通信
所谓消息功能,是指由一个进程产生并送出的消息,被放在一个叫做“消息队列”的列中管理,然后由接收方从消息队列中取出消息,这么一种功能。(先入先出)
做一个例子程序看看:
下面的两个程序分为发送方和接收方。发送方将自己的PID和从标准输入输入的一个字符串送至消息队列,接收方把消息队列中所有的PID和字符串读出,并输出至标准输出。
/* msend.c */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
int main()
{
int msqid;
key_t msgkey;
struct msgbuf
{
long mtype;
char mdata[256];
};
struct msgbuf msgdata , *p ;
p = &msgdata ;
printf('Enter message : ');
fflush( stdin ); /* 刷新标准输入缓冲区 */
gets( p->mdata ); /* 输入字符串 */
p->mtype = getpid();
msgkey = ftok ( 'mrecv' , 'a' ); /* 计算标识符 */
msqid = msgget( msgkey , IPC_CREAT | 0666 ) ; /* 建立消息队列 */
msgsnd( msqid , p , sizeof(p->mdata) , 0 ); /* 送消息 */
return 0;
}
/* mrecv.c */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
int main()
{
int msqid;
key_t msgkey;
struct msgbuf
{
long mtype;
char mdata[256];
};
struct msgbuf msgdata , *p ;
p = &msgdata ;
msgkey = ftok( 'mrecv' , 'a' ); /* 计算标识符 */
msqid = msgget( msgkey , IPC_CREAT | 0666 ) ; /* 取得消息队列的ID */
while(1)
{
msgrcv( msqid , p , sizeof(p->mdata) , 0 , 0 ) ; /* 读消息 */
printf('Message received from %ld\n%s\n' , p->mtype , p->mdata );
}
return 0;
}
执行例:
% msend
Enter message : Hello , world
%msend
Enter message : I am Syuu I
%mrecv
Message received from 321
Hello , world
Message received from 326
I am Syuu I
使用消息功能,可分如下几步
发送方:
1.生成消息队列,取得ID。
2.向消息队列送消息。
接收方:
1.根据标识符,取得ID。
2.从消息队列接收消息。
3.删除消息队列。
例程序相对来说比较简单。我们就不解释了。
消息队列的使用方法以及函数的构造等等基本上和上一章所讲的共享内存的使用方法一样。可参考上一章来理解本章内容。
说说出现的函数:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget( key_t msgkey , int flag );
取得一个消息队列的ID,如不存在则建立。
返回值: 成功时:消息队列的ID
失败时:-1
int msgsnd( int msqid , struct msgbuf *msgp , size_t msgsiz , int msgflag );
向消息队列送消息
返回值: 成功时:0
失败时:-1
msqid是消息队列的ID,size_t msgsiz是结构体成员mdata的大小,msgflag与上一章所讲的共享内存的flag起一样的作用,不过,当这个参数为IPC_NOWAIT的时候,如果消息队列已满,则返回错误值。如果不为IPC_NOWAIT,在消息队列已满 的情况下,会一直等到消息队列有空地方的时候再发送。
注意这里的这个 struct msgbuf *msgp 。要求的格式如下:
struct msgbuf
{
long mtype;
char mdata[256];
};
long mtype在这里我们用来保存本进程的PID。mdata则是保存要发送的数据。由于mdata的大小不一定(根据实际需要定义),所以这个结构体并没有事先定义好。但是我们定义这个结构体的时候一定要遵循这个规定。你可以改的,只有mdata的大小,和结构体的名称。尽量不要修改结构体成员的名称和类型。实际上,根据mtype,我们还可以有所选择地接受消息。这在下面将会谈到。
int msgrcv( int msqid , struct msgbuf *msgp , size_t msgsiz , long msgtyp , int msgflag );
从消息队列取得一个消息
返回值: 成功时:0
失败时:-1
msqid , *msgp , msgsiz不用说了。long msgtyp是结构体msgbuf的mtype成员。msgflag与上述一样。只不过为IPC_NOWAIT的时候,如果消息队列是空的,则等到有消息可读的时候再读。当不为IPC_NOWAIT的时候,如果消息队列是空的,则返回错误值(与字面上理解的有些相反)
同样地,为了控制管理消息队列,一样有一个函数msgctl()如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl( int msqid , int cmd , struct msqid_ds *buf );
返回值: 成功时:0
失败时:-1
cmd所指定的值与共享内存部分相同。
本来还应该有一章关于Semaphore的。其使用方法于共享内存和消息队列差不多,只不过使用场合不同。可是我自己也没太弄明白那个东东是怎么回事,为了避免以讹传讹……就不写了。
看了我这么多贴子,大家辛苦了。《UNIX系统程序设计》这一系列的贴子到此宣告完成。在这些文章的创作过程中得到了SOHU C\C++论坛诸位朋友的大力支持。在此笔者深表谢意。
接下来,如果有时间的话,我会继续写一套《UNIX网络程序设计(C语言版)》。不知大家有兴趣否。
再有一点。本文中所解释的所有的函数,严格地说,应该叫:系统调用( system call )。"
所谓消息功能,是指由一个进程产生并送出的消息,被放在一个叫做“消息队列”的列中管理,然后由接收方从消息队列中取出消息,这么一种功能。(先入先出)
做一个例子程序看看:
下面的两个程序分为发送方和接收方。发送方将自己的PID和从标准输入输入的一个字符串送至消息队列,接收方把消息队列中所有的PID和字符串读出,并输出至标准输出。
/* msend.c */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
int main()
{
int msqid;
key_t msgkey;
struct msgbuf
{
long mtype;
char mdata[256];
};
struct msgbuf msgdata , *p ;
p = &msgdata ;
printf('Enter message : ');
fflush( stdin ); /* 刷新标准输入缓冲区 */
gets( p->mdata ); /* 输入字符串 */
p->mtype = getpid();
msgkey = ftok ( 'mrecv' , 'a' ); /* 计算标识符 */
msqid = msgget( msgkey , IPC_CREAT | 0666 ) ; /* 建立消息队列 */
msgsnd( msqid , p , sizeof(p->mdata) , 0 ); /* 送消息 */
return 0;
}
/* mrecv.c */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
int main()
{
int msqid;
key_t msgkey;
struct msgbuf
{
long mtype;
char mdata[256];
};
struct msgbuf msgdata , *p ;
p = &msgdata ;
msgkey = ftok( 'mrecv' , 'a' ); /* 计算标识符 */
msqid = msgget( msgkey , IPC_CREAT | 0666 ) ; /* 取得消息队列的ID */
while(1)
{
msgrcv( msqid , p , sizeof(p->mdata) , 0 , 0 ) ; /* 读消息 */
printf('Message received from %ld\n%s\n' , p->mtype , p->mdata );
}
return 0;
}
执行例:
% msend
Enter message : Hello , world
%msend
Enter message : I am Syuu I
%mrecv
Message received from 321
Hello , world
Message received from 326
I am Syuu I
使用消息功能,可分如下几步
发送方:
1.生成消息队列,取得ID。
2.向消息队列送消息。
接收方:
1.根据标识符,取得ID。
2.从消息队列接收消息。
3.删除消息队列。
例程序相对来说比较简单。我们就不解释了。
消息队列的使用方法以及函数的构造等等基本上和上一章所讲的共享内存的使用方法一样。可参考上一章来理解本章内容。
说说出现的函数:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget( key_t msgkey , int flag );
取得一个消息队列的ID,如不存在则建立。
返回值: 成功时:消息队列的ID
失败时:-1
int msgsnd( int msqid , struct msgbuf *msgp , size_t msgsiz , int msgflag );
向消息队列送消息
返回值: 成功时:0
失败时:-1
msqid是消息队列的ID,size_t msgsiz是结构体成员mdata的大小,msgflag与上一章所讲的共享内存的flag起一样的作用,不过,当这个参数为IPC_NOWAIT的时候,如果消息队列已满,则返回错误值。如果不为IPC_NOWAIT,在消息队列已满 的情况下,会一直等到消息队列有空地方的时候再发送。
注意这里的这个 struct msgbuf *msgp 。要求的格式如下:
struct msgbuf
{
long mtype;
char mdata[256];
};
long mtype在这里我们用来保存本进程的PID。mdata则是保存要发送的数据。由于mdata的大小不一定(根据实际需要定义),所以这个结构体并没有事先定义好。但是我们定义这个结构体的时候一定要遵循这个规定。你可以改的,只有mdata的大小,和结构体的名称。尽量不要修改结构体成员的名称和类型。实际上,根据mtype,我们还可以有所选择地接受消息。这在下面将会谈到。
int msgrcv( int msqid , struct msgbuf *msgp , size_t msgsiz , long msgtyp , int msgflag );
从消息队列取得一个消息
返回值: 成功时:0
失败时:-1
msqid , *msgp , msgsiz不用说了。long msgtyp是结构体msgbuf的mtype成员。msgflag与上述一样。只不过为IPC_NOWAIT的时候,如果消息队列是空的,则等到有消息可读的时候再读。当不为IPC_NOWAIT的时候,如果消息队列是空的,则返回错误值(与字面上理解的有些相反)
同样地,为了控制管理消息队列,一样有一个函数msgctl()如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl( int msqid , int cmd , struct msqid_ds *buf );
返回值: 成功时:0
失败时:-1
cmd所指定的值与共享内存部分相同。
本来还应该有一章关于Semaphore的。其使用方法于共享内存和消息队列差不多,只不过使用场合不同。可是我自己也没太弄明白那个东东是怎么回事,为了避免以讹传讹……就不写了。
看了我这么多贴子,大家辛苦了。《UNIX系统程序设计》这一系列的贴子到此宣告完成。在这些文章的创作过程中得到了SOHU C\C++论坛诸位朋友的大力支持。在此笔者深表谢意。
接下来,如果有时间的话,我会继续写一套《UNIX网络程序设计(C语言版)》。不知大家有兴趣否。
再有一点。本文中所解释的所有的函数,严格地说,应该叫:系统调用( system call )。"
订阅:
帖子 (Atom)