unix环境高级编程(apue)一书中案例源代码的正确编译方法(大全)

UNIX环境高级编程(APUE) W. Richard Stevens的经典著作 
里面有很多很多系统调用的c代码实现,通过大量代码的讲解,使读者可以深入理解unix操作系统的运作原理

在编译书中c源代码例子时,需要三个文件(一个头文件,两个c源代码文件),这三个文件的内容参见 APUE一书的附录B
B-1 头文件 ourhdr.h
B-2 输出至标准出错文件的出错处理函数 把B-2的全部源代码写入一个叫做 myerror.c 的文件中
B-3 用于精灵进程的处理函数 把B-3的全部源代码写入一个文本文件中,取名叫 log.c

按照http://blog.sina.com.cn/s/blog_8312d78f01014uu8.html 可以在腾讯云主机VUY6 ubuntu20 上测试成功

先编写 myls.c (这是apue这本书里最开头第一个例子,myls.c编译后成为可执行文件myls , 命令行下运行 myls /usr/include 即可把 /usr/include下的所有文件全部列出 )
#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("a single argument (the directory name) is required");

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);
}

 


编译myls.c 时候报错,说缺少err_sys的引用,解决方法(1), (2),  (3)  [ 参考 http://blog.sina.com.cn/s/blog_8312d78f01014uu8.html  ]

方法(1)

把 myerror.c 拷贝到myls.c 所在目录
运行命令    gcc -Wall myls.c myerror.c -o myls

即可生成可执行文件myls

 

/*
* myerror.c 文件内容如下
*/
#include <errno.h>
#include <stdarg.h>
#include "ourhdr.h"

static void err_doit(int, const char *, va_list);
char *pname = NULL;

void
err_ret(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, fmt, ap);
va_end(ap);
return;
}

void
err_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, fmt, ap);
va_end(ap);
exit(1);
}

void
err_dump(const char *fmt, ...)
{
va_list ap;

va_start(ap, fmt);
err_doit(1, fmt, ap);
va_end(ap);
abort();
exit(1);
}

void
err_msg(const char *fmt, ...)
{
va_list ap;

va_start(ap, fmt);
err_doit(0, fmt, ap);
va_end(ap);
return;
}

void
err_quit(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(0, fmt, ap);
va_end(ap);
exit(1);
}

static void
err_doit(int errnoflag, const char *fmt, va_list ap)
{
int errno_save;
char buf[MAXLINE];

errno_save = errno;
vsprintf(buf, fmt, ap);
if (errnoflag)
sprintf (buf+strlen(buf), ": %s", strerror (errno_save));
strcat (buf, "\n");
fflush(stdout);
fputs(buf, stderr);
fflush(NULL);
return;
}

 

 

 

方法(2)

写一个makefile来编译myls.c   省去每次都要敲命令
在myls.c 同目录下用vim 新建一个文本文件,取名为 makefile
写入如下内容
myls: myls.c myerror.c ourhdr.h
        gcc -Wall myls.c myerror.c -o myls

在命令行输入 make
即可编译出可执行文件 myls

 

 

 

 

 

方法(3)

把 myerror.c 编译成 链接库 libmyerror.a  来使用,然后用gcc编译myls.c,编译的同时就可以链接 libmyerror.a 来生成可执行文件 myls

具体操作:

首先将myerror.c 编译生成 目标文件 myerror.o
gcc -Wall -c myerror.c

再用以下命令将 myerror.o 转化为 库文件 libmyerror.a  (提示:这里可将多个.o文件合并做成一个库文件)
ar cr libmyerror.a myerror.o

最后用 gcc 编译 myls.c的同时,链接已生成的库文件 libmyerror.a ,便可生成可执行文件myls
gcc -Wall myls.c libmyerror.a  -o myls 

 

 

 

 

 

方法(4) [  参考 https://www.cnblogs.com/etangyushan/p/5535685.html  ]

此方法最好,但不再使用前面提到的APUE附录B里面的B-1 头文件 ourhdr.h  

而是改为使用apue.h  该头文件是在ourhdr.h头文件的基础又扩增了其他一些内容,方便读者编译apue一书中的源代码例子

 

将以下内容写入头文件 apue.h
/*
* =====================================================================================
*
* Filename: apue.h
*
* Description:
*
* Version: 1.0
* Created: 05/27/2016 03:21:09 PM
* Revision: none
* Compiler: gcc
*
* Author: YOUR NAME (),
* Organization:
*
* =====================================================================================
*/
/* our own header, to be included before all standard system headers */
#ifndef _APUE_H
#define _APUE_H

#define _XOPEN_SOURCE 600 /* Single UNIX Specification Version 3 */

#include <sys/types.h> /* some systems still require this */
#include <sys/stat.h>
#include <sys/termios.h> /* for winsize */
#ifndef TIOCGWINSZ
#include <sys/ioctl.h>
#endif
#include <stdio.h> /* for convenience */
#include <stdlib.h> /* for convenience */
#include <stddef.h> /* for convenience */
#include <string.h> /* for convenience */
#include <unistd.h> /* for convenience */
#include <signal.h> /* for convenience */
#define MAXLINE 4096 /* max line length */

/*
* Default file access permissions for new files.
* */
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

/*
* Default permissions for new directories.
* */
#define DIR_MODE (FILE_MODE | S_IXUSR | S_IXGRP | S_IXOTH)

typedef void Sigfunc(int); /* for signal handlers */
#if defined(SIG_IGN) && !defined(SIG_ERR)
#define SIG_ERR ((Sigfunc *)-1)
#endif

#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))

/*
* Prototypes for our own functions.
* */
char *path_alloc (int *);
long open_max(void);
void clr_fl(int, int);
void set_fl(int, int);
void pr_exit(int);
void pr_mask(const char *);
Sigfunc *signal_intr (int, Sigfunc *);

int tty_cbreak(int);
int tty_raw(int);
int tty_reset(int);
void tty_atexit(void);
#ifdef ECHO /* only if <termios.h> has been included */
struct termios *tty_termios(void);
#endif

void sleep_us(unsigned int);
ssize_t readn(int, void *, size_t);
ssize_t writen(int, const void *, size_t);
void daemonize(const char *);

int s_pipe(int *);
int recv_fd(int, ssize_t (*func)(int, const void *, size_t));
int send_fd(int, int);
int send_err(int, int, const char *);
int serv_listen(const char *);
int serv_accept(int, uid_t *);
int cli_conn(const char *);
int buf_args(char *, int (*func)(int, char **));
int ptym_open(char *, int);
int ptys_open(char *);
#ifdef TIOCGWINSZ
pid_t pty_fork(int *, char *, int, const struct termios *, const struct winsize *);
#endif

int lock_reg(int, int, int, off_t, int, off_t);
#define read_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLK, F_RDLCK, (offset), (whence), (len))
#define readw_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLKW, F_RDLCK, (offset), (whence), (len))
#define write_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLK, F_WRLCK, (offset), (whence), (len))
#define writew_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLKW, F_WRLCK, (offset), (whence), (len))
#define un_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLK, F_UNLCK, (offset), (whence), (len))
pid_t lock_test(int, int, off_t, int, off_t);

#define is_read_lockable(fd, offset, whence, len) \
(lock_test((fd), F_RDLCK, (offset), (whence), (len)) == 0)
#define is_write_lockable(fd, offset, whence, len) \
(lock_test((fd), F_WRLCK, (offset), (whence), (len)) == 0)

void err_dump(const char *, ...);
void err_msg(const char *, ...);
void err_quit(const char *, ...);
void err_exit(int, const char *, ...);
void err_ret(const char *, ...);
void err_sys(const char *, ...);

void log_msg(const char *, ...);
void log_open(const char *, int, int);
void log_quit(const char *, ...);
void log_ret(const char *, ...);
void log_sys(const char *, ...);

void TELL_WAIT(void);
void TELL_PARENT(pid_t);
void TELL_CHILD(pid_t);
void WAIT_PARENT(void);
void WAIT_CHILD(void);

#endif /* _APUE_H */

 


把以下内容写入 error_apue.c   【这个c源代码文件中就包含了编译myls时需引用的err_sys函数和err_quit 函数的实现】
/*
* =====================================================================================
*
* Filename: err.c
*
* Description:
*
* Version: 1.0
* Created: 05/27/2016 04:06:55 PM
* Revision: none
* Compiler: gcc
*
* Author: YOUR NAME (),
* Organization:
*
* =====================================================================================
*/

#include "apue.h"
#include <errno.h> /* for definition of errno */
#include <stdarg.h> /* ISO C variable aruments */

static void err_doit (int, int, const char *, va_list);

/*
* Nonfatal error related to a system call
* Print a message and return
* */
void err_ret (const char *fmt, ...)
{
va_list ap;

va_start(ap, fmt);
err_doit(1, errno, fmt, ap);
va_end(ap);
}

/*
* Fatal error related to a system call.
* Print a message and terminate.
* */
void err_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, errno, fmt, ap);
va_end(ap);
exit(1);
}

/*
* Fatal error unrelated to a system call.
* Error code passed as explict parameter.
* Print a message and terminate.
* */
void err_exit(int error, const char *fmt, ...)
{
va_list ap;

va_start(ap, fmt);
err_doit(1, error, fmt, ap);
va_end(ap);
exit(1);
}

/*
* Fatal error related to a system call.
* Print a message, dump core, and terminate
* */
void err_dump(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, errno, fmt, ap);
va_end(ap);
abort(); /* dump core and terminate */
exit(1); /* shouldn`t get here */
}

/*
* Nonfatal error unrelated to a system call.
* print a message and return
* */
void err_msg(const char *fmt, ...)
{
va_list ap;

va_start(ap, fmt);
err_doit(0, 0, fmt, ap);
va_end(ap);
}

/*
* Fatal error unrelated to a system call.
* print a message and terminate
* */
void err_quit(const char *fmt, ...)
{
va_list ap;

va_start(ap, fmt);
err_doit(0, 0, fmt, ap);
va_end(ap);
exit(1);
}

/*
* Print a message and return to caller.
* Caller specifies "errnoflag".
* */
static void err_doit (int errnoflag, int error, const char *fmt, va_list ap)
{
char buf[MAXLINE];
vsnprintf(buf, MAXLINE, fmt, ap);
if (errnoflag)
{
snprintf(buf+strlen(buf), MAXLINE-strlen(buf), ": %s", strerror(error));
}

strcat(buf, "\n");
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(NULL); /* flushes all stdio output streams */
}

 

把以下内容写入 log_apue.c
/*
* =====================================================================================
*
* Filename: log.c
*
* Description:
*
* Version: 1.0
* Created: 05/27/2016 04:30:15 PM
* Revision: none
* Compiler: gcc
*
* Author: YOUR NAME (),
* Organization:
*
* =====================================================================================
*/

/*
* Error routines for programs that can run as a daemon
* */
#include "apue.h"
#include <errno.h>
#include <stdarg.h>
#include <syslog.h>

static void log_doit(int, int, const char *, va_list ap);

/*
* Caller must define and set this: nonzero if
* interactive, zero if daemon
* */
extern int log_to_stderr;

/*
* Initialize syslog(), if running as daemon.
* */
void log_open(const char *ident, int option, int facility)
{
if (log_to_stderr == 0)
{
openlog(ident, option, facility);
}
}

/*
* Nonfatal error related to a system call.
* Print a message with the system`s errno value and return.
* */
void log_ret(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_doit(1, LOG_ERR, fmt, ap);
va_end(ap);
}

/*
* Fatal error related to a system call.
* Print a message and terminate.
*
* */
void log_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_doit(1, LOG_ERR, fmt, ap);
va_end(ap);
exit(2);

}

/*
* Nofatal error unrelated to a system call.
* Print a message and return
* */
void log_msg (const char *fmt, ...)
{
va_list ap;

va_start(ap, fmt);
log_doit(0, LOG_ERR, fmt, ap);
va_end(ap);
}

/*
* Fatal error unrelated to a system call.
* print a message and terminate.
* */
void log_quit(const char *fmt, ...)
{
va_list ap;

va_start(ap, fmt);
log_doit(0, LOG_ERR, fmt, ap);
va_end(ap);
exit(2);
}

/*
* Print a message and return to caller.
* Caller specifies "errnoflag" and "priority"
* */
static void log_doit(int errnoflag, int priority, const char *fmt, va_list ap)
{
int errno_save;
char buf[MAXLINE];

errno_save = errno; /* value caller might want printed */
vsnprintf(buf, MAXLINE, fmt, ap);
if (errnoflag)
{
snprintf (buf+strlen(buf), MAXLINE-strlen(buf), ": %s", strerror(errno_save));
strcat(buf, "\n");
if (log_to_stderr)
{
fflush(stdout);
fputs(buf, stderr);
fflush(stderr);
}else{
syslog(priority, buf);
}
}
}


把 apue.h     error_apue.c      log_apue.c 这三个文件拷贝到 myls.c 所在的目录

然后按前面所述方法(1),(2),(3)中的一种来编译myls.c 即可

具体如下:

(1) gcc -Wall myls.c error_apue.c -o myls

 

(2) 或在myls.c 所在目录下 新建一个文本文件 makefile ,然后写入

myls: myls.c error_apue.c apue.h
        gcc -Wall myls.c error_apue.c -o myls

 

(3) 或把 error_apue 编译成库文件 liberror_apue.a   然后再用 gcc编译myls.c时链接该库文件
gcc -Wall -c error_apue.c
ar cr liberror_apue.a error_apue.o
gcc -Wall myls.c liberror_apue.a -o myls

 

备注:将log_apue.c 编译成库文件 liblog_apue.a的方法同上
这样,如果编译书中例子程序时,编译器报错说需要引用某某函数,而某某函数的实现是在log_apue.c当中时,

就可以和上面方法一样,采用  gcc -Wall  xxx.c  liblog_apue.a  -o  xxx   来生成 可执行文件  xxx

 

===================================

现在,修改myls.c 文件内容,将 #include "ourhdr.h"  替换为  #include "apue.h"

#include "apue.h"

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

int

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

if(argc != 2)
err_quit("a single argument (the directory name) is required");

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);
}

 然后 gcc -Wall myls.c error_apue.c -o myls     即可生成可执行文件 myls,其功能和 ls 命令一样,可以列出指定目录中的全部文件,例如   ./myls  /usr/include    就可以列出系统中/usr/include 下的所有文件

======================================================================

 

 

 

 

另一个测试例子(转自 https://www.cnblogs.com/etangyushan/p/5535685.html)

/*
* =====================================================================================
*
* Filename: line_test.c
*
* Description:   
*
* Version: 1.0
* Created: 05/27/2016 06:04:12 PM
* Revision: none
* Compiler: gcc
*
* Author: YOUR NAME (),
* Organization:
*
* =====================================================================================
*/
#include "apue.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int log_to_stderr = 0;
int main()
{
int n;
int fd[2];
pid_t pid;
char line[MAXLINE];

if (pipe(fd) < 0)
{
err_sys ("pipe error");
}
if ((pid = fork()) < 0)
{
err_sys ("fork error");
} else if (pid > 0) {
close (fd[0]);
write (fd[1], "hello world\n", 12);
} else {
close (fd[1]);
n = read (fd[0], line, MAXLINE);
write (STDOUT_FILENO, line, n);
}

exit(0);
}

 

用命令  gcc -o line_test line_test.c  error_apue.c  即可编译得到 可执行文件 line_test

输入  ./line_test   回车

会看到输出结果  hello world

假如使用 gcc -o line_test line_test.c  进行编译,则编译器会报错 : 

/usr/bin/ld: /tmp/ccTDzeNw.o: in function `main':
line_test.c:(.text+0x47): undefined reference to `err_sys'
/usr/bin/ld: line_test.c:(.text+0x6c): undefined reference to `err_sys'
collect2: error: ld returned 1 exit status

原因就是编译时需要引用的函数 err_sys找不到,而err_sys函数的实现是写在 error_apue.c 文件中的,因此gcc编译时需要加上 error_apue.c 

 

posted @ 2022-01-15 13:56  Thermo  阅读(517)  评论(0编辑  收藏  举报