发新话题
打印

linux操作系统下c语言编程入门

linux操作系统下c语言编程入门

整理编写:007xiong
/ _2 z* \/ Y3 S, u6 h原文:Hoyt等
/ Z, t# F0 R$ d) j
  I0 W' w' b7 V: ?8 O(一)目录介绍
) U  \8 M5 C% j* e9 Z/ n9 J' n8 b/ ]* q3 B# Y. k" X
1)Linux程序设计入门--基础知识
. i- j" f# u' b4 n6 s* w. a+ J4 J5 G2)Linux程序设计入门--进程介绍; y- V2 x+ S* M/ G6 M
3)Linux程序设计入门--文件操作
0 X, r$ [1 b3 a7 m2 _7 U. K3 g1 B4)Linux程序设计入门--时间概念+ H6 I, w( t3 F8 M3 s# l
5)Linux程序设计入门--信号处理
6 w# X2 O  p6 S7 P5 V3 T6)Linux程序设计入门--消息管理. W' [" M/ b' \9 ]4 b5 w" D
7)Linux程序设计入门--线程操作
$ J/ g' M& |) R8)Linux程序设计入门--网络编程
3 N3 F7 L1 Q/ W/ K9)Linux下C开发工具介绍 0 C- I& o/ |4 @! q) h. k
3 z( f! w# c! v4 }' f8 A& D7 @
1)Linux程序设计入门--基础知识2 y) ^' R3 d$ [
Linux下C语言编程基础知识+ C8 ~. r, z7 b
前言:
/ Y4 m0 J1 \& Q! e% g这篇文章介绍在LINUX下进行C语言编程所需要的基础知识.在这篇文章当中,我们将9 L4 y! q0 D0 H6 l- a( g+ W  X% a
会学到以下内容:" x/ P5 p! t! K" n+ G
   源程序编译
8 h$ G( P& U3 i8 i) I; R   Makefile的编写
# ?) O: C; K. S3 V" x# ^% t   程序库的链接
, m0 q: W' Y+ ^   程序的调试
" L# X, E7 ~5 |7 G2 r   头文件和系统求助
/ L' m5 a/ Z, e- ~9 ^/ k. c2 H5 I----------------------------------------------------------------------------
1 C* V* z7 n5 [% c' I" i----/ Q2 ]/ Z2 M4 ~" B! x6 I- `
1.源程序的编译+ r9 }2 K3 ]2 d7 ^
在Linux下面,如果要编译一个C语言源程序,我们要使用GNU的gcc编译器. 下面我们以一个实例来说明如何使用gcc编译器.
; g2 D4 A  e# g假设我们有下面一个非常简单的源程序(hello.c):
5 J( X" D; Q$ S& M, Yint main(int argc,char **argv)
8 ]7 x% X- N5 H{4 [( F' L" C0 H3 X$ A+ j
printf("Hello Linux\n");
4 R$ `6 Z9 V% J}
$ |# i$ ~, U) l1 \要编译这个程序,我们只要在命令行下执行:1 n* l( D  b4 G( I3 [
gcc -o hello hello.c0 `& ~' v4 f+ r2 c* D) t
gcc 编译器就会为我们生成一个hello的可执行文件.执行./hello就可以看到程序的输出结果了.命令行中 gcc表示我们是用gcc来编译我们的源程序,-o 选项表示我们要求编译器给我们输出的可执行文件名为hello 而hello.c是我们的源程序文件.! ~6 `3 Q9 e) U( _0 _+ N  L( r
gcc编译器有许多选项,一般来说我们只要知道其中的几个就够了. -o选项我们已经知道了,表示我们要求输出的可执行文件名. -c选项表示我们只要求编译器输出目标代码,而不必要输出可执行文件. -g选项表示我们要求编译器在编译的时候提供我们以后对程序进行调试的信息.知道了这三个选项,我们就可以编译我们自己所写的简单的源程序了,如果你想要知道更多的选项,可以查看gcc的帮助文档,那里有着许多对其它选项的详细说明.
* h, J; ?" R/ T. w) Y
, {5 f0 [  p4 g& m) x) [2.Makefile的编写- L' `  q2 h; ^
假设我们有下面这样的一个程序,源代码如下:
; V0 t7 _( D2 w; ~: v$ W5 [; n/* main.c */
% y, a" Y2 e( G( Q: I7 R#include "mytool1.h"+ a- Y& }7 y% g: {) H+ q
#include "mytool2.h"
  F1 g$ \& P" c$ i5 U" _int main(int argc,char **argv)
, K4 u  h- X( Q{
" p9 ?0 q3 `$ xmytool1_print("hello");/ J7 F# Q3 x2 _# ~% ~
mytool2_print("hello");$ S. h, `( l0 S* a0 B$ }. J
}
' W& R2 y( r2 I/* mytool1.h */
4 ^3 V+ j1 D% u9 L- A#ifndef _MYTOOL_1_H8 x! ~- O( p" `, s  {. i
#define _MYTOOL_1_H
! i7 y3 E7 r! f( A! G# kvoid mytool1_print(char *print_str);
2 `3 m8 ~" U* n7 Y1 \#endif" I7 c  G2 i5 o  K( S7 O
/* mytool1.c */1 S- C. w) Q( S- b7 w
#include "mytool1.h"
$ O1 h: Y5 l1 K8 M& x' f+ E6 E0 Fvoid mytool1_print(char *print_str)! a) w" c: b# R, q! P+ Y
{+ l. _0 H% i& U, K7 {8 V( `
printf("This is mytool1 print %s\n",print_str);
, L' x! ]* n+ j/ ~+ x/ v7 \+ |}! d' a2 F; H3 |4 T5 Z3 N9 C
/* mytool2.h */
9 p; @% K# U$ X$ G; i#ifndef _MYTOOL_2_H$ F$ l$ t" |9 h5 V! i
#define _MYTOOL_2_H# s1 \) h6 s: y- j& _7 {: e4 J% g
void mytool2_print(char *print_str);
/ V. l* H9 k7 j7 g: n, m/ ]#endif
3 |* M7 e9 H2 Z7 v/* mytool2.c */
/ V5 x2 J% X. B8 T* U#include "mytool2.h"
/ d- H- d! @" O0 j1 T, B8 g5 l& @void mytool2_print(char *print_str)2 v" I$ N8 a8 u4 V  E
{6 P  E' y: }' s0 x" f$ z5 B) t
printf("This is mytool2 print %s\n",print_str);+ _& Z" e6 r: ]( y; t7 Q
}
* x$ e% `, p$ Y/ z0 |, M1 s, Q. C当然由于这个程序是很短的我们可以这样来编译
* V2 q- l2 G  w2 o- ]gcc -c main.c* Z7 s( m  j  b+ p5 ?  N' l' g- |
gcc -c mytool1.c
% `. s& G. p& m+ Q3 A4 Qgcc -c mytool2.c  A# v" x( S$ l, Y# s- t- a4 m
gcc -o main main.o mytool1.o mytool2.o
' w+ p* g7 [3 W  o5 Y这样的话我们也可以产生main程序,而且也不时很麻烦.但是如果我们考虑一下如果有一天我们修改了其中的一个文件(比如说mytool1.c)那么我们难道还要重新输入上面的命令?也许你会说,这个很容易解决啊,我写一个SHELL脚本,让她帮我去完成不就可以了.是的对于这个程序来说,是可以起到作用的.但是当我们把事情想的更复杂一点,如果我们的程序有几百个源程序的时候,难道也要编译器重新一个一个的去编译?为此,聪明的程序员们想出了一个很好的工具来做这件事情,这就是make.我们只要执行以下make,就可以把上面的问题解决掉.在我们执行make之前,我们要先编写一个非常重要的文件.--Makefile.对于上面的那个程序来说,可能的一个Makefile的文件是:  Q1 ?; }! {2 l" r1 a" a( `: e
# 这是上面那个程序的Makefile文件
: p) W# Q: T5 c5 c4 X5 M+ U- K8 U0 rmain:main.o mytool1.o mytool2.o2 O+ }, K- ~# w2 p) s
gcc -o main main.o mytool1.o mytool2.o
7 S1 O/ M/ z8 {8 L, g3 `main.o:main.c mytool1.h mytool2.h
7 k: j. n, _/ G' ?, q3 C! qgcc -c main.c
8 d( y. B+ }/ I6 hmytool1.o:mytool1.c mytool1.h9 X/ O  {5 \& H
gcc -c mytool1.c* y; L" P% Q/ h, p
mytool2.o:mytool2.c mytool2.h$ ^$ F$ N$ P+ G3 D7 F" O' M
gcc -c mytool2.c
+ E5 X  W3 d! `1 g有了这个Makefile文件,不过我们什么时候修改了源程序当中的什么文件,我们只要执行make命令,我们的编译器都只会去编译和我们修改的文件有关的文件,其它的文件她连理都不想去理的.
& u$ g  x9 G% ~$ Y* h下面我们学习Makefile是如何编写的.
; U: s' {8 ?! A& P2 }5 c' h+ d$ O/ S在Makefile中也#开始的行都是注释行.Makefile中最重要的是描述文件的依赖关系的说明.一般的格式是:
' ]1 d9 ~: L6 y0 Y% h. C+ a+ Mtarget: components
5 z" v( q3 i. E* V, aTAB rule
0 B0 D  m/ a( ^, r; u4 a6 W2 f第一行表示的是依赖关系.第二行是规则.比如说我们上面的那个Makefile文件的第二行
4 H' i2 w: T* v) e! gmain:main.o mytool1.o mytool2.o  m1 |2 i9 J* e0 W3 R
表示我们的目标(target)main的依赖对象(components)是main.o mytool1.o mytool2.o当倚赖的对象在目标修改后修改的话,就要去执行规则一行所指定的命令.就象我们的上面那个Makefile第三行所说的一样要执行 gcc -o main main.o mytool1.o mytool2.o
7 _- T' O5 \+ `+ U# ]0 _: _9 m注意规则一行中的TAB表示那里是一个TAB键
. J8 M/ x/ h2 s) {1 \& MMakefile有三个非常有用的变量.分别是$@,$^,$<代表的意义分别是:4 {+ S' }1 J. F" o4 h3 r4 Q) q, R' `
$@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件." O; }, V1 J0 z
如果我们使用上面三个变量,那么我们可以简化我们的Makefile文件为:1 J) g5 f' q  ]4 `
# 这是简化后的Makefile5 [+ i7 E7 \- g$ P" g
main:main.o mytool1.o mytool2.o9 s$ U: f6 ]) m: x) ^" V/ T: M
gcc -o $@ $^
8 _+ M+ X6 W! p6 L! o# vmain.o:main.c mytool1.h mytool2.h
6 ^6 v* _1 I5 b1 z( W/ x$ m6 Zgcc -c $<
  Z# T3 h' l' q' z( E* c# ymytool1.o:mytool1.c mytool1.h
5 f4 D# s3 e  R' xgcc -c $<, J. ^: q4 G& H0 T9 S" U
mytool2.o:mytool2.c mytool2.h
8 X1 p2 ]% y, q, o% j/ B1 O/ Egcc -c $<
2 f: w7 h0 `1 x) v7 b经过简化后我们的Makefile是简单了一点,不过人们有时候还想简单一点.这里我们学习一个Makefile的缺省规则) T4 h6 Q- B; h' }& e: W: p
..c.o:
  j9 A. ?- C5 }  Q5 ~1 B/ ?' q' Rgcc -c $<
6 y: e6 @/ p2 N8 p8 g这个规则表示所有的 .o文件都是依赖与相应的.c文件的.例如mytool.o依赖于mytool.c
: L9 o6 K% b3 N# X这样Makefile还可以变为:' V* t& {  L; f9 L  _+ H
# 这是再一次简化后的Makefile/ ~' J. W+ ]; {/ S1 h
main:main.o mytool1.o mytool2.o' g9 L  R" G; c! R: N  X% d% |* L
gcc -o $@ $^4 l: D) y1 Z: i# X3 V  B5 ^
..c.o:; l) b7 O7 ]$ M0 B: x0 C9 J; o# V
gcc -c $<, t% V1 D0 ?& m- U7 a; X# K& K
好了,我们的Makefile 也差不多了,如果想知道更多的关于Makefile规则可以查看相应的文档.; n9 w# \& c# a; h# J( N+ m$ S$ q
# g; n$ y4 K; [- P/ `, j
3.程序库的链接
& g- T9 M/ o0 F# |% ~试着编译下面这个程序
" Z3 @" V' c2 i: b( `/* temp.c */
: U) _- d' c8 u; {! o#include <math.h># p) }6 ^  I) e8 A; h9 B
int main(int argc,char **argv)
) C, H. x- q1 t4 q$ q{" G6 g; z. B9 e3 ]! t
double value;
8 S# ]. @7 r8 q% @: l# a% M* z4 Uprintf("Value:%f\n",value);
6 L* z- I2 w& D6 u}% a) W0 h% W. G* k) o3 M% q4 _# }2 [
这个程序相当简单,但是当我们用 gcc -o temp temp.c 编译时会出现下面所示的错误.
4 X9 F, K) W' s) t  C( Y" z
9 K/ t! r; ]: G9 g( I" J( m/tmp/cc33Kydu.o: In function `main':& Z: t: k, @) A
/tmp/cc33Kydu.o(.text+0xe): undefined reference to `log'# c- m3 {# B2 X9 L. b
collect2: ld returned 1 exit status$ T) \5 y, R) l! R
出现这个错误是因为编译器找不到log的具体实现.虽然我们包括了正确的头文件,但是我们在编译的时候还是要连接确定的库.在Linux下,为了使用数学函数,我们必须和数学库连接,为此我们要加入 -lm 选项. gcc -o temp temp.c -lm这样才能够正确的编译.也许有人要问,前面我们用printf函数的时候怎么没有连接库呢?是这样的,对于一些常用的函数的实现,gcc编译器会自动去连接一些常用库,这样我们就没有必要自己去指定了. 有时候我们在编译程序的时候还要指定库的路径,这个时候我们要用到编译器的 -L选项指定路径.比如说我们有一个库在 /home/hoyt/mylib下,这样我们编译的时候还要加上 -L/home/hoyt/mylib.对于一些标准库来说,我们没有必要指出路径.只要它们在起缺省库的路径下就可以了.系统的缺省库的路径/lib /usr/lib /usr/local/lib 在这三个路径下面的库,我们可以不指定路径.
$ X3 O8 M" ]# I2 P, f; |5 C还有一个问题,有时候我们使用了某个函数,但是我们不知道库的名字,这个时候怎么办呢?很抱歉,对于这个问题我也不知道答案,我只有一个傻办法.首先,我到标准库路径下面去找看看有没有和我用的函数相关的库,我就这样找到了线程(thread)函数的库文件(libpthread.a). 当然,如果找不到,只有一个笨方法.比如我要找sin这个函数所在的库. 就只好用 nm -o /lib/*.so|grep sin>~/sin 命令,然后看~/sin文件,到那里面去找了. 在sin文件当中,我会找到这样的一行libm-2.1.2.so:00009fa0 W sin 这样我就知道了sin在libm-2.1.2.so库里面,我用 -lm选项就可以了(去掉前面的lib和后面的版本标志,就剩下m了所以是 -lm). 如果你知道怎么找,请赶快告诉我,我回非常感激的.谢谢!: F8 T8 L& u0 c3 B( g
9 x' G, q( u& K, \, P* Y% N
4.程序的调试0 U0 K- K4 X. q- ^: l9 u6 Z6 [2 C( D
我们编写的程序不太可能一次性就会成功的,在我们的程序当中,会出现许许多多我们想不到的错误,这个时候我们就要对我们的程序进行调试了.最常用的调试软件是gdb.如果你想在图形界面下调试程序,那么你现在可以选择xxgdb.记得要在编译的时候加入 -g选项.关于gdb的使用可以看gdb的帮助文件.由于我没有用过这个软件,所以我也不能够说出如何使用. 不过我不喜欢用gdb.跟踪一个程序是很烦的事情,我一般用在程序当中输出中间变量的值来调试程序的.当然你可以选择自己的办法,没有必要去学别人的.现在有了许多IDE环境,里面已经自己带了调试器了.你可以选择几个试8 t, U7 u4 Z2 R& F
一试找出自己喜欢的一个用.
4 B+ E: k* K3 j3 _5 \- f% h" a4 N/ `8 l; R3 m( x. Z
5.头文件和系统求助- X* \, n/ d# k( y9 y  _
有时候我们只知道一个函数的大概形式,不记得确切的表达式,或者是不记得着函数在那个头文件进行了说明.这个时候我们可以求助系统.比如说我们想知道fread这个函数的确切形式,我们只要执行 man fread 系统就会输出着函数的详细解释的.和这个函数所在的头文件<stdio.h>说明了. 如果我们要write这个函数的说明,当我们执行man write时,输出的结果却不是我们所需要的. 因为我们要的是write这个函数的说明,可是出来的却是write这个命令的说明.为了得到write的函数说明, 我们要用 man 2 write. 2表示我们用的write这个函数是系统调用函数,还有一个我们常用的是3表示函数是C的库函数.! A9 M% M; u( ?5 G) i# s( z# e" z
记住不管什么时候,man都是我们的最好助手.
+ z  q! C8 I2 t; |9 e; P' C- _5 l------------------------------------------------------------------------# `) u5 O9 P9 v7 {3 P5 U3 U! I8 b
好了,这一章就讲这么多了,有了这些知识我们就可以进入激动人心的Linux下的C程序探
2 E, T$ L5 O* B' _$ P1 ~险活动.
With your idea, Carry out together.

TOP

Linux下进程的创建
  J  D9 W; K: S" I1 F, X8 J前言:
! K  n% ?0 C" e- a, ^* F1 e这篇文章是用来介绍在Linux下和进程相关的各个概念.我们将会学到:5 r( y6 m* C- a6 s7 I" ~
进程的概念5 u) F0 I: f: [5 R8 r1 ?. E: o* G
进程的身份" p( }( q6 W8 Z3 P0 F! Z
进程的创建5 R5 |8 g" N1 T$ d/ n, W
守护进程的创建
  {: l1 ?% k# X. Q9 j4 ^--------------------------------------------------------------------------------
0 F5 W# i$ s) F+ J1 p, O& N0 p1。进程的概念
" Q0 T' T5 q5 e' K% G5 z1 MLinux 操作系统是面向多用户的.在同一时间可以有许多用户向操作系统发出各种命令.那么操作系统是怎么实现多用户的环境呢? 在现代的操作系统里面,都有程序和进程的概念.那么什么是程序,什么是进程呢? 通俗的讲程序是一个包含可以执行代码的文件,是一个静态的文件.而进程是一个开始执行但是还没有结束的程序的实例.就是可执行文件的具体实现. 一个程序可能有许多进程,而每一个进程又可以有许多子进程.依次循环下去,而产生子孙进程. 当程序被系统调用到内存以后,系统会给程序分配一定的资源(内存,设备等等)然后进行一系列的复杂操作,使程序变成进程以供系统调用.在系统里面只有进程没有程序,为了区分各个不同的进程,系统给每一个进程分配了一个ID(就象我们的身份证)以便识别. 为了充分的利用资源,系统还对进程区分了不同的状态.将进程分为新建,运行,阻塞,就绪和完成五个状态. 新建表示进程正在被创建,运行是进程正在运行,阻塞是进程正在等待某一个事件发生,就绪是表示系统正在等待CPU来执行命令,而完成表示进程已经结束了系统正在回收资源. 关于进程五个状态的详细解说我们可以看《操作系统》上面有详细的解说。( @4 h* Q0 ]% ]; X' ~
: Z0 l8 Z9 {3 m2 {* {& p3 i
2。进程的标志
$ D& k. |5 g) B( w6 M上面我们知道了进程都有一个ID,那么我们怎么得到进程的ID呢?系统调用getpid可以得到进程的ID,而getppid可以得到父进程(创建调用该函数进程的进程)的ID.
1 s9 R+ N8 R1 ]( ^. J#include <unistd>8 \+ G+ @$ U0 N; k7 w* E" f; ?
pid_t getpid(void);! h7 d" R" |* e0 N0 g
pid_t getppid(void);
) k' Y) Q1 e4 k0 ^进程是为程序服务的,而程序是为了用户服务的.系统为了找到进程的用户名,还为进程和用户建立联系.这个用户称为进程的所有者.相应的每一个用户也有一个用户ID.通过系统调用getuid可以得到进程的所有者的ID.由于进程要用到一些资源,而Linux对系统资源是进行保护的,为了获取一定资源进程还有一个有效用户ID.这个ID和系统的资源使用有关,涉及到进程的权限. 通过系统调用geteuid我们可以得到进程的有效用户ID. 和用户ID相对应进程还有一个组ID和有效组ID系统调用getgid和getegid可以分别得到组ID和有效组ID
% [' ?' L% l9 w#include <unistd>
- Q5 S6 @5 ?7 x% u+ N#include <sys/types.h>. v) K1 t6 Z( v3 R+ A3 _( }

  B/ Z, D9 u2 C4 ~uid_t getuid(void);
8 X- ~: {' D# [2 s0 u" }uid_t geteuid(void);8 |* B+ h4 g: B* J" h3 V
gid_t getgid(void);
4 g5 q2 e" r1 a  z2 a$ Bgit_t getegid(void);
. g: r% D) [1 o有时候我们还会对用户的其他信息感兴趣(登录名等等),这个时候我们可以调用getpwuid来得到.7 _; I) w  g% [) ], X7 R- H
struct passwd {6 t" X! z$ I- h. `: n
char *pw_name; /* 登录名称 */" t' i$ L  l& r+ ^
char *pw_passwd; /* 登录口令 */4 |: Y0 ]( Z5 }6 g/ I
uid_t pw_uid; /* 用户ID */
6 \+ @9 ^. F5 K( O) r+ ]# J/ {9 ggid_t pw_gid; /* 用户组ID */
; P; R9 f' e; Kchar *pw_gecos; /* 用户的真名 */
# _5 E& R7 G! W. Jchar *pw_dir; /* 用户的目录 */
$ y0 V0 B4 q: Y) q! \% Nchar *pw_shell; /* 用户的SHELL */
9 Y, D9 x. w) ]' K  J' Y) l};
+ e8 Y; z" D  k6 ^  w, R( g; u#include <pwd.h>
! Q* F5 `* A8 S( L+ C#include <sys/types.h>
- E5 m( a( z' R3 }) \8 W  z1 b$ O- V  x
struct passwd *getpwuid(uid_t uid);9 L0 o1 z0 |" B7 X3 ^$ H6 O! q
下面我们学习一个实例来实践一下上面我们所学习的几个函数:2 Q' w* Z  t9 `9 l" |
#include <unistd.h>
2 y% E8 e; G$ Z+ {! O& p#include <pwd.h>6 w- z* g. s5 I8 z
#include <sys/types.h>) O! U' ~/ p6 R6 C) |, r6 n* C
#include <stdio.h>
6 {4 O* V1 w+ n1 t8 R' D/ ?0 @' ?int main(int argc,char **argv)
: h; F& `" n" w6 p& [{
( P0 V2 d# v5 r: O- O' o$ o. J- cpid_t my_pid,parent_pid;+ i, f* ~6 i7 V1 J/ P% R
uid_t my_uid,my_euid;2 m3 ~; v8 `" ~- w
gid_t my_gid,my_egid;: B9 C7 i) T$ a% W$ Y, D) @
struct passwd *my_info;
6 {1 `" K+ A. j/ t6 G: ~my_pid=getpid();
2 H. @+ q6 A2 bparent_pid=getppid();
- v  k, i' f* [- k* B) k  h6 lmy_uid=getuid();9 v0 h' W) P. J& D- h
my_euid=geteuid();* F/ I  Q2 x, [( h
my_gid=getgid();
, A+ u- g% k. ~my_egid=getegid();) H7 A8 ^( [2 k  G% B5 g' y
my_info=getpwuid(my_uid);
( M1 M) d* R( s- lprintf("rocess ID:%ld\n",my_pid);
+ v7 t) n. _; L1 |% s1 Rprintf("arent ID:%ld\n",parent_pid);
7 L. T: _3 y( M$ D0 ]# H( `printf("User ID:%ld\n",my_uid);7 W* G) Q$ ~* n+ b) F% g
printf("Effective User ID:%ld\n",my_euid);
0 n$ m3 f6 K. y# x) @3 Vprintf("Group ID:%ld\n",my_gid);6 y& V- U% o' d8 I+ S6 w
printf("Effective Group ID:%ld\n",my_egid):
+ B! f* X# }: O& t2 _if(my_info)4 l0 }9 j3 R$ K
{! t+ H: r5 x* e
printf("My Login Name:%s\n" ,my_info->pw_name);
  q' q- e7 V1 hprintf("My Password :%s\n" ,my_info->pw_passwd);, ?4 X! f+ C, G' a2 H
printf("My User ID :%ld\n",my_info->pw_uid);
8 [- K% P  G+ H* n. d0 Nprintf("My Group ID :%ld\n",my_info->pw_gid);
0 U! W" y  c4 I! T  xprintf("My Real Name:%s\n" ,my_info->pw_gecos);1 J0 |8 C0 u; ^- q# k+ ~: r+ x7 Y( V
printf("My Home Dir :%s\n", my_info->pw_dir);( n: q" k) ]: s6 T" y4 y9 p
printf("My Work Shell:%s\n", my_info->pw_shell);
' L1 ?1 I8 P$ f4 i2 A+ g}  [3 Y5 a+ j2 b0 t3 V: u+ r* m
}% W# C+ m* @4 g5 d; C
3。进程的创建
" y7 K% M- a3 R; R) v& V; O/ o  j创建一个进程的系统调用很简单.我们只要调用fork函数就可以了.# u1 f6 N; m8 C. W2 Q& k- U
#include <unistd.h>
/ ]! L2 D  ^) Q: ?( \1 h9 x% A( _  N% Z0 B
pid_t fork();1 z; k" i/ ?4 s' b! n- e  E
当一个进程调用了fork以后,系统会创建一个子进程.这个子进程和父进程不同的地方只有他的进程ID和父进程ID,其他的都是一样.就象符进程克隆 (clone)自己一样.当然创建两个一模一样的进程是没有意义的.为了区分父进程和子进程,我们必须跟踪fork的返回值. 当fork掉用失败的时候(内存不足或者是用户的最大进程数已到)fork返回-1,否则fork的返回值有重要的作用.对于父进程fork返回子进程的 ID,而对于fork子进程返回0.我们就是根据这个返回值来区分父子进程的. 父进程为什么要创建子进程呢?前面我们已经说过了Linux是一个多用户操作系统,在同一时间会有许多的用户在争夺系统的资源.有时进程为了早一点完成任务就创建子进程来争夺资源. 一旦子进程被创建,父子进程一起从fork处继续执行,相互竞争系统的资源.有时候我们希望子进程继续执行,而父进程阻塞直到子进程完成任务.这个时候我们可以调用wait或者waitpid系统调用.
5 I+ ~+ [1 r. P- W#include <sys/types.h>  R' _; l0 N1 S1 _3 u$ U
#include <sys/wait.h>( K' Y5 H  z$ d7 c4 H* J# P  ]1 l' V

: r# t) Y5 D! Y* }% a4 g: Dpid_t wait(int *stat_loc);
  t6 R' e. a; X/ lpid_t waitpid(pid_t pid,int *stat_loc,int options);
, Y9 V( l2 l8 z2 h6 ywait 系统调用会使父进程阻塞直到一个子进程结束或者是父进程接受到了一个信号.如果没有父进程没有子进程或者他的子进程已经结束了wait回立即返回.成功时 (因一个子进程结束)wait将返回子进程的ID,否则返回-1,并设置全局变量errno.stat_loc是子进程的退出状态.子进程调用exit, _exit 或者是return来设置这个值. 为了得到这个值Linux定义了几个宏来测试这个返回值.* @- Y9 Q5 @  W# F. p6 Y+ _
WIFEXITED:判断子进程退出值是非0
, H0 s/ \$ L$ M* MWEXITSTATUS:判断子进程的退出值(当子进程退出时非0).# w6 I. T3 j0 n2 y9 C' M, J1 X
WIFSIGNALED:子进程由于有没有获得的信号而退出.  J0 M, s+ k! x3 A5 D. Y. V. m
WTERMSIG:子进程没有获得的信号号(在WIFSIGNALED为真时才有意义).- u- }. ]( o* l, v# }
waitpid 等待指定的子进程直到子进程返回.如果pid为正值则等待指定的进程(pid).如果为0则等待任何一个组ID和调用者的组ID相同的进程.为-1时等同于wait调用.小于-1时等待任何一个组ID等于pid绝对值的进程. stat_loc和wait的意义一样. options可以决定父进程的状态.可以取两个值 NOHANG:父进程立即返回当没有子进程存在时. WUNTACHED:当子进程结束时waitpid返回,但是子进程的退出状态不可得到.父进程创建子进程后,子进程一般要执行不同的程序.为了调用系统程序,我们可以使用系统调用exec族调用.exec族调用有着5个函数.) R. G* S; I' M
#include <unistd.h>; e4 m8 N2 c4 b8 K4 T  h
int execl(const char *path,const char *arg,...);+ }, |& M5 @$ R5 a( G
int execlp(const char *file,const char *arg,...);( u5 |4 g& B$ ?  T% F3 I6 ]
int execle(const char *path,const char *arg,...);* r, C: D6 ]9 T, g8 [1 D. i' B8 b
int execv(const char *path,char *const argv[]);7 X& T/ W# Q3 d3 o
int execvp(const char *file,char *const argv[]):
2 }  B8 {) X: Dexec族调用可以执行给定程序.关于exec族调用的详细解说可以参考系统手册(man execl). 下面我们来学习一个实例.注意编译的时候要加 -lm以便连接数学函数库.
9 s5 q% }1 I' K- H$ {( q- w; M#include <unistd.h>* s6 s5 Q* _; k, _! Z5 l$ s
#include <sys/types.h>- R& N5 ^* Q/ J9 ?! F4 r
#include <sys/wait.h>
2 m- M/ E: y0 l( j$ D/ \8 }#include <stdio.h>
0 T8 T( s: ?- w#include <errno.h>7 R9 }- u! ]( g7 ^1 z& O9 u
#include <math.h>  o% v4 K; Z% p9 I/ C% L
void main(void)
  k) w3 Y, [. A; Z* a{
; ?( r# ?; S0 e; F' P% S/ h4 S4 M& u" Apid_t child;2 b' I$ T! Z6 U$ |- |2 C' T" i
int status;
4 ^' ~3 J5 t& {) \* t+ R0 Gprintf("This will demostrate how to get child status\n");6 k5 ]0 |9 H; `4 w3 k
if((child=fork())==-1)- N' b) c+ W0 D: M
{- b. J, @6 o& M' \/ C, X0 K/ p
printf("Fork Error :%s\n",strerror(errno));
( j: Q; @2 F  m: rexit(1);
, t6 f- B% S& z! b( l4 N}: [! |7 Z7 R$ ~( X
else if(child==0)
) f" |$ o! N$ x8 S. f  c: L' Y{
$ c: w0 W9 X- I- O. Tint i;4 }3 S5 l7 E- t
printf("I am the child:%ld\n",getpid());7 k, G# ]6 g1 T; @  K* `# q
for(i=0;i<1000000;i++) sin(i);
' z, g7 q1 w- c) Xi=5;
, u. O# o$ x# q. H: X1 Aprintf("I exit with %d\n",i);
. I4 \& N1 N9 @0 ?* }; S0 I% Wexit(i);
# x# n1 y$ p5 @, z}! M1 b( g" j, n: @7 p1 R6 F
while(((child=wait(&status))==-1)&(errno==EINTR));
# ^1 H3 Z3 |- M( l3 D' x' M8 mif(child==-1)
! z& ^6 L0 |# w3 |+ Aprintf("Wait Error:%s\n",strerror(errno));5 K5 p& r& ]$ {* m
else if(!status)
2 W8 U" Z9 h+ Q( F; x$ l1 x$ s: Gprintf("Child %ld terminated normally return status is zero\n",
2 `) ~4 L1 M: Ychild);& o! {* ?3 q9 Z( \9 \' t6 n* ]3 I
else if(WIFEXITED(status))! L: c4 O2 x; Z
printf("Child %ld terminated normally return status is %d\n",
1 ]! r3 Z! S7 t' W' {6 Zchild,WEXITSTATUS(status));( k2 ^  K1 n# K' {% }' A. o" n5 `
else if(WIFSIGNALED(status))# G8 z: `- x: |
printf("Child %ld terminated due to signal %d znot caught\n",
( h* |  K' v# c, g& }. g# S( ichild,WTERMSIG(status));
& j) J- @- `7 \# I7 b( S6 Y}: B- f' z/ A& _+ Y# U
strerror函数会返回一个指定的错误号的错误信息的字符串.
: l1 v$ t, Q6 k# g- V$ Y  v- V/ `' X+ p; V3 d% p5 @
4。守护进程的创建; K. U' }" I, J, r' b+ e) Q3 ]; C0 x
如果你在DOS时代编写过程序,那么你也许知道在DOS下为了编写一个常驻内存的程序我们要编写多少代码了.相反如果在Linux下编写一个"常驻内存"的程序却是很容易的.我们只要几行代码就可以做到. 实际上由于Linux是多任务操作系统,我们就是不编写代码也可以把一个程序放到后台去执行的.我们只要在命令后面加上&符号SHELL就会把我们的程序放到后台去运行的. 这里我们"开发"一个后台检查邮件的程序.这个程序每个一个指定的时间回去检查我们的邮箱,如果发现我们有邮件了,会不断的报警(通过机箱上的小喇叭来发出声音). 后面有这个函数的加强版本加强版本后台进程的创建思想: 首先父进程创建一个子进程.然后子进程杀死父进程(是不是很无情?). 信号处理所有的工作由子进程来处理.( C/ O1 I9 |3 F! U' a- S
#include <unistd.h>+ P2 Y/ S4 l( \6 W; @1 u; Y- J% y. Z
#include <sys/types.h>
) M9 {' {! K! ?4 G& I, Z#include <sys/stat.h>
0 L1 V8 D9 |3 S) H# Q& ^6 @. L#include <stdio.h>9 u* Q# S: A! `# B3 z; q
#include <errno.h>  f. `. J9 N% n9 M4 l: K  g
#include <fcntl.h>
6 x3 J' _# M. u9 Q6 {#include <signal.h>
- ~7 |1 O  V, z2 Q. b2 o! J' H/* Linux 的默任个人的邮箱地址是 /var/spool/mail/用户的登录名 */5 s5 [+ `1 F4 w2 b5 W
#define MAIL "/var/spool/mail/hoyt"' u( F8 A% M" _
/* 睡眠10秒钟 */8 h1 u2 U! H' J+ V2 H4 L6 @
2 }2 m3 O/ P0 q7 Z  K
#define SLEEP_TIME 10* E2 U, `$ u: B  D
main(void)
- ]2 V; M0 ]$ f: P) N  @{6 t- X$ h. w- U, j" O
pid_t child;
" g2 @6 C( }- K" R4 A! p2 Zif((child=fork())==-1)
2 v; p5 h' `& K+ N" ^  b{
. o/ t5 v, N. c, q) p- u% ?printf("Fork Error:%s\n",strerror(errno));
, X. f) l* o3 \; y* K9 w  h7 qexit(1);
8 \; r! Y1 x; B5 I}
* D, C. U3 A! _( j1 n% l8 g$ A1 celse if(child>0)) g3 l( J6 D& U$ H, {, N9 t
while(1);+ X) [' J3 Y, n5 n+ S8 v  F: M
if(kill(getppid(),SIGTERM)==-1)/ k, M, h3 B2 H" u5 L4 q0 l/ G
{* ~( E( X- W  @6 ^
printf("Kill Parent Error:%s\n",strerror(errno));; I& T3 \' g" z
exit(1);
# D9 L1 F/ u$ M$ R" M7 A}, F, M9 H$ g7 L# W, N4 V
{( {- g3 l$ G! c0 m" o$ l1 p
int mailfd;
: ?4 O8 {8 Z& Z) A2 e6 V$ v- _+ Twhile(1)
. J! n& U; `7 R5 D{+ ?# v6 g* ?. d3 s! \5 h, c8 F
if((mailfd=open(MAIL,O_RDONLY))!=-1)- r! V/ C" n8 G- K/ y2 b
{% Z% q) y9 M) M" X4 r2 }" m6 ~1 k9 i
fprintf(stderr,"%s","\007");
' J, r* `$ z: D( Cclose(mailfd);
* f8 X& {- s+ C7 n( I) Q}
9 A) Q, l, Z' ~" o9 msleep(SLEEP_TIME);
. h. l. S* V& v+ g% Z' }}
$ n& i% g  {" R" M' |! ~/ X; @1 s}
2 i* J# |  c9 _4 b0 ]}
. U, G0 y% g3 ^$ G你可以在默认的路径下创建你的邮箱文件,然后测试一下这个程序.当然这个程序还有很多地方要改善的.我们后面会对这个小程序改善的,再看我的改善之前你可以尝试自己改善一下.比如让用户指定邮相的路径和睡眠时间等等.相信自己可以做到的.动手吧,勇敢的探险者.
$ m- U3 p6 F4 q# r7 A
$ q+ I3 I* O9 K! ?5 L5 m& ]好了进程一节的内容我们就先学到这里了.进程是一个非常重要的概念,许多的程序都会用子进程.创建一个子进程是每一个程序员的基本要求!
With your idea, Carry out together.

TOP

Linux下文件的操作
8 {; t. ?/ m  c9 f/ w4 z( H# I0 U前言:
2 d5 q' u) r/ E0 e, e7 Y我们在这一节将要讨论linux下文件操作的各个函数.
+ B6 E' G5 i. g! u: d% _/ d- Z2 N文件的创建和读写3 L8 C0 p6 `0 k
文件的各个属性
- |/ S7 a! |% {+ ^4 O目录文件的操作
- M3 B) k/ j: O* @4 ]9 Q5 K- Z8 s管道文件5 s; S1 u3 i# p% `
--------------------------------------------------------------------------------
+ i+ ^$ ^$ M5 W) Z1。文件的创建和读写9 z+ F8 Y! u! P5 U! M
我假设你已经知道了标准级的文件操作的各个函数(fopen,fread,fwrite等等).当然如果你不清楚的话也不要着急.我们讨论的系统级的文件操作实际上是为标准级文件操作服务的.1 I+ |, w; F( G0 [/ j- x# ~# B
当我们需要打开一个文件进行读写操作的时候,我们可以使用系统调用函数open.使用完成以后我们调用另外一个close函数进行关闭操作.
/ m: C% }+ ^% h$ v' [" l#include <fcntl.h>
. u" W' i$ Q2 K+ j' x6 y8 n#include <unistd.h>
% D" I1 q: Y! o* l5 L#include <sys/types.h>
+ L( {, p) C5 N( R0 ~#include <sys/stat.h>% W: A5 Y( c" a

. c% l+ C& w) [- a4 Rint open(const char *pathname,int flags);
' I& b1 l, i1 s1 i" i1 N: o" Sint open(const char *pathname,int flags,mode_t mode);# F7 ?9 L( y: U6 M  h8 S4 l* G
int close(int fd);
2 X' ]$ ]& J5 h0 w& F3 ^+ U) e9 @open函数有两个形式.其中pathname是我们要打开的文件名(包含路径名称,缺省是认为在当前路径下面).flags可以去下面的一个值或者是几个值的组合.- A- W; j# a9 A$ K! t! h2 x4 Y, z
O_RDONLY:以只读的方式打开文件./ W" s* |3 Y9 m, z
O_WRONLY:以只写的方式打开文件.
/ \1 ^6 e8 L$ }6 e  AO_RDWR:以读写的方式打开文件.
0 ~1 H, U& `/ |  ]5 t+ `O_APPEND:以追加的方式打开文件.
. G4 _3 [+ Z+ {3 w+ t8 Z8 a- YO_CREAT:创建一个文件.
! e( f2 Q2 k% v. T# n5 cO_EXEC:如果使用了O_CREAT而且文件已经存在,就会发生一个错误.& [7 u1 R9 a" A" F. v4 |/ s! m  K0 W# U
O_NOBLOCK:以非阻塞的方式打开一个文件.4 x3 F- C# {9 S. x
O_TRUNC:如果文件已经存在,则删除文件的内容.' i2 e! f; S6 {6 M* k! M6 J+ d
前面三个标志只能使用任意的一个.如果使用了O_CREATE标志,那么我们要使用open的第二种形式.还要指定mode标志,用来表示文件的访问权限.mode可以是以下情况的组合.7 c9 o7 ?; d$ _
-----------------------------------------------------------------0 Y0 T5 a1 Q. @! ~
S_IRUSR 用户可以读 S_IWUSR 用户可以写
- m3 j4 C5 M9 B) K( _+ \S_IXUSR 用户可以执行 S_IRWXU 用户可以读写执行0 w1 t& a2 ]. x' B
-----------------------------------------------------------------; y8 O2 w' x& O
S_IRGRP 组可以读 S_IWGRP 组可以写
% N" O" l3 h: D! PS_IXGRP 组可以执行 S_IRWXG 组可以读写执行
+ s5 ?6 o8 _" Q9 T, [, I-----------------------------------------------------------------: w: B, W& t7 m  E) U- v8 ^8 k$ P6 [
S_IROTH 其他人可以读 S_IWOTH 其他人可以写
# e+ R; P* o+ I' [' \5 h4 `; U4 TS_IXOTH 其他人可以执行 S_IRWXO 其他人可以读写执行. B! w4 O4 J0 k0 P1 Y( w
-----------------------------------------------------------------
$ w% c& b% ]3 U! R& `6 m! mS_ISUID 设置用户执行ID S_ISGID 设置组的执行ID6 }2 M: M; l- c- o) k9 {
-----------------------------------------------------------------2 Z4 c& R4 l! P+ e( n4 _+ T
我们也可以用数字来代表各个位的标志.Linux总共用5个数字来表示文件的各种权限.
: G5 j* f7 Q# y9 P/ Y00000.第一位表示设置用户ID.第二位表示设置组ID,第三位表示用户自己的权限位,第四
! W) P, r. E# |, q4 S位表示组的权限,最后一位表示其他人的权限.每个数字可以取1(执行权限),2(写权限),4(读权限),0(什么也没有)或者是这几个值的和..
2 f2 ?  K! r9 g0 z3 Q# X. ~比如我们要创建一个用户读写执行,组没有权限,其他人读执行的文件.设置用户ID位那么我们可以使用的模式是--1(设置用户ID)0(组没有设置)7(1+2+4)0(没有权限,使用缺省)5(1+4)即10705:( O8 ?6 ?6 J5 B3 O6 E( u' q
open("temp",O_CREAT,10705);
2 B* _( a* q( E1 ^如果我们打开文件成功,open会返回一个文件描述符.我们以后对文件的所有操作就可以对这个文件描述符进行操作了./ F4 l. f  X* y: X5 I0 I
当我们操作完成以后,我们要关闭文件了,只要调用close就可以了,其中fd是我们要关闭的文件描述符.文件打开了以后,我们就要对文件进行读写了.我们可以调用函数read和write进行文件的读写.
5 r5 l. o3 _% X0 {! q6 U7 o#include <unistd.h>% K' l% m! z4 m& u
ssize_t read(int fd, void *buffer,size_t count);& j" N6 c( q8 g, d# g
ssize_t write(int fd, const void *buffer,size_t count);
6 f4 J3 ?* o) B' l' T7 pfd是我们要进行读写操作的文件描述符,buffer是我们要写入文件内容或读出文件内容的内存地址.count是我们要读写的字节数.5 @# _$ a3 v6 h8 C
对于普通的文件read从指定的文件(fd)中读取count字节到buffer缓冲区中(记住我们必须提供一个足够大的缓冲区),同时返回count.
+ [. I+ m$ c) Y如果read读到了文件的结尾或者被一个信号所中断,返回值会小于count.如果是由信号中断引起返回,而且没有返回数据,read会返回-1,且设置errno为EINTR.当程序读到了文件结尾的时候,read会返回0.write从buffer中写count字节到文件fd中,成功时返回实际所写的字节数.
/ s: z4 f" c5 f6 o7 e下面我们学习一个实例,这个实例用来拷贝文件.3 C- P& f  O2 ^
#include <unistd.h>
$ |3 r( R6 @* s5 D# D#include <fcntl.h>
7 l, e% I0 F% ?4 X; ]. u#include <stdio.h>
2 p" w& [5 O: {#include <sys/types.h>
- M' z4 d: M  u5 h+ c  {#include <sys/stat.h>, D; q" T, U. R
#include <errno.h>
3 R9 j; a! z% D! D4 M#include <string.h>
, y6 j- R* a4 C$ g#define BUFFER_SIZE 1024
' [6 @' M6 @9 Nint main(int argc,char **argv)+ |( A  K' a9 i" H  s' a
{9 c4 W- f& e  _( l
int from_fd,to_fd;
' M7 n$ k6 |, _0 C+ {( ^' [# `int bytes_read,bytes_write;
- U' q- A5 f5 Z1 \char buffer[BUFFER_SIZE];/ \4 M4 v# N! \
char *ptr;  z, T! G% z$ d" ]& [( K3 ^
if(argc!=3)
) S5 W% e: U: h, C/ o/ }( R$ {$ T, B{
( m0 k* [9 ]  P9 B. |5 V; L  nfprintf(stderr,"Usage:%s fromfile tofile\n\a",argv[0]);1 H7 V. K1 B1 D) s
exit(1);! H! L6 {; }" @. X( b& Z9 O
}
: d. Y$ |: `: |+ y" n/* 打开源文件 */
! {: |+ r) C1 lif((from_fd=open(argv[1],O_RDONLY))==-1)& F& N' B) Z2 G$ O' u  P7 Q
{
* d8 ~# Q) k6 U; r, Ifprintf(stderr,"Open %s Error:%s\n",argv[1],strerror(errno));
, ~7 ^* ^5 t& X/ t2 I" A4 |exit(1);
7 N8 k0 V; A; Y" f% M9 w}
' [( S, M+ S7 O. H4 v/* 创建目的文件 */
. }  Q9 e. t! |* n( _6 pif((to_fd=open(argv[2],O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1): m) R8 e5 l! U8 H; k! u9 u
{! Q% `0 ^. Q6 a1 {6 g
fprintf(stderr,"Open %s Error:%s\n",argv[2],strerror(errno));* _$ g( g' R% F$ N, v0 [5 D
exit(1);
2 A& o2 O: `' H3 h- O* a" w}
; b- j' d/ l9 \9 s+ ?4 H4 Z3 f/* 以下代码是一个经典的拷贝文件的代码 */% a5 v  k/ F# \, N
while(bytes_read=read(from_fd,buffer,BUFFER_SIZE))% c2 j7 @# s6 |, F# [2 Z. b. O9 ~
{4 y( K: t3 Q, [1 ]& B
/* 一个致命的错误发生了 */
7 @5 R, |* n* s( [. y0 _if((bytes_read==-1)&&(errno!=EINTR)) break;
% U5 g" T: j0 Z' @* {: j1 v& Welse if(bytes_read>0)* Y& U5 r( n9 V1 X9 j2 E0 z( |7 P
{
; O) I. X, A8 Rptr=buffer;
' O& l) v. V6 rwhile(bytes_write=write(to_fd,ptr,bytes_read))
8 o, J7 I0 z) J6 B4 h/ ?6 I{
$ M0 }$ C; Y9 C! l7 d/ X" [5 _/* 一个致命错误发生了 */) t% B  _% D! L
if((bytes_write==-1)&&(errno!=EINTR))break;
5 n. ?0 {2 k$ v) B/* 写完了所有读的字节 */
- O. U, ^" A. P3 y1 eelse if(bytes_write==bytes_read) break;; E0 h5 m5 r  h# q0 I; a
/* 只写了一部分,继续写 */# U4 A. T- Y9 J' l1 x: I4 [" L6 ?
else if(bytes_write>0)/ N; h7 M$ j' w0 ~* H$ M# n
{& @! E4 g, k9 @7 c3 H% U/ n9 T
ptr+=bytes_write;5 C, f3 q* Z$ S( r5 o
bytes_read-=bytes_write;
4 |/ |6 p, ~0 Z, n6 O  V3 a4 z$ J}; q7 H4 ^# X2 ^( _; I5 o
}
  }* ?, R8 P  a! X2 M6 o& u/* 写的时候发生的致命错误 */6 k9 l# J4 Q6 o8 d7 B  @
if(bytes_write==-1)break;& `5 o- f/ Q4 K
}
" D0 m  O% ], V% E, t  e' d9 A9 ]}0 [: p/ A  c' c" T& A
close(from_fd);7 t8 X* w* G' n8 B  g
close(to_fd);
( F# t: o% u( z( h4 h& D0 zexit(0);" H( I" H: ?$ a" I# s; T
}
) A, b2 A, ^/ N5 U8 C: d2。文件的各个属性
; b  n3 u+ e8 _* ^文件具有各种各样的属性,除了我们上面所知道的文件权限以外,文件还有创建时间,大小等等属性.
$ `0 c- f7 K, d有时侯我们要判断文件是否可以进行某种操作(读,写等等).这个时候我们可以使用access函数.
+ z; ^3 Z. _8 Z#include <unistd.h>
$ F2 @8 B' \: s* r1 ^( g' k; [' I3 s' h- X& D4 j1 c  b
int access(const char *pathname,int mode);
$ f' S/ e: }+ Z6 Vpathname:是文件名称,mode是我们要判断的属性.可以取以下值或者是他们的组合.0 y' o  X, M. s: N0 F
R_OK文件可以读,W_OK文件可以写,X_OK文件可以执行,F_OK文件存在.当我们测试成功时& }; H. a+ M. M) \: |4 T
,函数返回0,否则如果有一个条件不符时,返回-1.6 {- I7 ^$ f+ @, a- |! I) N
如果我们要获得文件的其他属性,我们可以使用函数stat或者fstat.
- D/ Q" P9 U- I! q" v+ L#include <sys/stat.h>
6 p7 ~7 A7 t+ A) b' C& f( U. s#include <unistd.h>) [/ V3 e" V: w6 T' I
int stat(const char *file_name,struct stat *buf);7 G9 w( A" X' m3 e2 Y/ _* W
int fstat(int filedes,struct stat *buf);
( W* O& I" x+ A. T5 r7 Fstruct stat {
$ h  E5 H- v/ W( [$ b" odev_t st_dev; /* 设备 */8 M5 ^9 |1 H9 d! ~, _
ino_t st_ino; /* 节点 */
% ?$ ~7 C3 s7 V6 j0 d0 B- z4 Hmode_t st_mode; /* 模式 *// @0 |) k) `3 g* j! s
nlink_t st_nlink; /* 硬连接 */0 c9 w& L+ s/ v$ f
uid_t st_uid; /* 用户ID */2 h3 D+ a9 M& D
gid_t st_gid; /* 组ID */
9 W+ N9 @2 ^" W) w1 X  Wdev_t st_rdev; /* 设备类型 */
; D6 U. X6 ?3 p9 }* i$ ^  M3 h3 _off_t st_off; /* 文件字节数 */
3 o" e" R/ y* x4 Gunsigned long st_blksize; /* 块大小 */( D$ ~- d! J8 {$ ?  o) l) W
unsigned long st_blocks; /* 块数 */1 Z' s2 c7 O8 Z2 V8 x1 O
time_t st_atime; /* 最后一次访问时间 */* D3 x& c/ G3 i; q/ ^  b
time_t st_mtime; /* 最后一次修改时间 */
; e' |" W. D% P/ Ftime_t st_ctime; /* 最后一次改变时间(指属性) */
5 \* z8 ~' P+ [  W};, p) S& W3 E/ q6 @3 Y: b
stat用来判断没有打开的文件,而fstat用来判断打开的文件.我们使用最多的属性是st_mode.通过着属性我们可以判断给定的文件是一个普通文件还是一个目录,连接等等.可以使用下面几个宏来判断.
% k- l! P; G$ w! B1 w# A' ~S_ISLNK(st_mode):是否是一个连接.S_ISREG是否是一个常规文件.S_ISDIR是否是一个目
' x& P, s0 G. g录S_ISCHR是否是一个字符设备.S_ISBLK是否是一个块设备S_ISFIFO是否 是一个FIFO文
) i. |% D$ N  d" y3 n" Y1 v, k  o件.S_ISSOCK是否是一个SOCKET文件. 我们会在下面说明如何使用这几个宏的.& O" _, [- e+ u4 L6 X3 T
: J- p9 }0 v1 C8 `3 u* S
3。目录文件的操作
' O( o( B: S: Y在我们编写程序的时候,有时候会要得到我们当前的工作路径。C库函数提供了getcwd来解决这个问题。
8 n0 y# [6 u# Q% S3 P" J#include <unistd.h>5 m7 S6 l: N& p0 `! T7 S

0 I0 H7 m% R! z0 p9 w, rchar *getcwd(char *buffer,size_t size);# k4 n1 }* s+ c  f3 Q  h
我们提供一个size大小的buffer,getcwd会把我们当前的路径考到buffer中.如果buffer太小,函数会返回-1和一个错误号.
: P9 V6 Y9 I: mLinux提供了大量的目录操作函数,我们学习几个比较简单和常用的函数.
! j7 G* j  J- r0 c' l3 H#include <dirent.h>* q( C- S$ ]( w3 e* h8 D3 d
#include <unistd.h>
5 ^  z* c9 ~4 q2 U# g' G, Y#include <fcntl.h>
2 q. Y( n( @5 H2 g3 N7 b#include <sys/types.h>
' m& D1 E" g/ \4 R2 L& v#include <sys/stat.h>
8 S" j1 b0 t+ Mint mkdir(const char *path,mode_t mode);6 ~: x. a: e! o1 c
DIR *opendir(const char *path);2 z* e; F  B6 Q' d& U2 M/ R
struct dirent *readdir(DIR *dir);. P2 ?, J+ T* t! u; G9 Q, e5 j
void rewinddir(DIR *dir);1 N# ~9 F7 m, T) `6 ?
off_t telldir(DIR *dir);
0 V  B0 i0 V% Nvoid seekdir(DIR *dir,off_t off);
- b3 |9 _+ c# v" kint closedir(DIR *dir);+ w5 j' U8 K5 e) ]3 \' r
struct dirent {! h# o4 Y6 r+ ?# k4 M' K& g1 b* J
long d_ino;
( O! Q# z% D4 _+ f' ?/ L; roff_t d_off;
1 ^6 j) [  h2 [. P) xunsigned short d_reclen;; o! R8 W+ a% |" a6 W
char d_name[NAME_MAX+1]; /* 文件名称 */
% ~" Z+ |& F- imkdir很容易就是我们创建一个目录,opendir打开一个目录为以后读做准备.readdir读一个打开的目录.rewinddir是用来重读目录的和我们学的rewind函数一样.closedir是关闭一个目录.telldir和seekdir类似与ftee和fseek函数.
& I; k2 t5 W. y9 q* N% _4 ?0 B下面我们开发一个小程序,这个程序有一个参数.如果这个参数是一个文件名,我们输出这个文件的大小和最后修改的时间,如果是一个目录我们输出这个目录下所有文件的大小和修改时间.7 {6 I2 @6 \6 e$ W9 f% v& x
#include <unistd.h>3 Q, E" i( ~" `
#include <stdio.h>
0 s5 @9 ^. _: v1 Z0 T#include <errno.h>3 @& Y/ @( u0 n' ^
#include <sys/types.h>
! E3 @( V1 h& }# p$ ~! h#include <sys/stat.h>9 Y! i6 G% x9 z
#include <dirent.h>7 x* ~" P4 k+ {: ]3 ^3 z
#include <time.h>
2 S4 i( Y2 T' `7 H7 F( u3 w$ @* s( _static int get_file_size_time(const char *filename)+ Q3 Z& D$ V9 p$ s4 G; T9 ]
{, u3 k# a/ r! f7 j
struct stat statbuf;: y% S; q" R$ ?, }5 K5 a5 p
if(stat(filename,&statbuf)==-1)
+ N) Y. l& H. @$ Y) |$ o+ E{
$ y/ Q8 Z, ~. D3 {printf("Get stat on %s Error:%s\n",
9 [& v1 T$ f0 Xfilename,strerror(errno));' P7 z4 f4 ^! K( x' V6 f/ u
return(-1);
, d+ X8 V9 e( d7 o% s  b/ a) q}
3 s/ r% N" o1 A% w6 Sif(S_ISDIR(statbuf.st_mode))return(1);
2 W* J2 s* j! P% bif(S_ISREG(statbuf.st_mode))! m4 D1 G/ _" G- Q
printf("%s size:%ld bytes\tmodified at %s",
( r, p* r- V6 _: Z7 r6 l+ Bfilename,statbuf.st_size,ctime(&statbuf.st_mtime));
! F9 Z8 M& ]) w9 G2 b, F6 g. G  n+ h6 T
return(0);
3 u  W, }8 J4 O, d}
) F" a5 ~* w; y. I! M+ Qint main(int argc,char **argv)4 H% _5 P2 k* e" ?2 p
{
/ V" Z) d5 H) I* [  l. YDIR *dirp;/ X  ^( d! w' M; m# E2 k6 M2 U
struct dirent *direntp;" C1 [" J7 o8 Y% l
int stats;' w; b$ Z5 e( c% U7 c" H! @4 G1 f
if(argc!=2)
1 }# z: D# Y2 T' J# `9 o4 ~& q{
( L/ K9 H) s6 I  x$ Jprintf("Usage:%s filename\n\a",argv[0]);
! u1 O; }; l! y" h' L+ G& Aexit(1);& c$ w5 R* h: K% V1 X( L& @
}
4 _1 |' u; ~3 ^; }) ~) k/ wif(((stats=get_file_size_time(argv[1]))==0)||(stats==-1))exit(1);% H- e* G( R! z0 h
if((dirp=opendir(argv[1]))==NULL)
2 X; M* {  F) @% ^6 D{
% x' e* ]7 M3 U9 E% R( {- p$ I# Wprintf("Open Directory %s Error:%s\n",
# q) V" g* p) u$ N" |argv[1],strerror(errno));
4 I$ g. {' z* Lexit(1);
/ v* Y( J4 e# m; h" \' l}
3 I% L0 M0 O6 j9 \while((direntp=readdir(dirp))!=NULL)3 E! a5 X0 B4 V! X3 b5 v9 M7 p
if(get_file_size_time(direntp-<d_name)==-1)break;
1 m* Y, M$ J/ |: T4 g) Zclosedir(dirp);
8 N: }) O: y7 d. X; ^# aexit(1);0 E! F/ H" d; H. r7 H
}
- b) a1 b' N+ t/ v: D4。管道文件
; K! n% S' ]2 yLinux提供了许多的过滤和重定向程序,比如more cat等等.还提供了< > | <<等等重定向操作符.在这些过滤和重 定向程序当中,都用到了管道这种特殊的文件.系统调用pipe可以创建一个管道.
8 j. {4 @" G4 n#include<unistd.h>% {' h' ]  H& `8 y8 ^, e$ K
- z0 z0 a& _9 G3 ~- p3 a
int pipe(int fildes[2]);+ u  I6 c" p1 U- q2 C1 e
pipe调用可以创建一个管道(通信缓冲区).当调用成功时,我们可以访问文件描述符fildes[0],fildes[1].其中fildes[0]是用来读的文件描述符,而fildes[1]是用来写的文件描述符.
: o0 F' j* u2 a0 B/ |5 A在实际使用中我们是通过创建一个子进程,然后一个进程写,一个进程读来使用的.3 K) b; b5 e$ @' s. x
关于进程通信的详细情况请查看进程通信6 D( e( b) I' N0 |# T7 w  W. W
#include <stdio.h>" m  {+ m" ~$ y) A+ j
#include <stdlib.h>1 ]: k- T$ y5 I2 M. C" h! J
#include <unistd.h>
0 J( Z4 K& I! n! o6 [: g#include <string.h>
3 \* [' H3 u4 [- b' a- H/ X#include <errno.h>, `. P, |% Q! ^/ W& _
#include <sys/types.h>) M" o2 V6 p# r& w9 b
#include <sys/wait.h>
% {6 M: `1 ]# s2 l8 F# N#define BUFFER 255
1 z+ g' N6 P' [, m4 w  H* Fint main(int argc,char **argv), a# }7 ^) D8 e! H( Z
{  x. h& H& G/ J1 `+ g- p, Q0 |
char buffer[BUFFER+1];4 Y- D+ l/ ]9 P7 n! ^+ L
int fd[2];* u% F0 t) l4 J/ e
if(argc!=2)& |9 E: @  a& B9 t% Q8 I
{
: u: n  L0 d, Pfprintf(stderr,"Usage:%s string\n\a",argv[0]);( V; d3 a& E) |) ^' r$ i
exit(1);' A' Q  w( m8 X5 p- B; A+ G
}! p, p5 G& o% H0 p
if(pipe(fd)!=0)8 ^2 ?% k2 S9 w# [0 @
{
8 U4 M8 \) E9 H# b+ _fprintf(stderr,"ipe Error:%s\n\a",strerror(errno));
( z( A# p2 ^! d; o  Z0 Xexit(1);! ?. |! z- r3 `
}
/ i& ^+ G3 f. N8 pif(fork()==0)# T& X4 f3 e  P9 o. O8 ~4 W  g4 r
{
0 f) E2 l  ?3 w; e3 U1 i1 c, iclose(fd[0]);* m: w! p0 }- i- k. D% g# k8 ~' F0 h
printf("Child[%d] Write to pipe\n\a",getpid());
/ v8 T* u4 x! P* V2 K( Osnprintf(buffer,BUFFER,"%s",argv[1]);2 a' r; d; s: }0 e4 b0 t9 L
write(fd[1],buffer,strlen(buffer));* A) Y6 E/ L1 b3 G1 i! m" Y
printf("Child[%d] Quit\n\a",getpid());
3 Y- W+ N( y1 `: T! n' h! }- |( zexit(0);( s0 e5 V" m( Y
}
+ M* J! K( s9 B8 a# o, O9 belse
, Y! J; y/ Q9 `' t1 l; o# u% |{7 `( v) B2 g& d3 k
close(fd[1]);
! C) F+ S! E& j/ ?8 ~printf("arent[%d] Read from pipe\n\a",getpid());. W( T3 m& e) H# i
memset(buffer,'\0',BUFFER+1);+ n# s' u; O4 ]# r" W$ W6 X
read(fd[0],buffer,BUFFER);0 h0 O( p# e0 y$ X; G% h% v% ?
printf("arent[%d] Read:%s\n",getpid(),buffer);
8 ?9 W- t- T7 O4 M5 M. Q; h+ Mexit(1);
- c0 U3 o* \6 v6 \  {}
- s( ]0 E* H3 R+ a}
" i  j3 P- a, @( y为了实现重定向操作,我们需要调用另外一个函数dup2.
- N( }/ b8 v1 K! X' A9 ~#include <unistd.h>
, a# R  w/ h1 R. w* h* R' ]" t9 H: y$ S) r
int dup2(int oldfd,int newfd);
5 p1 [8 }5 }" @0 s, h/ Sdup2将用oldfd文件描述符来代替newfd文件描述符,同时关闭newfd文件描述符.也就是说,9 W3 x+ i' |% y. C  p
所有向newfd操作都转到oldfd上面.下面我们学习一个例子,这个例子将标准输出重定向到一个文件.
7 r+ A5 h! y8 D$ e4 P8 c! }#include <unistd.h>
, ^' O% F/ }9 @1 S6 V: c1 |, q; f#include <stdio.h>
5 T* M7 a& a1 L' I#include <errno.h># r2 o6 b" S7 ]" b, F
#include <fcntl.h>
9 a5 d- X# x0 B# a0 ^, @) e#include <string.h>
+ p# x6 r4 v( A2 H) j#include <sys/types.h>
$ T! \( R3 m! K" u#include <sys/stat.h>
# t: o5 o8 D# q2 \" u#define BUFFER_SIZE 1024
! s* @1 A1 e3 @: y! k8 gint main(int argc,char **argv)
* ~, a/ K& ]& N: Z/ {, V7 L{
! a- g' Q+ R% a6 fint fd;' n5 V9 c) C5 I+ e
char buffer[BUFFER_SIZE];) J9 T) L# i0 t; [: C
if(argc!=2)8 v9 C# K- {4 ?* e
{/ g. J2 H8 n2 E, N. I
fprintf(stderr,"Usage:%s outfilename\n\a",argv[0]);
4 w) c" U" \  }3 z7 u$ s0 Pexit(1);9 A8 v/ V2 @8 l& u) E4 y; s
}0 F+ n$ f6 v+ J$ |  q) a
if((fd=open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR))==-1)" N( q+ d- J4 i: d% _9 o; t0 {  g
{- S; S" g& y) ]. h" L
fprintf(stderr,"Open %s Error:%s\n\a",argv[1],strerror(errno));
4 h0 h( T: F" B0 zexit(1);
2 p7 h' p8 G5 {) w# f3 P}" [4 a) y7 S: L& o. a4 ^! F7 A
if(dup2(fd,STDOUT_FILENO)==-1)
  z2 n9 X. V# D9 V* |% s{! b$ R9 J  b/ ?' w& ^. x) l0 C  {
fprintf(stderr,"Redirect Standard Out Error:%s\n\a",strerror(errno));' ~/ t) f1 E  T
exit(1);
5 |. L* h9 S0 p) F3 L  z+ M2 X' j}
7 K9 }7 Z" o7 hfprintf(stderr,"Now,please input string");/ S# F% W9 p: r" _  w* k
fprintf(stderr,"(To quit use CTRL+D)\n");
7 s( L! V9 m) P+ F% x" s. k/ u. C1 rwhile(1)
- H4 R! j( A+ \  v$ x" v* f{) b* f& j1 d. F7 {
fgets(buffer,BUFFER_SIZE,stdin);1 J6 K# {6 T( q& x" v( |) t
if(feof(stdin))break;
6 t. |" P1 j2 S* m9 j( Q. Vwrite(STDOUT_FILENO,buffer,strlen(buffer));
2 C8 x# U6 V4 F; `5 x7 \}
# O7 s, W' T0 v  s& aexit(0);, V4 O0 i2 i/ K
}: k6 w1 c, v  r4 |- z% |
好了,文件一章我们就暂时先讨论到这里,学习好了文件的操作我们其实已经可以写出一些比较有用的程序了.我们可以编写一个实现例如dir,mkdir,cp,mv等等常用的文件操作命令了.
1 y- `6 V# t+ S- I( T" Y想不想自己写几个试一试呢?
With your idea, Carry out together.

TOP

前言inux下的时间概念
! @# I( K7 C0 N5 m这一章我们学习Linux的时间表示和计算函数. z& X+ s. C1 i$ g, {6 @
时间的表示
  P9 ]6 V: s% A  Y4 e+ O时间的测量7 k1 f+ X& A4 n9 ^% _5 j! }4 J7 \
计时器的使用
0 i  n/ Q8 H/ ?# K* x7 g& m7 ~1。时间表示 在程序当中,我们经常要输出系统当前的时间,比如我们使用date命令的输出结果.这个时候我们可以使用下面两个函数" k$ M1 d( i8 H+ C8 s, P( h6 @8 f+ N
#include <time.h>7 }  X9 a1 F$ g- z
% K' M% c( G# e6 n
time_t time(time_t *tloc);& p1 r) V7 p4 O9 L2 Y
char *ctime(const time_t *clock);) [- N; S" n: C
time函数返回从1970年1月1日0点以来的秒数.存储在time_t结构之中.不过这个函数的返回值对于我们来说没有什么实际意义.这个时候我们使用第二个函数将秒数转化为字符串.. 这个函数的返回类型是固定的:一个可能值为. Thu Dec 7 14:58:59 2000 这个字符串的长度是固定的为26/ f. m6 f, u9 W/ P/ @  x' [

2 k" f& B( ~1 ?2。时间的测量 有时候我们要计算程序执行的时间.比如我们要对算法进行时间分析..这个时候可以使用下面这个函数.6 }' z, `& ?: ~: D/ q
#include <sys/time.h>
# ~) x; q$ ]" v1 i3 }% Y8 s7 l' v. ~6 Z4 x# ^% |% ~
int gettimeofday(struct timeval *tv,struct timezone *tz);) N3 e% D' o) n& g  S- }1 h
strut timeval {' p3 _4 ~5 i/ d% d- g
long tv_sec; /* 秒数 */
+ Z$ q# H! @. |/ d6 `# h/ L$ u5 `long tv_usec; /* 微秒数 */
/ h& Y: ?3 w- s};
9 Z1 O6 Y6 w3 q9 t0 Sgettimeofday将时间保存在结构tv之中.tz一般我们使用NULL来代替.6 t% c" |1 f1 Q
#include <sys/time.h<
: }. ]: D; N, U" M6 V7 b9 c  Y#include <stdio.h<
! b% T! h7 L# G: d8 h4 q#include <math.h<
3 G8 e6 u; r: M, t  b. I* {9 {void function()
2 Y- K% _! i# h' A$ O6 O9 H{5 T: f4 _+ _8 X4 U. H
unsigned int i,j;
4 d! O7 d9 Y  E/ ]& @$ \* ], Edouble y;
& @2 o$ {* ~7 S" N6 J4 e+ tfor(i=0;i<1000;i++)
/ d' i3 S9 K9 W; i0 ~  F1 w) Efor(j=0;j<1000;j++). I# O! Y# A5 v1 d5 O# l
y=sin((double)i);7 }/ @. g3 c- h' G
}8 V* e/ ~! K+ T5 r
main()# N. F1 l* i+ A; W0 v4 ^0 }
{1 y) L* E/ \4 e$ x5 T4 l
struct timeval tpstart,tpend;
# f# b. O8 O' V1 c7 M2 j# U0 [1 {float timeuse;' t) `2 T/ {/ {3 ^& r2 \) Y, x
gettimeofday(&tpstart,NULL);- O  G  P6 |# q( X7 y
function();
8 ]0 o9 H" f" @% Q0 v- E/ A9 m6 Zgettimeofday(&tpend,NULL);- s6 _0 ?+ {" ~5 G) E$ P
timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+
' E& M" s, v# g( C# i( ^+ Itpend.tv_usec-tpstart.tv_usec;
' S8 d3 A% a% G$ p) Htimeuse/=1000000;( I* g# t, I/ K9 x9 T+ n
printf("Used Time:%f\n",timeuse);0 \. S* }9 K2 G" o# U9 e
exit(0);
, i0 _9 c* ?" X}
: X! X- R2 _; T/ y$ {/ W这个程序输出函数的执行时间,我们可以使用这个来进行系统性能的测试,或者是函数算法的效率分析.在我机器上的一个输出结果是: Used Time:0.556070
3 u1 U5 {  H9 f" r1 g
) Z! |3 U, ^/ `9 r( p3 L3。计时器的使用 Linux操作系统为每一个进程提供了3个内部间隔计时器.% K% d, d% s% q4 s3 a2 y& ?  H
ITIMER_REAL:减少实际时间.到时的时候发出SIGALRM信号.0 G& r- B1 G+ m, u5 {1 B7 {/ E
ITIMER_VIRTUAL:减少有效时间(进程执行的时间).产生SIGVTALRM信号.
/ S3 e* }$ ^0 m8 QITIMER_PROF:减少进程的有效时间和系统时间(为进程调度用的时间).这个经常和上面一个使用用来计算系统内核时间和用户时间.产生SIGPROF信号.
- j5 ?# d) K, {6 [具体的操作函数是:8 U; F& b6 ^8 c# T( o
#include <sys/time.h>5 W3 i& J1 I) ]% h
int getitimer(int which,struct itimerval *value);# `. M0 z$ k# y! t; v
int setitimer(int which,struct itimerval *newval,: V4 V& {; w2 J8 j
struct itimerval *oldval);
% g5 W( m& K8 N# k( astruct itimerval {
6 o! J% m/ o5 i1 ustruct timeval it_interval;
6 f" b5 Q. C- ~  `struct timeval it_value;
, I6 k* s) N) W% i$ [- y3 z" |}9 d, E/ w- f: G* @
getitimer函数得到间隔计时器的时间值.保存在value中 setitimer函数设置间隔计时器的时间值为newval.并将旧值保存在oldval中. which表示使用三个计时器中的哪一个.itimerval结构中的it_value是减少的时间,当这个值为0的时候就发出相应的信号了. 然后设置为it_interval值.
8 ]& u% ]# ]' @#include <sys/time.h>
9 F! S! f( B2 g/ }4 Y3 Y#include <stdio.h>3 t, V3 z; y* e  c* z; ~2 [: n
#include <unistd.h>* c' R4 d9 j1 N4 {1 d
#include <signal.h>
9 f+ H8 i1 S. I" [7 f6 d- m3 A#include <string.h>
1 F# f+ _5 h4 ^1 ]#define PROMPT "时间已经过去了两秒钟\n\a"
2 c7 r8 N% k, I; @6 ^! l1 Q! rchar *prompt=PROMPT;
$ Q+ j: S) B5 I6 {/ Ounsigned int len;4 v6 k7 w# R5 g8 b
void prompt_info(int signo)
5 a7 M8 a9 a6 M- q4 B$ D7 c{
% ^( f, N& k( ewrite(STDERR_FILENO,prompt,len);/ T9 t5 M" `% j- G) b" O& F
}5 ^! |- d7 p3 ^
void init_sigaction(void)
1 x( m# y( k6 Y' e" [- Y* P+ Y{
0 ~, O$ H/ y5 Sstruct sigaction act;4 ^- B$ F" e8 U9 g7 b
act.sa_handler=prompt_info;. F* Q5 x4 ^2 [& D+ d; }- y+ h; R
act.sa_flags=0;0 I% f% x# c- s4 H0 i( O0 ^6 k
sigemptyset(&act.sa_mask);- n* B% s; P( f7 |3 R/ O% ^
sigaction(SIGPROF,&act,NULL);& r! d: q- s0 d
}
2 D, `- p. {& }  Rvoid init_time()
) P+ H; [+ e% {& z& [{
6 j6 Y8 R. o/ \) k; |& Q* Pstruct itimerval value;* D. ]; x, C) t. {" q( M
value.it_value.tv_sec=2;
4 {  g5 n! \" V9 g: x8 P9 |value.it_value.tv_usec=0;6 o1 W8 x4 R2 [; R" [
value.it_interval=value.it_value;; ^- N) p3 c# b/ X% ?' t1 x
setitimer(ITIMER_PROF,&value,NULL);3 [+ j0 e; J) P7 b2 R$ B
}; {! L/ }9 ~& ?" M  v
int main()3 J; H0 A/ L% V) K2 T+ J
{
2 s6 m/ m$ F, m$ K; blen=strlen(prompt);
3 \" Y. m4 H1 {* |6 w/ \% ]1 yinit_sigaction();
* u( d& x$ G( ?6 Rinit_time();
/ L  u; |- a# I3 Fwhile(1);
! f- x) s2 S4 v# _+ f3 p8 l4 \exit(0);6 K$ g6 ], e8 S) `% Y
}
0 ?- @, c3 n5 |$ r  b5 C4 I/ m这个程序每执行两秒中之后会输出一个提示.
With your idea, Carry out together.

TOP

发新话题