UNIXI/O编程:从基础到深入
立即解锁
发布时间: 2025-08-22 01:29:01 阅读量: 1 订阅数: 7 


UNIX系统编程:通信、并发与线程精解
### UNIX I/O 编程:从基础到深入
#### 1. 信号与超时机制
在程序运行过程中,如果程序会定期接收信号,并且信号之间的时间间隔小于超时时间间隔,那么 `waitfdtimed` 函数将永远不会超时。2000 年版的 POSIX 引入了 `pselect` 函数,它与 `select` 函数类似,但使用了更精确的超时结构 `struct timespec`,并且在等待 I/O 可用时允许阻塞或解除阻塞信号。不过,在 2003 年 3 月编写本文时,测试的操作系统都不支持 `pselect` 函数。
#### 2. `poll` 函数详解
`poll` 函数与 `select` 函数类似,但它按文件描述符组织信息,而不是按条件类型组织。`poll` 函数的参数包括 `fds`(`struct pollfd` 数组,代表文件描述符的监控信息)、`nfds`(要监控的描述符数量)和 `timeout`(等待事件的超时时间,以毫秒为单位,-1 表示永不超时)。
```c
#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
```
`poll` 函数的返回值:
- 超时返回 0。
- 成功返回有事件发生的描述符数量。
- 失败返回 -1 并设置 `errno`。
`poll` 函数的常见错误及原因如下表所示:
| `errno` | 原因 |
| ---- | ---- |
| `EAGAIN` | 内部数据结构分配失败,但后续请求可能成功 |
| `EINTR` | 在 `poll` 期间捕获到信号 |
| `EINVAL` | `nfds` 大于 `OPEN_MAX` |
`struct pollfd` 结构体包含以下成员:
```c
int fd; /* 文件描述符 */
short events; /* 请求的事件 */
short revents; /* 返回的事件 */
```
事件标志及其含义如下表所示:
| 事件标志 | 含义 |
| ---- | ---- |
| `POLLIN` | 非高优先级数据无阻塞读取 |
| `POLLRDNORM` | 正常数据无阻塞读取 |
| `POLLRDBAND` | 优先级数据无阻塞读取 |
| `POLLPRI` | 高优先级数据无阻塞读取 |
| `POLLOUT` | 正常数据无阻塞写入 |
| `POLLWRNORM` | 同 `POLLOUT` |
| `POLLERR` | 描述符发生错误 |
| `POLLHUP` | 设备已断开连接 |
| `POLLNVAL` | 文件描述符无效 |
以下是使用 `poll` 函数监控多个文件描述符的示例代码:
```c
#include <errno.h>
#include <poll.h>
#include <stdlib.h>
#include <stropts.h>
#include <unistd.h>
#include "restart.h"
#define BUFSIZE 1024
void docommand(char *, int);
void monitorpoll(int fd[], int numfds)
{
char buf[BUFSIZE];
int bytesread;
int i;
int numnow = 0;
int numready;
struct pollfd *pollfd;
for (i = 0; i < numfds; i++)
/* 初始化轮询结构 */
if (fd[i] >= 0)
numnow++;
if ((pollfd = (void *)calloc(numfds, sizeof(struct pollfd))) == NULL)
return;
for (i = 0; i < numfds; i++) {
(pollfd + i)->fd = *(fd + i);
(pollfd + i)->events = POLLRDNORM;
}
while (numnow > 0) {
/* 继续监控直到描述符处理完毕 */
numready = poll(pollfd, numfds, -1);
if ((numready == -1) && (errno == EINTR))
continue;
/* poll 被信号中断,重试 */
else if (numready == -1)
/* 真正的 poll 错误,无法继续 */
break;
for (i = 0; i < numfds && numready > 0; i++)
{
if ((pollfd + i)->revents) {
if ((pollfd + i)->revents & (POLLRDNORM | POLLIN) ) {
bytesread = r_read(fd[i], buf, BUFSIZE);
numready--;
if (bytesread > 0)
docommand(buf, bytesread);
else
bytesread = -1;
/* 文件结束 */
} else if ((pollfd + i)->revents & (POLLERR | POLLHUP))
bytesread = -1;
else
/* 描述符未参与本轮 */
bytesread = 0;
if (bytesread == -1) {
/* 发生错误,移除描述符 */
r_close(fd[i]);
(pollfd + i)->fd = -1;
numnow--;
}
}
}
}
for (i = 0; i < numfds; i++)
r_close(fd[i]);
free(pollfd);
}
```
#### 3. 文件表示
在 C 程序中,文件可以通过文件指针或文件描述符来指定。ISO C 标准 I/O 库函数(如 `fopen`、`fscanf` 等)使用文件指针,而 UNIX I/O 函数(如 `open`、`read` 等)使用文件描述符。
标准输入、标准输出和标准错误对应的文件指针符号名分别为 `stdin`、`stdout` 和 `stderr`,定义在 `stdio.h` 中;对应的文件描述符符号名分别为 `STDIN_FILENO`、`STDOUT_FILENO` 和 `STDERR_FILENO`,定义在 `unistd.h` 中。
#### 4. 库函数与系统调用的区别
POSIX 标准没有区分库函数和系统调用。传统上,库函数是普通函数,通常因为有用、广泛使用或属于规范(如 C 语言规范)而被放在库中;系统调用是向操作系统请求服务,涉及陷入操作系统和上下文切换,与特定操作系统相关。许多库函数(如 `read` 和 `write`)实际上是系统调用的封装,它们将参数重新格式化为系统相关的形式,然后调用底层系统调用执行实际操作。
#### 5. 文件描述符
`open` 函数将文件或物理设备与程序中使用的逻辑句柄关联起来。文件或物理设备由字符串指定,句柄是一个整数,可以
0
0
复制全文
相关推荐






