unix环境高级编程 APUE.H最简单编译方法(第二版) Linux和Ubuntu

/*******************************************************************************
*第 0 种-最简单实用
*
*******************************************************************************/.
1.直接进入源码目录的lib目录
 cd lib
2.执行make命令
 make -f linux.mk
3.把生成的libapue.a与apue.h拷贝到你的源代码目录。如你的file目录下
4.使用gcc -o ls1 ls1.c  libapue.a来编译你的源代码
5.成功

/*******************************************************************************
*第 1 种 方法大致同上
*
*******************************************************************************/.

《UNIX环境高级编程》(这里使用的是第二版本的源码)每个历程中,都会有这样一行源码:
#include "apue.h"
    这个头文件是作者把把每个例程中常用的标准头文件,一些常用的出错处理函数(err_**()之类的函

数)和一些常用的宏定义给整理在一个头文件中。这个可以省去在每个例程中录入较多的重复代码,这样可

以减少每个例程的长度。给读者带来了不少麻烦。下面给出一种源代码的编译方法。


1、解压文件到apue.2e目录
2、修改相应平台的文件,我使用的是linux,所以修改Make.defines.linux
你修改的只需要这一行WKDIR=/home/your_dir/apue2e_src/apue.2e,改成自己的目录路径
3、cd到apue.2e目录执行make -f linux.mk,之后你会在lib目录下面找到libapue.a这个文件.
现在,你可以把它拷贝到你能寻找的地方,在编写例子的时候,你就可以

4、拷贝apue2e_src/apue.2e/include/apue.h和apue2e_src/apue.2e/lib/libapue.a

到你的源代码目录。

5、使用gcc -o hello hello.c libapue.a来编译你的源代码

/*******************************************************************************
*第 2 种
*
*******************************************************************************/

最近在看apue的第二版,刚才在Linux下把随书的源代码编译了一遍,还是稍微花了点时间,作为备忘把编译过

程记录下来
随书的源代码可从www.apuebook.com上获得,下载后的解压得到名为apue.2e的目录,在我的系统中该目录的

完整路径为/home/se/apue.2e
接着首先是要阅读/home/se/apue.2e/README,这是由apue第二版的作者Steve Rago写的如何编译随书代码的

基本指导以及部分自本书第一版以来的更改,主要内容如下:
Some source changes needed to be made after the book went out for the first
printing.  I forgot to make corresponding changes in the source tree on the
system used to develop the book.  The changes are summarized below.
1. lib/recvfd.c and sockets/recvfd.c - needed sys/uio.h on Mac OS X
2. lib/sendfd.c and sockets/sendfd.c - needed sys/uio.h on Mac OS X
3. stdio/buf.c - added code for Mac OS X
4. threadctl/suspend.c - changed wait to waitloc to avoid symbol definition
 clash on Solaris
5. include/apue.h - FreeBSD compiles work better if we rely on the default
 system settings.  Solaris needed a different XOPEN_SOURCE definition
 and also a CMSG_LEN definition.
To build the source, edit the Make.defines.* file for your system and set
WKDIR to the pathname of the tree containing the source code.  Then just
run "make".  It should figure out the system type and build the source for
that platform automatically.  If you are running on a system other than
FreeBSD, Linux, Mac OS X, or Solaris, you'll need to modify the makefiles
to include the settings for your system.  Also, you'll probably need to
modify the source code to get it to build on a different operating system.
The example source was compiled and tested using FreeBSD 5.2.1, Linux 2.4.22,
Mac OS X 10.3, and Solaris 9.
For FAQs, updated source code, and the lost chapter, see http://www.apuebook.com.
Please direct questions, suggestions, and bug reports to [email protected].
 
基本内容就是你用的系统如果是FreeBSD,Linux,Mac OS X或是Solaris,那么你只要修改相应的

Make.defines.*文件(即如果你使用的是Linux,那么你需要修改 Make.defines.linux文件的内容),将其中的

设置改为你自己系统的设置然后在apue.2e目录下运行make就ok了.所有的代码都在 FreeBSD 5.2.1,Linux 

2.4.22,Mac OS X 10.3,Solaris 9上编译通过.
 
总的来说要编译成功是很简单的,但总会因为平台的不同会出现一些错误,这时你就要根据自己系统的配置情

况来进行修改了
1.首先粗略的看了一下makefile的内容,make首先会执行脚本文件systype.sh,判断所用系统的类型,然后根

据该类型选择对应的Make.defines文件.这里所要做的就是给systype.sh添加执行权限,chmod u+x 

systype.sh
2.因为我用的是Linux,所以先看Make.defines.linux,需要修改的地方是WKDIR=/home/sar/apue.2e,把WKDIR

改为你自己的工作目录,在我这就是改为WKDIR=/home/se/apue.2e,这个路径在编译时寻找"apue.h"头文件时

使用.

3.然后我尝试性的运行了一次make,果然有问题,在进入std目录后报错了,说找不到nawk命令,nawk是new 

awk,而我的系统上只有awk,这时你有两种选择,可以在运行make之前执行alias nawk='awk',这样本质上是给

awk取了个叫nawk的别名,实际上运行的还是awk,另一种方法就是修改WKDIR/std /linux.mk,把第10行和15行

中的nawk都改为awk,至于什么是awk和nawk,以及它们的使用方法可以参考我之前收藏的一篇文章

http://www.360doc.com/showWeb/0/0/308938.aspx
在这里,awk用来分别从makeconf.awk和makeopt.awk生成conf.c和options.c源文件,注意,在修改了linux.mk

或是添加了alias之后要先把之前make失败时生成的conf.c和options.c删除,否则会报错

4.进行了上述的修改后,回到WKDIR,运行make,ok,没有报错,编译成功了

之后,如果你要利用apue的lib,编译运行自己的代码,必须在编译时加上-I/home/se/apue.2e/include选项,

在连接时加上-L/home/se/apue.2e/lib source.c /home/se/apue.2e/lib/libapue.a选项,这样你就可以利

用apue提供的想err_sys等函数了^_^


/*******************************************************************************
*第三种  不建议使用
*
*******************************************************************************/




unix环境高级编程编译方法

    这里要谈到的一个问题就是该书中的源代码编译的问题。此书中差不多每个历程中,都会有这样一行源

码:

    #include "ourhdr.h"
      
    在第二版中改为:
    #include "apue.h"

        这个头文件是作者把把每个例程中常用的标准头文件,一些常用的出错处理函数(err_**()之类

的函数)和一些常用的宏定义给整理在一个头文件中。这个可以省去在每个例程中录入较多的重复代码,这

样可以减少每个例程的长度。但是,这样就给读者带来了不少麻烦。因为我们还要去搞明白如何把这个头文

件编译,然后做成库文件,添加到我们的系统中。特别读于初学者,本来满怀信心的,结果在编译第一个程

序的时候就出现了问题。我也没有搞明白如何把 "ourhdr.h"静态的编译到系统中。

        不过,不明白如何使用"ourhdr.h"这个头文件,并不会影响我们学习APUE,也不会影响我们编译和

运行每一个例程。其实,简单的想一下,如果一个 C程序要能顺利的编译和运行,除了我们要语法正确等方

面外,最根本的是要保证我们程序中所调用的函数以及宏等等都要有完整的来源,也就是必须包含所有调用

函数和宏所在的头文件。对于一个具体的源程序,如果我们正确的包含了头文件,那么剩下的就是程序本生

语法方面应该注意的事项。

        如何确定系统调用函数包含在那个头文件中呢?这在Unix/Linux系统下并非一件难事。Unix/Linux

下命令man可以帮助我们找到。man命令不仅可以帮助我们查找一般命令的用法,同时提供不同层次的帮助诸

如系统调用或者管理员级别的命令等等(譬如FreeBSD6.1中,man 1是用户专用手册,man 2是系统调用,

man 3是库函数查询等等)。

        下面我们就以APUE书中程序1-1 (实现ls命令部分功能)为例,来说明如何将书中的程序改编成全

部使用标准头文件的程序。其中,操作系统用的是FreeBSD6.1,经过相应的修改可以在书中所说的几个Unix

系统及Linux系统中运行,我也曾在Debian Linux下成功编译和运行该程序。书中1-1.c的原始代码如下:

    #include <sys/types.h>
    #include <dirent.h>
    #include "ourhdr.h"

    int
    main(int argc, char *argv[])
    {
        DIR                *dp;
        struct dirent    *dirp;

        if (argc != 2)
            err_quit("usage: ls directory_name");

        if ((dp = opendir(argv[1])) == NULL)
            err_sys("can't open %s", argv[1]);
        while ((dirp = readdir(dp)) != NULL)
            printf("%s"n", dirp->d_name);

        closedir(dp);
        exit(0);
    }

        从书后面的附录中可以看到"ourhdr.h"的内容比较多,包含了比较多的常用头文件,一些宏定义和

一些常用函数和出错函数的定义。其实,对于每一个具体的程序,我们只需要找到该程序中用到的头文件即

可。

        该1-1.c中所用到的系统函数调用有:opnedir(),readdir(),printf(),closedir()和exit()。
    其中,对于常用的函数prinft()和exit(),它们所在的头文件一般都知道,分别是<stdio.h>和< 

stdlib.h>。而对于opnedir (),readdir()和closedir(),我们可以通过man opendir,man readdir,man 

closedir得到这三个关于目录操作的函数所在的头文件都是:<sys/types.h>和<dirent.h>。这两个头文件

在源程序中也已经列出。

        其次,1-1.c中还用到了作者自定义的两个函数:err_quit()和err_sys()。这两个函数主要使用来

进行出错处理的。当然,使用这两个函数对错误信息的处理是比较完善的。但是,作为我们学习来讲,了解

程序的核心功能是首要的,我们可以将出错处理简化一点,即当遇到错误的时候,我们只简单的使用

printf()函数来提示一下有错误发生。当然,用printf()来进行出错处理并不是一种很合理的方法,而且往

往我们看不到更关键的错误信息,但对于我们仅仅作为学习来用还是可以接受的。毕竟我们要理解的核心部

分是程序的功能实现,出错处理在于其次。

       通过以上的说明,我们可以将1-1.c修改为如下内容:

    #include <sys/types.h>
    #include <dirent.h>
    #include <stdio.h>
    #include <stdlib.h>
    int main(int argc, char* argv[])
    {
        DIR *dp;
        struct dirent *dirp;
       
        if(argc != 2)
        {
            printf("You need input the directory name."n");
            exit(1);  
        }
       
        if((dp = opendir(argv[1])) == NULL)
        {
            printf("cannot open %s"n", argv[1]);
            exit(1);   
        }

        while ((dirp = readdir(dp)) != NULL)
            printf("%s"n", dirp->d_name);


        closedir(dp);

        exit(0);
    }

        这样修改后的程序已经与作者的头文件"ourhdr.h"没有关系,可以单独的进行编译。我使用的是

root用户,执行命令:

    # gcc 1-1.c  //生成目标文件a.out
    或者
    # gcc -o 1-1 1-1.c  //生成目标文件1-1

        没有任何错误和警告,说明编译成功。这时我们执行生成的目标文件:

    # ./a.out /home
    或者
    # ./1-1 /home

        则会列出/home路径下的所有文件,包括目录(.)和(..)。

        通过这样的方法,基本上我们可以将该书中所有的例程修改成不包含"ourhdr.h"的程序。这样,我

们就可以单独的编译每一个例程,而不用顾及作者所给的杂凑的头文件。同时这种比较笨的方法,反而有利

于帮助我们了解不同系统调用所对应的头文件,对于学习来说,这应该是一件好事。


/****************************************************************************************************************

*   其他1

******************************************************************************************************************/


apue.h 是作者自定义的一个头文件,并不是Unix/Linux系统自带的,此头文件包括了Unix程序所需的常用头文件及作者Richard自己写的出错处理函数。所以在默认情况下,gcc在编译时是读不到这个头文件的。

先在这个网站 http://www.apuebook.com/src.tar.gz 下载tar.gz格式的源码包,然后解压至某个目录,比如说/home/godsoul/下,然后进入目录apue.2e,把文件 Make.defines.linux 中的 WKDIR=/home/xxx/apue.2e 修改为 WKDIR=/home/godsoul/apue.2e ,然后再进入apue.2e目录下的std目录,打开linux.mk,将里面的nawk全部替换为awk,如果是用的vi/vim编辑器,可以使用这个 命令  :1.$s/nawk/awk/g (注意前面有冒号)
然后在此目录下运行make命令,即回到 /home/godsoul/apue.2e 目录在终端中输入 “./make” (不含引号)

然后把 /home/godsoul/apue.2e/inlcude 目录下的 apue.h 文件和位于 /home/godsoul/apue.2e/lib 目录下的 error.c 文件都复制到 /usr/include 目录下,apue.2e/lib/libapue.a 到/usr/lib/和 /usr/lib64下。注意复制这文件你需要有root权限。之所以要这样做,是因为gcc在链接头文件时会到 /usr/include 这个目录下寻找需要的头文件,若找不到则报错。

最终还要编辑一下复制过来的 apue.h 文件
在最后一行 #endif 前面添加一行 #include “error.c”

然后进入apue.2e/std 目录,编辑linux.mk。修改里面所有的nawk为awk。

这样就不会报错了。

还又可能遇到的问题如下:

如果出现stropts.h找不到的情况,则下载glibc-2.11,解压缩
cp ./glibc-2.11/streams/stropts.h /usr/include
cp ./glibc-2.11/bits/stropts.h /usr/include/bits
cp ./glibc-2.11/sysdeps/x86_64/bits/xtitypes.h /usr/include/bits

在我的机器上编译时,提示ARG_MAX未定义,可以这么修改。
在apue.2e/include/apue.h中添加一行:
#define ARG_MAX 4096
打开apue.2e/threadctl/getenv1.c 和apue.2e/threadctl/getenv3.c,添加一行:
#include “apue.h”

改好后make clean再重新make

2. 使用apue.h文件和libapue.a库。
假定/tmp下有一个文件:threadid.c,内容如下(apue线程章节的例子):
#include <apue.h>
#include <pthread.h>

pthread_t ntid;

void
printids(const char *s)
{
pid_t           pid;
pthread_t       tid;

pid = getpid();
tid = pthread_self();
printf(“%s pid %u tid %u (0x%x)\n”, s, (unsigned int)pid,
(unsigned int)tid, (unsigned int)tid);
}

void *
thr_fn(void *arg)
{
printids(“new thread: “);
return((void *)0);
}

int
main(void)
{
int             err;

err = pthread_create(&ntid, NULL, thr_fn, NULL);
if (err != 0)
err_quit(“can’t create thread: %s\n”, strerror(err));
printids(“main thread:”);
sleep(1);
exit(0);
}

使用如下命令编译:
cc -o threadid threadid.c -lapue -lpthread
可以运行一下:
dan@dan-laptop:/tmp$ ./threadid
new thread:  pid 17490 tid 816015696 (0x30a36950)
main thread: pid 17490 tid 823949040 (0x311c76f0)

3. 编译《UNP》
这个稍微麻烦些。

http://www.unpbook.com/unpv13e.tar.gz

我们首先产生一个目录,以后自己的代码就敲在这个目录里。
mkdir /home/dan/study/unp

仍然是下载到/home/dan/download/,解压缩,进入目录
cd /home/dan/download/unpv13e/
README文件中说的很详细:
========================================
Execute the following from the src/ directory:

./configure    # try to figure out all implementation differences

cd lib         # build the basic library that all programs need
make           # use “gmake” everywhere on BSD/OS systems

cd ../libfree  # continue building the basic library
make

cd ../libroute # only if your system supports 4.4BSD style routing sockets
make           # only if your system supports 4.4BSD style routing sockets

cd ../libxti   # only if your system supports XTI
make           # only if your system supports XTI

cd ../intro    # build and test a basic client program
make daytimetcpcli
========================================
这里只编译lib下的文件,这样可以产生libunp.a,复制这个静态库到/usr/lib/和/usr/lib64/
如果提示:
unp.h:139: error: conflicting types for ‘socklen_t’
/usr/include/bits/socket.h:35: error: previous declaration of ‘socklen_t’ was here
需要注释掉当前目录中unp.h的第139行。
复制libunp.a到系统目录:
root@dan-laptop:/home/dan/download/unpv13e/lib# cp ../libunp.a /usr/lib
root@dan-laptop:/home/dan/download/unpv13e/lib# cp ../libunp.a /usr/lib64/

4.使用unp.h和libunp.a
如果直接复制unpv13e/lib/unp.h到/usr/include,那么在别的目录编译书上代码时,很可会得到类似下面的错误:
In file included from daytimetcpsrv1.c:1:
/usr/include/unp.h:227: error: redefinition of ‘struct sockaddr_storage’
In file included from daytimetcpsrv1.c:1:
/usr/include/unp.h:249:30: error: ../lib/addrinfo.h: No such file or directory
/usr/include/unp.h:263: error: redefinition of ‘struct timespec’
/usr/include/unp.h:363: error: conflicting types for ‘gai_strerror’
/usr/include/netdb.h:647: error: previous declaration of ‘gai_strerror’ was here
/usr/include/unp.h:367: error: conflicting types for ‘getnameinfo’
/usr/include/netdb.h:653: error: previous declaration of ‘getnameinfo’ was here
/usr/include/unp.h:371: error: conflicting types for ‘gethostname’
/usr/include/unistd.h:857: error: previous declaration of ‘gethostname’ was here
/usr/include/unp.h:387: error: conflicting types for ‘inet_ntop’
/usr/include/arpa/inet.h:65: error: previous declaration of ‘inet_ntop’ was here
/usr/include/unp.h:395: error: conflicting types for ‘pselect’
/usr/include/sys/select.h:121: error: previous declaration of ‘pselect’ was here
daytimetcpsrv1.c: In function ‘main’:
daytimetcpsrv1.c:9: error: ‘MAX_LINE’ undeclared (first use in this function)
daytimetcpsrv1.c:9: error: (Each undeclared identifier is reported only once
daytimetcpsrv1.c:9: error: for each function it appears in.)
dan@dan-laptop:~/study/unp/4rmf/usr/include/unp.hcd/home/dan/study/unpconfig.hunp.hdan@danlaptop: /study/unpcp /home/dan/download/unpv13e/config.h .
dan@dan-laptop:~/study/unp$ cp /home/dan/download/unpv13e/lib/unp.h .
修改unp.h,
#include “../config.h”改成 #include “config.h”
添加一行:
#define MAX_LINE 2048
练习书上代码时,在unp目录下建立相应的章节目录,文件中添加一行:
#include “../unp.h”
编译时链接unp库就可以了。

以第四章的daytimetcpsrv1.c为例:
dan@dan-laptop:~/study/unp/4pwd/home/dan/study/unp/4dan@danlaptop: /study/unp/4 cat daytimetcpsrv1.c
#include “../unp.h”
#include <time.h>

int main(int argc, char **argv)
{
int    listenfd, connfd;
socklen_t    len;
struct sockaddr_in    servaddr, cliaddr;
char    buff[MAX_LINE];
time_t    ticks;

listenfd = Socket(AF_INET, SOCK_STREAM, 0);

bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(13);

Bind(listenfd, (SA *)&servaddr, sizeof(servaddr));

Listen(listenfd, LISTENQ);

for (;;) {
len = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *)&cliaddr, &len);
printf(“connection from %s, port %d\n”,
Inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
ntohs(cliaddr.sin_port));

ticks = time(NULL);
snprintf(buff, sizeof(buff), “%.24s\r\n”, ctime(&ticks));
Write(connfd, buff, strlen(buff));

Close(connfd);
}
}
编译:
cc -o daytimetcpsrv1 daytimetcpsrv1.c -lunp
运行一下:
root@dan-laptop:/home/dan/study/unp/4# ./daytimetcpsrv1 &
[1] 22106
root@dan-laptop:/home/dan/study/unp/4#
root@dan-laptop:/home/dan/study/unp/4# ./daytimetcpcli
usage: a.out <IPaddress>
root@dan-laptop:/home/dan/study/unp/4# ./daytimetcpcli 127.0.0.1
connection from 127.0.0.1, port 42064
Fri Aug 21 23:03:56 2009
root@dan-laptop:/home/dan/study/unp/4# netstat -nt
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 127.0.0.1:13            127.0.0.1:42064         TIME_WAIT




/*****************************************************************************************************

*   其他2

*****************************************************************************************************/


提示 "错误:apue.h:没有那个文件或目录".

很是受打击,果断google解决之。

apue.h是作者自定义的一个头文件,包括程序所需的常用头文件及出错处理函数。所以因该将它放入系统头文件中(Linux下是 /usr/include),这样gcc编译器就可以找到它了。

http://www.linuxidc.com/Linux/2013-01/77467.htm下载src.tar.gz包,然后解压至电脑中的某个目录,比如我的是在/home/user/下

user@user-desktop:~$ sudo tar xzf src.tar.gz

进入解压目录apue.2e,修改 Make.defines.linux中的WKDIR=/home/xxx/apue.2e,为WKDIR=/home/user/apue.2e

进入std目录,修改linux.mk,将里面的nawk全部改为awk。

复制apue.h和error.c

将apue.h和error.c两个文件copy到/usr/include目录下。(apue.h位于 your_apue_path/inlcude ; error.c位于your_apue_path/lib )

以我的路径为例:

user@user-desktop:/usr/include$ cp /home/user/apue.2e/inlcude/apue.h apue.h
user@user-desktop:/usr/include$ cp /home/user/apue.2e/lib/error.c error.c (实现apue.h中的出错处理函数)

修改apue.h

在最后一行#endif  前面添加一行 #include "error.c"

貌似这样就OK了。

UNIX环境高级编程中文第二版PDF高清版 http://www.linuxidc.net/thread-2063-1-1.html



/******************************************************************

****** Ubuntu系统的移植编译

*******************************************************************


按照上面的诸多方法变异会出现下面两个错误:

                        make[2]: *** [printd.o] 错误 1
                        make[2]:正在离开目录 `/tmp/apue.2e/ipp'
                        make[1]: *** [linux] 错误 1
                        make[1]:正在离开目录 `/tmp/apue.2e'
                        make: *** [all] 错误 2

错误的原因:

1. 在apue.2e/ipp.h中定义了一个宏定义status和/usr/include/i386-linux-gnu/bits/timex.h中的成员status冲突

2. ARG_MAX未定义


解决办法:

1. 修改这个apue.2e/ipp/ipp.h文件中的宏名称,例如改为Status;然后将apue.2e/ipp/printd.c中977行的 hp->status 改为hp->Status;

2. 在apue.2e/include/apue.h中添加一行:  #define ARG_MAX 4096   

3. 在路径下的文件apue.2e/threadctl/getenv1.c 和apue.2e/threadctl/getenv3.c,添加一行:#include "apue.h"


/*   编辑成库文件以供其他程序使用  */

1、修改相应平台的文件,将Make.defines.linux文件中的 WKDIR=/home/your_dir/apue.2e, 其中/home/your_dir为Apue.2e包的路径;

2.  使用make命令在上述路径下编译通过;

3. 把apue的头文件和库文件放入系统,具体如下:使用sudo su使用root权限,复制文件到系统默认搜索路径下

            cp ~/apue.2e/include/apue.h /usr/include
    cp ~/apue.2e/lib/libapue.a /usr/lib/

             其中~为/home/your_dir

4.  这样库文件就编译和设置好了,使用APUE书中带#include "apue.h"的程序,只需引用此库即可

        gcc  source.c -o excutename  -lapue

       如:gcc Hello.c -0  Hello -lapue      Hello.c 为要编译的程序名,Hello为可执行文件名



发布了11 篇原创文章 · 获赞 13 · 访问量 17万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章