• 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 

  • 相关阅读:
    RHEL5.8使用yum安装xclock
    Linux下磁盘分区、挂载、卸载操作记录
    CentOS6.5磁盘分区和挂载操作记录
    CentOS环境下下调整home和根分区大小
    PowerDesigner连接Oracle数据库(32位)反向生成物理数据模型
    Create-React-App脚手架使用方法
    react 简书开发笔记
    本地连接服务器的mongodb
    使用mongoose连接mongodb(转载文章)
    将koa+vue部署到服务器
  • 原文地址:https://www.cnblogs.com/Thermo/p/15806888.html
Copyright © 2020-2023  润新知