找回密码
 立即注册
  linux文件I/O用:open、read、write、lseek以及close函数实现了文件的打开、读写等基本操作。fcntl函数可以根据文件描述词来操作文件。5 N. \! S$ |" `8 B, t! i( i; x8 w

  |1 f. \: s3 m$ y3 P- T: Q! w  用法:
. \: g: G# X/ u0 I
" l0 i, g1 V2 o' E: Z+ }& o  intfcntl(intfd,intcmd);# O8 g+ z. N' r" j4 U2 e+ P

, B& R9 J, {$ M! d- {) [  intfcntl(intfd,intcmd,longarg);
" X$ Z, q3 S+ D  ~9 M6 Z. E% N
; a2 Y3 i* w9 |& r- |" ~/ U/ H' @/ ~5 q  intfcntl(intfd,intcmd,structflock*lock);& `! S: m; u. g7 u/ m# m

$ D8 b2 ^- w8 m4 y  参数:
" F2 ^: S+ |; z) o ) w* {3 P0 ^" h2 `  m1 n
  fd:文件描述词。
4 K+ @9 _6 O* D. j) U
8 g1 n. P+ ~4 o" L0 P  cmd:操作命令。
3 r; O# Z8 q) B" m9 Y* x* c
1 M* a% b) z; H  arg:供命令使用的参数。; T6 O. J' b: D; J; f' a7 M
2 i" y& k4 W) d. e3 S0 t! B
  lock:同上。3 q0 M9 l# u$ Y" j& t0 h/ b+ {  O
1 ?% D. H/ z3 `/ ?4 E
  有以下操作命令可供使用
  D$ Q& c( a8 g: w. L- j5 i
! |# c! D# o' J. [  1.F_DUPFD:复制文件描述词。; `1 I% L  j4 _# J6 p

$ G2 g* s1 S; c. i' |! Q  2.FD_CLOEXEC:设置close-on-exec标志。如果FD_CLOEXEC位是0,执行execve的过程中,文件保持打开。反之则关闭。
8 F# ]+ C) d9 x2 ]) d9 v+ F
# Y6 d! K; S3 u# Q1 K: E  3.F_GETFD:读取文件描述词标志。% a# l" N& O6 ?  O7 f' A8 ?5 c4 W0 `- w: m

* M$ T, W% {) L' n- j. z, f: S  4.F_SETFD:设置文件描述词标志。
  M# }8 f+ |2 m$ |   p$ ^" d/ U1 n$ D8 M
  5.F_GETFL:读取文件状态标志。
. t  f/ B" h) C5 N0 u% u  O
# V! o$ Y3 E+ [3 P  6.F_SETFL:设置文件状态标志。
3 _+ V. R( F) V5 N( Q% |
. z4 Z  X, w& h1 ]# _6 e  其中O_RDONLY,O_WRONLY,O_RDWR,O_CREAT,O_EXCL,O_NOCTTY和O_TRUNC不受影响,
3 C9 I: V& U) S2 X
% r1 F3 U% c# c* n0 Z* ?3 w  可以更改的标志有O_APPEND,O_ASYNC,O_DIRECT,O_NOATIME和O_NONBLOCK。) p. J, J' L6 a9 v; k

  u0 u/ g' u9 X7 j  7.F_GETLK,F_SETLK和F_SETLKW:获取,释放或测试记录锁,使用到的参数是以下结构体指针:/ ?1 h* t7 y( R( B& |
% y; b7 V( ~+ F4 g
  F_SETLK:在指定的字节范围获取锁(F_RDLCK,F_WRLCK)或者释放锁(F_UNLCK)。如果与另一个进程的锁操作发生冲突,返回-1并将errno设置为EACCES或EAGAIN。& M9 s  }9 `7 u& H' {) p" p

( F( Q/ R! s8 S) Y( P4 G# H( Q0 @  F_SETLKW:行为如同F_SETLK,除了不能获取锁时会睡眠等待外。如果在等待的过程中接收到信号,会立即返回并将errno置为EINTR。- `1 U& H# j6 u

9 I  [5 p4 G% k5 y# p  E7 P  F_GETLK:获取文件锁信息。* X& ~9 ]2 b8 O6 Q6 n2 w2 A
; @; A6 i  a& U8 s" }
  F_UNLCK:释放文件锁。
- p$ Y7 W$ v7 _5 B5 u: i 4 ^: E! P# w2 V$ ]) k; b
  为了设置读锁,文件必须以读的方式打开。为了设置写锁,文件必须以写的方式打开。为了设置读写锁,文件必须以读写的方式打开。* [, e, t  h. o4 [3 E9 j
6 \( C( k1 \- q/ C3 J* a1 V" M5 _
  8.信号管理6 w  k$ ~* P7 h3 y  x

8 c: s9 r' Q: `2 r6 \$ g  F_GETOWN,F_SETOWN,F_GETSIG和F_SETSIG被用于IO可获取的信号。
: `/ v5 q  O- F' ~
" d" K, O+ F) T% N" K  F_GETOWN:获取当前在文件描述词fd上接收到SIGIO或SIGURG事件信号的进程或进程组标识。
& M  u: G! F. A' c! z' y; \
( l2 \3 f1 |0 E4 M) K% f1 t  F_SETOWN:设置将要在文件描述词fd上接收SIGIO或SIGURG事件信号的进程或进程组标识。7 m; u: A8 w' ^- M5 O

8 v  j( @0 i+ K: J: p  F_GETSIG:获取标识输入输出可进行的信号。
8 D9 ]. W% s# [0 E% g$ i # j5 U9 c' J% f% q- c
  F_SETSIG:设置标识输入输出可进行的信号。
; A/ {4 Y! i3 M, R6 Z+ W% f* \+ S* q8 G
& ~3 r5 d, l5 T5 R  使用以上命令,大部分时间程序无须使用select()或poll()即可实现完整的异步I/O。
) {4 g2 h( B* k" F+ k4 k+ T 0 \* C6 ?+ I- l& k
  9.租约(Leases)
+ n  ?  ]1 i( y2 ] : S1 D3 T) V$ t8 j' K
  F_SETLEASE和F_GETLEASE被用于当前进程在文件上的租约。文件租约提供当一个进程试图打开或折断文件内容时,拥有文件租约的进程将会被通告的机制。" R) G! W& X9 f, }1 P" O2 X

0 W# t0 G" R2 o+ L8 j& J  F_SETLEASE:根据以下符号值设置或者删除文件租约
, D) ^4 `" S6 c
) i: N8 T  b0 K+ T  F_RDLCK设置读租约,当文件由另一个进程以写的方式打开或折断内容时,拥有租约的当前进程会被通告。4 ^" D2 A  r. y+ H
  |+ H! `/ r8 s
  F_WRLCK设置写租约,当文件由另一个进程以读或以写的方式打开或折断内容时,拥有租约的当前进程会被通告。2 Y1 v. Z0 P$ D7 p

8 ~* @; x5 j, B+ I8 ]2 i! Y  F_UNLCK删除文件租约。
  g& A) @# [* T1 { ) T6 C2 _2 E! ^8 D% A2 K' A
  F_GETLEASE:获取租约类型。: C/ y/ ]0 M7 \% Y/ R- G3 f

) x6 |5 X+ u% o( ?  10.文件或目录改变通告5 H% a# x  X! Y" m6 J9 ^* h
( c8 ^4 G! w. ]0 G1 ]9 Z
  (linux2.4以上)当fd索引的目录或目录中所包含的某一文件发生变化时,将会向进程发出通告。arg参数指定的通告事件有以下,两个或多个值可以通过或运算组合。4 J7 J/ k6 L( i% G& ]

8 ]7 G1 y6 x7 k! u  1.DN_ACCESS文件被访问(read,pread,readv)
  H1 K0 Z9 @3 S3 R
/ l2 @6 g5 U3 \4 s1 S$ Z2 T! c  2.DN_MODIFY文件被修改(write,pwrite,writev,truncate,ftruncate)
+ i! b' r5 z8 W: ^8 r   e. @* c0 e: K& j2 _
  3.DN_CREATE文件被建立(open,creat,mknod,mkdir,link,symlink,rename)
& N5 I- D) R0 Z( ]' N' ^$ D   _* v& P# W: d* Z, S! O
  4.DN_DELETE文件被删除(unlink,rmdir)
. z9 D! g0 `7 F0 [; C1 D" M- [% ] 1 K, S6 z5 `% \& r& H! P' v
  5.DN_RENAME文件被重命名(rename). A' R' a' t, ~' u1 W9 w$ ?( q

+ F" P$ s) D$ p  6.DN_ATTRIB文件属性被改变(chown,chmod,utime)9 f" {; i$ m6 I0 O7 C: i' _- K+ ?- i

- n) q1 h9 [% p+ C# `* L( Y  返回说明:
/ x, s& [8 s; m7 Q
, J1 o& x* ^2 r- m  成功执行时,对于不同的操作,有不同的返回值
# v, E6 ]. U' \  O
( Z/ @* _0 V0 V3 T; X  F_DUPFD:新文件描述词8 p4 g$ O8 O8 ^% R7 F5 Z
5 m6 y+ L6 ^. a. d( ]( T3 t
  F_GETFD:标志值
, Q" B, L4 W9 e# f   ^' u8 Z0 {9 s8 z
  F_GETFL:标志值
5 n1 G7 F! S6 G0 u3 Q ' J7 ^0 W5 O' h4 d  B: f  [
  F_GETOWN:文件描述词属主
2 j2 J/ ^+ U1 {4 H( a) K ' n5 C; B0 @5 N. ?: w
  F_GETSIG:读写变得可行时将要发送的通告信号,或者0对于传统的SIGIO行为
& |; {( s- I$ d3 ] % x8 t% Z% e  B1 X" D
  对于其它命令返回0。
5 ?: q$ T" j3 D2 w
) a- ]5 |1 d; o7 N8 }/ @$ p3 @: X  失败返回-1,errno被设为以下的某个值
5 M- M% U2 x: v; ] / Q$ L" Q1 A  s& \8 S* [8 l) P
  EACCES/EAGAIN:操作不被允许,尚未可行/ Z7 h% R/ D* \0 G% I8 C
6 }7 k9 U5 z3 I
  EBADF:文件描述词无效
+ x0 S: y: t- b/ A 8 _: i7 ]+ L: E/ I# O
  EDEADLK:探测到可能会发生死锁
* A6 M, l6 m% D+ D , Z" Y* }/ v) y& B& m% y0 R
  EFAULT:锁操作发生在可访问的地址空间外
/ P: ^# o1 w6 B9 ^; \) T" D# e
/ ^3 ~3 ~+ @  q8 l9 L8 Y  EINTR:操作被信号中断
* Z* p6 s7 b  E- b/ ` ' N  n) u8 Q: x& C6 C
  EINVAL:参数无效
3 ~/ p# @' v7 _* X# O5 F
/ V" ^% a! M/ p- V$ ?+ `  EMFILE:进程已超出文件的最大可使用范围
& Q9 \+ Y. w0 l1 _' Q1 N% p; T2 n2 c, s4 A* U 1 k# g( ^. v, O- P6 Y9 ~& R
  ENOLCK:锁已被用尽
* U2 N+ V/ |/ G$ M' [  K4 {
5 k5 u  D: k# q  EPERM:权能不允许) Z# y( ]1 X8 H' E9 U6 s/ K9 c
; p  W4 m, q$ G+ M9 v2 M/ e
  在文件已经共享的情况下如何操作,也就是当多个用户共同使用、操作一个文件时,Linux通常采用的方法是给文件上锁,来避免共享的资源产生竞争的状态。
7 V* O" Y0 {4 |6 y9 o4 _3 A ; v2 h1 q7 u/ r
  文件锁包括建议性锁和强制性锁。
2 S/ s' b. M& ?% W3 x! r . f' v9 K' u( R
  建议性锁要求每个上锁文件的进程都要检查是否有锁存,并且尊重已有的锁。在一般情况下,内核和系统都不使用建议性锁。强制性锁是由内核执行的锁,当一个文件被上锁进行写入操作的时候,内核将阻止其他任何文件对其进行读写操作。采用强制性锁对性能的影响很大,每次读写操作都必须检查是否有锁存在。1 q, u( x6 A- k! X0 o$ ?6 K
! |+ h$ w" V, W7 Y2 h: M( }6 a
  在Linux中,实现文件上锁的函数有lock和fcntl,其中flock用于对文件施加建议性锁,而fcntl不仅可以施加建议性锁,还可以施加强制锁。同时,fcntl还能对文件的某一记录进行上锁,也就是记录锁。) }. g; l" W* H3 @+ Y

& U0 g- u7 r8 S% r. q  记录锁又可分为读取锁和写入锁,其中读取锁又称为共享锁,它能够使多个进程都能在文件的同一部分建立读取锁。而写入锁又称为排斥锁,在任何时刻只能有一个进程在文件的某个部分上建立写入锁。当然,在文件的同一部分不能同时建立读取锁和写入锁。' H; A# A8 a$ x" Y4 X+ Q" `
' s# r1 n6 n. w5 i8 Y
  注意:
" y' i3 g0 ?+ @3 `: a+ _3 W  r/ d4 U
  _* e( t1 k$ j: S. w0 i, u1 c2 Z  fcntl是一个非常通用的函数,它还可以改变文件进程各方面的属性,在本节中,主要介绍它建立记录锁的方法,关于它其他用户感兴趣的读者可以参看fcntl手册。
. z8 K8 G& x% j 7 p7 Y, h& a3 y# L
  下面首先给出了使用fcntl函数的文件记录锁函数。在该函数中,首先给flock结构体的对应位赋予相应的值。接着使用两次fcntl函数分别用于给相关文件上锁和判断文件是否可以上锁,这里用到的cmd值分别为F_SETLK和F_GETLK。: E6 S7 {, F4 ?2 b- B! {

! n" ?' @9 w3 o) {6 u9 O% t6 b  这个函数的源代码如下所示:" ?- o& s% N! q2 j: @+ J
! K' y- z2 ~: Z- t1 b- a
  /*lock_set函数*/4 O% y9 I: C, J" M8 F
1 U( ?2 `6 v0 u  {
  voidlock_set(intfd,inttype)- {. C$ U' q* J/ i9 H
* ~9 x3 }. F7 j4 W
  {
3 w' H7 u& N# S , R- [: t; t0 _
  structflocklock;
2 d$ P5 C) v1 P
0 y3 e5 J" i& ^6 _$ F, |- ~2 B/ p$ F  lock.l_whence=SEEK_SET;//赋值lock结构体
9 T% _$ M$ M- x6 P, J 3 j) G) }' v4 P- f
  lock.l_start=0;4 l8 i2 {) K# Y3 ]# c

8 H1 ]( \/ N# n9 T8 |  lock.l_len=0;. a6 ~0 S* Q+ `! e: j# N

! E( z' Q- O* {2 |  while(1){: h4 n: H4 d* @5 V$ `& ~

& n" {8 k6 `% j5 V& Q$ J; j# {  lock.l_type=type;
6 L/ V6 f2 p% g, o# c1 j/ U+ y# l
. b/ a0 M) L# x( D: Z( ^! a  /*根据不同的type值给文件上锁或解锁*/& h  \0 Q$ \; \
6 q% \4 L7 x9 K4 t* k, m( a
  if((fcntl(fd,F_SETLK,&lock))==0){
1 \; R1 |2 F6 I! I# x. d 7 U* B$ B  x) I( }9 |0 F* z* k
  if(lock.l_type==F_RDLCK)
2 ^: P% {# Z6 @3 Z6 K
$ v% D9 w( H1 y( t) ~% ~3 L/ E  printf("readlocksetby%d\n",getpid());6 o: T/ P+ B  L2 \& `# a9 y) c
$ o0 h% h, B* A" l4 a: {5 Z
  elseif(lock.l_type==F_WRLCK)
1 U: {3 [9 f$ O+ N, |
0 |7 Y; }9 L* {- b' d6 y. n  printf("writelocksetby%d\n",getpid());
4 ?+ |) V) [1 f2 q3 j2 n8 z$ a* {
, |# v! |7 b; u8 n- Z: ^  elseif(lock.l_type==F_UNLCK)& U, ?; m* k" V- q
% T% D( d( ?9 H9 a' r1 c0 ]
  printf("releaselockby%d\n",getpid());- N8 [. p7 Y& H+ q* M9 k0 ?

0 |" B" C' u- N4 m" L! i; K  return;( J# L) G; y: b3 ~, a

  ?- K/ r7 {1 s( g& h  }
1 Y+ Q9 r! \0 j7 K
0 ~4 r) N& j* ?  /*判断文件是否可以上锁*/
4 Z) Y7 _1 \0 H6 u# m' i 1 @! J- e8 q9 g7 G& }8 R) u
  fcntl(fd,F_GETLK,&lock);/ W; i/ ?. k+ s* u* \, `

8 W. j/ a9 t  G/ t. u4 x  /*判断文件不能上锁的原因*/+ L' r2 |9 F6 [$ v$ u
) f* f0 h0 o) ]) [
  if(lock.l_type!=F_UNLCK){; q# q( H8 _1 E/ Y5 _9 H; |
0 B( N. m' m# Q- Z# \0 y
  /*/该文件已有写入锁*/
# R0 ?  `  ~5 \5 b. L- c. u
6 K+ D5 }2 j3 [9 \  if(lock.l_type==F_RDLCK)* s. ?2 x; B" r. D) ]8 `

' ~9 F# H  F5 E; `8 s+ s  printf("readlockalreadysetby%d\n",lock.l_pid);5 }- g; w) c1 ]' w# z
1 X  q, P4 j; n. Q9 X
  /*该文件已有读取锁*/2 x. H3 {4 w" Q- ]9 ^" P: X( `

) z. ^  H" `0 Q+ \  elseif(lock.l_type==F_WRLCK)
6 d( a& t0 M6 ]% y' k! b7 p
" ?* M& C4 I8 M& B9 Z  printf("writelockalreadysetby%d\n",lock.l_pid);
, s5 u; J8 y/ |9 o! D+ c- W - P8 m7 K5 s$ |& s, Z" g3 g
  getchar();! B( q) W5 H3 O+ ~  G* F

% X( r. G0 M$ ^0 U  }. `! r* z$ D( f$ _7 x2 k, |9 R0 K& n8 t# B

' m3 C  p1 i. n: H  }
' T! \* l2 j8 Q+ U . r2 l# S" u# h7 a6 N+ y
  }2 f, l0 y9 l6 x9 `. Q! d
# c: S* _4 U% G, O+ {2 f
  下面的实例是测试文件的写入锁,这里首先创建了一个hello文件,之后对其上写入锁,最后释放写入锁。代码如下所示:
5 u* c! ^& E$ q  Z
. S. m3 r5 ?, o% k  t  /*fcntl_write.c测试文件写入锁主函数部分*/
$ q, l& F' R$ R. F" Y1 g2 g
. l. e& \6 G" o$ u0 ^5 c  #include<unistd.h>, r/ }% r* N. C+ Z

9 \3 k4 j% c& ]  \! j4 B& y: v  #include<sys/file.h>
' \- j7 W( \# m5 f2 }( h! @
9 Z2 U0 N2 {* m* q( L2 J  #include<sys/types.h>1 r+ L( \+ i/ m+ q$ h- K* C' }

1 r* J! s, Y$ p% [* a  #include<sys/stat.h>; c- y! e4 K4 {

$ K4 U. h  y6 b4 [8 K  #include<stdio.h>
  W  _. L  x/ k4 k- ^8 B
9 H( u2 f7 N) p" J, ?2 D* `  #include<stdlib.h>
% v  B1 ~, F3 [' i5 f9 l* L 6 i9 W2 j% A+ P& Q; T/ @
  intmain(void)
; e. ]; a6 }  f" O' |& i & ^$ w! Y6 a1 {+ G; s5 _3 c
  {+ d, J" ~! D5 _+ N) V& t
' m5 }8 {- E2 b% [. l1 S
  intfd;( w" N8 N7 Y; ^- C; n8 M1 P

: @7 ^% E2 Z1 q* z  /*首先打开文件*/
1 Z/ E, I# ^$ g + ~9 {$ c' e9 Q% E1 {( J1 i
  fd=open("hello",O_RDWR|O_CREAT,0666);
3 j2 N3 f1 v2 d2 E" \
7 @+ i2 K) r1 f0 d  if(fd<0){
, U$ q2 l5 r8 Q  L1 ?
$ j4 z6 ?4 G& W: u  perror("open");! m3 r) R/ w! e' Z3 l, T4 r, F: C

' W# t& c: K: ?5 u% o( \, ^  exit(1);" f$ x3 f( R* U# N; I2 P
% f( S* o7 ^  R; r0 e2 {+ ^) ]5 R
  }5 u) k- r2 w4 G
3 T3 ~% s  b% L2 ?
  /*给文件上写入锁*/
9 P! _3 i. k: T" q" t2 b
7 l  G6 n8 a+ T, l  lock_set(fd,F_WRLCK);* N4 }* [' C. }
! e+ r& o0 Z- |1 e7 k4 E( [' e
  getchar();
4 m" [9 X7 x0 {- U7 Q7 F* n  g % k2 {* [' v# T) s4 D
  /*给文件接锁*/
4 b' o4 H8 D3 ~9 U$ p
7 y5 C7 a6 |) O! k  ]  lock_set(fd,F_UNLCK);
+ i) U* r" H7 a" s# F ! F: z: I( l0 P1 I( K& J& h
  getchar();
. N' G* I9 E5 q4 X 5 d6 k) G7 p- f3 G( f, Y* Z+ P- ]" |
  close(fd);
6 M& @7 ^% y& O- e/ F3 \" H+ ? 9 g$ S0 N6 J$ m
  exit(0);
4 k! t; R% V4 f! u
: f6 i5 @$ B  b  }4 S( p* B* W$ T

7 p$ m( z  j# ^+ ~9 s. I  接下来的程序是测试文件的读取锁,原理同上面的程序一样。
% C8 H7 F! I/ b5 F. s
4 t( g+ K3 O& v. s  /*fcntl_read.c测试文件读取锁主函数部分*/
/ @7 i1 E, y# I* h
' w8 m5 Z2 m' Y5 N( ]  #include<unistd.h>9 j' p0 Z$ l$ J+ N, M
, y$ ~# |. \/ l+ [- T6 o
  #include<sys/file.h>
/ }6 G5 n; M) R# ]6 V
3 l7 i" V6 T+ U0 A0 R7 H  #include<sys/types.h>
" b6 Y2 \8 a/ L: P1 P) ~& [
0 `9 c0 R" S+ Q4 P  #include<sys/stat.h>1 f4 Q2 U. T7 O/ r) I: Y+ k. m' w

/ i- R8 O6 O  Q/ `; E  #include<stdio.h>* _* {5 @0 a0 A# V: Y" ~; ]% g

. t/ y4 }8 N: y5 N  \  @  #include<stdlib.h>
7 ]* m' f* d4 W4 M# X
. J) S7 p' }/ A# L! s  intmain(void)- J( ]+ i+ E# M6 K' o

* r8 s& p. H' y6 s$ B- m3 Y1 o  {( z7 A2 N: e" g2 g9 v. Y

" F: n# m5 u9 h% e; J* U  intfd;, Y; q3 n! k9 i6 ~
  j7 g1 _0 y8 b% k! q2 f" G
  fd=open("hello",O_RDWR|O_CREAT,0666);
  R" C7 @0 v0 n, @" u0 `% A: O 9 O: W# U8 {4 M6 G  y
  if(fd<0){! |, q' i* B+ s1 X- @3 Q8 ?

6 ~- K4 f2 j) f0 C+ B  perror("open");2 E, a/ s' E, l' G4 g0 x3 t8 F
7 O  }' \- @# C0 w7 Y) r. V
  exit(1);, ]( Z  ~" i5 T, {4 A. Z4 Q

+ ]6 x5 l* X& C# R* u3 B" Y  }. g! @  q" s+ k. d7 n2 G
5 ~/ C7 g- l8 K. n0 f( ?! F
  /*给文件上读取锁*/
' O' x; R+ ^# S; @  D4 \1 n) v
# c; t8 z, ~0 q. D) A  lock_set(fd,F_RDLCK);8 f1 U' i/ s  V$ Q/ G
5 n( q3 |; ?( x9 q8 Q
  getchar();0 u) P% l3 T) M( I0 L5 |/ f* R
4 j1 T# I6 A' U# G; t
  /*给文件接锁*/
. }- n( E4 H: n; q2 c6 V 2 K* f9 s+ O9 ]& B3 ]0 I
  lock_set(fd,F_UNLCK);
# e9 R0 E  m3 ]# T $ R% c1 ~) }6 T+ X: T. v# C+ h
  getchar();8 c" t; k) b/ [
% [; y$ Z" A1 S; R. |" C' w( ]  o
  close(fd);
5 M4 U6 d! ^9 t' _6 g/ K 1 X1 Q  N# J. ^# I, T, D, T! h7 S9 A
  exit(0);
7 v& M, ], q' u6 h" A1 V4 K, F ) A8 l  g# Q: i3 Z: F/ z' [8 Y
  }

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
收藏
[url=https://jq.qq.com/?_wv=1027&k=55PJPtJ][/url]

0 个回复

您需要登录后才可以回帖 登录 | 立即注册