unix环境高级编程 电子版_unix环境高级编程电子版 - CSDN
  • Unix环境高级编程-第三 之前学习了《Linux系统编程》对于常见的概念和函数都有了基础的认知,这里准备通过这本书,深入学习系统API相关内容。笔记内容会有所倾向,不会严格反应书本内容。 基础概念 +----------+ |...

    Unix环境高级编程-第三版

    之前学习了《Linux系统编程》对于常见的概念和函数都有了基础的认知,这里准备通过这本书,深入学习系统API相关内容。笔记内容会有所倾向,不会严格反应书本内容。

    基础概念

    +----------+
    |应用程序   |
    +------+   |
    | Shell|   |
    +------+   |
    | 内核  |   |
    +------+   |
    | Libs |   |
    +------+---+
    

    内核的接口称为系统调用(system call)。公用函数库(libs)建立在系统调用基础上,应用程序既可以使用公共函数库,也可以系统调用。Shell是比较特殊的应用程序,为其他程序提供一个接口。Linux是GNU操作系统使用的内核,一般称为GNU/Linux操作系统,更常见的叫法为Linux。

    登陆

    用户在登陆Unix系统时,输入用户名和口令,系统在口令文件/etc/passwd中查看。口令文件是由7个冒号分割的字段组成,依次是:登录名、加密口令、用户ID、数字组ID、注释字段、起始目录、Shell程序。
    如:sar:x:205:105:Stephen Rago:/home/sar:/bin/ksh.目前,所有系统的加密口令移动到单独文件中了。

    Shell是一个命令行解释权,它读取用户输入,然后执行命令。sh是Unix默认shell, csh是BSD默认shell, bash是sh的改进,支持csh的特色,所有Linux都有。

    文件和目录

    文件名的最大长度是255个字符(characters),文件路径的最大长度是4096字符(characters), 即可以包含16级的最大文件长度的路径。

    API说明

    #include <sys/types.h>
    #include <dirent.h>
    
    DIR *opendir(const char *name);
    DIR *fdopendir(int fd);
    
    struct dirent {
        ino_t          d_ino;       /* Inode number */
        off_t          d_off;       /* Not an offset; see below */
        unsigned short d_reclen;    /* Length of this record */
        unsigned char  d_type;      /* Type of file; not supported
                                        by all filesystem types */
        char           d_name[256]; /* Null-terminated filename */
    };
    struct dirent *readdir(DIR *dirp);
    
    #include <unistd.h>
    
    pid_t getpid(void);
    pid_t getppid(void);
    pid_t fork(void);
    
    extern char **environ;
    
    int execl(const char *path, const char *arg, ...
                    /* (char  *) NULL */);
    int execlp(const char *file, const char *arg, ...
                    /* (char  *) NULL */);
    int execle(const char *path, const char *arg, ...
                    /*, (char *) NULL, char * const envp[] */);
    int execv(const char *path, char *const argv[]);
    int execvp(const char *file, char *const argv[]);
    int execvpe(const char *file, char *const argv[], char *const envp[]);
    
    #include <stdio.h>
    int fgetc(FILE *stream);
    char *fgets(char *s, int size, FILE *stream);
    int getc(FILE *stream);
    int getchar(void);
    int ungetc(int c, FILE *stream);
    
    #include <sys/wait.h>
    // 这一类的api都是为了让父进程等待子进程,并且获取子进程状态变化
    pid_t wait(int *wstatus);
    pid_t waitpid(pid_t pid, int *wstatus, int options);
    
    char* strerror(int errnum); // 映射消息的文本信息
    void perror(const char* msg); // 输出一条出错消息,然后返回
    

    模仿的Demo:

    /* 模拟ls */
    #include "apue.h"
    #include <dirent.h>
    int main(int argc, char *argv[]) {
        DIR *dp;
        struct dirent *dirp;
        if( argc != 2 ){
            err_quit("usage: ls <path>");
        }
        if( (dp = opendir(argv[1])) == NULL ){
            err_sys("can't open %s", argv[1]);
        }
        while( (dirp = readdir(dp))!=NULL ){
            printf("%s -> %d %d\n", dirp->d_name, dirp->d_reclen, dirp->d_type);
        }
        closedir(dp);
        return 0;
    }
    // xls ./
    
    /* 无缓冲IO */
    #include "apue.h"
    #define BUFFSIZE 4096
    
    int main(){
        int n;
        char buf[BUFFSIZE];
        while( (n=read(STDIN_FILENO, buf, BUFFSIZE)) >0 ){
            if( write(STDOUT_FILENO, buf, n)!=n ){
                err_sys("write error!");
            }
        }
        if(n<0)
            err_sys("read error!");
        return 0;
    }
    
    // xcp < in.txt > out.txt
    
    /* 标准IO */
    // 库提供缓冲管理
    #include "apue.h"
    
    int main(){
        int c;
        while( (c=getc(stdin))!=EOF ){
            if( putc(c, stdout) == EOF ){
                err_sys("output error");
            }
        }
        if( ferror(stdin) ){
            err_sys("input error");
        }
        return 0;
    }
    // xcp2 < in.txt > out.txt
    
    /* 类似shell功能 */
    #include "apue.h"
    #include <sys/wait.h>
    
    int main(){
        char buf[MAXLINE];
        pid_t pid;
        int status;
        printf("%% ");
        while(fgets(buf, MAXLINE, stdin) != NULL ){
            if( buf[strlen(buf)-1] == '\n' ){
                buf[strlen(buf)-1] = 0;
            }
            if( (pid=fork()) < 0 ){
                err_sys("fork_error");
            }else if( pid == 0 ){
                // child
                execlp(buf, buf, (char*)0); // 这个实现不是很完美
                err_ret("couldn't execute: %s", buf);
                exit(127);
            }
            // parent
            if( (pid = waitpid(pid, &status, 0)) < 0 ){
                err_sys("waitpid error");
            }  
            printf("%% ");
        } 
        return 0;
    }
    

    用户组

    除了用户ID和组ID,大多数Unix还允许用户属于另外其他的组,这个功能从4.2BSD开始,最多允许用户属于16个其他组。
    查询用户属于的组cat /etc/group | grep root, 分别为用户组name,用户组口令,用户组ID,用户。使用指令groups可获取到当前用户所在的组。

    头文件标准

    ISO C标准定义

    头文件 说明
    <assert.h> 验证程序断言
    <complex.h> 复数算术运算支持
    <ctype.h> 字符分类和映射支持
    <errno.h> 出错码
    <fenv.h> 浮点环境
    <float.h> 浮点常量及特性
    <inttypes.h> 整形格式变换
    <iso646.h> 赋值、关系、一元操作宏
    <limits.h> 实现常量
    <locale.h> 本地化类别及定义
    <math.h> 数学函数、类型、常量
    <setjmp.h> 非局部goto
    <signal.h> 信号
    <stdarg.h> 可变长度参数表
    <stdbool.h> 布尔类型和值
    <stddef.h> 标准定义
    <stdint.h> 整形
    <stdio.h> 标准IO库
    <stdlib.h> 实用函数
    <string.h> 字符串操作
    <tgmath.h> 通用类型数学宏
    <time.h> 时间和日期
    <wchar.h> 扩充的多字符和宽字符支持
    <wctype.h> 宽字符分类和映射支持

    POSIX.1标准 包含了iso c标准库

    头文件 说明
    <aio.h> 异步IO
    <cpio.h> cpio归档值
    <dirent.h> 目录项
    <dlfcn.h> 动态链接库
    <fcntl.h> 文件控制
    <fnmatch.h> 文件名匹配类型
    <glob.h> 路径名模式匹配与生成
    <grp.h> 组文件
    <iconv.h> 代码集变换实用程序
    <langinfo.h> 语言信息常量
    <monetary.h> 货币类型与函数
    <netdb.h> 网络数据库操作
    <nl_types.h> 消息类
    <poll.h> 投票函数
    <pthread.h> 线程
    <pwd.h> 口令文件
    <regex.h> 正则表达式
    <sched.h> 执行调度
    <semaphore.h> 信号量
    <strings.h> 字符串操作
    <tar.h> tar归档
    <termios.h> 终端IO
    <unistd.h> 符号常量
    <wordexp.h> 字扩充类型
    <arpa/inet.h> 因特网定义
    <net/if.h> 套接字本地接口
    <netinet/in.h> 因特网地址族
    <netinet/tcp.h> 传输协议定义
    <sys/mman.h> 存储管理
    <sys/select.h> select函数
    <sys/socket.h> 套接字接口
    <sys/stat.h> 文件状态
    <sys/statvfs.h> 文件系统信息
    <sys/times.h> 进程时间
    <sys/types.h> 基本系统数据类型
    <sys/un.h> UNIX套接字定义
    <sys/utsname.h> 系统名
    <sys/wait.h> 进程控制

    POSIX.1 2008 可选头文件

    头文件 说明
    <fmtmsg.h> 消息显示结构
    <ftw.h> 文件树漫游
    <libgen.h> 路径名管理函数
    <ndbm.h> 数据库操作
    <search.h> 搜索表
    <syslog.h> 系统出错日志记录
    <utmpx.h> 用户账户数据库
    <sys/ipc.h> IPC
    <sys/msg.h> XSI消息队列
    <sys/resource.h> 资源操作
    <sys/sem.h> XSI信号量
    <sys/shm.h> XSI共享存储
    <sys/time.h> 时间类型
    <sys/uio.h> 矢量IO操作
    <mqueue.h> 消息队列
    <spawn.h> 实时spawn接口

    IO

    IO操作是所有操作中最常用的一个,一般只需要5个函数open/read/write/lseek/close,这些函数都是不带缓冲的IO。实际使用时,更多时候需要使用dup/fcntl/sync/fsync/ioctl配合使用。

    文件IO基础操作比较简单,不做说明。

    特殊的,当fork等手段产生多个进程读写同一文件时,会导致数据丢失或者异常,为此,设计了原子性操作函数:pread/pwrite,作用等同lseek然后read/write,但是这个过程不会被打断。

    #include <unistd.h>
    
    int dup(int oldfd);
    int dup2(int oldfd, int newfd);
    // 等效于 dcntl(fd, F_DUPFD, 0), dup是原子操作
    
    // sync/fsync/fdatasync 
    
    #include <fcntl.h>
    int fcntl(int fd, int cmd, ... /* arg */ ); // 改变fd的属性
    
    #include <sys/ioctl.h>
    int ioctl(int fd, unsigned long request, ...); // IO杂物箱,其他函数不能做的都要它来做
    
    

    因为大部分情况下,针对串口、摄像头等设备,都需要使用ioctl设置参数,所以,这里需要注意ioctl的request参数,它根据设备不同,参数也不同。

    类别 常量名 头文件 ioctl数
    盘标号 DIOxxx <sys/disklabel.h> 4
    文件IO FIOxxx <sys/fileio.h> 14
    磁带IO MTIOxxx <sys/mtio.h> 11
    套接字IO SIOxxx <sys/sockio.h> 73
    终端IO TIOxxx <sys/ttycom.h> 43

    /dev/fd/x等价与复制描述符n。

    文件和目录

    之前了解了基础的IO操作,这里主要关注文件系统和文件的性质。

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    int stat(const char *pathname, struct stat *statbuf);
    int fstat(int fd, struct stat *statbuf);
    int lstat(const char *pathname, struct stat *statbuf);
    
    #include <fcntl.h>           /* Definition of AT_* constants */
    #include <sys/stat.h>
    int fstatat(int dirfd, const char *pathname, struct stat *statbuf, int flags);
    
    struct stat {
        dev_t     st_dev;         /* ID of device containing file */
        ino_t     st_ino;         /* Inode number */
        mode_t    st_mode;        /* File type and mode */
        nlink_t   st_nlink;       /* Number of hard links */
        uid_t     st_uid;         /* User ID of owner */
        gid_t     st_gid;         /* Group ID of owner */
        dev_t     st_rdev;        /* Device ID (if special file) */
        off_t     st_size;        /* Total size, in bytes */
        blksize_t st_blksize;     /* Block size for filesystem I/O */
        blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */
    
        /* Since Linux 2.6, the kernel supports nanosecond
            precision for the following timestamp fields.
            For the details before Linux 2.6, see NOTES. */
    
        struct timespec st_atim;  /* Time of last access */
        struct timespec st_mtim;  /* Time of last modification */
        struct timespec st_ctim;  /* Time of last status change */
    
    #define st_atime st_atim.tv_sec      /* Backward compatibility */
    #define st_mtime st_mtim.tv_sec
    #define st_ctime st_ctim.tv_sec
    };
    

    这类函数都返回文件的信息结构。其中,文件类型可以通过对st_mode宏S_ISREG/S_ISDIR/S_ISCHR/S_ISBLK/S_ISFIFO/S_ISLNK/S_ISSOCK 判断文件类型。在IPC中对整个结构体判断IPC对象类型S_TYPEISMQ/S_TYPEISSEM/S_TYPEISSHM.

    st_mode中也保存了对文件的访问权限位。除了常见的权限,其实每个文件都有9个访问权限。

    st_mode屏蔽 含义
    S_IRUSR 用户读
    S_IWUSR 用户写
    S_IXUSR 用户执行
    S_IRGRP 组读
    S_IWGRP 组写
    S_IXGRP 组执行
    S_IROTH 其他读
    S_IWOTH 其他写
    S_IXOTH 其他执行

    当create文件或者目录时,默认使用进程的有效用户ID和组ID,如果想要测试是否有权限,如下:

    #include <unistd.h>
    int access(const char *pathname, int mode);
    
    // 如果需要改变权限,如下
    #include <sys/stat.h>
    int chmod(const char *pathname, mode_t mode);
    int fchmod(int fd, mode_t mode);
    
    // 改变用户ID或者组ID
    #include <unistd.h>
    int chown(const char *pathname, uid_t owner, gid_t group);
    int fchown(int fd, uid_t owner, gid_t group);
    int lchown(const char *pathname, uid_t owner, gid_t group);
    
    // 文件截断
    #include <unistd.h>
    #include <sys/types.h>
    int truncate(const char *path, off_t length);
    int ftruncate(int fd, off_t length);
    
    // 链接
    int link(const char *oldpath, const char *newpath);
    int unlink(const char *pathname);
    
    int remove(const char *pathname); // 通用文件与文件夹
    
    int rename(const char *oldpath, const char *newpath);
    
    int symlink(const char *target, const char *linkpath);
    
    ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
    
    // 文件
    // st_atim 访问时间/st_mtim 内容修改时间/st_ctim 属性更改时间
    int futimens(int fd, const struct timespec times[2]);
    int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags);
    
    // 目录
    int mkdir(const char *pathname, mode_t mode);
    int rmdir(const char *pathname);
    
    #include <dirent.h>
    DIR *opendir(const char *name);
    DIR *fdopendir(int fd);
    struct dirent *readdir(DIR *dirp);
    void rewinddir(DIR *dirp);
    int closedir(DIR *dirp);
    long telldir(DIR *dirp);
    void seekdir(DIR *dirp, long loc);
    
    int chdir(const char *path);
    int fchdir(int fd);
    
    char *getcwd(char *buf, size_t size);
    char *getwd(char *buf);
    char *get_current_dir_name(void);
    
    

    流和FILE对象

    #include <stdio.h>
    #include <wchar.h>
    
    int fwide(FILE *stream, int mode); // 设置流的定向,决定是单字符还是宽字符模式
    void setbuf(FILE *restrict stream, char *restrict buf); // 修改缓冲机制
    int setvbuf(FILE *restrict stream, char *restrict buf, int type, size_t size);
    int fflush(FILE *stream); // 强制冲洗一个流
    fopen/freopen/fdopen // 打开、重新打开、获取描述符
    int fclose(FILE* fp); // 关闭一个流
    
    getc/fgetc/getchar // 一次读取一个字节
    ferror/feof // 获取流错误码
    clearerr // 清除错误码
    ungetc // 将字符压回流
    putc/fputc/putchar // 输出一个字符
    
    fgets/gets // 读取一行
    fputs/puts // 写入一串数据,不一定是一行
    
    fread/fwrite // 读写二进制数据
    
    ftell/fseek/rwind/fgetpos/fsetpos // 读、写指针位置
    
    printf/sprintf/fprintf/dpintf/snprintf // 格式化io
    vprintf/vfprintf/vdprintf/vsprintf/vsnprintf // 可变参数的
    // %[flags][fldwidth][precision][lenmodifier]convtype
    
    // 百分号后跟随字符意义 flags
    // , 整数按照千分位分组
    // - 左对齐
    // + 总是显示正负号
    //   如果第一个字符不是正负号,则添加一个空格
    // # 指定另一种转换形式,比如16进制加0x
    // 0 添加前导0填充,不使用空格
    
    // lenmodifier 参数长度
    // hh (u)char 类型长度
    // h  (u)short
    // l  (u)long
    // ll  (u)long long
    // j  (u)intmax_t
    // z  size_t
    // t  ptrdiff_t
    // L  long double
    
    // convtype 如何解释参数
    // d,i  有符号10进制
    // o  无符号八进制
    // u  无符号十进制
    // x,X  无符号16进制
    // f,F  双精度浮点
    // e,E  指数格式双精度
    // g,G  解释为f/F/e/E
    // a,A  16进制指数格式双精度
    // c  字符,lc是宽字符
    // s  字符串,ls是宽字符串
    // p  void指针
    // n  printf输出字符数目写入到带符号整数中
    // %  %自身
    // C  宽字符,=lc
    // S  宽字符串,=ls
    
    // convtype 还存在显式指定第n个参数的格式化,%n$,与上面的不能同时使用
    
    scanf/fscanf/sscanf // 格式化输入
    // %[*][fldwidth][m][lenmodifier]convtype
    // d  有符号10进制
    // i  有符号10进制,基数由输入格式确定
    // o  无符号八进制
    // u  有符号十进制,基数10
    // x,X  无符号16进制
    // f,F  双精度浮点
    // e,E  指数格式双精度
    // g,G  解释为f/F/e/E
    // a,A  16进制指数格式双精度
    // c  字符,lc是宽字符
    // s  字符串,ls是宽字符串
    // p  void指针
    // n  printf输出字符数目写入到带符号整数中
    // %  %自身
    // C  宽字符,=lc
    // S  宽字符串,=ls
    // [ 匹配列出的字符序列,以]结束
    // [^ 匹配列出字符以外的序列,以]终止
    
    #include <stdlib.h>
    #include <stdio.h>
    
    int main(int argc, char *argv[]) {
    	printf("Hello!\n");
    	unsigned int n = 0;
    	printf("%2$.2f %1$4.2d -%3$n\n", -12, -3.2567,&n);
    	printf("%*s%d\n",n,"",n);
    	return 0;
    }
    // %n$ 从1开始,指明第几个参数
    // %n 指定一个uint地址,存储%n所在位置的序号
    // %*s 需要2个参数,第一个是类似fildwidth,第二个是%s的值,效果与%.2f类似
    
    int fileno(FILE *stream); 
    char *tmpnam(char *s); // 产生一个与现有文件名不同的有效路径名字符串,最多TMP_MAX
    FILE *tmpfile(void); // 产生一个临时二进制文件(wb+),关闭或程序结束是自动删除
    char *mkdtemp(char *template);
    
    // 与上述不同的是,产生的临时文件不会主动删除,需要自己unlink
    int mkstemp(char *template);
    int mkostemp(char *template, int flags);
    int mkstemps(char *template, int suffixlen);
    int mkostemps(char *template, int suffixlen, int flags);
    
    // 内存流
    FILE *fmemopen(void *buf, size_t size, const char *mode);
    
    FILE *open_memstream(char **ptr, size_t *sizeloc);
    #include <wchar.h>
    FILE *open_wmemstream(wchar_t **ptr, size_t *sizeloc);
    

    系统数据文件

    // /etc/passwd
    #include <pwd.h>
    struct passwd {
        char   *pw_name;       /* username */
        char   *pw_passwd;     /* user password */
        uid_t   pw_uid;        /* user ID */
        gid_t   pw_gid;        /* group ID */
        char   *pw_gecos;      /* user information */
        char   *pw_dir;        /* home directory */
        char   *pw_shell;      /* shell program */
    };
    struct passwd *getpwnam(const char *name);
    struct passwd *getpwuid(uid_t uid);
    struct passwd *getpwent(void);
    void setpwent(void);
    void endpwent(void);
    
    // demo
    #include <stdlib.h>
    #include <stdio.h>
    #include <pwd.h>
    
    int main(int argc, char *argv[]) {
    	struct passwd* ptr = NULL;
    	while ( (ptr = getpwent())!= NULL ) {
    		printf("U/P:%s %s \nID:%d %d \nI:%s \nDIR:%s \nSHELL:%s\n",
    			ptr->pw_name, ptr->pw_passwd, ptr->pw_uid, ptr->pw_gid, ptr->pw_gecos,
    			ptr->pw_dir, ptr->pw_shell);
    			printf("-----------------\n");
    	}
    	endpwent();
    	return 0;
    }
    
    // 在.etc/passwd 中,用户密码都是x,因为它被移到单独的文件/etc/shadow中,并且加密了,阴影口令文件
    #include <shadow.h>
    struct spwd {                                                  
        char *sp_namp;     /* Login name */                        
        char *sp_pwdp;     /* Encrypted password */                
        long  sp_lstchg;   /* Date of last change                  
                              (measured in days since              
                              1970-01-01 00:00:00 +0000 (UTC)) */  
        long  sp_min;      /* Min # of days between changes */     
        long  sp_max;      /* Max # of days between changes */     
        long  sp_warn;     /* # of days before password expires    
                              to warn user to change it */         
        long  sp_inact;    /* # of days after password expires     
                              until account is disabled */         
        long  sp_expire;   /* Date when account expires            
                              (measured in days since              
                              1970-01-01 00:00:00 +0000 (UTC)) */  
        unsigned long sp_flag;  /* Reserved */                     
    };                                                             
    struct spwd *getspnam(const char *name);
    struct spwd *getspent(void);
    void setspent(void);
    void endspent(void);
    struct spwd *fgetspent(FILE *stream);
    struct spwd *sgetspent(const char *s);
    int putspent(const struct spwd *p, FILE *stream);
    int lckpwdf(void);
    int ulckpwdf(void);
    
    char *crypt(const char *key, const char *salt); // 密码hash, -lcrypt
    // demo, 可以验证自己的密码
    #define _GNU_SOURCE
    #include <crypt.h>
    
    int main(int argc, char *argv[]) {
        char* pT1 = crypt("my_pwd", "$6$tFoDYuJAMie9hU/s");
        printf("%s\n", pT1);
        return 0;
    }
    
    // 组文件 /etc/group
    #include <sys/types.h>
    #include <grp.h>
    struct group {
        char   *gr_name;        /* group name */
        char   *gr_passwd;      /* group password */
        gid_t   gr_gid;         /* group ID */
        char  **gr_mem;         /* NULL-terminated array of pointers
                                    to names of group members */
    };
    struct group *getgrnam(const char *name);
    struct group *getgrgid(gid_t gid);
    struct group *getgrent(void);
    void setgrent(void);
    void endgrent(void);
    
    // 4.2BSD 开始,用户有附属组ID
    #include <sys/types.h>
    #include <unistd.h>
    #include <grp.h>
    int getgroups(int size, gid_t list[]);
    int setgroups(size_t size, const gid_t *list);
    int initgroups(const char *user, gid_t group);
    

    其他数据文件:
    BSD各网络服务器提供服务的数据文件/etc/services,记录协议信息的/etc/protocols,记录网络信息的/etc/networks

    系统标志:

    #include <sys/utsname.h>
    struct utsname {
       char sysname[];    /* Operating system name (e.g., "Linux") */
       char nodename[];   /* Name within "some implementation-defined
                               network" */
       char release[];    /* Operating system release (e.g., "2.6.28") */
       char version[];    /* Operating system version */
       char machine[];    /* Hardware identifier */
    };
    int uname(struct utsname *buf);
    // cat /etc/lsb-release
    /*
    Linux 
    xinvm
    4.15.0-39-generic
    #42-Ubuntu SMP Tue Oct 23 15:48:01 UTC 2018
    x86_64
    */
    int gethostname(char *name, size_t len);
    int sethostname(const char *name, size_t len);
    

    时间:

    #include <time.h>
    struct tm {
        int tm_sec;    /* Seconds (0-60) */
        int tm_min;    /* Minutes (0-59) */
        int tm_hour;   /* Hours (0-23) */
        int tm_mday;   /* Day of the month (1-31) */
        int tm_mon;    /* Month (0-11) */
        int tm_year;   /* Year - 1900 */
        int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
        int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
        int tm_isdst;  /* Daylight saving time */
    };
    char *asctime(const struct tm *tm);
    char *asctime_r(const struct tm *tm, char *buf);
    
    char *ctime(const time_t *timep);
    char *ctime_r(const time_t *timep, char *buf);
    
    struct tm *gmtime(const time_t *timep);
    struct tm *gmtime_r(const time_t *timep, struct tm *result);
    
    struct tm *localtime(const time_t *timep);
    struct tm *localtime_r(const time_t *timep, struct tm *result);
    
    time_t mktime(struct tm *tm);
    // 将时间格式化为字符串,把字符串转化为time_t
    size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
    char *strptime(const char *s, const char *format, struct tm *tm);
    

    进程

    进程终止有5中正常行为:

    • main返回
    • exit调用
    • _exit/_Exit调用
    • 最后一个线程返回
    • 最后一个线程调用pthread_exit

    异常终止有3中方式:

    • abort调用
    • 接到一个信号
    • 最后一个线程读取消请求做出相应

    一个进程可以最多登记32个函数,当exit时自动调用,这些函数称之为终止处理程序,他们通过atexit函数注册。调用与登记的顺序相反。

    参数表:int main(int argc, char* argv[])(一般第一个是自身,第二个开始正常使用,最后一个是NULL)
    环境表:extern char **environ; 最后一个字符串指针是NULL。name=value形式存储.

    char *getenv(const char *name);
    int putenv(char *string);
    int setenv(const char *name, const char *value, int overwrite);
    int unsetenv(const char *name);
    int clearenv(void);
    

    存储空间分配:

    #include <stdlib.h>
    
    void *malloc(size_t size);
    void free(void *ptr);
    void *calloc(size_t nmemb, size_t size);
    void *realloc(void *ptr, size_t size);
    void *reallocarray(void *ptr, size_t nmemb, size_t size);
    

    C语言中,goto是不能跨越函数的,所以有了setjmp/longjmp.

    #include <setjmp.h>
    
    int setjmp(jmp_buf env); // 设置跳转点环境
    int sigsetjmp(sigjmp_buf env, int savesigs);
    
    void longjmp(jmp_buf env, int val); // 跳转,全局、静态、易逝变量保持不变
    void siglongjmp(sigjmp_buf env, int val);
    

    资源限制:getrlimit/setrlimit

    exec系列函数

    解释器文件(interpreter file):
    文本文件起始行:#! pathname [optional-args]

    int system(const char *command); // 内部实现了fork/exec/waitpid

    #include <unistd.h>
    char *getlogin(void);
    int getlogin_r(char *buf, size_t bufsize);
    #include <stdio.h>
    char *cuserid(char *string);
    
    #include <unistd.h>
    int nice(int inc); // 调整nice值
    
    #include <sys/time.h>
    #include <sys/resource.h>
    int getpriority(int which, id_t who);
    int setpriority(int which, id_t who, int prio);
    
    #include <sys/times.h>
    clock_t times(struct tms *buf); // 进程时间
    

    进程组,每个进程除了有进程ID还有一个进程组。同一进程组中的各个进程接收来自同一终端的各种信号。

    #include <sys/types.h>
    #include <unistd.h>
    
    int setpgid(pid_t pid, pid_t pgid);
    pid_t getpgid(pid_t pid);
    
    pid_t getpgrp(void);                 /* POSIX.1 version */
    pid_t getpgrp(pid_t pid);            /* BSD version */
    
    int setpgrp(void);                   /* System V version */
    int setpgrp(pid_t pid, pid_t pgid);  /* BSD version */
    

    会话是多个进程组的集合。使用setsid创建一个新的会话。

    会话和进程组还有一些其他特性:

    • 一个会话可以有一个控制终端,它通常是终端设备或者伪终端设备。
    • 建立与终端连接会话的首进程称为控制进程。
    • 一个会话中的几个进程组可分为一个前台进程组以及多个后台进程组。
    • 如果会话有一个控制终端,则它由一个前台进程组,其他的都为后台进程组。
    • 无论何时键入终端中断键(Delete/Ctrl+C),都会中断信号发送到前台进程组所有进程。
    • 如果终端检测到网络或者连接断开,则挂断信号发送到所有控制进程。
    #include <unistd.h>
    pid_t tcgetpgrp(int fd);
    int tcsetpgrp(int fd, pid_t pgrp);
    
    #define _XOPEN_SOURCE 500        /* See feature_test_macros(7) */
    #include <termios.h>
    pid_t tcgetsid(int fd);
    

    信号:

    #include <signal.h>
    typedef void (*sighandler_t)(int);
    sighandler_t signal(int signum, sighandler_t handler);
    
    int kill(pid_t pid, int sig); // 发送信号给pid, <0 是发送给进程组
    int raise(int sig); // 发送信号给自身
    unsigned int alarm(unsigned int seconds); // 发送定时器信息,由内核产生信号
    int pause(void); // 进程挂起,直到收到一个信号,并且执行了信号处理函数并返回
    
    int sigemptyset(sigset_t *set);
    int sigfillset(sigset_t *set);
    int sigaddset(sigset_t *set, int signum);
    int sigdelset(sigset_t *set, int signum);
    int sigismember(const sigset_t *set, int signum);
    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 
    
    int sigpending(sigset_t *set);
    int sigsuspend(const sigset_t *mask);
    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
    void abort(void);
    
    #include <time.h>
    unsigned int sleep(unsigned int seconds);
    int nanosleep(const struct timespec *req, struct timespec *rem);
    int clock_nanosleep(clockid_t clock_id, int flags,const struct timespec *request, struct timespec *remain);
    
    void psignal(int sig, const char *s); // print signal info
    void psiginfo(const siginfo_t *pinfo, const char *s); // print signal info
    
    extern const char *const sys_siglist[]; // 信号名称与编号的映射数组
    

    线程

    线程接口也称之为pthread或者POSIX线程

    #include <pthread.h> // link with -pthread
    
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
    pthread_t pthread_self(void);
    int pthread_equal(pthread_t t1, pthread_t t2);
    void pthread_exit(void *retval);
    int pthread_join(pthread_t thread, void **retval);
    int pthread_cancel(pthread_t thread); // 发送取消指令,不等待完成
    void pthread_cleanup_push(void (*routine)(void *), void *arg); // 退出时清理函数
    void pthread_cleanup_pop(int execute);
    int pthread_detach(pthread_t thread); // 清理,之后不能使用join
    
    // demo1 
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <string.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 %lu (0x%lx) \n", s, (unsigned long)pid, (unsigned long)tid, (unsigned long)tid);
    }
    
    void *thr_fn(void* arg) {
        printids("new thread:");
        return (void*)NULL;
    }
    
    int main(){
        int err;
        err = pthread_create(&ntid, NULL, thr_fn, NULL);
        if( err != NULL ){
            perror("can't create thread");
        }
        printids("main thread:");
        sleep(1);
        return 0;
    }
    
    // Demo 2
    #include <pthread.h>
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <ctype.h>
    
    #define handle_error_en(en, msg) \
        do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
    
    #define handle_error(msg) \
        do { perror(msg); exit(EXIT_FAILURE); } while (0)
    
    struct thread_info {    /* Used as argument to thread_start() */
        pthread_t thread_id;        /* ID returned by pthread_create() */
        int       thread_num;       /* Application-defined thread # */
        char     *argv_string;      /* From command-line argument */
    };
    
    /* Thread start function: display address near top of our stack,
      and return upper-cased copy of argv_string */
    
    static void *thread_start(void *arg)
    {
        struct thread_info *tinfo = arg;
        char *uargv, *p;
        
        printf("Thread %d: top of stack near %p; argv_string=%s\n",
               tinfo->thread_num, &p, tinfo->argv_string);
        
        uargv = strdup(tinfo->argv_string);
        if (uargv == NULL)
            handle_error("strdup");
        
        for (p = uargv; *p != '\0'; p++)
            *p = toupper(*p);
        
        return uargv;
    }
    
    int main(int argc, char *argv[])
    {
        int s, tnum, opt, num_threads;
        struct thread_info *tinfo;
        pthread_attr_t attr;
        int stack_size;
        void *res;
        
        /* The "-s" option specifies a stack size for our threads */
        
        stack_size = -1;
        while ((opt = getopt(argc, argv, "s:")) != -1) {
            switch (opt) {
            case 's':
                stack_size = strtoul(optarg, NULL, 0);
                break;
                
            default:
                fprintf(stderr, "Usage: %s [-s stack-size] arg...\n",
                        argv[0]);
                exit(EXIT_FAILURE);
            }
        }
        
        num_threads = argc - optind;
        
        /* Initialize thread creation attributes */
        
        s = pthread_attr_init(&attr);
        if (s != 0)
            handle_error_en(s, "pthread_attr_init");
        
        if (stack_size > 0) {
            s = pthread_attr_setstacksize(&attr, stack_size);
            if (s != 0)
                handle_error_en(s, "pthread_attr_setstacksize");
        }
        
        /* Allocate memory for pthread_create() arguments */
        
        tinfo = calloc(num_threads, sizeof(struct thread_info));
        if (tinfo == NULL)
            handle_error("calloc");
        
        /* Create one thread for each command-line argument */
        
        for (tnum = 0; tnum < num_threads; tnum++) {
            tinfo[tnum].thread_num = tnum + 1;
            tinfo[tnum].argv_string = argv[optind + tnum];
            
            /* The pthread_create() call stores the thread ID into
              corresponding element of tinfo[] */
            
            s = pthread_create(&tinfo[tnum].thread_id, &attr,
                               &thread_start, &tinfo[tnum]);
            if (s != 0)
                handle_error_en(s, "pthread_create");
        }
        
        /* Destroy the thread attributes object, since it is no
          longer needed */
        
        s = pthread_attr_destroy(&attr);
        if (s != 0)
            handle_error_en(s, "pthread_attr_destroy");
        
        /* Now join with each thread, and display its returned value */
        
        for (tnum = 0; tnum < num_threads; tnum++) {
            s = pthread_join(tinfo[tnum].thread_id, &res);
            if (s != 0)
                handle_error_en(s, "pthread_join");
            
            printf("Joined with thread %d; returned value was %s\n",
                   tinfo[tnum].thread_num, (char *) res);
            free(res);      /* Free memory allocated by thread */
        }
        
        free(tinfo);
        exit(EXIT_SUCCESS);
    }
    

    进程原语与线程原语十分相似,这里做一个对比:

    进程原语 线程原语 描述
    fork pthread_create 创建新的控制流
    exit pthread_exit 从现有的控制流退出
    waitpid pthread_join 从控制流中得到退出状态
    atexit pthread_cleanup_push 注册退出控制流时的函数
    getpid pthread_self 获取控制流ID
    abort pthread_cancel 请求控制流的非正常退出

    线程同步:

    // 互斥锁
    #include <pthread.h>
    
    int pthread_mutex_destroy(pthread_mutex_t *mutex); // 动态分配必须destroy
    int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 静态分配
    
    int pthread_mutex_lock(pthread_mutex_t *mutex);
    int pthread_mutex_trylock(pthread_mutex_t *mutex);
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    
    int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
    
    // 读写锁
    int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER;
    
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
    
    int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abstime);
    int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abstime);
    
    // 条件变量
    int pthread_cond_destroy(pthread_cond_t *cond);
    int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    
    int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
    int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
    
    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond);
    
    // 自旋锁
    // 自旋锁与互斥量类似,但是不通过休眠阻塞,而是在获取锁之前一直处于忙等(自旋)阻塞状态,它一般用于:锁持有时间短,而且线程不想再重新调度上花太多成本。
    pthread_spin_init/destroy/lock/trylock/unlock
    
    // 屏蔽
    // 屏蔽barrier是用户协调多个线程并行工作的同步机制,屏蔽允许每个线程等待,直到所有线程到达某个点。
    pthread_barrier_init/destroy/wait
    

    线程中的属性:

    // 线程属性
    int pthread_attr_init(pthread_attr_t *attr);
    int pthread_attr_destroy(pthread_attr_t *attr);
    int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
    int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
    int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
    int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
    int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
    int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
    int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
    int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize);
    
    // 同步属性
    int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
    int pthread_mutexattr_init(pthread_mutexattr_t *attr);
    int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *pshared);
    int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
    int pthread_mutexattr_getrobust(const pthread_mutexattr_t *attr, int *robustness);
    int pthread_mutexattr_setrobust(const pthread_mutexattr_t *attr, int robustness);
    
    int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr, int *restrict type);
    int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); // 更改锁状态
    
    // 读写锁属性
    pthread_rwlockattr_init/destory/setpshared/getpshared
    
    // 条件变量属性
    pthread_condattr_init/destory/setpshared/getpshared/setclock/getclock
    
    // 屏蔽属性
    pthread_barrierattr_init/destory/setpshared/getpshared
    
    
    // 线程安全方式管理FILE对象
    void flockfile(FILE *filehandle);
    int ftrylockfile(FILE *filehandle);
    void funlockfile(FILE *filehandle);
    
    int getc_unlocked(FILE *stream);
    int getchar_unlocked(void);
    int putc_unlocked(int c, FILE *stream);
    int putchar_unlocked(int c);
    
    void clearerr_unlocked(FILE *stream);
    int feof_unlocked(FILE *stream);
    int ferror_unlocked(FILE *stream);
    int fileno_unlocked(FILE *stream);
    int fflush_unlocked(FILE *stream);
    int fgetc_unlocked(FILE *stream);
    int fputc_unlocked(int c, FILE *stream);
    size_t fread_unlocked(void *ptr, size_t size, size_t n, FILE *stream);
    size_t fwrite_unlocked(const void *ptr, size_t size, size_t n, FILE *stream);
    char *fgets_unlocked(char *s, int n, FILE *stream);
    int fputs_unlocked(const char *s, FILE *stream);
    // wchar版本省略
    

    线程特定数据:
    默认情况下,所有的数据是共享的,我们期望没个线程改变自己特定的数据而不影响其他线程。

    int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
    int pthread_key_delete(pthread_key_t key);
    
    void *pthread_getspecific(pthread_key_t key);
    int pthread_setspecific(pthread_key_t key, const void *value);
           
    int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
    pthread_once_t once_control = PTHREAD_ONCE_INIT;
    

    创建的键存储在keyp指向的内存中,这个键可以被进程中的所有线程使用,但是每个线程把这个键与不同的线程特定数据地址关联。通过specific函数设置关联内容。
    如果每个线程都调用pthread_once,系统就能保证对init_routine只调用一次。

    线程与信号
    每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。进程的信号是投递到单个线程的,如果一个信号与硬件故障有关,则该信号会被发送到引起该事件的线程中,而其他信号被发送到任意线程中。

    int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
    int sigwait(const sigset_t *set, int *sig); // 多个线程同时等待则由操作系统决定
    int pthread_kill(pthread_t thread, int sig);
    

    线程与Fork
    当线程调用fork,则为子进程创建了整个进程地址空间的副本,在子进程内部只存在一个线程,它是由父进程中调用fork的线程副本构成的。
    fork后的子线程与父线程共享锁等内容,为避免死锁等,子进程fork后可以立即调用exec函数中一个,这样旧的地址空间会被丢弃。
    在多线程中,为避免不一致的问题,POSIX声明,在fork返回和子进程调用exec函数之间,子进程只能带哦用一部信号安全的函数。
    要清除锁状态,可以通过pthread_atfork函数处理fork后程序。

    线程与IO
    多线程环境下,pread/pwrite函数是很有用的,因为进程公用相同的文件描述符,多线程同时读写就可以变为原子操作。

    守护进程

    守护进程是生存周期长的一种进程,常在系统引导时启动,系统关闭时终止,他们没有终端,只在后台运行。
    ps -axj

    • 父进程ID为0的是内核进程,系统引导时启动。他们放在方括号名字中。
    • 进程ID=1通常是init,它是系统守护进程,主要负责启动特定的系统服务。
    • rpcbind是提供rpc服务,将程序号映射为网络端口号的服务。
    • inetd侦听系统网络接口,取得来自网络的对各种网络服务进程的请求。
    • nfsd,nfsiod,lockd,rpciod,rpc.idmapd,rpc.statd,rpc.mountd提供NFS文件系统支持。
    • cron是定期安排命令执行。
    • cupsd是打印假脱机服务
    • sshd是远程登陆和执行服务

    创建一个守护进程:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <syslog.h>
    #include <fcntl.h>
    #include <sys/resource.h>
    
    int main(){
        int i,fd0,fd1,fd2;
        pid_t pid;
        struct rlimit r1;
        struct sigaction sa;
        // 1. clear file creation mask
        umask(0);
        // 2. Get Maximum number of file description
        getrlimit(RLIMIT_NOFILE, &rl);
        // 3. Become a seesion leader to lose tty
        if( (pid=fork()) < 0 ){
            perror("fork error");
        }else if( pid != 0 ){
            exit(0); // parent process exit
        }
        setsid();
    
    	/* ensure future opens won't allocate controlling ttys */
    	
        // 4. change current working directory
        chdir("/");
    
        // 5. close all opened file description
        if( rl.rlim_max == RLIM_INFINITY ){
            rl.rlim_max = 1024;
        }
        for(i=0;i<rl.rlim_max;i++){
            close(i);    
        }
        // 6. attach 0,1,2 to /dev/null
        fd0 = open("/dev/null",O_RDWR);
        fd1 = dup(0);
        fd2 = dup(0);
    
        // 7. init log file
        openlog("xxxx",LOG_CONS, LOG_DAEMON);
    
        return 0;
    }
    

    单例守护进程:
    每个守护进程创建一个固定名字的文件,并且在这个文件上加锁,那么只允许创建一个这样的锁。其他进程再尝试都会失败,这就可以保证只有一个副本运行。

    一些遵循的惯例:

    • 若使用锁文件,一般在/var/run/xxx.pid
    • 若支持配置文件,一般在/etc/xxx.conf.
    • 守护进程可使用命令行启动,它脚本一般在/etc/rc*或/etc/init.d/*
    • 守护进程启动时读取配置,中途配置改变时,一般不处理。一般使用SIGHUP重读配置。

    系统日志:

    #include <syslog.h>
    
    void openlog(const char *ident, int option, int facility);
    void syslog(int priority, const char *format, ...);
    void closelog(void);
    

    高级IO

    非阻塞IO、记录锁、IO多路转接(select/poll),异步IO、readv/writev、存储映射IO(mmap)。

    非阻塞IO: flags = fcntl(fd,F_GETFL,0); flags |= O_NONBLOCK; fcntl(fd,F_SETFL,flags);
    记录锁:当程序使用文件某部分时,阻止其他进程修改同一区域。 fcntl F_RDLCK/F_WRLCLK/F_UNLCK
    IO多路转接:

    • select/pselect 告诉内核我们关心的描述符和条件,等待select返回,告诉我们哪个描述符哪个条件好了。
    /* According to POSIX.1-2001, POSIX.1-2008 */
    #include <sys/select.h>
    
    /* According to earlier standards */
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    void FD_CLR(int fd, fd_set *set);
    int  FD_ISSET(int fd, fd_set *set);
    void FD_SET(int fd, fd_set *set);
    void FD_ZERO(fd_set *set);
    
    #include <sys/select.h>
    int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);
    
    • poll与select类似,但是将条件聚合为数组,每个元素指明我们感兴趣的文件描述符和条件
    #include <poll.h>
    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    
    #define _GNU_SOURCE         /* See feature_test_macros(7) */
    #include <signal.h>
    #include <poll.h>
    int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *tmo_p, const sigset_t *sigmask);
    

    异步IO:系统不告诉我们描述符的状态,需要我们主动查询。

    #include <aio.h> // Link with -lrt
    int aio_read(struct aiocb *aiocbp);
    int aio_write(struct aiocb *aiocbp);
    int aio_fsync(int op, struct aiocb *aiocbp);
    int aio_error(const struct aiocb *aiocbp);
    aio_return/suspend/cancel/listio
    

    readv/writev: 散步度、聚集写

    #include <sys/uio.h>
    ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
    ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
    ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset);
    ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset);
    ssize_t preadv2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags);
    ssize_t pwritev2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags);
    

    存储映射IO:将磁盘文件映射到缓冲上,读写缓冲就相当于读写文件。

    #include <sys/mman.h>
    void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
    int munmap(void *addr, size_t length);
    int mprotect(void *addr, size_t len, int prot); // 更改映射权限
    int pkey_mprotect(void *addr, size_t len, int prot, int pkey);
    int msync(void *addr, size_t length, int flags); // 刷洗缓冲区到文件
    

    IPC

    管道、FIFO、XSI、消息队列、共享存储、套接字
    管道:
    历史上是半双工的,只能在由公共父进程的之间使用。

    #include <unistd.h>
    int pipe(int pipefd[2]);
    
    #define _GNU_SOURCE             /* See feature_test_macros(7) */
    #include <fcntl.h>              /* Obtain O_* constant definitions */
    #include <unistd.h>
    int pipe2(int pipefd[2], int flags);
    
    FILE *popen(const char *command, const char *type); // 创建一个pipe然后fork执行cmdstring
    int pclose(FILE *stream);
    

    FIFO:
    命名管道,未命名的管道只能在相关进程中使用,但是FIFO可以在不相关的进程中使用。

    #include <sys/types.h>
    #include <sys/stat.h>
    int mkfifo(const char *pathname, mode_t mode);
    

    XSI: (消息队列、信号量、共享存储)
    msgget/msgctl/msgsnd/msgrcv/semget/semctl/semop/shmget/shmctl/shmat/shmdt/sem_open/sem_close/sem_unlink/sem_wait/set_post/sem_init/sem_getvalue/

    Socket:
    只有socket在进程间通信模型中可以跨计算机和网络。

    #include <sys/socket.h>
    
    int socket(int domain, int type, int protocol);
    int shutdown(int sockfd, int how);
    
    // Name                Purpose                          Man page
    // AF_UNIX, AF_LOCAL   Local communication              unix(7)
    // AF_INET             IPv4 Internet protocols          ip(7)
    // AF_INET6            IPv6 Internet protocols          ipv6(7)
    // AF_IPX              IPX - Novell protocols
    // AF_NETLINK          Kernel user interface device     netlink(7)
    // AF_X25              ITU-T X.25 / ISO-8208 protocol   x25(7)
    // AF_AX25             Amateur radio AX.25 protocol
    // AF_ATMPVC           Access to raw ATM PVCs
    // AF_APPLETALK        AppleTalk                        ddp(7)
    // AF_PACKET           Low level packet interface       packet(7)
    // AF_ALG              Interface to kernel crypto API
    
    // SOCK_STREAM     Provides sequenced, reliable, two-way, connection-based byte streams.   An  out-
    //                of-band data transmission mechanism may be supported.
    // 
    // SOCK_DGRAM      Supports  datagrams  (connectionless,  unreliable  messages  of  a fixed maximum
    //                length).
    // 
    // SOCK_SEQPACKET  Provides a sequenced, reliable, two-way connection-based data transmission  path
    //                for  datagrams of fixed maximum length; a consumer is required to read an entire
    //                packet with each input system call.
    // 
    // SOCK_RAW        Provides raw network protocol access.
    // 
    // SOCK_RDM        Provides a reliable datagram layer that does not guarantee ordering.
    // 
    // SOCK_PACKET     Obsolete and should not be used in new programs; see packet(7).
    
    #include <arpa/inet.h>
    uint32_t htonl(uint32_t hostlong);
    uint16_t htons(uint16_t hostshort);
    uint32_t ntohl(uint32_t netlong);
    uint16_t ntohs(uint16_t netshort);
    
    const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
    int inet_pton(int af, const char *src, void *dst);
    
    // 地址查询,主机数据库
    #include <netdb.h>
    extern int h_errno;
    struct hostent *gethostbyname(const char *name);
    
    #include <sys/socket.h>       /* for AF_INET */
    struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);
    void sethostent(int stayopen);
    void endhostent(void);
    void herror(const char *s);
    const char *hstrerror(int err);
    
    //host相关函数认为过时了,使用net替代
    int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
    void freeaddrinfo(struct addrinfo *res);
    int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags);
    
    struct netent *getnetent(void);
    struct netent *getnetbyname(const char *name);
    struct netent *getnetbyaddr(uint32_t net, int type);
    void setnetent(int stayopen);
    void endnetent(void);
    
    struct protoent *getprotoent(void);
    struct protoent *getprotobyname(const char *name);
    struct protoent *getprotobynumber(int proto);
    void setprotoent(int stayopen);
    void endprotoent(void);
    
    struct servent *getservent(void);
    struct servent *getservbyname(const char *name, const char *proto);
    struct servent *getservbyport(int port, const char *proto);
    void setservent(int stayopen);
    void endservent(void);
    
    // 端口号小于1024需要sudo权限
    int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
    int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen); // 本机地址
    int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen); // 对方地址
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    int listen(int s, int backlog);
    int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
    int send(int s, const void *msg, size_t len, int flags); // 仅用于tcp
    int sendto(int  s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);
    int sendmsg(int s, const struct msghdr *msg, int flags);
    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
    ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
    int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
    int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
    

    UNIX域套接字:
    UNIX域套接字用于在同一台计算机上运行的进程间通信。它比Socket效率更高。它提供流和数据报表两种接口,就像是Socket与Pipe的混合。

    socket(AF_UNIX, SOCK_STREAM, 0)
    int socketpair(int domain, int type, int protocol, int sv[2]);
    

    终端IO(TTY)

    termios.h

    函数 说明
    isatty 是否是TTY
    tcgetattr 获取属性
    tcsetattr 设置属性
    cfgetispeed 获取输入速度(波特率)
    cfgetospeed 获取输出速度(波特率)
    cfsetispeed 设置输入速度
    cfsetospeed 设置输出速度
    tcdrain 等待所有输出都被传输
    tcflow 挂起传输或者接收
    tcflush 冲洗未决输入输出
    tcsendbreak 发送break字符
    tcgetpgrp 获取前台进程组ID
    tcsetpgrp 设置前台进程组ID
    tcgetsid 得到控制TTY会话进程组ID
    getpass // 读入用户在终端上键入的口令
    ioctl(TIOCGWINSZ) // 终端大小, TIOCSWINSZ 设置大小
    

    伪终端
    网络登陆服务器、窗口系统终端模拟、script程序、expect程序、运行协同进程…
    posix_openpt/grantpt/ptsname/unlockpt
    ``

    数据库

    dbm在Unix系统中很流行,BSD扩充为ndbm。4.4BSD提供了一个新的库db。

    网络打印机

    IPP是网络打印机的通信规则。IPP建立在HTTP上。

    展开全文
  • Unix环境高级编程 高清电子版Unix学习经典书籍,程序员必读
  • UNIX环境高级编程_第二中文,高清有标签!
  • UNIX环境高级编程 第2 完整带书签.pdf 个人收集电子书,仅用学习使用,不可用于商业用途,如有版权问题,请联系删除!
  • 本书精选了Unix中400多个系统调用和库函数,涵盖了Unix/Linux系统编程的方方面面。有助于读者对整个编程环境有全面深入的了解。
  • 以下是本书的目录部分和电子书的效果,个人觉得效果还是非常好非常清晰的。...第1章 UNIX基础知识 第2章 UNIX标准及实现 第3章 文件I/O 第4章 文件和目录 第5章 标准I/O库 第6章 系统数据文件和信息 第7章 进...

    以下是本书的目录部分和电子书的效果,个人觉得效果还是非常好非常清晰的。

    有需要的朋友可以自取,下载链接:

    链接:https://pan.baidu.com/s/1KG5XU63GI4TlQMpe2G3nnw
    提取码:pr3z

    第1章 UNIX基础知识

    第2章 UNIX标准及实现

    第3章 文件I/O

    第4章 文件和目录

    第5章 标准I/O库

    第6章 系统数据文件和信息

    第7章 进程环境

    第8章 进程控制

    第9章 进程关系

    第10章 信号

    第11章 线程

    第12章 线程控制

    第13章 守护进程

    第14章 高级I/O

    展开全文
  • 由于以后使用的需要,要对它进行比较深入的学习,为此需要阅读不少的书籍,这本《Unix环境高级编程》便在此列。希望能通过这本书了解Linux的API,并在这个过程中了解Linux系统的机制。书中内容丰富,有以前了解的,...

    以前在课堂上学习过《Unix初级教程(第四版)》,对于Unix有了一点了解。由于以后使用的需要,要对它进行比较深入的学习,为此需要阅读不少的书籍,这本《Unix环境高级编程》便在此列。希望能通过这本书了解Linux的API,并在这个过程中了解Linux系统的机制。书中内容丰富,有以前了解的,更多的是不了解的。作为第一次阅读,目的不在于事无巨细,过目不忘,而是有个大体了解,从点到面地熟悉这个系统。为了构建整体的印象,对已知的和刚刚了解的都有所涉及。同时作为笔记,不希望成为对目录的简单复制,而是能成为对学习的程度和体会的记录。

     

    第一章  基础知识

      这一章本身就是概括,略。

     旧知:

      体系结构、登录、shell简介

     

    第二章  UNIX标准化及实现

     旧知:

      ISO C、POSIX、系统实现(SVR4、4.4BSD、FreeBSD、Linux、Mac OS X、Solaris)

     新知:

      Single UNIX Specification(POSIX.1的一个超集)、各个标准作出的限制(sysconf、pathconf、fpathconf可以获得)

     

    第三章  文件I/O

     旧知:

      文件描述符、文件I/O函数(open、creat、close、lseek、read、write以及可能造成的文件空洞)、I/O效率(缓冲区大小)、

      打开文件使用的数据结构(进程表项、文件表、v节点表)、原子操作

     新知:

      文件描述符的复制(dup和dup2)、及时手动更新缓冲区(sync、fsync、fdatasync)、获取和设置已打开的文件的性质(fcntl)

      另外从这一章起,逐渐发现和文件操作有关的同一种功能的函数经常有两个版本,前面带f的参数一般是文件描述符filedes,不带f的使用路径pathname。

     

    第四章  文件和目录

     旧知:

      文件类型、用户ID和组ID、文件访问权限、权限的修改(chmod、fchmod)、文件长度(不含空洞情况)、基本操作(remove、rename、mkdir、rmdir)

     新知:

      查看文件属性(stat、fstat、lstat)、目录的读权限和执行权限、权限测试(access)、权限屏蔽字的设置(umask)、粘住位(如果支持,在交换区保护正文)、

      更改用户ID和组ID(chown、fchown、lchown)、文件截短、文件系统(i节点、数据块和目录块、目录文件和普通文件的不同)、

      硬链接(在原有知识之上,link、unlink的使用和具体操作、临时文件的删除)、符号链接(symlink、readlink、构成循环的符号链接)、文件时间的修改(utime)

      工作目录及更改(chdir、fchdir、getcwd)、设备特殊文件(st_dev和st_rdev的区别)

     

    第五章  标准I/O库

     类似于涉及文件操作的一些函数,流操作的一些函数的也有三个版本:前缀s的把字符写入缓冲区buf,前缀f的写入指定流,不带前缀的写入到标准输入输出。这只是一个概括。

     旧知:

       流和FILE对象、标准输入、标准输出、标准出错、流的打开(fopen、freopen、fdopen)

       字符读(getc、fgetc、getchar和相关的ferror、feof、clearerr、ungetc)和写(putc、fputc、putchar)

       按行I/O(fgets、gets、fputs、puts)

       格式化I/O(printf、fprintf、sprintf、snprintf、scanf、fscanf、sscanf和一些其他变体)

     新知:

       标准I/O效率、二进制I/O(一次处理整个结构fread、fwrite)、定位流(ftell、fseek、rewind、ftello、fseeko)

       临时文件(创建和维护:tmpnam、tmpfile)

       标准I/O库实际并不完善,这和基本设计和不同的实现有关。不足之处之一是效率不高,软件包是一个代替方法。

     

    第六章  系统数据文件和信息

     旧知:

       口令文件

     新知:

       阴影口令、组文件、附加组ID、登录账户记录、时间日期函数

     

     第七章  进程环境

      旧知:

       exit函数(exit、_exit和_Exit)、C程序存储空间布局、存储器分配(malloc、calloc、realloc)、环境变量、跨函数跳转(setjmp和longjmp)

     新知:

       atexit函数、环境表、共享库(库函数的动态链接,减小可执行文件长度)、进程资源限制(getrlimit和setrlimit函数)

     

    第八章  进程控制

      旧知:

        进程标识符、fork()、exit()、wait()和waitpid()、竞争条件、exec()、

     新知:

        vfork()、waitid()、wait3和wait4、exec的六种形式和工作过程、更改用户ID和组ID、解释器文件(执行其第一行所指定的文件)、system()、

        进程会计(产生进程的各项运行记录)、用户标识、进程时间(不是很理解墙上时钟时间)

     对于程序清单8-9中使用的变量environ可以参考:http://topic.csdn.net/t/20060820/13/4962381.html

     

    第九章  进程关系

     新知:

        终端登录过程、getty程序、进程组、会话、控制终端(与会话和进程组相关)、与前面三项相关联的tcgetpgrp、tcsetpgrp、tcgetsid

        作业控制(中断、退出、挂起;后台运行和转为前台)、孤儿进程组

        一个作业只是几个进程的集合,通常是一个进程的管道线。对于这章内容,之前只知道如何让程序后台运行(加&)和查看,其他方面了解的很少。

        9.9节shell执行程序想说明在不同版本的UNIX上shell执行程序的方式以及这和上面一些概念的关系,感觉自己的理解还是不到位。

     

    第十章  信号

     旧知:

        signal()(告诉内核出现某个信号时应采取的操作)

     新知:

        可重入函数、一些信号语义、kill()和raise()、alarm()和pause()

        信号集和相关操作(sigemptyset、sigfillset、sigaddset、sigdelset、sigismember、sigprocmsak、sigpending、sigaction)

        sigsetjmp()和siglongjmp()、sigsuspend()、abort()与system()和sleep()在信号情景下的实现、作业控制信号

        对于信号,以往了解的比较少,这部分的阅读时间拖得也比较长,了解仍不够深入。目前只是对信号机制的作用方式有所了解,但读后感觉确实对于理解UNIX里的一些其它机制的实现有了不同的认识。

     

    第十一章  线程

     旧知:

        线程标识tid、线程创建pthread_create()、线程终止pthread_exit()和相关的pthread_join()、线程同步之互斥量mutex

     新知:

        对同一个互斥量加锁两次会造成死锁(以前只知道两个线程循环请求的死锁);条件变量作为同步机制的应用

     

    第十二章  线程管理

     新知:

        线程属性(分离状态、警戒缓冲区、线程栈低地址和大小、取消)、线程的同步对象属性(互斥量、读写锁)、线程级的函数重入、线程私有数据

        线程和信号(由同一个进程共享)、线程与fork和I/O

     

    第十三章  守护进程

     新知:

        常见守护进程、创建守护进程、出错日志

        本章内容也是第一次了解,由于涉及信号处理,对于示例仍处于一知半解的状态。

     

    第十四章  高级I/O

     新知:

        非阻塞I/O、记录锁、STREAMS、I/O多路转接的实现(select()、pselect()、poll())、异步I/O(涉及不同系统)

        readv和writev()、readn()和writen()、存储映射I/O:mmap()

        对于记录锁,以往知道一些,这里做了进一步了解。

        STREAMS只是大概了解了其功能和普通的流的差异,多路转接也是只做了简单了解。

        关于mmap、readn()、writen()与传统的read()&write()的性能差异书中亦有分析。

     

    第十五章  进程间通信

      进程间通信即IPC,对于这章提到的管道、FIFO、消息队列、信号量和共享存储,以前有所了解。这章细节内容比较多,暂且根据目前的理解总结一下。

      管道只限于具有公共祖先的进程之间使用,使用方式:初始化管道->关闭不用的读或写端->数据传送。由管道的特性和使用方式,有相关的函数popen()和pclose()。协同进程的例子试了一试,加深了理解。

      FIFO是一种文件类型,文件I/O函数自然可以用于操作FIFO。

      除了管道和FIFO,后三种都属于XSI IPC,它们的相似处在于其IPC结构(标识符ID和键key、权限结构、结构限制),在系统范围内使用。它们的操作函数的格式比较类似:由key获得ID的msgget()、semget()、shmget();操作函数msgctl()、semctl()、shmctl()。消息队列和信号量还各有自己的特有结构:前者是消息,后者是信号量集和信号量。消息队列特有的操作:消息入队msgsnd()和出队msgrcv();信号量特有操作semop();共享存储特有操作:连接到地址空间shmat()、地址空间脱离shmdt()。

      书中信号量部分似乎漏了一行:信号量集是如何提供它所包含的信号量的入口的。在书中struct semid_ds中遗漏的一行如下:

     struct sem                *sem_base        /*指向集合中第一个信号量的地址*/

      参考了APUE的电子版(似乎是第一版)和这个帖子:http://bbs.chinaunix.net/thread-483853-1-1.html才有了上面的结论。个人认为如果是有意省略,确实不恰当。

    第十六章  网络IPC:套接字

      对于套接字之前通过《TCP/IP Sockets编程(C语言实现) 》有所学习,这章对于作者来说是个复习。地址族、字节序、通用地址结构sockaddr、套接字地址sockaddr_in和一些相应的转换函数不必再提;查询配置信息的函数gethostent()、getnetent()、getprotoent()、getservent()和一些相关函数是第一次遇到,或许是因为它们是BSD网络软件提供的接口,简单的了解了一下。getaddrinfo()和getnameinfo()比较熟悉,也不细述。

      对于一些和套接字操作相关的函数,根据阅读和对程序清单的理解,大体使用方式简述为如下,

      客户机,创建并填充socket后,面向连接的需要先connect()后send()或recv();无连接不用connnect()而是直接使用sendto()和recvfrom()。sendmsg()和recvmsg()用于使用多重缓冲区进行消息传送,其参数不涉及destaddr,应该是面向连接的。

      服务器,创建并填充socket,先bind(),打开listen()。面向连接需要在收到请求后使用新的socket进行accept(),原socket继续侦听;无连接侦听到后即可使用原socket进行数据传输。使用函数和客户机一样,按是否面向连接选用。

      带外数据是一些通信协议所支持的可选特征。TCP仅支持一个字节的紧急数据,在普通数据传递机制数据流之外传输。这里涉及到信号发送和处理。

      recv()和send()涉及到非阻塞和异步I/O,其又与信号有关,行为和阻塞模式下不同,使用方法介绍的比较简单,不在此赘述。

     

    第十七章  高级进程间通信

      本章主要介绍的是基于STREAMS的管道和域套接字这两种进程间通信方式,后面的实例即是对它们的使用。理解的还不够,目前还是一知半解的状态。

     

    第十八至二十一章

      这四章分别介绍终端I/O、伪终端、数据库函数库、与网络打印机通信,前两者以前完全没接触过,现在仅仅了解基本概念;后两者是具体实现,源代码也没有研读。

     

      花了近两个月终于算是把这本书大体浏览了一遍,更加感到不了解的东西还很多,需要学习的东西还很多。

    展开全文
  • UNIX环境高级编程(第三).pdf 个人收集电子书,仅用学习使用,不可用于商业用途,如有版权问题,请联系删除!
  • 序我差不多每次在接受专访当中,或是做技术讲座后的提问时间里,总会被问及这样一个问题:“你想到过UNIX会生存这么长时间吗?”自然,每次的回答都是:没有,我们没想到会是这样。从某种角度说,UNIX系统已经伴随了...

    我差不多每次在接受专访当中,或是做技术讲座后的提问时间里,总会被问及这样一个问题:“你想到过UNIX会生存这么长时间吗?”自然,每次的回答都是:没有,我们没想到会是这样。从某种角度说,UNIX系统已经伴随了商用计算行业历史的大半,而这也早就不是什么新闻了。

    发展的历程错综复杂,充满变数。自20世纪70年代初以来,计算机技术经历了沧海桑田般的变化,尤其体现在网络技术的普遍应用、图形化的无所不在、个人计算的触手可及,然而UNIX系统却奇迹般地容纳和适应了所有这些变化。虽然商业应用环境在桌面领域目前仍然为微软和英特尔所统治,但是在某些方面已经从单一供应商向多种来源转变,近年来对公共标准和免费开放资源的信赖已经与日俱增。

    UNIX作为一种现象而不单是商标品牌,有幸能与时俱进,乃至领导潮流。在上个世纪70~80年代,AT&T虽对UNIX的实际源代码进行了版权保护,但却鼓励在系统的接口和语言基础上进行标准化的工作。例如,AT&T发布了SVIDSystem V Interface Definition,系统V接口定义),这成为POSIX及其后续工作的基础。后来,UNIX可以说相当优雅地适应了网络环境,虽不那么轻巧却也充分地适应了图形环境。再往后,开源运动的技术基础中集成了UNIX的基本内核接口和许多它独特的用户级工具。

    即使在UNIX软件系统本身还是专有的时候,鼓励出版UNIX系统方面的论文和书籍也是至关重要的,著名的例子就是Maurice Bach的《UNIX操作系统设计》一书。其实我要说明的是,UNIX长寿的主要原因是,它吸引了极具天分的技术作者,为大众解读它的优美和神秘所在。Brian Kernighan是其中之一,Rich Stevens自然也是。本书第1版连同Stevens所著的系列网络技术书籍,被公认为优秀的、匠心独具的名著,成为极其畅销的作品。

    然而,本书第1版毕竟出版时间太早了,那时还没有出现Linux,源自伯克利 CSRGUNIX接口的开源版本还没有广为流行,很多人的网络还在用串行调制解调器。Steve Rago认真仔细地更新了本书,以反映所有这些技术进展,同时还考虑到各种ISO标准和IEEE标准这些年来的变化。因此,他的例子是最新的,也是最新测试过的。

    总之,这是一本弥足珍贵的经典著作的更新版。

     

    Dennis Ritchie

    20053月于新泽西州Murray Hill

        

    引言

    我与Rich Stevens最早是通过电子邮件开始交往的,当时我发邮件报告他的第一本书《UNIX网络编程》的一个排版错误。他回信开玩笑说我是第一个给他发这本书勘误的人。到他1999年故去之前,我们时不时地会通些邮件,一般都是在有了问题认为对方能解答的时候。我们在USENIX会议期间多次相见,并共进晚餐,Rich在会议中给大家做技术培训。

    Rich Stevens真是个益友,行为举止很有绅士风度。我在1993年写《UNIX系统V网络编程》时,试图把书写成他的《UNIX网络编程》的系统V版。Rich发自内心地高兴地为我审阅了好几章,并不把我当成竞争对手,而是当作一起写书的同事。我们曾多次谈到要合作给他的《TCP/IP详解》写个STREAMS版。天若有情,我们或许已经完成了这个心愿。然而,Rich已经驾鹤西去,修订《UNIX环境高级编程》就成为我跟他一起写书的最易实现的方式。

    Addison-Wesley公司的编辑找到我说想修订Rich的这本书时,我第一反应是这本书没有多少要改的。尽管13年过去了,Rich的书还是巍然屹立。但是,与当初本书出版的时候相比,今日的UNIX行业已经有了巨大的变化。

    * 系统V的各个变种渐渐被Linux所取代。原来生产硬件配以各自的UNIX版本的几个主要厂商,要么提供了Linux的移植版本,要么宣布支持LinuxSolaris可能算是硕果仅存的占有一定市场份额的UNIX系统V版本4的后裔了。

    * 加州大学伯克利分校的CSRG(计算机科学研究组)在发布了4.4BSD之后,已经决定不再开发UNIX操作系统,只有几个志愿者小组还维护着一些可公开获得的版本。

    * Linux受到数以千计的志愿者的支持,它的引入使任何一个拥有计算机的人都能运行类似于UNIX系统的操作系统,并且可以免费获得源代码支持哪怕最新的硬件设备。在已经存在几种免费BSD版本的情况下,Linux的成功确实是个奇迹。

    * 苹果公司作为一个富有创新精神的公司,已经放弃了老的Mac操作系统,换之以一个在MachFreeBSD基础上开发的新系统。

    因此,我已经努力更新本书中的内容,以反映这4个平台。

    Rich1992年出版《UNIX环境高级编程》之后,我扔掉了手头几乎所有的UNIX程序员手册。这些年来,我桌上最常摆放的就是两本书,一本是字典,另一本就是《UNIX环境高级编程》。我希望读者也能认为本修订版一样有用。

    对第1版的改动

    Rich的书依然屹立,我试图不去改动他这本书原来的风格。但是13年间世事兴衰,尤其是影响UNIX编程接口的有关标准变化很大。

    我依据标准化组织的标准,更新了全书相关的接口方面的内容。第2章改动较大,因为它主要是讨论标准的。本书第1版是根据POSIX.1标准的1990年版写的,本修订版依据2001年版的新标准,内容要丰富很多。1990ISOC标准在1999年也更新了,有些改动影响到POSIX.1标准中的接口。

    目前的POSIX.1规范涵盖了更多的接口。The Open Group(原称X/Open)发布的“Single UNIX Specification”的基本规范现在已经并入POSIX.1,后者包含了几个1003.1标准和另外几个标准草案,原来这些标准是分开出版的。

    我也相应地增加了些章节,讨论新主题。线程和多线程编程是相当重要的概念,因为它们为程序员处理并发和异步提供了更清楚的方式。

    套接字接口现在也是POSIX.1的一部分了。它为进程间通信(IPC)提供了单一的接口,而不考虑进程的位置。它成为IPC章节的自然扩展。

    我省略了POSIX.1中的大部分实时接口。这些内容最好是在一本专门讲述实时编程的书中介绍。参考文献里有一本这方面的书。

    我把最后面几章的案例研究也更新了,用了更接近现实的例子。例如,现在很少有系统通过串口或并口连接PostScript打印机了,多数PostScript打印机是通过网络连接的,所以我对PostScript打印机通信的例子做了修改。

    有关调制解调器通信的那一章如今已经不太适用了。原始材料我们保留在本书网站上,有两种格式:PostScripthttp://www.apuebook.com/lostchapter/modem.ps)和PDFhttp://www. apuebook.com/lostchapter/modem.pdf)。

    书中示例的源代码也可以从www.apuebook.com上获得。多数示例已经在下述4种平台上运行过了:

    (1) FreeBSD 5.2.1,这是加州大学伯克利分校CSRG4.4BSD的一个变种,在英特尔奔腾处理器上运行。

    (2) Linux 2.4.22Mandrake 9.2发布),是一个免费的类UNIX操作系统,运行于英特尔奔腾处理器上。

    (3) Solaris 9,是Sun公司系统V版本4的变种,运行于64位的UltraSPARC IIi处理器上。

    (4) Darwin 7.4.0,是基于FreeBSDMach的操作系统环境,也是Apple Mac OS X 10.3版本的核心,运行于PowerPC处理器上。

    致谢

    (首先要感谢)Rich Stevens独立创作了本书第1版,它立即成为一本经典著作。

    没有家人的支持,我不可能修订此书。他们容忍我满屋子散落稿纸(比平常还甚),霸占了家里的好几台机器,成天埋头于电脑屏幕前。我的妻子Jeanne甚至亲自动手帮我在一台测试的机器上安装了Linux

    多名技术审校者提出了很多改进意见,确保内容准确。我非常感谢David BausumDavid BorehamKeith BosticMark EllisPhil HowardAndrew JoseyMukesh KackerBrian KernighanBengt KlebergBen KupermanEric RaymondAndy Rudoff

    我还要谢谢Andy Rudoff给我解答有关Solaris的问题,谢谢Dennis Ritchie不惜花时间从故纸堆中为我寻找有关历史方面问题的答案。再次谢谢Addison-Wesley公司的员工,与他们合作令人愉快,谢谢Tyrrell AlbaughMary FranzJohn FullerKaren GettmanJessica GoldsteinNoreen ReginaJohn Wait。特别感谢Evelyn Pyle细致地编辑了本书。

    就像Rich曾经做到的那样,我非常欢迎读者发来邮件,发表评论,提出建议,订正错误。

     

     

    Stephen A.Rago

    sar@apuebook.com

    20054月于新泽西州Warren

    展开全文
  • Unix环境高级编程(高清非扫描).PDF ,超清晰,就是电子版本
  • 本系列文章是学习被誉为UNIX编程圣经的《UNIX环境高级编程》的读书笔记。《UNIX环境高级编程》的英文全称为《Advanced Programming in the UNIX Environment》,简称《APUE》,其作者是UNIX和网络技术领域的知名专家...
  • Unix环境高级编程 》笔记 本书全面介绍了UNIX系统的程序设计界面—**提供的许多函数。 前15章着重于理论知识的阐述,主要内容包括**。 在此基础上,分别按章介绍了多个应用实例,包括如何**等。译者序(电子版...
  • unix网络编程.pdf

    2020-07-30 23:30:14
    unix网络编程.pdf unix网络编程.pdf unix网络编程.pdf
  • 本书基于 Python 3.5 版本进行讲解,深度揭示了 Python 编程高级技巧。本书从 Python 语言及其社区的现状开始介绍,对 Python 语法、命名规则、Python 包的编写、部署代码、扩展程序开发、管理代码、文档编写、...
  • UNIX网络编程阅读建议

    2017-08-03 23:52:54
    - 这本书不能一次性所有都想看完。 >要有目的性的看,因为这本书类似... >这本书必须配合TCP/IP详解和UNIX环境高级编程(简称APUE)以及The Linux Programming Interface(不知道这本书的译名是什么, 简称TLPI)来看
  • “本章快速浏览了UNIX系统。说明了某些以后会多次用到的基础术语,介绍了一些小的UNIX程序实例。读者可以从中大概了解到本书其余部分将要介绍的内容。”
  • Shell编程Unix命令http://download.chinaitlab.com/soft/11149.htm实践大师:Unix Shell编程篇http://download.chinaitlab.com/soft/11127.htmLinux与Unix SHELL编程指南读书笔记...
  • 虽然UNIX应用程序在不同的UNIX操作系统版本之间进行移植相同容易,但是20世纪80年代UNIX版本的剧增以及它们之间的差别扩大,导致很多用户呼吁对其进行标准化。标准化工作的一个重要部分是对每种实现必须定义的各种...
  • 首先要说讲述TCP/IP的书很多,其中有3泰书很全。...其中TCP/IP详解的作者还写了另外2本经典著作,《Unix环境高级编程》,《Unix网络编程》 作者W.Richard Stevens个人网站 http://www.kohala.com/
  • ▲Linux/Unix环境编程电子资料汇总: Understanding The Linux Kernel 第一 http://bbs.topsage.com/dispbbs.asp?boardID=119&ID=157562 Understanding The Linux Kernel 第二 ...
1 2 3 4 5 ... 20
收藏数 13,587
精华内容 5,434
关键字:

unix环境高级编程 电子版