基于visual c++之windows核心编程代码分析(20) 纤程与线程的互相转换
2016-12-09 20:25
561 查看
在Windows2000/XP中,纤程(fiber)相当于用户级别的线程或轻进程.纤程由Win32库函数支持,对核心是不可见的.纤程可以通过SwitchToFiber显示至另一合作纤程,以实现合作纤程之间的协同.
纤程包含独立的目态栈,寄存器状态的控制信息.目态控制的纤程转接要求较高的编程经验.由于纤程属于目态对象,一个纤程被封锁意味着所在线程被封锁.应用程序可以通过ConvertThreadToFiber将线程转换为纤程.与线程对比,纤程具有切换速度快的特点.
Microsoft公司给Windows添加了一种纤程,以便能够非常容易地将现有的UNIX服务器应用程序移植到Windows中。UNIX服务器应用程序属于单线程应用程序(由Windows定义),但是它能够为多个客户程序提供服务。换句话说, UNIX应用程序的开发人员已经创建了他们自己的线程结构库,他们能够使用这种线程结构库来仿真纯线程。该线程包能够创建多个堆栈,保存某些C P U寄存器,并且在它们之间进行切换,以便为客户机请求提供服务。
显然,若要取得最佳的性能,这些UNIX应用程序必须重新设计,仿真的线程库应该用Windows提供的纯线程来替代。然而,这种重新设计需要花费数月甚至更长的时间才能完成,因此许多公司首先将它们现有的UNIX代码移植到Windows中,这样就能够将某些应用软件推向Windows市场。
使用纤程
线程是在Windows内核中实现的,纤程是在用户模式下实现的,内核对纤程一无所知,内核会根据我们定义的算法来对纤程进行调度。
一个线程可以包含一个或多个纤程。
转化线程为纤程
使用纤程的第一个步骤是将已有的线程转换为一个纤程。ConvertThreadToFiber这个函数会为纤程的上下文分配内存,这个上下文的构成是:
# 一个用户自定义的值
# 结构化异常处理链的头
# 纤程栈的顶部和底部的内存地址
# 某些CPU寄存器,其中包括栈指针、指令指针以及其他寄存器
当我们分配了纤程执行上下文并对其进行初始化之后,还必须将执行上下文的地址与线程关联起来。这样我们就将线程转换成了一个纤程,该纤程在这个线程中执行。
其实,除非我们打算创建更多的纤程,并让它们在同一个线程中运行,否则没有理由将一个线程转换为纤程。
CreateFiber:创建一个纤程
SwitchToFiber:调用一个纤程(同一个线程中,同一时刻只能执行一个纤程)
DeleteFiber:通常为一个纤程调用,来删除另一个纤程
GetCurrentFiber:得到当前正在运行的纤程
[cpp] view
plain copy
**************************************/
/* 预定义 */
#define _WIN32_WINNT 0x0501
/* 头文件 */
#include <windows.h>
#include <stdio.h>
/* 函数声明 */
VOID WINAPI ReadFiberFunc( LPVOID lpParameter );
VOID WINAPI WriteFiberFunc( LPVOID lpParameter );
/* 结构定义 */
// 用于向纤程传递参数
// 本实例是使用读、写文件来演示纤程的调度
// 用户可根据实际情况自行定义
typedef struct _FIBERDATASTRUCT
{
DWORD dwParameter; // 预留给向纤程传递待定参数
DWORD dwFiberResultCode; // GetLastError() 值
HANDLE hFile; // 纤程所操作文件的句柄
DWORD dwBytesProcessed; // 已经处理了的字节
}FIBERDATASTRUCT, *LPFIBERDATASTRUCT;
/* 常量定义 */
#define RTN_OK 0 // 返回值 成功
#define RTN_USAGE 1 // 返回值 参数不正确
#define RTN_ERROR 2 // 返回值 错误
#define BUFFER_SIZE 32768 // 缓冲区大小
#define FIBER_COUNT 3 // 主纤程、读纤程、写纤程,共三个
#define PRIMARY_FIBER 0 // 主纤程
#define READ_FIBER 1 // 读纤程
#define WRITE_FIBER 2 // 写纤程
LPVOID g_lpFiber[FIBER_COUNT]; // 纤程地址的数组
LPBYTE g_lpBuffer; // 缓冲区
DWORD g_dwBytesRead; // 已读的字节
int main( int argc, char *argv[] )
{
LPFIBERDATASTRUCT fs;
// 用法说明
if (argc != 3)
{
printf("Usage: %s <SourceFile> <DestinationFile>\n", argv[0]);
return RTN_USAGE;
}
// 分配FIBERDATASTRUCT空间,FIBER_COUNT个
fs = (LPFIBERDATASTRUCT)HeapAlloc(
GetProcessHeap(), 0,
sizeof(FIBERDATASTRUCT) * FIBER_COUNT);
if (fs == NULL)
{
printf("HeapAlloc error! (rc%=lu)\n", GetLastError());
return RTN_ERROR;
}
// 分配读、写缓冲区
g_lpBuffer = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, BUFFER_SIZE);
if (g_lpBuffer == NULL)
{
printf("HeapAlloc error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
// 打开源文件,将句柄赋值给fs结构的hFile成员,使纤程可以使用该句柄
fs[READ_FIBER].hFile = CreateFile(
argv[1], GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN, NULL
);
if (fs[READ_FIBER].hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFile error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
// 打开目标文件
fs[WRITE_FIBER].hFile = CreateFile(
argv[2], GENERIC_WRITE,
0, NULL, CREATE_NEW,
FILE_FLAG_SEQUENTIAL_SCAN, NULL
);
if (fs[WRITE_FIBER].hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFile error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
// 将主线程切换为纤程,为主纤程,只有转换为纤程后才可以切换至其他纤程
g_lpFiber[PRIMARY_FIBER]=ConvertThreadToFiber(&fs[PRIMARY_FIBER]);
if (g_lpFiber[PRIMARY_FIBER] == NULL)
{
printf("ConvertThreadToFiber failed! rc=%lu\n", GetLastError());
return RTN_ERROR;
}
// 主纤程数据
fs[PRIMARY_FIBER].dwParameter = 0;
fs[PRIMARY_FIBER].dwFiberResultCode = 0;
fs[PRIMARY_FIBER].hFile = INVALID_HANDLE_VALUE;
// 创建读纤程
g_lpFiber[READ_FIBER]=CreateFiber(0,ReadFiberFunc,&fs[READ_FIBER]);
if (g_lpFiber[READ_FIBER] == NULL)
{
printf("CreateFiber error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
// 将纤程指针作为参数传给纤程,没有实际意义,为了显示相关信息时区别各纤程
fs[READ_FIBER].dwParameter = (DWORD)g_lpFiber[READ_FIBER];
// 创建写纤程
g_lpFiber[WRITE_FIBER]=CreateFiber(0,WriteFiberFunc,&fs[WRITE_FIBER]);
if (g_lpFiber[WRITE_FIBER] == NULL)
{
printf("CreateFiber error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
fs[WRITE_FIBER].dwParameter = (DWORD)g_lpFiber[WRITE_FIBER];
// 切换到读程序执行
SwitchToFiber(g_lpFiber[READ_FIBER]);
// 由读纤程获写纤程切换回主纤程
// 显示相关信息
printf("ReadFiber result == %lu Bytes Processed == %lu\n",
fs[READ_FIBER].dwFiberResultCode, fs[READ_FIBER].dwBytesProcessed);
printf("WriteFiber result == %lu Bytes Processed == %lu\n",
fs[WRITE_FIBER].dwFiberResultCode, fs[WRITE_FIBER].dwBytesProcessed);
// 删除读写纤程
DeleteFiber(g_lpFiber[READ_FIBER]);
DeleteFiber(g_lpFiber[WRITE_FIBER]);
// 关闭文件句柄、释放内存、返回
CloseHandle(fs[READ_FIBER].hFile);
CloseHandle(fs[WRITE_FIBER].hFile);
HeapFree(GetProcessHeap(), 0, g_lpBuffer);
HeapFree(GetProcessHeap(), 0, fs);
return RTN_OK;
}
VOID WINAPI ReadFiberFunc( LPVOID lpParameter )
{
LPFIBERDATASTRUCT fds = (LPFIBERDATASTRUCT)lpParameter;
// 判断参数
if (fds == NULL)
{
printf("Passed NULL fiber data. Exiting current thread.\n");
return;
}
// 显示纤程信息
printf("Read Fiber (dwParameter == 0x%lx)\n", fds->dwParameter);
// 初始化处理的字节数为0
fds->dwBytesProcessed = 0;
// 循环读
while (1)
{
if (!ReadFile(fds->hFile, g_lpBuffer, BUFFER_SIZE,
&g_dwBytesRead, NULL))
{
break;
}
// 判断文件是否已经读完
if (g_dwBytesRead == 0) break;
// 已经处理的字节,累加
fds->dwBytesProcessed += g_dwBytesRead;
// 读一次后切换到写纤程,将读出的数据写入到目标文件
printf("Switch To Write");
SwitchToFiber(g_lpFiber[WRITE_FIBER]);
}
// 读操作完成,准备交出执行,返回到主纤程中
fds->dwFiberResultCode = GetLastError();
SwitchToFiber(g_lpFiber[PRIMARY_FIBER]);
}
VOID WINAPI WriteFiberFunc( LPVOID lpParameter )
{
LPFIBERDATASTRUCT fds = (LPFIBERDATASTRUCT)lpParameter;
DWORD dwBytesWritten;
// 判断参数
if (fds == NULL)
{
printf("Passed NULL fiber data. Exiting current thread.\n");
return;
}
// 显示纤程信息
printf("Write Fiber (dwParameter == 0x%lx)\n", fds->dwParameter);
// 初始化,注意和读纤程的不同
fds->dwBytesProcessed = 0;
fds->dwFiberResultCode = ERROR_SUCCESS;
while (1)
{
// 写入数据
if (!WriteFile(fds->hFile, g_lpBuffer, g_dwBytesRead,
&dwBytesWritten, NULL))
{
// 如果发生错误,退出循环
break;
}
fds->dwBytesProcessed += dwBytesWritten;
// 写入完成,切换到读纤程
printf("Switch To Write");
SwitchToFiber(g_lpFiber[READ_FIBER]);
}
// 出错,切换到主纤程
// 如果写操作不出错,是不可能由写纤程切换回主纤程的
fds->dwFiberResultCode = GetLastError();
SwitchToFiber(g_lpFiber[PRIMARY_FIBER]);
}
纤程包含独立的目态栈,寄存器状态的控制信息.目态控制的纤程转接要求较高的编程经验.由于纤程属于目态对象,一个纤程被封锁意味着所在线程被封锁.应用程序可以通过ConvertThreadToFiber将线程转换为纤程.与线程对比,纤程具有切换速度快的特点.
Microsoft公司给Windows添加了一种纤程,以便能够非常容易地将现有的UNIX服务器应用程序移植到Windows中。UNIX服务器应用程序属于单线程应用程序(由Windows定义),但是它能够为多个客户程序提供服务。换句话说, UNIX应用程序的开发人员已经创建了他们自己的线程结构库,他们能够使用这种线程结构库来仿真纯线程。该线程包能够创建多个堆栈,保存某些C P U寄存器,并且在它们之间进行切换,以便为客户机请求提供服务。
显然,若要取得最佳的性能,这些UNIX应用程序必须重新设计,仿真的线程库应该用Windows提供的纯线程来替代。然而,这种重新设计需要花费数月甚至更长的时间才能完成,因此许多公司首先将它们现有的UNIX代码移植到Windows中,这样就能够将某些应用软件推向Windows市场。
使用纤程
线程是在Windows内核中实现的,纤程是在用户模式下实现的,内核对纤程一无所知,内核会根据我们定义的算法来对纤程进行调度。
一个线程可以包含一个或多个纤程。
转化线程为纤程
使用纤程的第一个步骤是将已有的线程转换为一个纤程。ConvertThreadToFiber这个函数会为纤程的上下文分配内存,这个上下文的构成是:
# 一个用户自定义的值
# 结构化异常处理链的头
# 纤程栈的顶部和底部的内存地址
# 某些CPU寄存器,其中包括栈指针、指令指针以及其他寄存器
当我们分配了纤程执行上下文并对其进行初始化之后,还必须将执行上下文的地址与线程关联起来。这样我们就将线程转换成了一个纤程,该纤程在这个线程中执行。
其实,除非我们打算创建更多的纤程,并让它们在同一个线程中运行,否则没有理由将一个线程转换为纤程。
CreateFiber:创建一个纤程
SwitchToFiber:调用一个纤程(同一个线程中,同一时刻只能执行一个纤程)
DeleteFiber:通常为一个纤程调用,来删除另一个纤程
GetCurrentFiber:得到当前正在运行的纤程
[cpp] view
plain copy
**************************************/
/* 预定义 */
#define _WIN32_WINNT 0x0501
/* 头文件 */
#include <windows.h>
#include <stdio.h>
/* 函数声明 */
VOID WINAPI ReadFiberFunc( LPVOID lpParameter );
VOID WINAPI WriteFiberFunc( LPVOID lpParameter );
/* 结构定义 */
// 用于向纤程传递参数
// 本实例是使用读、写文件来演示纤程的调度
// 用户可根据实际情况自行定义
typedef struct _FIBERDATASTRUCT
{
DWORD dwParameter; // 预留给向纤程传递待定参数
DWORD dwFiberResultCode; // GetLastError() 值
HANDLE hFile; // 纤程所操作文件的句柄
DWORD dwBytesProcessed; // 已经处理了的字节
}FIBERDATASTRUCT, *LPFIBERDATASTRUCT;
/* 常量定义 */
#define RTN_OK 0 // 返回值 成功
#define RTN_USAGE 1 // 返回值 参数不正确
#define RTN_ERROR 2 // 返回值 错误
#define BUFFER_SIZE 32768 // 缓冲区大小
#define FIBER_COUNT 3 // 主纤程、读纤程、写纤程,共三个
#define PRIMARY_FIBER 0 // 主纤程
#define READ_FIBER 1 // 读纤程
#define WRITE_FIBER 2 // 写纤程
LPVOID g_lpFiber[FIBER_COUNT]; // 纤程地址的数组
LPBYTE g_lpBuffer; // 缓冲区
DWORD g_dwBytesRead; // 已读的字节
int main( int argc, char *argv[] )
{
LPFIBERDATASTRUCT fs;
// 用法说明
if (argc != 3)
{
printf("Usage: %s <SourceFile> <DestinationFile>\n", argv[0]);
return RTN_USAGE;
}
// 分配FIBERDATASTRUCT空间,FIBER_COUNT个
fs = (LPFIBERDATASTRUCT)HeapAlloc(
GetProcessHeap(), 0,
sizeof(FIBERDATASTRUCT) * FIBER_COUNT);
if (fs == NULL)
{
printf("HeapAlloc error! (rc%=lu)\n", GetLastError());
return RTN_ERROR;
}
// 分配读、写缓冲区
g_lpBuffer = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, BUFFER_SIZE);
if (g_lpBuffer == NULL)
{
printf("HeapAlloc error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
// 打开源文件,将句柄赋值给fs结构的hFile成员,使纤程可以使用该句柄
fs[READ_FIBER].hFile = CreateFile(
argv[1], GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN, NULL
);
if (fs[READ_FIBER].hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFile error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
// 打开目标文件
fs[WRITE_FIBER].hFile = CreateFile(
argv[2], GENERIC_WRITE,
0, NULL, CREATE_NEW,
FILE_FLAG_SEQUENTIAL_SCAN, NULL
);
if (fs[WRITE_FIBER].hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFile error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
// 将主线程切换为纤程,为主纤程,只有转换为纤程后才可以切换至其他纤程
g_lpFiber[PRIMARY_FIBER]=ConvertThreadToFiber(&fs[PRIMARY_FIBER]);
if (g_lpFiber[PRIMARY_FIBER] == NULL)
{
printf("ConvertThreadToFiber failed! rc=%lu\n", GetLastError());
return RTN_ERROR;
}
// 主纤程数据
fs[PRIMARY_FIBER].dwParameter = 0;
fs[PRIMARY_FIBER].dwFiberResultCode = 0;
fs[PRIMARY_FIBER].hFile = INVALID_HANDLE_VALUE;
// 创建读纤程
g_lpFiber[READ_FIBER]=CreateFiber(0,ReadFiberFunc,&fs[READ_FIBER]);
if (g_lpFiber[READ_FIBER] == NULL)
{
printf("CreateFiber error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
// 将纤程指针作为参数传给纤程,没有实际意义,为了显示相关信息时区别各纤程
fs[READ_FIBER].dwParameter = (DWORD)g_lpFiber[READ_FIBER];
// 创建写纤程
g_lpFiber[WRITE_FIBER]=CreateFiber(0,WriteFiberFunc,&fs[WRITE_FIBER]);
if (g_lpFiber[WRITE_FIBER] == NULL)
{
printf("CreateFiber error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
fs[WRITE_FIBER].dwParameter = (DWORD)g_lpFiber[WRITE_FIBER];
// 切换到读程序执行
SwitchToFiber(g_lpFiber[READ_FIBER]);
// 由读纤程获写纤程切换回主纤程
// 显示相关信息
printf("ReadFiber result == %lu Bytes Processed == %lu\n",
fs[READ_FIBER].dwFiberResultCode, fs[READ_FIBER].dwBytesProcessed);
printf("WriteFiber result == %lu Bytes Processed == %lu\n",
fs[WRITE_FIBER].dwFiberResultCode, fs[WRITE_FIBER].dwBytesProcessed);
// 删除读写纤程
DeleteFiber(g_lpFiber[READ_FIBER]);
DeleteFiber(g_lpFiber[WRITE_FIBER]);
// 关闭文件句柄、释放内存、返回
CloseHandle(fs[READ_FIBER].hFile);
CloseHandle(fs[WRITE_FIBER].hFile);
HeapFree(GetProcessHeap(), 0, g_lpBuffer);
HeapFree(GetProcessHeap(), 0, fs);
return RTN_OK;
}
VOID WINAPI ReadFiberFunc( LPVOID lpParameter )
{
LPFIBERDATASTRUCT fds = (LPFIBERDATASTRUCT)lpParameter;
// 判断参数
if (fds == NULL)
{
printf("Passed NULL fiber data. Exiting current thread.\n");
return;
}
// 显示纤程信息
printf("Read Fiber (dwParameter == 0x%lx)\n", fds->dwParameter);
// 初始化处理的字节数为0
fds->dwBytesProcessed = 0;
// 循环读
while (1)
{
if (!ReadFile(fds->hFile, g_lpBuffer, BUFFER_SIZE,
&g_dwBytesRead, NULL))
{
break;
}
// 判断文件是否已经读完
if (g_dwBytesRead == 0) break;
// 已经处理的字节,累加
fds->dwBytesProcessed += g_dwBytesRead;
// 读一次后切换到写纤程,将读出的数据写入到目标文件
printf("Switch To Write");
SwitchToFiber(g_lpFiber[WRITE_FIBER]);
}
// 读操作完成,准备交出执行,返回到主纤程中
fds->dwFiberResultCode = GetLastError();
SwitchToFiber(g_lpFiber[PRIMARY_FIBER]);
}
VOID WINAPI WriteFiberFunc( LPVOID lpParameter )
{
LPFIBERDATASTRUCT fds = (LPFIBERDATASTRUCT)lpParameter;
DWORD dwBytesWritten;
// 判断参数
if (fds == NULL)
{
printf("Passed NULL fiber data. Exiting current thread.\n");
return;
}
// 显示纤程信息
printf("Write Fiber (dwParameter == 0x%lx)\n", fds->dwParameter);
// 初始化,注意和读纤程的不同
fds->dwBytesProcessed = 0;
fds->dwFiberResultCode = ERROR_SUCCESS;
while (1)
{
// 写入数据
if (!WriteFile(fds->hFile, g_lpBuffer, g_dwBytesRead,
&dwBytesWritten, NULL))
{
// 如果发生错误,退出循环
break;
}
fds->dwBytesProcessed += dwBytesWritten;
// 写入完成,切换到读纤程
printf("Switch To Write");
SwitchToFiber(g_lpFiber[READ_FIBER]);
}
// 出错,切换到主纤程
// 如果写操作不出错,是不可能由写纤程切换回主纤程的
fds->dwFiberResultCode = GetLastError();
SwitchToFiber(g_lpFiber[PRIMARY_FIBER]);
}
相关文章推荐
- 基于visual c++之windows核心编程代码分析(20) 纤程与线程的互相转换
- 基于visual c++之windows核心编程代码分析(15)使用Mutex同步线程
- 基于visual c++之windows核心编程代码分析(12)使用信号量同步线程
- 基于visual c++之windows核心编程代码分析(14)使用Event同步线程
- 基于visual c++之windows核心编程代码分析(28)实现开机自启动
- 基于visual c++之windows核心编程代码分析(31)SNMP协议编程
- 基于visual c++之windows核心编程代码分析Mapping File编程
- 基于visual c++之windows核心编程代码分析(26)实现文件关联
- 基于visual c++之windows核心编程代码分析(22)Mapping File编程
- 基于visual c++之windows核心编程代码分析(31)SNMP协议编程
- 基于visual c++之windows核心编程代码分析(21)获取和设置环境变量
- 基于visual c++之windows核心编程代码分析(29)ICMP实现远程控制
- 基于visual c++之windows核心编程代码分析(27)保持程序单实例运行
- 基于visual c++之windows核心编程代码分析(35)实践NT服务的框架
- 基于visual c++之windows核心编程代码分析(29)ICMP实现远程控制
- 基于visual c++之windows核心编程代码分析(22)Mapping File编程
- 基于visual c++之windows核心编程代码分析(37)实践信息安全隐患监测-嗅探
- 基于visual c++之windows核心编程代码分析(37)实践信息安全隐患监测-嗅探
- 基于visual c++之windows核心编程代码分析(22)Mapping File编程
- 基于visual c++之windows核心编程代码分析(23)遍历驱动器并获取驱动器属性