﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-勤奋、严谨、求实、创新-文章分类-Linux/UNIX编程</title><link>http://www.cnitblog.com/Lionsir/category/306.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 29 Sep 2011 22:51:34 GMT</lastBuildDate><pubDate>Thu, 29 Sep 2011 22:51:34 GMT</pubDate><ttl>60</ttl><item><title>Linux 下 C 编程从零开始</title><link>http://www.cnitblog.com/Lionsir/articles/911.html</link><dc:creator>葡萄</dc:creator><author>葡萄</author><pubDate>Tue, 12 Jul 2005 17:40:00 GMT</pubDate><guid>http://www.cnitblog.com/Lionsir/articles/911.html</guid><wfw:comment>http://www.cnitblog.com/Lionsir/comments/911.html</wfw:comment><comments>http://www.cnitblog.com/Lionsir/articles/911.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/Lionsir/comments/commentRss/911.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/Lionsir/services/trackbacks/911.html</trackback:ping><description><![CDATA[这里向大家介绍一下在Linux/UNIX 的机器上，进行 C/C++ 编程的一些入门级知识。<BR><BR>· 所需具备的背景知识<BR>· 开发所需的基本环境<BR>· 获得帮助的途径<BR>· 通过一个实例了解基本步骤<BR><BR><BR>Prerequisite 先决条件：<BR>在Linux上编写 C 程序，至少要熟悉以下两方面的基础知识：<BR><BR>1． C语言的编程基础，至少要知道一些基本的语法，控制流程等编程常识。<BR>对常用的标准 C 函数库有常识性的了解。<BR><BR>2． 对Linux/UNIX 的操作有常识性的了解，掌握常用的shell 命令，如 ls, cat, cp, mkdir …etc.<BR><BR><BR>Environment 所需环境:<BR><BR>1． Linux/ Unix 的操作系统，也可以使用windows下的cygwin。<BR><BR>我们这里讨论的都是通过shell命令行进行操作的。那如果进入了图形界面的Linux 怎么办呢？只要打开一个终端命令，就和命令行环境完全一样了（打开开始菜单可以找到终端命令）。<BR><BR>2． 必备的开发工具：<BR><BR>1) 输入程序需要一个编辑器。常用的有 vi , emacs. 在命令行上输入 vi, emacs, … 就可进入编辑环境<BR>关于 vi <BR>关于 EMACS <BR><BR>2) C语言的编译器。常用的是GNU的c语言编译器 gcc(编译 C 程序), g++(编译 C ++程序)。<BR>关于 gcc / g++ <BR>关于 makefile &gt;&gt;&gt;&gt; 用于简化编译过程<BR>这里有一片入门文章 Linux下C语言编程基础知识，可以先看一下<BR><BR>3) 调试程序的常用工具：gdb.<BR>关于 gdb<BR><BR><BR>Get help获得帮助：<BR><BR>关于 Linux 的文档是非常丰富的。最快捷，方便，全面的资料就在你的机器里，不要浪费。<BR><BR>在命令行上输入shell命令 man 或者 info： <BR>$man gcc &gt;&gt;&gt;&gt; 这个命令可以获得GNU 的C语言编译器的文档。当然，他们是英文的。<BR>关于 man<BR>关于 info<BR><BR>网络上的资源也很多，多得以至于不知道什么才是自己最需要的。<BR>关于如何获得有价值的信息<BR><BR>看一下 loveunix 上的相关资源 &gt;&gt;&gt;&gt;<BR><BR>无双补充的一些Linux下入门编程资料 &gt;&gt;&gt;&gt;<BR><BR>　<BR><BR>Basic steps 基本步骤：<BR><BR>1． 输入源代码<BR>2． 编译，链接，运行<BR>3． 调试程序<BR><BR>我们从最基本的 hello world 程序开始，实际操作一下：<BR><BR>1． 输入源代码<BR>引用&nbsp;&nbsp;<BR><BR>$ emacs hello.c&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &gt;&gt;&gt;&gt; 进入emacs 编辑器环境<BR><BR>#include &lt;stdio.h&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&gt;&gt;&gt;&gt; 如果你看不懂这个函数，就去好好的看c语言的书<BR>int main()<BR>{<BR>printf(“Hello World.\n”);&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&gt;&gt;&gt;&gt; Emacs环境下，按下 Tab 键，有自动缩进功能<BR>exit(0);<BR>} <BR><BR><BR>完成输入后，按住CTRL 键，按下 x, 再按下 c , 最后松开 CTRL。 &gt;&gt;&gt;&gt; 程序保留并退出emacs 环境。 <BR><BR>2． 编译，链接，运行<BR><BR>引用&nbsp;&nbsp;<BR>$ gcc –o hello hello.c<BR>$ ./hello&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&gt;&gt;&gt;&gt; ./ 指明了所执行程序的路径<BR>Hello World. <BR>$ <BR><BR>一个linux平台上的c程序开发已经完成咯<BR><BR>3． 调试<BR><BR>如果要使用 gdb 调试程序，那么在上一步编译的时候，记得加上 –g 选项<BR><BR><BR>引用&nbsp;&nbsp;<BR>$ gcc –g –o hello hello.c<BR>$ gdb hello&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &gt;&gt;&gt;&gt; 进入 gdb 调试环境 <BR><BR>　<BR><BR>4. CVS版本控制软件 —— 协同工作和保留版本的工具<BR>关于 CVS<BR><img src ="http://www.cnitblog.com/Lionsir/aggbug/911.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/Lionsir/" target="_blank">葡萄</a> 2005-07-13 01:40 <a href="http://www.cnitblog.com/Lionsir/articles/911.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux 的编程常识</title><link>http://www.cnitblog.com/Lionsir/articles/910.html</link><dc:creator>葡萄</dc:creator><author>葡萄</author><pubDate>Tue, 12 Jul 2005 17:36:00 GMT</pubDate><guid>http://www.cnitblog.com/Lionsir/articles/910.html</guid><wfw:comment>http://www.cnitblog.com/Lionsir/comments/910.html</wfw:comment><comments>http://www.cnitblog.com/Lionsir/articles/910.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/Lionsir/comments/commentRss/910.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/Lionsir/services/trackbacks/910.html</trackback:ping><description><![CDATA[1.4.1&nbsp;&nbsp;标准 (ANSI C, POSIX, SVID, XPG, ...)<BR>ANSI C：这一标准是 ANSI（美国国家标准局）于 1989 年制定的 C 语言标准。 后来被 ISO（国际标准化组织）接受为标准，因此也称为 ISO C。<BR>ANSI C 的目标是为各种操作系统上的 C 程序提供可移植性保证，而不仅仅限于 UNIX。 该标准不仅定义了 C 编程语言的语发和语义，而且还定义了一个标准库。这个库可以根据 头文件划分为 15 个部分，其中包括：字符类型 (&lt;ctype.h&gt;)、错误码 (&lt;errno.h&gt;)、 浮点常数 (&lt;float.h&gt;)、数学常数 (&lt;math.h&gt;)、标准定义 (&lt;stddef.h&gt;)、 标准 I/O (&lt;stdio.h&gt;)、工具函数 (&lt;stdlib.h&gt;)、字符串操作 (&lt;string.h&gt;)、 时间和日期 (&lt;time.h&gt;)、可变参数表 (&lt;stdarg.h&gt;)、信号 (&lt;signal.h&gt;)、 非局部跳转 (&lt;setjmp.h&gt;)、本地信息 (&lt;local.h&gt;)、程序断言 (&lt;assert.h&gt;) 等等。 <BR>POSIX：该标准最初由 IEEE 开发的标准族，部分已经被 ISO 接受为国际标准。该标准的具体内容 见 1.1.3。POSIX.1 和 POSIX.2 分别定义了 POSIX 兼容操作系统的 C 语言系统接口 以及 shell 和工具标准。这两个标准是通常提到的标准。 <BR>SVID：System V 的接口描述。System V 接口描述（SVID）是描述 AT&amp;&amp;;T Unix System V 操作 系统的文档，是对 POSIX 标准的扩展超集。 <BR>XPG：X/Open 可移植性指南。X/Open 可移植性指南（由 X/Open Company, Ltd.出版）， 是比 POSIX 更为一般的标准。X/Open 拥有 Unix 的版权，而 XPG 则指定成为 Unix 操作系统必须满足的要求。 <BR>1.4.2&nbsp;&nbsp;函数库和系统调用<BR>1. glibc<BR>众所周知，C 语言并没有为常见的操作，例如输入/输出、内存管理，字符串操作等提供内置的支持。相反，这些功能一般由标准的“函数库”来提供。GNU 的 C 函数库，即 glibc，是 Linux 上最重要的函数库，它定义了 ISO C 标准指定的所有的库函数，以及由 POSIX 或其他 UNIX 操作系统统变种指定的附加特色，还包括有与 GNU 系统相关的扩展。目前，流行的 Linux 系统使用 glibc 2.0 以上的版本。glibc 基于如下标准：<BR><BR>ISO C: C 编程语言的国际标准，即 ANSI C。 <BR>POSIX：GNU C 函数库实现了 ISO/IEC 9945-1:1996 （POSIX 系统应用程序编程接口， 即 POSIX.1）指定的所有函数。该标准是对 ISO C 的扩展，包括文件系统接口原 语、设备相关的终端控制函数以及进程控制函数。同时，GUN C 函数库还支持部分由 ISO/IEC 9945-2:1993（POSIX Shell 和 工具标准，即 POSIX.2）指定的函数， 其中包括用于处理正则表达式和模式匹配的函数。 <BR>Berkeley Unix：BSD 和 SunOS。GNU C 函数库定义了某些 UNIX 版本中尚未标准化的函数， 尤其是 4.2 BSD, 4.3 BSD, 4.4 BSD Unix 系统（即“Berkeley Unix”)以及“SunOS” （流行的 4.2 BSD 变种，其中包含有某些 Unix System V 的功能）。BSD 函数包括 符号链接、select 函数、BSD 信号处理函数以及套接字等等。 <BR>SVID：System V 的接口描述。GNU C 函数库定义了大多数由 SVID 指定而未被 ISO C 和 POSIX 标准指定的函数。来自 System V 的支持函数包括进程间通信和共享内存、 hsearch 和 drand48 函数族、fmtmsg 以及一些数学函数。 <BR>XPG：X/Open 可移植性指南。GNU C 函数库遵循 X/Open 可移植性指南（Issue 4.2） 以及所有的 XSI（X/Open 系统接口）兼容系统的扩展，同时也遵循所有的 X/Open Unix 扩展。 <BR>2. 其他重要函数库<BR>除 glibc 之外，流行的 Linux 发行版中还包含有一些其他的函数库，这些函数库具有重要地位，例如：<BR><BR>GNU Libtool：GNU Libtool 实际是一个脚本生成工具，它可以为软件包开发者提供一般性 的共享库支持。<BR>以前，如果源代码包的开发者要利用共享库的优点，则必须为每个软件包可支持的平台编写 定制的支持代码。并且还需要设计配置接口，以便软件包的安装程序能够正确选择要建立的 库类型。利用 GNU Libtool，则可以简化开发者的这一工作。它在一个单独的脚本中同时封装 了与平台相关的依赖性以及用户界面。GNU Libtool 可使每个宿主类型的完整功能可通过 一般性的接口获得，同时为程序员隐藏了宿主的特殊性。GNU Libtool 一致性接口是可靠的， 用户不必阅读那些晦涩的文档，以便在每个平台上建立共享库。他们只需运行软件包的配置 脚本，而由 libtool 完成繁复的工作。 <BR>CrackLib：CrackLib 为用户提供了一个 C 语言函数接口，利用这一函数，可避免用户选择 容易破解的密码。该函数库可在类似 passwd 的程序中使用。 <BR>LibGTop：LibGTop 是一个能够获取进程信息以及系统运行信息的函数库，这些信息包括： 系统的一般信息、SYS V IPC 限制、进程列表、进程信息、进程映射、文件系统使用信息等。 <BR>图形文件操作函数库：包括 libungif、libtiff、libpng、Imlib, libjpeg 等，可分别用来操作 GIF、TIFF、PNG、JPEG 以及其他一些格式图形文件。 <BR>3. 系统调用<BR>系统调用是操作系统提供给外部程序的接口。在 C 语言中，操作系统的系统调用通常通过函数调用的形式完成，这是因为这些函数封装了系统调用的细节，将系统调用的入口、参数以及返回值用 C 语言的函数调用过程实现。在 Linux 系统中，系统调用函数定义在 glibc 中。 <BR><BR>谈到系统调用时，需要注意如下几点：<BR><BR>系统调用函数通常在成功时返回 0 值，不成功时返回非零值。如果要检查失败原因，则 要判断 errno 这个全局变量的值，errno 中包含有错误代码。 <BR>许多系统调用的返回数据通常通过引用参数传递。这时，需要在函数参数中传递一个 缓冲区地址，而返回的数据就保存在该缓冲区中。 <BR>不能认为系统调用函数就要比其他函数的执行效率高。要注意，系统调用是一个非常耗时 的过程。 <BR>有关系统调用我们将在以后详细讲述。 <BR><BR>1.4.3&nbsp;&nbsp;在线文档 (man, info, HOW-TO, ...)<BR>1. man<BR>man，即 manunal，是 UNIX 系统手册的电子版本。根据习惯，UNIX 系统手册通常分为不同的部分（或小节，即 section），每个小节阐述不同的系统内容。目前的小节划分如下： <BR><BR>命令：普通用户命令 <BR>系统调用：内核接口 <BR>函数库调用：普通函数库中的函数 <BR>特殊文件：/dev 目录中的特殊文件 <BR>文件格式和约定：/etc/passwd 等文件的格式 <BR>游戏。 <BR>杂项和约定：标准文件系统布局、手册页结构等杂项内容 <BR>系统管理命令。 <BR>内核例程：非标准的手册小节。便于 Linux 内核的开发而包含 <BR>其他手册小节：<BR><BR>l: PostgreSQL 数据库命令 <BR>n: TCL/TK 命令 <BR>手册页一般保存在 /usr/man 目录下，其中每个子目录（如 man1, man2, ..., manl, mann）包含不同的手册小节。使用 man 命令查看手册页。<BR><BR>man 命令行：<BR>man [-acdfFhkKtwW] [-m system] [-p string] [-C config_file] [-M path] [-P pager] [-S section_list] [section] name <BR><BR>常用命令行：<BR>$ man open<BR>$ man 7 man<BR>$ man ./myman.3<BR><BR><BR>2. info<BR>Linux 中的大多数软件开发工具都是来自自由软件基金会的 GNU 项目，这些工具软件件的在线文档都以 info 文件的形式存在。info 程序是 GNU 的超文本帮助系统。 <BR><BR>info 文档一般保存在 /usr/info 目录下，使用 info 命令查看 info 文档。<BR><BR>要运行 info，可以在 shell 提示符后输入 info，也可以在 GNU 的 emacs 中键入 Esc-x 后跟 info。 <BR><BR>info 帮助系统的初始屏幕显示了一个主题目录，你可以将光标移动到带有 * 的主题菜单上面，然后按回车键<BR>进入该主题，也可以键入 m，后跟主题菜单的名称而进入该主题。例如，你可以键入 m，然后再键入 gcc 而进<BR>进入 gcc 主题中。<BR>如果你要在主题之间跳转，则必须记住如下的几个命令键：<BR>* n：跳转到该节点的下一个节点；<BR>* p：跳转到该节点的上一个节点；<BR>* m: 指定菜单名而选择另外一个节点；<BR>* f：进入交叉引用主题；<BR>* l：进入该窗口中的最后一个节点；<BR>* TAB：跳转到该窗口的下一个超文本链接；<BR>* RET：进入光标处的超文本链接；<BR>* u：转到上一级主题；<BR>* d：回到 info 的初始节点目录；<BR>* h：调出 info 教程；<BR>* q：退出 info。<BR><BR>#DEMO#<BR><BR>3. HOW-TO<BR>可供用户参考的联机文档的另一种形式是 HOWTO 文件，这些文件位于系统的 /usr/doc/HOWTO 目录下。 HOWTO 文件的文件名都有一个 -HOWTO 后缀，并且都是文本文件。<BR><BR>每一个 HOWTO 文件包含 Linux 某一方面的信息，例如它支持的硬件或如何建立一个引导盘。 <BR><BR>要想查看这些文件，进入 /usr/doc/HOWTO 目录，使用 more 命令，具体形式如下：<BR>$ cd /usr/doc/HOWTO; more topic-name-HOWTO <BR><BR>另外，HOWTO 文档还有其他格式的文件，例如 HTML 和 PS 等，保存在 /usr/doc/HOWTO/other-formats 下。 <BR><BR>4. 其他<BR>Linux 的内核文档一般包含在内核源代码中，目录如下：/usr/src/linux-2.x.x/Documentation <BR><BR>/usr/doc 目录下包含有大量与特定软件或函数库相关的说明性文档。 <BR><BR>1.4.4&nbsp;&nbsp;C 语言编程风格<BR>编写这一小节的目的是提醒大家在编程过程中注意编程风格。如果你只是在编写一些小的练习程序，程序只有一两百行长的话，编程风格可能并不重要。然而，如果你和许多人一起进行开发工作，或者，你希望在过一段时间之后，还能够正确理解自己的程序的话，就必须养成良好的编程习惯。在诸多编程习惯当中，编程风格是最重要的一项内容。 <BR><BR>良好的编程风格可以在许多方面帮助开发人员。如果你阅读过 Linux 内核源代码的话，可能会对程序的优美编排所倾倒。良好的编程风格可以增加代码的可读性，并帮助你理清头绪。如果程序非常杂乱，大概看一眼就该让你晕头转向了。编程风格最能体现一个程序员的综合素质。 <BR><BR>许多读者可能对 Windows 所推崇的匈牙利命名法很熟悉。这种方法定义了非常复杂的函数、变量、类型等的命名方法，典型的命名方法是采用大小写混写的方式，对于变量名称，则采用添加前缀的办法来表示其类型，例如:<BR>char szBuffer[20];<BR>int nCount;<BR>利用 sz 和 n 分别代表字符串和整数。为了表示一个变量名称，采用如下的变量名称是可能的：<BR>int iThisIsAVeryLongVariable; <BR><BR>在 Linux 中，我们经常看到的是定义非常简单的函数接口和变量名称。在 Linux 内核的源代码中，可以看到 Linux 内核源代码的编码风格说明（/ Documentation/CodingStyle）。UNIX 系统的一个特点是设计精巧，并遵守积木式原则。C 语言最初来自 UNIX 操作系统，与 UNIX 的设计原则一样， C 语言被广泛认可和使用的一个重要原因是它的灵活性以及简洁性。因此，在利用 C 语言编写程序时，始终应当符合其简洁的设计原则，而不应当使用非常复杂的变量命名方法。Linus 为 Linux 内核定义的 C 语言编码风格要点如下： <BR><BR>缩进时，使用长度为 8 个字符宽的 Tab 键。如果程序的缩进超过 3 级，则应考虑重新设计程序。 <BR>大括号的位置。除函数的定义体外，应当将左大括号放在行尾，而将右大括号放在行首。函数的定义体应将左右大括号放在行首。如下所示：<BR>int function(int x, int y)<BR>{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (x == y) {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ...<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;} else if (x &gt; y) {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ...<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;} else {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ...<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return 0;<BR>}<BR><BR>应采用简洁的命名方法。对变量名，不赞成使用大小写混写的形式，但鼓励使用描述性的名称；尽可能不使用全局变量；不采用匈牙利命名法表示变量的类型；采用短小精悍的名称表示局部变量；保持函数短小，从而避免使用过多的局部变量。 <BR>保持函数短小精悍。 <BR>不应过分强调注释的作用，应尽量采用好的编码风格而不是添加过多的注释。 <BR>1.4.5&nbsp;&nbsp;库和头文件的保存位置<BR>1. 函数库<BR>/lib：系统必备共享库 <BR>/usr/lib：标准共享库和静态库 <BR>/usr/i486-linux-libc5/lib：libc5 兼容性函数库 <BR>/usr/X11R6/lib：X11R6 的函数库 <BR>/usr/local/lib：本地函数库 <BR>2. 头文件<BR>/usr/include：系统头文件 <BR>/usr/local/include：本地头文件 <BR>1.4.6&nbsp;&nbsp;共享库及其相关配置<BR>/etc/ld.so.conf：包含共享库的搜索位置 <BR>ldconfig：共享库管理工具，一般在更新了共享库之后要运行该命令 <BR>ldd：可查看可执行文件所使用的共享库<img src ="http://www.cnitblog.com/Lionsir/aggbug/910.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/Lionsir/" target="_blank">葡萄</a> 2005-07-13 01:36 <a href="http://www.cnitblog.com/Lionsir/articles/910.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GCC使用</title><link>http://www.cnitblog.com/Lionsir/articles/909.html</link><dc:creator>葡萄</dc:creator><author>葡萄</author><pubDate>Tue, 12 Jul 2005 17:33:00 GMT</pubDate><guid>http://www.cnitblog.com/Lionsir/articles/909.html</guid><wfw:comment>http://www.cnitblog.com/Lionsir/comments/909.html</wfw:comment><comments>http://www.cnitblog.com/Lionsir/articles/909.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/Lionsir/comments/commentRss/909.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/Lionsir/services/trackbacks/909.html</trackback:ping><description><![CDATA[摘要: <BR><BR>要想读懂本文，你需要对C语言有基本的了解，本文将介绍如何使用gcc编译器。<BR>首先，我们介绍如何在命令行方式下使用编译器编译简单的C源代码。然后，我<BR>们简要介绍一下编译器究竟作了那些工作，以及如何控制编译过程。我们也简要<BR>介绍了调试器的使用方法。 <BR><BR>GCC rules <BR>你能想象使用封闭源代码的私有编译器编译自由软件吗？你怎么知道编译器在你<BR>的可执行文件中加入了什么？可能会加入各种后门和木马。Ken Thompson是一个<BR>著名的黑客，他编写了一个编译器，当编译器编译自己时，就在'login'程序中<BR>留下后门和永久的木马。幸运的是，我们有了gcc。当你进行 configure; make;<BR>make install 时， gcc在幕后做了很多繁重的工作。如何才能让gcc为我们工作<BR>呢？我们将开始编写一个纸牌游戏，不过我们只是为了演示编译器的功能，所以<BR>尽可能地精简了代码。我们将从头开始一步一步地做，以便理解编译过程，了解<BR>为了制作可执行文件需要做些什么，按什么顺序做。我们将看看如何编译C程序<BR>，以及如何使用编译选项让gcc按照我们的要求工作。步骤（以及所用工具）如<BR>下： 预编译 (gcc -E)， 编译 (gcc)， 汇编 (as)，和 连接 (ld)。 <BR><BR><BR><BR><BR><BR>开始... <BR><BR>首先，我们应该知道如何调用编译器。实际上，这很简单。我们将从那个著名的<BR>第一个C程序开始。（各位老前辈，请原谅我）。 <BR>#include &lt;stdio.h&gt;<BR>int main()<BR>{<BR>&nbsp;&nbsp;printf("Hello World!\n");<BR>}<BR>把这个文件保存为 game.c。 你可以在命令行下编译它： <BR>gcc game.c<BR>在默认情况下，C编译器将生成一个名为 a.out 的可执行文件。你可以键入如下<BR>命令运行它： <BR>a.out<BR>Hello World<BR>每一次编译程序时，新的 a.out 将覆盖原来的程序。你无法知道是哪个程序创<BR>建了 a.out。我们可以通过使用 -o 编译选项，告诉 gcc我们想把可执行文件叫<BR>什么名字。我们将把这个程序叫做 <BR>game，我们可以使用任何名字，因为C没有Java那样的命名限制。 <BR>gcc -o game game.c<BR>game<BR>Hello World<BR><BR>到现在为止，我们离一个有用的程序还差得很远。如果你觉得沮丧，你可以想一<BR>想我们已经编译并运行了一个程序。因为我们将一点一点为这个程序添加功能，<BR>所以我们必须保证让它能够运行。似乎每个刚开始学编程的程序员都想一下子编<BR>一个1000行的程序，然后一次修改所有的错误。没有人，我是说没有人，能做到<BR>这个。你应该先编一个可以运行的小程序，修改它，然后再次让它运行。这可以<BR>限制你一次修改的错误数量。另外，你知道刚才做了哪些修改使程序无法运行，<BR>因此你知道应该把注意力放在哪里。这可以防止这样的情况出现：你认为你编写<BR>的东西应该能够工作，它也能通过编译，但它就是不能运行。请切记，能够通过<BR>编译的程序并不意味着它是正确的。 <BR>下一步为我们的游戏编写一个头文件。头文件把数据类型和函数声明集中到了一<BR>处。这可以保证数据结构定义的一致性，以便程序的每一部分都能以同样的方式<BR>看待一切事情。 <BR><BR>#ifndef DECK_H<BR>#define DECK_H<BR><BR>#define DECKSIZE 52<BR>typedef struct deck_t<BR>{<BR>&nbsp;&nbsp;int card[DECKSIZE];<BR>&nbsp;&nbsp;/* number of cards used */<BR>&nbsp;&nbsp;int dealt;<BR>}deck_t;<BR><BR>#endif /* DECK_H */<BR>把这个文件保存为 deck.h。只能编译 .c 文件，所以我们必须修改 game.c。在<BR>game.c的第2行，写上 #include "deck.h"。在第5行写上 deck_t deck;。为了<BR>保证我们没有搞错，把它重新编译一次。 <BR>gcc -o game game.c<BR>如果没有错误，就没有问题。如果编译不能通过，那么就修改它直到能通过为止<BR>。 <BR><BR>预编译 <BR>编译器是怎么知道 deck_t <BR><BR>类型是什么的呢？因为在预编译期间，它实际上把"deck.h"文件复制到了"game.<BR>c"文件中。源代码中的预编译指示以"#"为前缀。你可以通过在gcc后加上 -E 选<BR>项来调用预编译器。 <BR>gcc -E -o game_precompile.txt game.c<BR>wc -l game_precompile.txt<BR>&nbsp;&nbsp;3199 game_precompile.txt<BR>几乎有3200行的输出！其中大多数来自 stdio.h 包含文件，但是如果你查看这<BR>个文件的话，我们的声明也在那里。如果你不用 -o 选项指定输出文件名的话.<BR>它就输出到控制台。预编译过程通过完成三个主要任务给了代码很大的灵活性。 <BR><BR>1. 把"include"的文件拷贝到要编译的源文件中。 <BR>2. 用实际值替代"define"的文本。 <BR>3. 在调用宏的地方进行宏替换。 <BR><BR>这就使你能够在整个源文件中使用符号常量（即用DECKSIZE表示一付牌中的纸牌<BR>数量），而符号常量是在一个地方定义的，如果它的值发生了变化，所有使用符<BR>号常量的地方都能自动更新。在实践中，你几乎不需要单独使用 -E 选项，而是<BR>让它把输出传送给编译器。 <BR><BR>编译 <BR><BR><BR>作为一个中间步骤，gcc把你的代码翻译成汇编语言。它一定要这样做，它必须<BR>通过分析你的代码搞清楚你究竟想要做什么。如果你犯了语法错误，它就会告诉<BR>你，这样编译就失败了。人们有时会把这一步误解为整个过程。但是，实际上还<BR>有许多工作要gcc去做呢。 <BR><BR>汇编 <BR><BR>as 把汇编语言代码转换为目标代码。事实上目标代码并不能在CPU上运行，但它<BR>离完成已经很近了。编译器选项 -c 把 .c 文件转换为以 .o 为扩展名的目标文<BR>件。 如果我们运行 <BR>gcc -c game.c<BR>我们就自动创建了一个名为game.o的文件。这里我们碰到了一个重要的问题。我<BR>们可以用任意一个 .c 文件创建一个目标文件。正如我们在下面所看到的，在连<BR>接步骤中我们可以把这些目标文件组合成可执行文件。让我们继续介绍我们的例<BR>子。因为我们正在编写一个纸牌游戏，我们已经把一付牌定义为 deck_t，我们<BR>将编写一个洗牌函数。这个函数接受一个指向deck类型的指针，并把一付随机的<BR>牌装入deck类型。它使用'drawn' <BR>数组跟踪记录那些牌已经用过了。这个具有DECKSIZE个元素的数组可以防止我们<BR>重复使用一张牌。 <BR>#include &lt;stdlib.h&gt;<BR>#include &lt;stdio.h&gt;<BR>#include &lt;time.h&gt;<BR>#include "deck.h"<BR><BR>static time_t seed = 0;<BR><BR>void shuffle(deck_t *pdeck)<BR>{<BR>&nbsp;&nbsp;/* Keeps track of what numbers have been used */<BR>&nbsp;&nbsp;int drawn[DECKSIZE] = ;<BR>&nbsp;&nbsp;int i;<BR><BR>&nbsp;&nbsp;/* One time initialization of rand */<BR>&nbsp;&nbsp;if(0 == seed)<BR>&nbsp;&nbsp;{<BR>&nbsp; &nbsp; seed = time(NULL);<BR>&nbsp; &nbsp; srand(seed);<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;for(i = 0; i &lt; DECKSIZE; i++)<BR>&nbsp;&nbsp;{<BR>&nbsp; &nbsp; int value = -1;<BR>&nbsp; &nbsp; do<BR>&nbsp; &nbsp; {<BR>&nbsp; &nbsp;&nbsp; &nbsp;value = rand() % DECKSIZE;<BR>&nbsp; &nbsp; }<BR>&nbsp; &nbsp; while(drawn[value] != 0);<BR><BR>&nbsp; &nbsp; /* mark value as used */<BR>&nbsp; &nbsp; drawn[value] = 1;<BR><BR>&nbsp; &nbsp; /* debug statement */<BR>&nbsp; &nbsp; printf("%i\n", value);<BR>&nbsp; &nbsp; pdeck-&gt;card[i] = value;<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;pdeck-&gt;dealt = 0;<BR>&nbsp;&nbsp;return;<BR>}<BR>把这个文件保存为 shuffle.c。我们在这个代码中加入了一条调试语句，以便运<BR>行时，能输出所产生的牌号。这并没有为我们的程序添加功能，但是现在到了关<BR>键时刻，我们看看究竟发生了什么。因为我们的游戏还在初级阶段，我们没有别<BR>的办法确定我们的函数是否实现了我们要求的功能。使用那条printf语句，我们<BR>就能准确地知道现在究竟发生了什么，以便在开始下一阶段之前我们知道牌已经<BR>洗好了。在我们对它的工作感到满意之后，我们可以把那一行语句从代码中删掉<BR>。这种调试程序的技术看起来很粗糙，但它使用最少的语句完成了调试任务。以<BR>后我们再介绍更复杂的调试器。 <BR>请注意两个问题。 <BR><BR>1. 我们用传址方式传递参数，你可以从'&amp;'（取地址）操作符看出来。这把变量<BR>的机器地址传递给了函数，因此函数自己就能改变变量的值。也可以使用全局变<BR>量编写程序，但是应该尽量少使用全局变量。指针是C的一个重要组成部分，你<BR>应该充分地理解它。 <BR>2. 我们在一个新的 .c 文件中使用函数调用。操作系统总是寻找名为'main'的<BR>函数，并从那里开始执行。 shuffle.c 中没有'main'函数，因此不能编译为独<BR>立的可执行文件。我们必须把它与另一个具有'main'函数并调用'shuffle'的程<BR>序组合起来。 <BR><BR><BR>运行命令 <BR>gcc -c shuffle.c<BR>并确定它创建了一个名为 shuffle.o 的新文件。编辑game.c文件，在第7行，在 <BR>deck_t类型的变量 deck 声明之后，加上下面这一行： <BR>shuffle(&amp;deck);<BR>现在，如果我们还象以前一样创建可执行文件，我们就会得到一个错误 <BR>gcc -o game game.c<BR><BR>/tmp/ccmiHnJX.o: In function `main':<BR>/tmp/ccmiHnJX.o(.text+0xf): undefined reference to `shuffle'<BR>collect2: ld returned 1 exit status<BR>编译成功了，因为我们的语法是正确的。但是连接步骤却失败了，因为我们没有<BR>告诉编译器'shuffle'函数在哪里。那么，到底什么是连接？我们怎样告诉编译<BR>器到哪里寻找这个函数呢？ <BR><BR>连接 <BR>连接器ld，使用下面的命令，接受前面由 as 创建的目标文件并把它转换为可执<BR>行文件 <BR>gcc -o game game.o shuffle.o<BR>这将把两个目标文件组合起来并创建可执行文件 game。 <BR>连接器从shuffle.o目标文件中找到 shuffle 函数，并把它包括进可执行文件。<BR>目标文件的真正好处在于，如果我们想再次使用那个函数，我们所要做的就是包<BR>含"deck.h" 文件并把 shuffle.o 目标文件连接到新的可执行文件中。 <BR>象这样的代码重用是经常发生的。虽然我们并没有编写前面作为调试语句调用的 <BR>printf 函数，连接器却能从我们用 #include &lt;stdlib.h&gt; 语句包含的文件中找<BR>到它的声明，并把存储在C库（/lib/libc.so.6）中的目标代码连接进来。这种<BR>方式使我们可以使用已能正确工作的其他人的函数，只关心我们所要解决的问题<BR>。这就是为什么头文件中一般只含有数据和函数声明，而没有函数体。一般，你<BR>可以为连接器创建目标文件或函数库，以便连接进可执行文件。我们的代码可能<BR>产生问题，因为在头文件中我们没有放入任何函数声明。为了确保一切顺利，我<BR>们还能做什么呢？ <BR><BR>另外两个重要选项 <BR>-Wall 选项可以打开所有类型的语法警告，以便帮助我们确定代码是正确的，并<BR>且尽可能实现可移植性。当我们使用这个选项编译我们的代码时，我们将看到下<BR>述警告： <BR>game.c:9: warning: implicit declaration of function `shuffle'<BR><BR><BR>这让我们知道还有一些工作要做。我们需要在头文件中加入一行代码，以便告诉<BR>编译器有关 shuffle 函数的一切，让它可以做必要的检查。听起来象是一种狡<BR>辩，但这样做 可以把函数的定义与实现分离开来，使我们能在任何地方使用我<BR>们的函数，只要包含新的头文件 并把它连接到我们的目标文件中就可以了。下<BR>面我们就把这一行加入deck.h中。 <BR>void shuffle(deck_t *pdeck);<BR>这就可以消除那个警告信息了。 <BR><BR><BR>另一个常用编译器选项是优化选项 -O# (即 -O2)。 这是告诉编译器你需要什么<BR>级别的优化。编译器具有一整套技巧可以使你的代码运行得更快一点。对于象我<BR>们这种小程序，你可能注意不到差别，但对于大型程序来说，它可以大幅度提高<BR>运行速度。你会经常碰到它，所以你应该知道它的意思。 <BR><BR>调试 <BR><BR><BR>&nbsp; &nbsp;我们都知道，代码通过了编译并不意味着它按我们得要求工作了。你可以使<BR>用下面的命令验证是否所有的号码都被使用了 <BR>game | sort - n | less<BR>　　并且检查有没有遗漏。如果有问题我们该怎么办？我们如何才能深入底层查<BR>找错误呢？ <BR>你可以使用调试器检查你的代码。大多数发行版都提供著名的调试器：gdb。如<BR>果那些众多的命令行选项让你感到无所适从，那么你可以使用KDE提供的一个很<BR>好的前端工具 KDbg。还有一些其它的前端工具，它们都很相似。要开始调试，<BR>你可以选择 File-&gt;Executable 然后找到你的 game 程序。当你按下F5键或选择 <BR>Execution-&gt;从菜单运行时，你可以在另一个窗口中看到输出。怎么回事？在那<BR>个窗口中我们什么也看不到。不要担心，KDbg没有出问题。问题在于我们在可执<BR>行文件中没有加入任何调试信息，所以KDbg不能告诉我们内部发生了什么。编译<BR>器选项 -g 可以把必要的调试信息加入目标文件。你必须用这个选项编译目标文<BR>件（扩展名为.o），所以命令行成了： <BR><BR>　　gcc -g -c shuffle.c game.c<BR>　　gcc -g -o game game.o shuffle.o<BR><BR>　　这就把钩子放入了可执行文件，使gdb和KDbg能指出运行情况。调试是一种<BR>很重要的技术，很值得你花时间学习如何使用。调试器帮助程序员的方法是它能<BR>在源代码中设置“断点”。现在你可以用右键单击调用 shuffle 函数的那行代<BR>码，试着设置断点。那一行边上会出现一个红色的小圆圈。现在当你按下F5键时<BR>，程序就会在那一行停止执行。按F8可以跳入shuffle函数。呵，我们现在可以<BR>看到 shuffle.c 中的代码了！我们可以控制程序一步一步地执行，并看到究竟<BR>发生了什么事。如果你把光标暂停在局部变量上，你将能看到变量的内容。太好<BR>了。这比那条 printf 语句好多了，是不是？ <BR><BR>&nbsp; &nbsp;&nbsp;&nbsp;小结 <BR><BR>　　本文大体介绍了编译和调试C程序的方法。我们讨论了编译器走过的步骤，<BR>以及为了让编译器做这些工作应该给gcc传递哪些选项。我们简述了有关连接共<BR>享函数库的问题，最后介绍了调试器。真正了解你所从事的工作还需要付出许多<BR>努力，但我希望本文能让你正确地起步。你可以在 gcc、 as 和 ld的 man 和 <BR>info page中找到更多的信息。 <BR><BR>　　自己编写代码可以让你学到更多的东西。作为练习你可以以本文的纸牌游戏<BR>为基础，编写一个21点游戏。那时你可以学学如何使用调试器。使用GUI的KDbg<BR>开始可以更容易一些。如果你每次只加入一点点功能，那么很快就能完成。切记<BR><BR>，一定要保持程序一直能运行！ <BR>　　要想编写一个完整的游戏，你需要下面这些内容： <BR>* 一个纸牌玩家的定义（即，你可以把deck_t定义为player_t）。 <BR>* 一个给指定玩家发一定数量牌的函数。记住在纸牌中要增加“已发牌”的数量，以便能知道还有那些牌可发。还要记住玩家手中还有多少牌。 <BR>* 一些与用户的交互，问问玩家是否还要另一张牌。 <BR>* 一个能打印玩家手中的牌的函数。 card 等于value % 13 （得数为0到12），<BR><BR>suit 等于 value / 13 （得数为0到3）。 <BR>* <BR><BR>一个能确定玩家手中的value的函数。Ace的value为零并且可以等于1或11。King的value为12并且可以等于10。<BR><img src ="http://www.cnitblog.com/Lionsir/aggbug/909.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/Lionsir/" target="_blank">葡萄</a> 2005-07-13 01:33 <a href="http://www.cnitblog.com/Lionsir/articles/909.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux 守护进程的编程</title><link>http://www.cnitblog.com/Lionsir/articles/883.html</link><dc:creator>葡萄</dc:creator><author>葡萄</author><pubDate>Tue, 12 Jul 2005 02:06:00 GMT</pubDate><guid>http://www.cnitblog.com/Lionsir/articles/883.html</guid><wfw:comment>http://www.cnitblog.com/Lionsir/comments/883.html</wfw:comment><comments>http://www.cnitblog.com/Lionsir/articles/883.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/Lionsir/comments/commentRss/883.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/Lionsir/services/trackbacks/883.html</trackback:ping><description><![CDATA[<P><SPAN id=ArticleContent1_ArticleContent1_lblContent>守护进程（Daemon）是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。Linux的大多数服务器就是用守护进程实现的。比如，Internet服务器inetd，Web服务器httpd等。同时，守护进程完成许多系统任务。比如，作业规划进程crond，打印进程lpd等。 <BR>守护进程的编程本身并不复杂，复杂的是各种版本的Unix的实现机制不尽相同，造成不同Unix环境下守护进程的编程规则并不一致。这需要读者注意，照搬某些书上的规则（特别是BSD4.3和低版本的System V）到Linux会出现错误的。下面将全面介绍Linux下守护进程的编程要点并给出详细实例。 <BR>一． 守护进程及其特性 <BR>守护进程最重要的特性是后台运行。在这一点上DOS下的常驻内存程序TSR与之相似。其次，守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符，控制终端，会话和进程组，工作目录以及文件创建掩模等。这些环境通常是守护进程从执行它的父进程（特别是shell）中继承下来的。最后，守护进程的启动方式有其特殊之处。它可以在Linux系统启动时从启动脚本/etc/rc.d中启动，可以由作业规划进程crond启动，还可以由用户终端（通常是shell）执行。 <BR>总之，除开这些特殊性以外，守护进程与普通进程基本上没有什么区别。因此，编写守护进程实际上是把一个普通进程按照上述的守护进程的特性改造成为守护进程。如果读者对进程有比较深入的认识就更容易理解和编程了。 <BR>二． 守护进程的编程要点 <BR>前面讲过，不同Unix环境下守护进程的编程规则并不一致。所幸的是守护进程的编程原则其实都一样，区别在于具体的实现细节不同。这个原则就是要满足守护进程的特性。同时，Linux是基于Syetem V的SVR4并遵循Posix标准，实现起来与BSD4相比更方便。编程要点如下； <BR>1. 在后台运行。 <BR>为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止，让Daemon在子进程中后台执行。 <BR>if(pid=fork()) <BR>exit(0);//是父进程，结束父进程，子进程继续 <BR>2. 脱离控制终端，登录会话和进程组 <BR>有必要先介绍一下Linux中的进程与控制终端，登录会话和进程组之间的关系：进程属于一个进程组，进程组号（GID）就是进程组长的进程号（PID）。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。 <BR>控制终端，登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们，使之不受它们的影响。方法是在第1点的基础上，调用setsid()使进程成为会话组长： <BR>setsid(); <BR>说明：当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后，进程成为新的会话组长和新的进程组长，并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性，进程同时与控制终端脱离。 <BR>3. 禁止进程重新打开控制终端 <BR>现在，进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端： <BR><BR>if(pid=fork()) <BR>exit(0);//结束第一子进程，第二子进程继续（第二子进程不再是会话组长） <BR>4. 关闭打开的文件描述符 <BR>进程从创建它的父进程那里继承了打开的文件描述符。如不关闭，将会浪费系统资源，造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们： <BR>for(i=0;i 关闭打开的文件描述符close(i);&gt; <BR>5. 改变当前工作目录 <BR>进程活动时，其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心，写运行日志的进程将工作目录改变到特定目录如/tmpchdir("/") <BR>6. 重设文件创建掩模 <BR>进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点，将文件创建掩模清除：umask(0); <BR>7. 处理SIGCHLD信号 <BR>处理SIGCHLD信号并不是必须的。但对于某些进程，特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束，子进程将成为僵尸进程（zombie）从而占用系统资源。如果父进程等待子进程结束，将增加父进程的负担，影响服务器进程的并发性能。在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN。 <BR>signal(SIGCHLD,SIG_IGN); <BR>这样，内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同，BSD4下必须显式等待子进程结束才能释放僵尸进程。 <BR>三． 守护进程实例 <BR>守护进程实例包括两部分：主程序test.c和初始化程序init.c。主程序每隔一分钟向/tmp目录中的日志test.log报告运行状态。初始化程序中的init_daemon函数负责生成守护进程。读者可以利用init_daemon函数生成自己的守护进程。 <BR>1． init.c清单 <BR><BR>#include &lt; unistd.h &gt; <BR>#include &lt; signal.h &gt; <BR>#include &lt; sys/param.h &gt; <BR>#include &lt; sys/types.h &gt; <BR>#include &lt; sys/stat.h &gt; <BR>void init_daemon(void) <BR>{ <BR>int pid; <BR>int i; <BR>if(pid=fork()) <BR>exit(0);//是父进程，结束父进程 <BR>else if(pid&lt; 0) <BR>exit(1);//fork失败，退出 <BR>//是第一子进程，后台继续执行 <BR>setsid();//第一子进程成为新的会话组长和进程组长 <BR>//并与控制终端分离 <BR>if(pid=fork()) <BR>exit(0);//是第一子进程，结束第一子进程 <BR>else if(pid&lt; 0) <BR>exit(1);//fork失败，退出 <BR>//是第二子进程，继续 <BR>//第二子进程不再是会话组长 <BR><BR>for(i=0;i&lt; NOFILE;++i)//关闭打开的文件描述符 <BR>close(i); <BR>chdir("/tmp");//改变工作目录到/tmp <BR>umask(0);//重设文件创建掩模 <BR>return; <BR>} <BR>2． test.c清单 <BR>#include &lt; stdio.h &gt; <BR>#include &lt; time.h &gt; <BR><BR>void init_daemon(void);//守护进程初始化函数 <BR><BR>main() <BR>{ <BR>FILE *fp; <BR>time_t t; <BR>init_daemon();//初始化为Daemon <BR><BR>while(1)//每隔一分钟向test.log报告运行状态 <BR>{ <BR>sleep(60);//睡眠一分钟 <BR>if((fp=fopen("test.log","a")) &gt;=0) <BR>{ <BR>t=time(0); <BR>fprintf(fp,"Im here at %s\n",asctime(localtime(&amp;t)) ); <BR>fclose(fp); <BR>} <BR>} <BR>} <BR>以上程序在RedHat Linux6.0下编译通过。步骤如下： <BR>编译：gcc -g -o test init.c test.c <BR>执行：./test <BR>查看进程：ps -ef <BR>从输出可以发现test守护进程的各种特性满足上面的要求</SPAN></P><img src ="http://www.cnitblog.com/Lionsir/aggbug/883.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/Lionsir/" target="_blank">葡萄</a> 2005-07-12 10:06 <a href="http://www.cnitblog.com/Lionsir/articles/883.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>