# 附录A Wrapper 库参考

## 概述

Wrapper 库是课程提供的 C 语言工具库，封装了课程用到的所有系统调用。每个 Wrapper 函数在内部调用对应的系统调用并检查返回值，出错时自动打印错误信息并终止程序，从而简化实验代码的编写。

源文件组成：

| 文件 | 说明 |
|------|------|
| `wrapper.h` | 头文件，包含所有函数声明、类型定义和常量 |
| `wrapper.c` | Wrapper 函数实现 |
| `ptwrapper.c` | POSIX 线程相关 Wrapper 函数 |
| `libwrapper.a` | 预编译的静态库 |

## 编译与使用

```bash
# 方法一：链接静态库
gcc -o program program.c -L. -lwrapper

# 方法二：链接静态库 + 线程支持
gcc -o program program.c -L. -lwrapper -lpthread

# 方法三：自行编译
gcc -c wrapper.c ptwrapper.c
ar rc libwrapper.a wrapper.o ptwrapper.o
gcc -o program program.c -L. -lwrapper -lpthread
```

## 头文件包含的系统头文件

```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <math.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
```

## 常量与类型定义

```c
typedef struct sockaddr SA;   // 简化 socket 地址类型转换

#define MAXLINE  8192         // 最大文本行长度
#define MAXBUF   8192         // 最大 I/O 缓冲区大小
#define LISTENQ  1024         // listen() 第二参数（连接队列长度）

#define DEF_MODE  S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
#define DEF_UMASK S_IWGRP|S_IWOTH

// Robust I/O 缓冲区
typedef struct {
    int rio_fd;
    int rio_cnt;
    char *rio_bufptr;
    char rio_buf[8192];
} rio_t;

// System V 信号量联合体
union semun {
    int              val;
    struct semid_ds *buf;
    unsigned short  *array;
    struct seminfo  *__buf;
};
```

---

## 进程控制

| 函数原型 | 对应系统调用 | 说明 |
|----------|-------------|------|
| `pid_t Fork(void)` | `fork` | 创建子进程 |
| `void Execve(const char *f, char *const a[], char *const e[])` | `execve` | 执行程序 |
| `pid_t Wait(int *status)` | `wait` | 等待任意子进程 |
| `pid_t Waitpid(pid_t pid, int *iptr, int options)` | `waitpid` | 等待指定子进程 |
| `void Kill(pid_t pid, int signum)` | `kill` | 发送信号 |
| `unsigned int Sleep(unsigned int secs)` | `sleep` | 休眠指定秒数 |
| `void Pause(void)` | `pause` | 暂停直到信号到达 |
| `unsigned int Alarm(unsigned int seconds)` | `alarm` | 设置定时器 |
| `void Setpgid(pid_t pid, pid_t pgid)` | `setpgid` | 设置进程组 |
| `pid_t Getpgrp(void)` | `getpgrp` | 获取当前进程组 |

**示例：**

```c
pid_t pid = Fork();
if (pid == 0) {
    char *args[] = {"ls", "-la", NULL};
    Execve("/bin/ls", args, environ);
} else {
    int status;
    Waitpid(pid, &status, 0);
    printf("子进程退出状态: %d\n", WEXITSTATUS(status));
}
```

---

## 文件 I/O

| 函数原型 | 对应系统调用 | 说明 |
|----------|-------------|------|
| `int Open(const char *path, int flags, mode_t mode)` | `open` | 打开/创建文件 |
| `ssize_t Read(int fd, void *buf, size_t count)` | `read` | 读取数据 |
| `ssize_t Write(int fd, const void *buf, size_t count)` | `write` | 写入数据 |
| `off_t Lseek(int fd, off_t offset, int whence)` | `lseek` | 文件偏移定位 |
| `void Close(int fd)` | `close` | 关闭文件描述符 |
| `int Dup2(int fd1, int fd2)` | `dup2` | 复制文件描述符 |
| `int Select(int n, fd_set *r, fd_set *w, fd_set *e, struct timeval *t)` | `select` | I/O 多路复用 |
| `void Stat(const char *filename, struct stat *buf)` | `stat` | 获取文件状态 |
| `void Fstat(int fd, struct stat *buf)` | `fstat` | 获取文件状态 |

**示例：**

```c
int fd = Open("data.txt", O_RDONLY, 0);
char buf[MAXLINE];
ssize_t n = Read(fd, buf, MAXLINE);
Write(STDOUT_FILENO, buf, n);
Close(fd);
```

---

## 信号

| 函数原型 | 对应系统调用 | 说明 |
|----------|-------------|------|
| `handler_t *Signal(int signum, handler_t *handler)` | `signal` | 注册信号处理函数 |
| `void Sigprocmask(int how, const sigset_t *set, sigset_t *old)` | `sigprocmask` | 修改信号屏蔽字 |
| `void Sigemptyset(sigset_t *set)` | `sigemptyset` | 清空信号集 |
| `void Sigfillset(sigset_t *set)` | `sigfillset` | 填满信号集 |
| `void Sigaddset(sigset_t *set, int signum)` | `sigaddset` | 添加信号到集合 |
| `void Sigdelset(sigset_t *set, int signum)` | `sigdelset` | 从集合移除信号 |
| `int Sigismember(const sigset_t *set, int signum)` | `sigismember` | 判断信号是否在集合中 |

**示例：**

```c
void handler(int sig) {
    printf("收到信号 %d\n", sig);
}
Signal(SIGINT, handler);   // Ctrl+C 时调用 handler
```

---

## 标准 I/O

| 函数原型 | 对应标准库函数 | 说明 |
|----------|--------------|------|
| `FILE *Fopen(const char *name, const char *mode)` | `fopen` | 打开文件 |
| `void Fclose(FILE *fp)` | `fclose` | 关闭文件 |
| `char *Fgets(char *ptr, int n, FILE *stream)` | `fgets` | 读取一行 |
| `void Fputs(const char *ptr, FILE *stream)` | `fputs` | 写入一行 |
| `size_t Fread(void *ptr, size_t size, size_t nmemb, FILE *stream)` | `fread` | 二进制读取 |
| `void Fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)` | `fwrite` | 二进制写入 |
| `FILE *Fdopen(int fd, const char *type)` | `fdopen` | 文件描述符转 FILE* |

---

## 内存管理

| 函数原型 | 对应标准库函数 | 说明 |
|----------|--------------|------|
| `void *Malloc(size_t size)` | `malloc` | 分配内存 |
| `void *Realloc(void *ptr, size_t size)` | `realloc` | 重新分配内存 |
| `void *Calloc(size_t nmemb, size_t size)` | `calloc` | 分配并清零 |
| `void Free(void *ptr)` | `free` | 释放内存 |

---

## 内存映射

| 函数原型 | 对应系统调用 | 说明 |
|----------|-------------|------|
| `void *Mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)` | `mmap` | 创建内存映射 |
| `void Munmap(void *start, size_t length)` | `munmap` | 取消内存映射 |

---

## Socket 编程

| 函数原型 | 对应系统调用 | 说明 |
|----------|-------------|------|
| `int Socket(int domain, int type, int protocol)` | `socket` | 创建套接字 |
| `void Setsockopt(int s, int level, int opt, const void *val, int len)` | `setsockopt` | 设置套接字选项 |
| `void Bind(int sockfd, struct sockaddr *addr, int addrlen)` | `bind` | 绑定地址 |
| `void Listen(int s, int backlog)` | `listen` | 监听连接 |
| `int Accept(int s, struct sockaddr *addr, socklen_t *addrlen)` | `accept` | 接受连接 |
| `void Connect(int sockfd, struct sockaddr *addr, int addrlen)` | `connect` | 发起连接 |

---

## DNS 解析

| 函数原型 | 说明 |
|----------|------|
| `struct hostent *Gethostbyname(const char *name)` | 按主机名解析 |
| `struct hostent *Gethostbyaddr(const char *addr, int len, int type)` | 按地址解析 |

---

## 线程控制

| 函数原型 | 对应系统调用 | 说明 |
|----------|-------------|------|
| `void Pthread_create(pthread_t *tid, pthread_attr_t *attr, void *(*r)(void *), void *arg)` | `pthread_create` | 创建线程 |
| `void Pthread_join(pthread_t tid, void **retval)` | `pthread_join` | 等待线程结束 |
| `void Pthread_detach(pthread_t tid)` | `pthread_detach` | 分离线程 |
| `void Pthread_cancel(pthread_t tid)` | `pthread_cancel` | 取消线程 |
| `void Pthread_exit(void *retval)` | `pthread_exit` | 终止线程 |
| `pthread_t Pthread_self(void)` | `pthread_self` | 获取当前线程 ID |
| `void Pthread_once(pthread_once_t *once, void (*init)())` | `pthread_once` | 一次性初始化 |

---

## POSIX 信号量

| 函数原型 | 对应系统调用 | 说明 |
|----------|-------------|------|
| `void Sem_init(sem_t *sem, int pshared, unsigned int value)` | `sem_init` | 初始化信号量 |
| `void P(sem_t *sem)` | `sem_wait` | 等待（P 操作，值减 1） |
| `void V(sem_t *sem)` | `sem_post` | 释放（V 操作，值加 1） |

**示例（生产者-消费者）：**

```c
sem_t mutex, slots, items;
Sem_init(&mutex, 0, 1);
Sem_init(&slots, 0, N);
Sem_init(&items, 0, 0);

// 生产者
P(&slots);
P(&mutex);
// 放入缓冲区
V(&mutex);
V(&items);

// 消费者
P(&items);
P(&mutex);
// 从缓冲区取出
V(&mutex);
V(&slots);
```

---

## Robust I/O (Rio)

| 函数原型 | 说明 |
|----------|------|
| `ssize_t rio_readn(int fd, void *buf, size_t n)` | 无缓冲读取 n 字节 |
| `ssize_t rio_writen(int fd, void *buf, size_t n)` | 无缓冲写入 n 字节 |
| `void rio_readinitb(rio_t *rp, int fd)` | 初始化缓冲读取 |
| `ssize_t rio_readnb(rio_t *rp, void *buf, size_t n)` | 带缓冲读取 n 字节 |
| `ssize_t rio_readlineb(rio_t *rp, void *buf, size_t maxlen)` | 带缓冲读取一行 |

**Wrapper 版本：** `Rio_readn`、`Rio_writen`、`Rio_readinitb`、`Rio_readnb`、`Rio_readlineb`

---

## 客户端/服务器辅助函数

| 函数原型 | 说明 |
|----------|------|
| `int open_client_sock(char *hostname, int port)` | 创建客户端套接字并连接 |
| `int open_listen_sock(int port)` | 创建服务器套接字并监听 |
| `int Open_client_sock(char *hostname, int port)` | 同上（带错误检查） |
| `int Open_listen_sock(int port)` | 同上（带错误检查） |

**示例（服务器）：**

```c
int listen_fd = Open_listen_sock(8080);
struct sockaddr_in clientaddr;
socklen_t clientlen = sizeof(clientaddr);
int conn_fd = Accept(listen_fd, (SA *)&clientaddr, &clientlen);
```

**示例（客户端）：**

```c
int sock_fd = Open_client_sock("localhost", 8080);
Send(sock_fd, "Hello", 5, 0);
```

---

## System V IPC

### 消息队列

| 函数原型 | 说明 |
|----------|------|
| `int Msgget(key_t key, int msgflg)` | 创建/获取消息队列 |
| `int Msgsnd(int msqid, const void *msg, size_t sz, int msgflg)` | 发送消息 |
| `int Msgrcv(int msqid, void *msg, size_t sz, long type, int msgflg)` | 接收消息 |
| `int Msgctl(int msqid, int cmd, struct msqid_ds *buf)` | 控制消息队列 |

### 共享内存

| 函数原型 | 说明 |
|----------|------|
| `int Shmget(key_t key, size_t size, int shmflg)` | 创建/获取共享内存 |
| `char *Shmat(int shmid, const void *addr, int shmflg)` | 附加共享内存 |
| `int Shmdt(const void *addr)` | 分离共享内存 |
| `int Shmctl(int shmid, int cmd, struct shmid_ds *buf)` | 控制共享内存 |

### System V 信号量

| 函数原型 | 说明 |
|----------|------|
| `int Semget(key_t key, int nsems, int semflg)` | 创建/获取信号量集 |
| `int Semctl(int semid, int semnum, int cmd, union semun arg)` | 控制信号量 |
| `int Semop(int semid, struct sembuf *sem, int sops)` | 操作信号量 |

### 命名管道

| 函数原型 | 说明 |
|----------|------|
| `int Mkfifo(const char *pathname, mode_t mode)` | 创建命名管道 (FIFO) |

---

## 错误处理函数

Wrapper 库内部使用以下错误处理函数，也可在自己的代码中直接调用：

| 函数 | 说明 |
|------|------|
| `void unix_error(char *msg)` | 打印系统调用错误并退出 |
| `void posix_error(int code, char *msg)` | 打印 POSIX 错误并退出 |
| `void dns_error(char *msg)` | 打印 DNS 错误并退出 |
| `void app_error(char *msg)` | 打印应用错误并退出 |
