Files
obsidian/操作系统/附录/附录A_Wrapper库参考.md

361 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 附录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)` | 打印应用错误并退出 |