发新话题
打印

编程开发版精华FAQ

Q. Serial programming HOWTO?# U/ V, e2 t+ |+ z0 ~

9 o: |8 c/ c1 c7 Z, A& s& f/ h, KA. Attachment is a good guide for it!      
附件: 您所在的用户组无法下载或查看附件
class faraway : public GNU { public: faraway() : _M_name("faraway"), _M_sex("Male"), _M_age(26) { } ~faraway(); private: std::string _M_name; std::string _M_sex; unsigned long _M_age; };

TOP

Q. How to get local IP?
% c$ r3 z5 t3 B& u) K4 N. ?( Q& P+ I3 M* k* e7 {% i
A.
复制内容到剪贴板
代码:
#include <stdio.h>

#include <sys/ioctl.h>

#include <sys/socket.h>

#include <net/if.h>

#include <netdb.h>

#include <netinet/in.h>



int main( int argc, char *argv[] )

{

        int __fd;

        struct ifreq __if;

        struct sockaddr_in* __sin;



        __fd = socket(PF_INET, SOCK_DGRAM, 0);

        memset(&__if, 0x00, sizeof(__if));

        strcpy(__if.ifr_name, "eth0");

        ioctl(__fd, SIOCGIFADDR, &__if);

        close(__fd);

        __sin = (struct sockaddr_in* )&__if.ifr_addr;

        printf("Local IP address is:%s\n", inet_ntoa(__sin->sin_addr));



        return 0;

}
      
class faraway : public GNU { public: faraway() : _M_name("faraway"), _M_sex("Male"), _M_age(26) { } ~faraway(); private: std::string _M_name; std::string _M_sex; unsigned long _M_age; };

TOP

如何制作静态库和动态库

创建一个静态库通常使用 ar 程序把一些目标文件(.o)组合在一起, ( I+ N. h1 w1 D
成为一个单独的库,然后运行 ranlib,以给库加入一些索引信息。 0 R# ?% y; T/ n6 I+ \  D

; K- n9 _" s5 R1 m( h! w, q( v1 L$ A
. J* K/ y# B! _4 D编译与命名动态链接库
2 A% N1 j7 ~- e% c/ _* x: i8 t3 ?; |0 m, r% H8 A/ B

1 t$ h+ y. `" o+ h1 ]为了让GCC编译器生成动态链接库,编译时须加选项-shared.(这点须牢记)
0 X* s( K8 J. ]0 C2 R
3 s; i" v4 f& k: R% Q- y' o7 ]& @" RLINUX系统中,为了让动态链接库能被系统中其它程序共享,其名字应符合“lib*.so*”这种格式.如果某个动态链接库不符合此格式,则LINUX的动态链接库自动装入程序(ld.so)将搜索不到此链接库,其它程序也无法共享之.
: u4 {8 L. M( Z* Y+ L6 w8 t! w4 h8 m
格式中,第一个*通常表示为简写的库名,第二个*通常表示为该库的版本号.如:在我的系统中,基本C动态链接库的名字为libc.so.6,线程pthread动态链接库的名字为libpthread.so.0等等.本文例子所生成的动态链接库的名字为libmy.so,虽没有版本号,但也符合所要求的格式. 8 A5 Z( p9 l1 ?6 [' I% M) _" p. J, U- t

! \3 r! ?  P5 p. j为了让动态链接库为系统所使用,需要维护动态链接库的配置文件--/etc/ld.so.conf.此文件内,存放着可被LINUX共享的动态链接库所在目录的名字(系统目录/lib,/usr/lib除外),各个目录名间以空白字符(空格,换行等)或冒号或逗号分隔.
  T4 m' Q* X1 }& q- y
/ Z' j6 m9 Q/ B6 L  h5 Z7 u  e! s2 K3 l使用ldconfig在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如前介绍,lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件.缓存文件默认为/etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表.
/ R- B2 q9 s# G! e4 R# q
; @/ Y- Q, r! d5 u* F9 K/ K( n5 gldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令.      

TOP

如何使用静态库和动态库

编译时引用动态链接库的几种方式 0 |: k6 {' O, U/ u4 J( q2 R

7 @9 s; r  S: P/ D0 K7 a' M) b# A(1)当所用的动态链接库在系统目录(/lib,/usr/lib)下时,可用编译选项-l来引用如假设要使用/lib目录下的libmy.so6 S6 H* S# P4 U0 ^$ T1 w+ ~
#cc -lmy -o ady ady.c
7 T) j" Q) K5 ~7 b& a) n(2)当所用的动态链接库在系统目录(/lib,/usr/lib)以外的目录时,须用编译选项-L来指定动态链接库所在的目录(供编译器查找用),同时用-l选项指定缩写的动态链接库名.即:
4 w# i& \" b* ?4 R: X( B0 O
& ?" g- F, f& |: B4 w4 }) d# cc -L绝对路径或相对路径 -lmy -o mytest mytest.c ( p* ?0 l7 n( k& i, N
(3)直接引用所需的动态链接库.即:
4 R* N: A/ U' o0 I
( U5 K. Q: G7 O8 t; e# cc -o ady ady.c libmy.so
# [4 X- o' F% q4 S9 I+ v
, h7 v6 v* a# l0 `0 Z. R  {使用静态库类似于动态库; N. s! ?( n/ l; T  L" m8 R
8 \, I& S1 D- I* P$ F; f' e) ~& D
假如我们制作了一个静态库:2 K: W! V( [4 X: M: }0 ^+ \8 M
#ar libmy.a mylib1.o mylib2.o / K2 f& j6 E2 C3 o# G. B
#ranlib libmy.a
) }+ W* o- ?* y2 v# o
& c# T2 f/ S! p7 T0 E! Z; ^也可以用编译选项-l来使用7 L( v, k  o9 z7 Z3 a  z6 G6 V+ U
#cc -o mytest mytest.c  -L绝对路径或相对路径 -lmy
1 D; L4 e+ F5 d9 r8 ]  l" F
9 k. D+ `0 o, h9 u/ E" }- V3 o
0 E1 X( P! u, W. d/ h: @4 J检查动态链接程序究竟需要哪些共享库,可以使用命令ldd。* _7 f/ ?" X1 ?5 [  g* \" k% l
ldd命令行用法如下: 4 M& i) m8 g8 H! e0 E+ z

3 q* B: V. K3 y2 H; h' fldd [--version] [-v|--verbose] [-d|--data-relocs] [-r|--function-relocs] [--help] FILE...
7 S+ c4 w) |$ b* C2 ]. C. U3 X- W6 v/ M, ~/ E/ [
各选项说明如下:
7 E, S: S  U$ Z7 u+ G
" o3 Z: }) ^) s(1) --version : 此选项用于打印出ldd的版本号. 9 M; v6 S9 z! L6 n5 m

1 \# k$ S5 z" N! g(2) -v 或 --verbose : 此选项指示ldd输出关于所依赖的动态链接库的尽可能详细的信息.
+ C. u& {* e( @4 H1 }9 E4 s3 d$ m$ Z$ M8 R) L
(3) -d 或 --data-relocs : 此选项执行重定位,并且显示不存在的函数. / f* h3 w; _, I$ ]0 T" G
5 C* o$ c0 M) L6 a  [6 z0 V
(4) -r 或 --function-relocs : 此选项执行数据对象与函数的重定位,同时报告不存在的对象.
' z9 P. b' R' ?. J8 o) E4 p. [- k0 H
# e8 z( _; i6 c" t% g& ?) p8 z(5) --help : 此选项用于打印出ldd的帮助信息.
& p' k3 e0 ~9 I- H/ V+ r% G# t& C' ~4 Z% w: @0 A6 W# w* M* \% s
注: 上述选项中,常用-v(或--verbose)选项.
$ E% y5 U" Z( ?/ i1 Q- x4 i/ p6 K& v6 T5 i# t/ {3 e+ W! H* h
ldd的命令行参数为FILE...,即一个或多个文件名(动态链接程序或动态链接库).      

TOP

并口编程的方法(原文出自Forest_Leo)

哥们我,又经过了一个下午的摸索,现在有了点眉目,拿出来和大家分享,也算是补充一下国内目前并口文章(尤其C在后台开发)少得可怜的状况!!
8 G$ T( M9 p. h' x- R- O: a  B# A/ u5 g& P6 s! @- k: H8 G1 e
声明:所有东东都是我自己的理解,又告人觉得不对,希望尽量批评6 G: j- \/ m* y4 ^  ^
所有原码,函数名,.h文件名都是在Red Hat Linux上的东东
: [9 E, E4 T$ v; D; j7 e4 T其它平台可能会有所变化,程序是完全编译通过并正常运行
$ B% v7 D/ k8 X/ `) O; }4 w9 N
1 y* E: Z; l" F首先,用C语言操作并口,本身就存在两种方法
  q* y$ f% L) {5 P& J一种是:ioperm inb outb
* v, h* V4 b! b; f( W9 `8 |2 k6 H另外一种是:open read write ioctl
' i9 K  \+ V( _' K( b# q5 N& L( b$ j9 k& K$ a* {7 Y- l% m! N
第一种方法是操作并口的内存地址! G+ _+ B4 x9 T0 }
第二种方法是对文件描述符操作(是人就知道)
4 L# Q& |$ e  }
, @" J! S5 U# C; j# U. @( m, p9 K! ]先说第一种
1 [/ P9 i+ q/ d0 W; W- Yioperm是获取地址操作权限的函数,使用前后必须两次调用,
0 h+ t! H0 l. f# d" u$ O类似于open 和 close 但是他仅仅是获得权限,且一旦写入程序& Y1 ^  d( f; I' Q6 W3 L+ `  I
,该程序就必须用root用户来运行,无论你对其他用户如何授权,其他用户就是调用不了该函数(该点理解给予我的实践,希望有异议者多多批评)。
1 I* V( Z/ r$ _inb是读取寄存器种数据的,outb就是写入,但是对于并口来说,有些特性,即. R1 `! M' f3 Z
数据位地址可读写 控制位地址可写 状态位地址可读 通常/dev/lp0的地址是0x378,所以控制位就是0x378+2 状态位就是0x378+22 e' a: y2 `0 M* }2 A

9 ]% B4 S  d, }8 y2 p状态位有
4 ^. J1 o4 J( `' D3 Y1 r+ I* ~busy ack pe select error
! v/ S9 t& c& e/ A# x: `控制位有  D8 r7 V( [6 b6 D; u% L2 ?
select_in init autofeed strobe& j. F* I# e. j1 I/ s
数据位有1 L1 I) v; R( _7 P. e$ \0 M5 r3 E
D0--D7
3 }. {5 B6 Z% K" m1 A+ Y1 q7 r0 w) _! X2 I9 m. F3 T2 @
如果只熟悉C语言的哥们,估计看到这里就觉得很烦了,建议去看看并口电路的基本针脚介绍就知道我在说什么了
7 a7 J6 |8 q: \% ?7 p. c- t. J' F7 w9 ^1 c" A6 F
一句话,通过上面的这些东东我们就可以知道打印机是什么状态,是否可以发数据,是否缺纸,并且可以传递数据给打印机,但是要清楚,我们说的事并口,并不是打印机,打印机在这里只是个例子,当然并口很多东西当初也是从打印机那里慢慢演化而来的。
. Y% p7 [, J. I  K% o  }/ B9 q8 x
& ?% y9 \  a7 }$ b  @$ k这些控制位和状态位都是做什么的,我研究的也不是很清楚,但是网上有这些文章可查,我这里不多说了,总之如果用ioperm inb outb就要知道确切的作用,否则你控制不了打印机的动作,优点是直接操作地址,速度够快,不过如果操作打印机的话,通常瓶颈在打印机上,而不会是对并口的读写上。3 l1 Y0 ^3 E8 W9 N4 p
% U4 Y0 y6 x2 i! |& @' ?
说了这么多屁话,我想不如我下面的原码来的直观3 N+ v0 h1 d$ h1 u, _6 b

( w% I; v2 }. F# C* G大家请看
7 Q$ ~" X" r$ c+ }* {; a# g3 C0 u' g* C
#include <stdio.h>
# k! e# U" [! [" D+ p" u  C#include <unistd.h>
( e6 M8 r- r7 n/ K$ m#include <asm/io.h>
  |! I( Q( I2 M1 o& _7 Z/ d5 X5 p  J
#define BASEPORT 0x378 /* lp1 */
" w/ _4 i0 g; v4 m7 v, I: u% r; a  Q/ o7 e+ r9 P% q/ l: m& L
int main()0 l$ x  {& c, w
{
0 q- M  K' j& M. _/* Get access to the ports */) L! U: M# N2 `. I9 `* L
if (ioperm(BASEPORT, 3, 1)) {perror("ioperm"); exit(1);}- }$ q+ m: _4 p6 @

# `2 s1 m# ~* E! s/* Set the data signals (D0-7) of the port to all low (0) */( O& b& u5 C! _; o/ k. D( `
outb(0, BASEPORT);
% W( h& ]( F% t! ~* ]/ a; r+ j/ u, ^. p5 t
/* Sleep for a while (100 ms) */3 f- p. D+ ?3 M
usleep(100000);3 X: v# \, h( g7 C8 u
7 U6 P" C/ R* b0 X) a5 F
/* Read from the status port (BASE+1) and display the result */
& T# n* `# ]& k( K- a; o1 pprintf("status: %d\n", inb(BASEPORT + 1));
' ], I0 V# A2 D4 m
: e0 P3 t' I0 u& Moutb(0x80,BASEPORT+2);  e! J+ ]" Y% y( T& J& l: r' w# F
system("/home/ShenLin/ParPort/Check");
. T1 I* O, |- \/ y7 ?usleep(10000000);! \3 x0 _* J6 q# r1 c, ?# j3 q5 O, m
system("/home/ShenLin/ParPort/Check");
% d7 E6 i; }6 ?: F8 M% ]! @
/ s& k4 y- P, g! r0 R- y6 moutb(0x40,BASEPORT+2);
9 M5 d1 W- [9 i# d9 {' d4 l( csystem("/home/ShenLin/ParPort/Check");
* |5 _2 {' K3 Z0 ]3 \* ausleep(10000000);- l9 p1 B4 Z' F$ U* V: Z
system("/home/ShenLin/ParPort/Check");
% d2 E' f% S% D8 e* [& g$ h+ Q1 I7 H6 ^8 \/ \6 ?& n
outb(0x20,BASEPORT+2);4 j. }9 Z* }( p) E% e
system("/home/ShenLin/ParPort/Check");# z. n* n6 z& a
usleep(10000000);
! [# e2 i8 F$ q# \system("/home/ShenLin/ParPort/Check");
' u8 Y7 A! j; a! U# c9 g# Z# H
& p4 I9 ?- B7 Foutb(0x10,BASEPORT+2);
3 q9 c0 k% W5 c* d% C% g$ wsystem("/home/ShenLin/ParPort/Check");
+ M2 K- q( T- k- fusleep(10000000);
+ o" d' H4 D, {5 v1 @6 `& bsystem("/home/ShenLin/ParPort/Check");
0 k! v4 O/ a/ E5 e7 j5 ~2 Z" p
" R  V- M* [( w! Y4 Houtb(0x08,BASEPORT+2);; f! P4 d% s/ q
system("/home/ShenLin/ParPort/Check");
' E0 ^( q" J) Xusleep(10000000);! u* D" u5 x1 Z. X" z
system("/home/ShenLin/ParPort/Check");
: E# T. x2 L+ h) h. d2 x  I* t# d  `$ x# A7 S
/* We don't need the ports anymore */9 p" {4 ?" ]" J2 k
if (ioperm(BASEPORT, 3, 0)) {perror("ioperm"); exit(1);}+ h6 ]* [3 w$ V% S0 X7 H& \0 m2 E4 I& P
. Q% f5 I2 x& e8 k* A$ u
exit(0);
; H2 ~6 @5 k' u3 p2 C- W
9 K' s- R0 Q$ n  e}1 x% x- T9 f. @! f  X$ {+ [
上面的程序要注意的是下面这几点. i  _3 B2 P* q$ h
ioperm(BASEPORT, 3, 1) 这个函数就如我说的调了两次,注意区别
9 V' m+ y# z( h5 k9 v7 h& Rsystem("/home/ShenLin/ParPort/Check");这里面调用的Check程序是我自己写的(就是用open ioctl写的),这个明天再介绍,我要吃饭去了
. G3 J2 f! X0 X6 o6 jinb(BASEPORT + 1));这个就是在读取状态地址的值,用它就可以判断是忙还是缺纸还是什么别的(在Check程序中,我用ioctl来获得)
! o' e% j0 m. ~7 s5 [5 F( Y. y
; x' v( F* _8 f9 C( Q至于打印机的字符集,输出格式等等,不再并口操作讨论范围内,我个人理解他们应该和串口的是一样的,即串口的fd被write了什么,并口就应该被write什么,不知道我理解得对不对,换句话说如果有一个串口的打印机驱动程序,那么我要移植到成并口的,理论上讲应该只替换掉其中打开设备的部分,即open的部分,还有就是替换掉状态判断部分,即ioctl部分,至于别的,我理解应该不需要替换的,这一点我不敢肯定,请各位高人给予分析,多谢!!      

TOP

继续转载并口的编程

补充一下昨天方法中要注意的,就是如果用到了ioperm这些函数,编译的时候要加入 O2或者O 。/ K; m) n' m, \4 ?+ p" b  K% r$ y
即gcc -O2 -o test test.c,不要这样gcc -o test test.c* U0 {' u3 Z+ \6 [
- `' A) |7 o4 A+ F* }

3 _5 e( N# \9 }我接着把昨天的写完/ Q7 ?( ]3 ?- u% h

1 R4 Q' f) H! L用操作文件描述符的方法主要问题在于ioctl函数上,这个函数实在是变化无穷,第一个参数是文件描述符,但是该文件描述符是如何获得的,将直接影响第二个参数用什么,而第二个参数又直接影响第三个参数的指针类型6 }( j$ u$ c' J4 U* V3 Z

& k" b/ ]: ]! X" C+ k1 ~总之,就第二个参数而言,其针对串口,并口,socket,普通文件等等是各有一套定义的,而且分别存放在不同的.h中的,如果用错了(假如把socket的用到并口上),编译的时候不会有问题,但是运行的时候,肯定会告诉你参数无效的。9 N6 @2 U- {- ~: h, P0 m6 b
2 F1 v, J# [1 @# N3 E
对于并口来说,首先你的系统就要支持并口的那种,如果各位裁减内核的时候把这个东西裁掉了,那么,你用open 打开/dev/lp0的时候会告诉你没有这个设备,昨天我下午曾经一度陷入了这个设备中/dev/parport0,这也是个并口,但是不对应硬件,我不敢肯定的说他可能是个虚拟的并口(希望研究比较明白的人给个答案),对于这个并口的操作,我们必须用到
, K+ Q# o1 D' _% {* f8 ~#include <linux/parport.h>
% }; n9 s+ `1 A; }#include <linux/ppdev.h>8 y/ v; E& n# Z
这两个库文件,这里的名字和路径,我在强调一下,是Red Hat Linux上的,其它平台应该会有少许差别。这两个库里是什么东西呢,只要看了,就会明白,其实这里定义了ioctl中第二个参数可以用哪些,以及对应第三个参数是什么类型的指针,包括各种并口状态位的宏定义,以及并口的模式标准(EPP,IEEE1284等)等等,但是问题在于这个设备对应的不是打印机口,所以虽然可以操作了,但是也没用,操作这个东东的ioctl函数的第二个参数可谓十分丰富,只是跟下面将要说的/dev/lp0比起来,/dev/lp0提供给ioctl函数的第二个参数就少得可怜了。7 o' _1 l2 t1 n5 z/ V0 h
这也是我很疑惑的问题,难道对于/dev/lp0来说,只要我open了,就不需要对这个被open的文件描述符进行任何初始化了吗?2 V; \! W7 h3 z9 ~0 b
下面我们先看一下操作lp0得源码,强调一下,虽然有了4 a. J8 e8 p5 N! b0 S7 l- W4 y
#include <linux/parport.h>
+ O  S8 ?- q7 l#include <linux/ppdev.h>
, N: {+ K1 T/ T3 T1 B) \这两个库中定义的那么多的东东,很可惜他们全都不能用到lp0上,否则告诉你参数无效
" I" k4 a, H. r% }: f' w3 A  Q要操作lp0关键在于下面的库,上面两个可以不要
$ e) P! z3 b& E* {( B$ b, N3 T#include <linux/lp.h>  O& Q. h$ D. w/ ~4 [$ [) S

6 P- P/ j4 X5 e. {. a. \: F7 g, l源码如下:
" o! Q. l4 r. w  t6 L. R5 m! N/ Y; ~# I: o7 L$ A
#include <sys/ioctl.h>
2 w# o# e8 [2 E- g1 \, i7 i1 {#include <stdio.h>
6 R2 O: P4 Y1 J$ I#include <unistd.h>1 t; z) V  q0 @0 ~- T
#include <fcntl.h># e) @2 B2 j+ _1 V
#include <errno.h>
9 p1 P6 z8 [) ]1 f# t#include <getopt.h>% {7 P  }5 q7 ]% J' j8 W2 B8 J/ z
" @9 E- `1 \! `# p
#include <linux/lp.h>
/ l' p' a8 Q: f0 m& v: f7 [8 {4 C0 U

, y) n$ Q! X- M$ L+ fint CheckParPortState(int fd, int m_bits)3 Z8 n% D! ^5 ~
{
7 n- C' |- D5 i$ n3 [  X, `" L; xint status;
/ D* `2 ?( y  P# j/ Q9 e# N/ \3 B. d
. {$ T  x! ?5 z  d$ W9 b/* Get Par Port State */3 `' M. Y/ `& F+ \4 s  s0 S
if (ioctl(fd, LPGETSTATUS, (char*)&status) < 0) {9 z8 P8 l% ^4 k
return (0);6 U( j( k" P: b% m" w- V5 c5 F
}
* v& S! V- U% ]3 xif (m_bits != 0x20) return ( status & m_bits );
% b) d- e" l( R0 Z+ selse return( !(status & m_bits) );
$ \" ]9 J& M4 X, x}
$ y4 V+ m* c9 p3 l( {8 |! q' a' d) h$ G* L
main()7 y6 k. L# y. o
{7 K$ }# R. x  r
int fd;& x' U9 I( Y! T+ l
2 G% v; ?% Y7 _& m+ q. X8 L
if ( (fd = open("/dev/lp0", O_WRONLY , 0)) < 0) {
6 T. ^$ d. {; S7 F* Q6 a: Bperror("/dev/lp0 error");
9 U: \  _" [0 Q5 W3 ^' Texit(-1);
7 Y. e5 D/ d  R7 n8 i. z9 z. u" L};2 i4 Q- R3 a6 W
" r4 p( r  {$ P$ M" C

$ {4 p1 A9 W3 \/ |if (!CheckParPortState(fd, LP_PBUSY))3 u: M7 s. D% C4 f# K  s" \
printf("printer is busy \n");/ y2 N; @  }- j7 a5 I

+ K/ b, r: W3 j4 n% F. bif (!CheckParPortState(fd, LP_PACK))
' U6 u+ O$ m- N* {printf("printer answer \n");; j: `% S3 D2 v. R- B) I
8 _# ^, O6 y7 G
if (!CheckParPortState(fd, LP_POUTPA))
+ W2 o+ U& R$ m+ [$ Xprintf("printer paper out \n");
" c- |4 F3 g  n4 y; n
. M% H* U+ l* Q4 Q9 K1 ~$ H9 S8 Yif (!CheckParPortState(fd, LP_PERRORP))   J* ~5 l  l; r' b: O  q. X
printf("printer error \n");
4 m3 p# L, d# w* \5 R
  V/ N8 E( \/ U: n1 C* p) l' [
# \8 Z, B# i" z" o! I: _close(fd);* D. a1 I$ y2 G( V; z- [. U

0 m; J$ C5 a. D: e}! w( q$ _% H  I+ P5 {0 C" s: s. @

; q3 A, O5 l5 X8 E0 n& N上面这个程序就是我昨天说到的那个Check的源码: w/ X$ a) `1 U3 P" S  x
程序要注意的:
/ l( \5 Q! X+ ?' K1.判断paper out的时候,其逻辑与其他判断相反
% i7 a$ R, v* i0 `2.我到现在也不知道打开lp0这个并口后要不要做什么初始化,
7 l! s* \! y5 p好像这个工作bios里设定了,包括并口传输模式,物理地址,中断等等
9 @) b" i7 Z  x, \" ?. C3.lp.h中提供的东东比较少,目前我觉得有用的就是这个获得状态位的
2 t6 p# [; o8 I, ~& J" ycommand,以及这些状态位判断用的宏。$ E/ a, b% q5 Z' l: O
其他的,如果哪位高人领悟了含义和具体用法,请告诉我,谢谢!8 {) |* n* c) r* O8 C( u+ p
4.某种状态不是唯一出现,比如我关闭打印机,可能同时报忙.缺纸.Error$ X3 ~$ m5 w) }. [& o( L: A4 E
如果缺纸,可能同时报缺纸.忙两个状态* f4 n& }3 {1 t9 f' }& A* N
5.状态以打印机的指示灯为准,即,有的打印机没有纸的时候不会提醒缺纸,只有当你触发换行或换页,缺纸指示灯才会亮,程序也才会受到这个状态
$ @$ W1 ?8 k* n( {
/ T4 c- T( j! _2 l5 D! l( M4 n0 _. Y' d
到这里我们只是了解了个大概,希望能有高人能告诉我打印机对应的lp0是否只要open了就可以了,还是要类似于串口一样设置一堆东西(如校验位,波特率等)
7 O" c' a. v# [此外,对lp0的控制位的操作,我猜想可能被write屏蔽掉了,即它不需要像操作地址一样改变不同的控制位,以便控制打印机能否接受数据等等动作,调用write就会默认去修改一些控制位等。这是我的猜想,希望有高人能证明。
: Z2 l, Z3 G& Z2 Y) B. ~8 O5 H9 v; A3 O6 e4 R' _! ~- @6 k
2 z, R, a9 X* u5 O) g
以上出自Forest_Leo,欢迎更多的原创。      

TOP

在linux下写一个des密码程序,可以用到linux提供的哪些东西。! s% j. _2 [  N: j1 t
   谢谢。
( B6 v- C' T0 g6 j9 Q  r    os=rh9.0;      
好好学习,天天向上。

TOP

使用动态库补充:使用动态库装载函数dlopen、dlsym、dlclose,在程序需要的时候动态装载。3 [7 F- F0 _- I! w
dlopen函数定义为
3 U% f$ b# _5 Fvoid* dlopen(const char *filename,int flag);flag指明是否立刻计算库的依赖性。如果设置为 RTLD_NOW 的话,则立刻计算;如果设置的是 RTLD_LAZY,则在需要的时候才计算。另外,可以指定 RTLD_GLOBAL,它使得那些在以后才加载的库可以获得其中的符号。
3 d. u) U6 Q4 U, Y6 c0 E& Gdlsym函数定义为
7 t; l9 w) K2 u& z* q/ @void*dlsym(void *handle,char *symble);handle为dlopen返回值,symble为需要使用的符号名。
, P$ P% J# w- _* d: \. J. hdlclose函数定义为
7 v9 G. k& a0 v7 jint dlclose(void *handle);dlclose把动态库的引用减一,dlopen把引用加一,引用为0时,动态库被卸载。7 s1 c, ?. I( S) I3 U- a! v- S
关于如何使用,见帖子:http://linuxeden.com/forum/showthread.php?t=110260      
上帝说,有问题,找GOOGLE 写程序是很神圣的事情!同样只是装系统,卖菜的大娘会的事情不见得就跟卖菜一样了。

TOP

hao tie wo ding      

TOP

这里有一片关于windows和linux下动态库比较的文章
7 p( v( B! K8 d6 d* w原文摘自http://linuxeden.com/forum/showthread.php?t=121533) U! H& S* @9 j: H0 r
分析Windows和Linux动态库
; h8 j: `; e$ Q; P 出处:yesky , P2 q6 T! i: f5 ~1 r+ l. x% x
摘要:动态链接库技术实现和设计程序常用的技术,在Windows和Linux系统中都有动态库的概念,采用动态库可以有效的减少程序大小,节省空间,提高效率,增加程序的可扩展性,便于模块化管理。但不同操作系统的动态库由于格式 不同,在需要不同操作系统调用时需要进行动态库程序移植。本文分析和比较了两种操作系统动态库技术,并给出了将Visual C++编制的动态库移植到Linux上的方法和经验。
$ |5 c, M1 E. q& g, H  1、引言
7 q  O9 Z3 C5 _; L: h$ B, p( ~) L" A$ X0 y! w
  动态库(Dynamic Link Library abbr,DLL)技术是程序设计中经常采用的技术。其目的减少程序的大小,节省空间,提高效率,具有很高的灵活性。采用动态库技术对于升级软件版本更加容易。与静态库(Static Link Library)不同,动态库里面的函数不是执行程序本身的一部分,而是根据执行需要按需载入,其执行代码可以同时在多个程序中共享。
' j# w. G4 y; q( o  {9 w4 L
' W* i# `- H! g4 v0 I0 H& e/ B, n  在Windows和Linux操作系统中,都可采用这种方式进行软件设计,但他们的调用方式以及程序编制方式不尽相同。本文首先分析了在这两种操作系统中通常采用的动态库调用方法以及程序编制方式,然后分析比较了这两种方式的不同之处,最后根据实际移植程序经验,介绍了将VC++编制的Windows动态库移植到Linux下的方法。& K( U( Z9 i: O, K

# S* b' q1 M% K5 d1 f  2、动态库技术
5 K. U& _  L+ [& J
% W' Q( r' A3 C7 k  2.1 Windows动态库技术4 [7 G% u" a5 h* ^" ]5 S) n7 O) ]

; y2 Z6 y$ F, r/ v  动态链接库是实现Windows应用程序共享资源、节省内存空间、提高使用效率的一个重要技术手段。常见的动态库包含外部函数和资源,也有一些动态库只包含资源,如Windows字体资源文件,称之为资源动态链接库。通常动态库以.dll,.drv、.fon等作为后缀。相应的windows静态库通常以.lib结尾,Windows自己就将一些主要的系统功能以动态库模块的形式实现。1 v! u) p+ D- j
( F2 J0 ]3 @9 L+ o; J6 p" [3 D+ i
  Windows动态库在运行时被系统加载到进程的虚拟空间中,使用从调用进程的虚拟地址空间分配的内存,成为调用进程的一部分。DLL也只能被该进程的线程所访问。DLL的句柄可以被调用进程使用;调用进程的句柄可以被DLL使用。DLL模块中包含各种导出函数,用于向外界提供服务。DLL可以有自己的数据段,但没有自己的堆栈,使用与调用它的应用程序相同的堆栈模式;一个DLL在内存中只有一个实例;DLL实现了代码封装性;DLL的编制与具体的编程语言及编译器无关,可以通过DLL来实现混合语言编程。DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。
5 O$ q# N! l6 |) k3 B$ q! |3 L  C6 u! E6 [
  根据调用方式的不同,对动态库的调用可分为静态调用方式和动态调用方式。. h7 y2 i5 q1 o) F7 h
1 m* k' M2 G8 O; S2 H' h. K& m
  (1)静态调用,也称为隐式调用,由编译系统完成对DLL的加载和应用程序结束时DLL卸载的编码(Windows系统负责对DLL调用次数的计数),调用方式简单,能够满足通常的要求。通常采用的调用方式是把产生动态连接库时产生的.LIB文件加入到应用程序的工程中,想使用DLL中的函数时,只须在源文件中声明一下。 LIB文件包含了每一个DLL导出函数的符号名和可选择的标识号以及DLL文件名,不含有实际的代码。Lib文件包含的信息进入到生成的应用程序中,被调用的DLL文件会在应用程序加载时同时加载在到内存中。
- t& z  ]! q: F! m$ X
% k. o  p/ y. J4 D# ^% z2 d  (2)动态调用,即显式调用方式,是由编程者用API函数加载和卸载DLL来达到调用DLL的目的,比较复杂,但能更加有效地使用内存,是编制大型应用程序时的重要方式。在Windows系统中,与动态库调用有关的函数包括:* N8 i# ?2 _1 n$ _
$ N5 L' e  |4 F, o
  ①LoadLibrary(或MFC 的AfxLoadLibrary),装载动态库。
$ c  M: R1 w% M% @- U  ②GetProcAddress,获取要引入的函数,将符号名或标识号转换为DLL内部地址。2 ]; I, ^' [. t5 C! Y0 f
  ③FreeLibrary(或MFC的AfxFreeLibrary),释放动态链接库。
5 R* {$ }" M* z9 v6 X$ V$ I: o( @$ Q, p0 @0 v# U
  在windows中创建动态库也非常方便和简单。在Visual C++中,可以创建不用MFC而直接用C语言写的DLL程序,也可以创建基于MFC类库的DLL程序。每一个DLL必须有一个入口点,在VC++中,DllMain是一个缺省的入口函数。DllMain负责初始化(Initialization)和结束(Termination)工作。动态库输出函数也有两种约定,分别是基于调用约定和名字修饰约定。DLL程序定义的函数分为内部函数和导出函数,动态库导出的函数供其它程序模块调用。通常可以有下面几种方法导出函数:: J( d, z  i* R, s4 d# J( W
0 w3 @9 G  y+ P2 Q! X
  ①采用模块定义文件的EXPORT部分指定要输入的函数或者变量。
, \; X6 E( V  u+ ?7 n" @: {  ②使用MFC提供的修饰符号_declspec(dllexport)。
$ m, R* T; k8 Y# u, W  ③以命令行方式,采用/EXPORT命令行输出有关函数。
) p8 M7 h4 ~/ C4 U9 }1 H3 U/ \
. r/ u, z4 @+ ~0 A, J7 |4 j  ?  在windows动态库中,有时需要编写模块定义文件(.DEF),它是用于描述DLL属性的模块语句组成的文本文件。0 v' Y" Q% O( x! g, V# j

. W! W) k8 A$ k6 E5 k* [ 2.2 Linux共享对象技术 0 i# ^: R' q4 w- N
  w2 ]  \' K2 L7 \9 A. D) G7 w5 H

, M; E1 [7 m) Y+ _6 X  在Linux操作系统中,采用了很多共享对象技术(Shared Object),虽然它和Windows里的动态库相对应,但它并不称为动态库。相应的共享对象文件以.so作为后缀,为了方便,在本文中,对该概念不进行专门区分。Linux系统的/lib以及标准图形界面的/usr/X11R6/lib等目录里面,就有许多以so结尾的共享对象。同样,在Linux下,也有静态函数库这种调用方式,相应的后缀以.a结束。Linux采用该共享对象技术以方便程序间共享,节省程序占有空间,增加程序的可扩展性和灵活性。Linux还可以通过LD-PRELOAD变量让开发人员可以使用自己的程序库中的模块来替换系统模块。7 l% N2 I% r) k4 {/ R/ j

& O7 r, v! ^" y1 o  l  同Windows系统一样,在Linux中创建和使用动态库是比较容易的事情,在编译函数库源程序时加上-shared选项即可,这样所生成的执行程序就是动态链接库。通常这样的程序以so为后缀,在Linux动态库程序设计过程中,通常流程是编写用户的接口文件,通常是.h文件,编写实际的函数文件,以.c或.cpp为后缀,再编写makefile文件。对于较小的动态库程序可以不用如此,但这样设计使程序更加合理。
) a2 N" I& K8 {: l3 Z
" z0 `; n" X, E1 F4 N1 Z  编译生成动态连接库后,进而可以在程序中进行调用。在Linux中,可以采用多种调用方式,同Windows的系统目录(..\system32等)一样,可以将动态库文件拷贝到/lib目录或者在/lib目录里面建立符号连接,以便所有用户使用。下面介绍Linux调用动态库经常使用的函数,但在使用动态库时,源程序必须包含dlfcn.h头文件,该文件定义调用动态链接库的函数的原型。
. ^- y8 Q; Y) G; n9 d8 a- G9 ]! d$ N, p" S5 q7 }
  (1)_打开动态链接库:dlopen,函数原型void *dlopen (const char *filename, int flag);
0 Y* ]/ c' E- o8 Jdlopen用于打开指定名字(filename)的动态链接库,并返回操作句柄。
# ~8 t8 g9 I5 \4 \# Z" s6 T4 U% L3 t; \$ Z' E
  (2)取函数执行地址:dlsym,函数原型为: void *dlsym(void *handle, char *symbol); 4 E. o  D7 u/ I0 u( o! |2 p
dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。
* D3 `3 p7 E/ i3 g- j# F1 u( @+ t7 |, e6 {
  (3)关闭动态链接库:dlclose,函数原型为: int dlclose (void *handle); ! M8 V% j. y' E
dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
4 U8 K7 ~& L. a0 E: {9 Z6 ^& h" L5 B0 x
3 D1 I4 d. L/ T# X' O7 \5 f  (4)动态库错误函数:dlerror,函数原型为: const char *dlerror(void); 当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。
- x0 }4 [9 U2 p. c' B( a2 c& v& \: W- |8 S4 R
  在取到函数执行地址后,就可以在动态库的使用程序里面根据动态库提供的函数接口声明调用动态库里面的函数。在编写调用动态库的程序的makefile文件时,需要加入编译选项-rdynamic和-ldl。
" \1 {5 K, Q( v* I( T! n; l- I+ k( h" i& J6 I
  除了采用这种方式编写和调用动态库之外,Linux操作系统也提供了一种更为方便的动态库调用方式,也方便了其它程序调用,这种方式与Windows系统的隐式链接类似。其动态库命名方式为“lib*.so.*”。在这个命名方式中,第一个*表示动态链接库的库名,第二个*通常表示该动态库的版本号,也可以没有版本号。在这种调用方式中,需要维护动态链接库的配置文件/etc/ld.so.conf来让动态链接库为系统所使用,通常将动态链接库所在目录名追加到动态链接库配置文件中。如具有X window窗口系统发行版该文件中都具有/usr/X11R6/lib,它指向X window窗口系统的动态链接库所在目录。为了使动态链接库能为系统所共享,还需运行动态链接库的管理命令./sbin/ldconfig。在编译所引用的动态库时,可以在gcc采用 ?l或-L选项或直接引用所需的动态链接库方式进行编译。在Linux里面,可以采用ldd命令来检查程序依赖共享库。; f' P8 z$ R( a0 [( {+ u' y7 |+ u6 X
, f2 b" m* g+ T; V$ I0 g7 Y3 x  i  j
    3、两种系统动态库比较分析
3 U1 W3 G8 f0 }# \3 ~  _% ?& G6 Q' @/ G( q6 I4 s
" ?0 l! J7 x  U. q$ R" ~* b3 Q) }
  Windows和Linux采用动态链接库技术目的是基本一致的,但由于操作系统的不同,他们在许多方面还是不尽相同,下面从以下几个方面进行阐述。, t, ]% G7 Y9 P' T
- m2 V; U. c1 g
  (1)动态库程序编写,在Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数作为初始化的人口,通常在导出函数的声明时需要有_declspec(dllexport)关键字。Linux下的gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要到函数做特别声明,编写比较方便。
; B# k1 _0 [2 c, y1 _
& B$ O0 H1 ]0 o; m2 I& t  (2)动态库编译,在windows系统下面,有方便的调试编译环境,通常不用自己去编写makefile文件,但在linux下面,需要自己动手去编写makefile文件,因此,必须掌握一定的makefile编写技巧,另外,通常Linux编译规则相对严格。2 B+ }: B7 W2 N8 _$ ?. p1 s

: ~- r) ^" n0 e' q# O+ K8 `1 _  (3)动态库调用方面,Windows和Linux对其下编制的动态库都可以采用显式调用或隐式调用,但具体的调用方式也不尽相同。
( T' q7 }. \7 B9 ^# y) Q/ P
4 W  G; y* u/ ], ?  (4)动态库输出函数查看,在Windows中,有许多工具和软件可以进行查看DLL中所输出的函数,例如命令行方式的dumpbin以及VC++工具中的DEPENDS程序。在Linux系统中通常采用nm来查看输出函数,也可以使用ldd查看程序隐式链接的共享对象文件。# q, p; A  p" S2 v7 ]" ?% g2 d

4 K6 ~) {! I8 E9 ?- ?0 v7 m  (5)对操作系统的依赖,这两种动态库运行依赖于各自的操作系统,不能跨平台使用。因此,对于实现相同功能的动态库,必须为两种不同的操作系统提供不同的动态库版本。
" U% _) b4 ]4 @( @3 ^" O9 J6 l
* W9 ]4 D- p( y0 w$ d, E4 }# Z; s    4、动态库移植方法
1 y$ L* g3 K: W- }# V
& X+ I! \2 l1 f" q0 p5 K  E7 @% B" W7 w8 n1 ~9 `
  如果要编制在两个系统中都能使用的动态链接库,通常会先选择在Windows的VC++提供的调试环境中完成初始的开发,毕竟VC++提供的图形化编辑和调试界面比vi和gcc方便许多。完成测试之后,再进行动态库的程序移植。通常gcc默认的编译规则比VC++默认的编译规则严格,即使在VC++下面没有任何警告错误的程序在gcc调试中也会出现许多警告错误,可以在gcc中采用-w选项关闭警告错误。, ~, ^; M7 F) H

3 B7 F  i; K0 g- E$ V  下面给出程序移植需要遵循的规则以及经验。5 n% x% o+ l$ g3 p* q( t
+ l' l% `' u7 _
  (1)尽量不要改变原有动态库头文件的顺序。通常在C/C++语言中,头文件的顺序有相当的关系。另外虽然C/C++语言区分大小写,但在包含头文件时,Linux必须与头文件的大小写相同,因为ext2文件系统对文件名是大小写敏感,否则不能正确编译,而在Windows下面,头文件大小写可以正确编译。
' G, T' o8 D: q% |- j
7 f7 O# K' @( b  (2)不同系统独有的头文件。在Windows系统中,通常会包括windows.h头文件,如果调用底层的通信函数,则会包含winsock..h头文件。因此在移植到Linux系统时,要注释掉这些Windows系统独有的头文件以及一些windows系统的常量定义说明,增加Linux都底层通信的支持的头文件等。
3 Z5 L% ~* O% m; m) e6 e2 M" s6 I
  (3)数据类型。VC++具有许多独有的数据类型,如__int16,__int32,TRUE,SOCKET等,gcc编译器不支持它们。通常做法是需要将windows.h和basetypes.h中对这些数据进行定义的语句复制到一个头文件中,再在Linux中包含这个头文件。例如将套接字的类型为SOCKET改为int。# q4 T5 ?$ A! H

2 @4 U4 k2 D9 m1 O# c5 w  (4)关键字。VC++中具有许多标准C中所没有采用的关键字,如BOOL,BYTE,DWORD,__asm等,通常在为了移植方便,尽量不使用它们,如果实在无法避免可以采用#ifdef 和#endif为LINUX和WINDOWS编写两个版本。2 [/ R3 k7 J) R4 q! u/ O3 f' p3 G
  (5)函数原型的修改。通常如果采用标准的C/C++语言编写的动态库,基本上不用再重新编写函数,但对于系统调用函数,由于两种系统的区别,需要改变函数的调用方式等,如在Linux编制的网络通信动态库中,用close()函数代替windows操作系统下的closesocket()函数来关闭套接字。另外在Linux下没有文件句柄,要打开文件可用open和fopen函数,具体这两个函数的用法可参考文献[2]。
! K, J+ ?( H- `2 K7 E, m4 e' Y: T3 D
( f+ A9 c4 g+ n. p6 Y  (6)makefile的编写。在windows下面通常由VC++编译器来负责调试,但gcc需要自己动手编写makefile文件,也可以参照VC++生成的makefile文件。对于动态库移植,编译动态库时需要加入-shared选项。对于采用数学函数,如幂级数的程序,在调用动态库是,需要加入-lm。
5 m, W4 l4 d# a' h
2 W" W7 r7 W4 I1 J1 e  (7)其它一些需要注意的地方
8 v3 }# J( R+ N
+ ^$ S4 a, c% y: r. J, \" y* P  ①程序设计结构分析,对于移植它人编写的动态库程序,程序结构分析是必不可少的步骤,通常在动态库程序中,不会包含界面等操作,所以相对容易一些。: Q+ g3 t" \5 R
  ②在Linux中,对文件或目录的权限分为拥有者、群组、其它。所以在存取文件时,要注意对文件是读还是写操作,如果是对文件进行写操作,要注意修改文件或目录的权限,否则无法对文件进行写。
' z& F; u) ~8 R+ c8 q: e  ]  h  ③指针的使用,定义一个指针只给它分配四个字节的内存,如果要对指针所指向的变量赋值,必须用malloc函数为它分配内存或不把它定义为指针而定义为变量即可,这点在linux下面比windows编译严格。同样结构不能在函数中传值,如果要在函数中进行结构传值,必须把函数中的结构定义为结构指针。# G6 K+ ^- Q6 z, U
  ④路径标识符,在Linux下是“/”,在Windows下是“\”,注意windows和Linux的对动态库搜索路径的不同。7 w6 w4 G( H% x8 T& A4 O5 b) t
  ⑤编程和调试技巧方面。对不同的调试环境有不同的调试技巧,在这里不多叙述。
( P4 h8 _' l' c  o: x" Q- R* g! l$ C' M# S( c# w1 p" C
  5、结束语* E# {3 z4 P$ x" r" F- d9 r- \
, ?! H; ~% R" A6 r' y6 U" W
  本文系统分析了windows和Linux动态库实现和使用方式,从程序编写、编译、调用以及对操作系统依赖等方面综合分析比较了这两种调用方式的不同之处,根据实际程序移植经验,给出了将VC++编制的Windows动态库移植到Linux下的方法以及需要注意的问题,同时并给出了程序示例片断,实际在程序移植过程中,由于系统的设计等方面,可能移植起来需要注意的方面远比上面复杂,本文通过总结归纳进而为不同操作系统程序移植提供了有意的经验和技巧。      
上帝说,有问题,找GOOGLE 写程序是很神圣的事情!同样只是装系统,卖菜的大娘会的事情不见得就跟卖菜一样了。

TOP

发新话题