转载请您注明出处:http://www.cnblogs.com/lsh123/p/7358702.html

一.驱动程序与驱动程序的事件交互    IoCreateNotificationEvent ———> IoCreateNotificationEvent

  

  在内核驱动中可以通过给某个内核对象创建一个命名对象,然后在另一个驱动中通过名字来获取这个对象,然后操作它来实现两个驱动之间的内核对象的通讯,针对事件对象来说,要实现两个驱动交互事件对象,通过这样几步:

1. 在驱动Server中调用IoCreateNotificationEvent或者IoCreateSynchronizationEvent来创建一个通知事件对象或者同步事件对象 
2. 在驱动Client中调用
IoCreateNotificationEvent或者IoCreateSynchronizationEvent获取已经有名字的内核对象的句柄 ,设置事件的激发状态
(3. 在驱动B中调用ObReferenceObjectByHandle根据上面两个函数返回的句柄来获取A中的事件对象,并操作它)

源代码:

Server.c

#include <ntifs.h>
#define EVENT_NAME L"\\BaseNamedObjects\\ServerKernelEvent" void ThreadProcedure(PVOID ParameterData);
VOID DriverUnload(PDRIVER_OBJECT DriverObject); PKEVENT __Event;
HANDLE __EventHandle; NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING EventName;
PDEVICE_OBJECT DeviceObject = NULL;
HANDLE ThreadHandle = NULL;
CLIENT_ID ClientID = { 0 }; DriverObject->DriverUnload = DriverUnload; RtlInitUnicodeString(&EventName, EVENT_NAME);
DriverObject->DriverUnload = DriverUnload; __Event = IoCreateNotificationEvent(
&EventName, //自定义事件名
&__EventHandle); //返回的事件句柄
KeResetEvent(__Event); Status = PsCreateSystemThread(&ThreadHandle, 0, NULL, NtCurrentProcess(), &ClientID,
(PKSTART_ROUTINE)ThreadProcedure,NULL); return Status;
} void ThreadProcedure(PVOID ParameterData)
{
NTSTATUS Status;
KeWaitForSingleObject(__Event, Executive, KernelMode, FALSE, NULL);
DbgPrint("ThreadProcedure() Exit\r\n");
PsTerminateSystemThread(STATUS_SUCCESS);
} VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
DbgPrint("DriverUnload()\r\n");
if (__EventHandle != NULL)
{
KeClearEvent(__Event); ZwClose(__EventHandle); __EventHandle = NULL;
__Event = NULL;
} }

Client.c

#include <ntifs.h>

#define EVENT_NAME  L"\\BaseNamedObjects\\ServerKernelEvent"
VOID DriverUnload(PDRIVER_OBJECT DriverObject); PKEVENT __Event;
HANDLE __EventHandle;
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING EventName;
PDEVICE_OBJECT DeviceObject = NULL;
DriverObject->DriverUnload = DriverUnload;
RtlInitUnicodeString(&EventName, EVENT_NAME);
__Event = IoCreateNotificationEvent(&EventName, &__EventHandle); //获取已经有名字的内核对象的句柄
KeSetEvent(__Event, IO_NO_INCREMENT, FALSE); //设置激发态
return Status;
} VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
DbgPrint("DriverUnload()\r\n");
if (__EventHandle != NULL)
{
KeClearEvent(__Event);
ZwClose(__EventHandle);
__EventHandle = NULL;
__Event = NULL;
} }

  

二.驱动程序与应用程序的事件交互(驱动程序创建事件——>应用程序设置事件)IoCreateNotificationEvent ——> OpenEvent

  应用程序中创建的事件和在内核模式下创建的事件对象,本质上是同一个东西。在用户模式下,它用句柄代表,在内核模式下,它用KEVENT数据结构代表。
在应用程序中,所有内核对象都不会被用户看到,用户看到的只是代表内核对象的对象句柄。

  __Event = IoCreateNotificationEvent(&EventName, &__EventHandle); //DriverEntry 进程回调通知

  EventHandle = OpenEvent(
    SYNCHRONIZE, //请求访问权限
    FALSE, // 不继承
    L"Global\\Ring0KernelEvent"); //事件对象名称

  1.驱动程序IoCreateNotificationEvent创建事件
  2.应用程序OpenEvent得到事件句柄

Ring3.cpp

// Ring3(设置).cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <windows.h>
#include <iostream>
using namespace std; int main()
{ HANDLE EventHandle = NULL; while (TRUE)
{
EventHandle = OpenEvent(
SYNCHRONIZE, //请求访问权限
FALSE, // 不继承
L"Global\\Ring0KernelEvent"); //事件对象名称 if (EventHandle == NULL)
{
continue;
} break;
} cout << "Ring3等待" << endl;
while (TRUE)
{ int Index = WaitForSingleObject(EventHandle, 3000); Index = Index - WAIT_OBJECT_0; if (Index == WAIT_TIMEOUT)
{ //注意这里当驱动卸载并关闭事件时事件对象是不能够得到及时的销毁 因为应用层占用了该对象
//所以我们长时间等待不到授信 就关闭并重新打开
if (EventHandle != NULL)
{
CloseHandle(EventHandle);
EventHandle = NULL;
EventHandle = OpenEvent(SYNCHRONIZE, FALSE, L"Global\\Ring0KernelEvent"); if (EventHandle == NULL)
{
cout << "对象已经不存在" << endl;
break;
}
} continue;
} if (Index == 0) //有信号状态
{
cout << "Ring0触发Ring3" << endl;
} if (Index == WAIT_FAILED)
{
break;
} Sleep(1);
} cout << "Input AnyKey To Exit" << endl; getchar();
if (EventHandle != NULL)
{
CloseHandle(EventHandle);
EventHandle = NULL; }
return 0;
}

  

Ring0.c

#include <ntifs.h>

#define EVENT_NAME  L"\\BaseNamedObjects\\Ring0KernelEvent"
VOID DriverUnload(PDRIVER_OBJECT DriverObject); PKEVENT __Event;
HANDLE __EventHandle; NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_OBJECT DeviceObject = NULL;
UNICODE_STRING EventName; RtlInitUnicodeString(&EventName, EVENT_NAME);
DriverObject->DriverUnload = DriverUnload; __Event = IoCreateNotificationEvent(&EventName, &__EventHandle); //DriverEntry 进程回调通知 return Status;
} VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
DbgPrint("DriverUnload()\r\n"); if (__EventHandle != NULL)
{
KeClearEvent(__Event); ZwClose(__EventHandle); __EventHandle = NULL;
__Event = NULL;
} }

  

三.应用程序与驱动程序的事件交互(应用程序创建事件——>驱动程序设置事件) DeviceIoControl ——> ObReferenceObjectByHandle

  要将用户模式下创建的事件传递给驱动程序,可以用DeviceIoControl API函数。DDK提供了内核函数将句柄转化为指针,该函数如下:
  NTSTATUS 
     ObReferenceObjectByHandle(
      IN HANDLE  Handle,
      IN ACCESS_MASK  DesiredAccess,
      IN POBJECT_TYPE  ObjectType  OPTIONAL,
      IN KPROCESSOR_MODE  AccessMode,
      OUT PVOID  *Object,
      OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL
      );

  ObReferenceObjectByHandle函数在得到指针的同时,会为对象的指针维护一个计数。每次调用ObReferenceObjectByHandle函数时会使计数加1.因此为了计数平衡,在使用完 ObReferenceObjectByHandle函数后,需要调用如下函数:             
  VOID 
    ObDereferenceObject(
      IN PVOID  Object
      );
  ObDereferenceObject函数使计数减一。

  1.应用程序通过符号链接名由CreateFile函数得到设备句柄

  HANDLE DeviceHandle = CreateFile(DeviceLinkName,
    GENERIC_READ | GENERIC_WRITE,
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL);
  2.应用程序CreateEvent创建事件

    //创建自动重置的,初始为未激发的事件对象
    EventHandle[i] = CreateEvent(NULL, FALSE, FALSE, NULL);

  3.应用程序通过DeviceIoControl 函数将用户模式下创建的事件传递给驱动程序

    //调用DeviceIoControl把事件句柄传进内核
    IsOk = DeviceIoControl(DeviceHandle, CTL_EVENT,
      EventHandle,
      sizeof(HANDLE) * 2,
      NULL,
      0,
      &ReturnLength,
      NULL);

    //调用DeviceIoControl,通知驱动程序设置事件激发状态
    IsOk = DeviceIoControl(DeviceHandle, CTL_SET_EVENT,
      NULL,
      0,
      NULL,
      0,
      &ReturnLength,
      NULL);

  4.驱动程序通过ObReferenceObjectByHandle将句柄转化为PKEVENT指针

    /把句柄转化为KEvent结构
    Status = ObReferenceObjectByHandle(
      (HANDLE)EventHandle[i], //Irp->AssociatedIrp.SystemBuffer 句柄
      SYNCHRONIZE, //权限
      *ExEventObjectType, //对象类型,对象类型
      KernelMode, //访问模式分KernelMode
      &__KernelEvent[i], //指向映射句柄对象的指针
      NULL);

Ring3.cpp

#include "stdafx.h"
#include <windows.h>
#include <iostream>
using namespace std; #define CTL_EVENT \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS) #define CTL_SET_EVENT \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x831,METHOD_BUFFERED,FILE_ANY_ACCESS) HANDLE SeOpenDeviceObject(WCHAR* DeviceLinkName);
DWORD WINAPI ThreadProcedure(LPVOID ParameterData);
int main()
{
HANDLE DeviceHandle = SeOpenDeviceObject(L"\\\\.\\Ring0DeviceLinkName");
if (DeviceHandle == NULL)
{
return 0;
} ULONG i = 0;
HANDLE EventHandle[3] = { 0 };
for (i = 0; i < 3; i++)
{
//创建自动重置的,初始为未激发的事件对象
EventHandle[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
}
BOOL IsOk = 0;
DWORD ReturnLength = 0;
//调用DeviceIoControl把事件句柄传进内核
IsOk = DeviceIoControl(DeviceHandle, CTL_EVENT,
EventHandle,
sizeof(HANDLE) * 2,
NULL,
0,
&ReturnLength,
NULL); if (IsOk == FALSE)
{
goto Exit;
} HANDLE ThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProcedure,
(PVOID)EventHandle, 0, NULL);
//调用DeviceIoControl,通知驱动程序设置事件激发状态
IsOk = DeviceIoControl(DeviceHandle, CTL_SET_EVENT,
NULL,
0,
NULL,
0,
&ReturnLength,
NULL); if (IsOk == FALSE)
{
cout << "Send IoCode Error" << endl;
SetEvent(EventHandle[2]);
WaitForSingleObject(ThreadHandle, INFINITE);
goto Exit;
} WaitForSingleObject(ThreadHandle, INFINITE); Exit:
{
for (i = 0; i < 3; i++)
{
if (EventHandle[i] != NULL)
{
CloseHandle(EventHandle[i]);
EventHandle[i] = NULL;
}
}
if (ThreadHandle != NULL)
{
CloseHandle(ThreadHandle);
ThreadHandle = NULL;
}
if (DeviceHandle != NULL)
{
CloseHandle(DeviceHandle);
DeviceHandle = NULL;
}
} printf("卸载驱动后 Input AnyKey To Exit\r\n");
getchar();
getchar(); return 0; } DWORD WINAPI ThreadProcedure(LPVOID ParameterData)
{ cout << "Ring3等待" << endl;
//等待三个之中有激发状态的信号
DWORD Index = WaitForMultipleObjects(3, (HANDLE*)ParameterData, FALSE, INFINITE);
Index = Index - WAIT_OBJECT_0;
if (Index == 2) //0 1 2
{
printf("ThreadProcedure() Exit\r\n");
return 0;
}
cout << "Ring0触发Ring3" << endl;
cout << "输入任意键Ring3触发Ring0" << endl; getchar();
getchar(); SetEvent(((HANDLE*)ParameterData)[1]); //Ring0中KeWaitForSingleObject响应
printf("ThreadProcedure() Exit\r\n");
return 0;
} HANDLE SeOpenDeviceObject(WCHAR* DeviceLinkName)
{
HANDLE DeviceHandle = CreateFile(DeviceLinkName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL); if (DeviceHandle == INVALID_HANDLE_VALUE)
{
return NULL;
} return DeviceHandle; }

  

Ring0.c

#include <ntifs.h>

#define CTL_EVENT \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define CTL_SET_EVENT \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x831,METHOD_BUFFERED,FILE_ANY_ACCESS) #define DEVICE_OBJECT_NAME L"\\Device\\Ring0DeviceObjectName"
//设备与设备之间通信
#define DEVICE_LINK_NAME L"\\DosDevices\\Ring0DeviceLinkName"
NTSTATUS PassThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp);
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
NTSTATUS Ring3EventHandleToRing0KernelEvent(HANDLE* EventHandle, ULONG_PTR EventHandleCount); PKEVENT __KernelEvent[20] = { 0 };
ULONG_PTR __KernelEventCount = 0; extern
POBJECT_TYPE* ExEventObjectType; NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
UNREFERENCED_PARAMETER(RegisterPath);
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_OBJECT DeviceObject = NULL;
UNICODE_STRING DeviceObjectName;
UNICODE_STRING DeviceLinkName;
ULONG i;
DriverObject->DriverUnload = DriverUnload; //创建设备对象名称
RtlInitUnicodeString(&DeviceObjectName, DEVICE_OBJECT_NAME); //创建设备对象
Status = IoCreateDevice(DriverObject, NULL,
&DeviceObjectName,
FILE_DEVICE_UNKNOWN,
0, FALSE,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
return Status;
}
//创建设备连接名称
RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME); //将设备连接名称与设备名称关联
Status = IoCreateSymbolicLink(&DeviceLinkName, &DeviceObjectName); if (!NT_SUCCESS(Status))
{
IoDeleteDevice(DeviceObject);
return Status;
}
//设计符合我们代码的派遣历程
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = PassThroughDispatch; //函数指针
}
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ControlThroughDispatch; return STATUS_SUCCESS;
} NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{ NTSTATUS Status = STATUS_UNSUCCESSFUL;
ULONG_PTR Information = 0;
PVOID InputData = NULL;
ULONG InputDataLength = 0;
PVOID OutputData = NULL;
ULONG OutputDataLength = 0;
ULONG IoControlCode = 0;
PEPROCESS EProcess = NULL;
PIO_STACK_LOCATION IoStackLocation = IoGetCurrentIrpStackLocation(Irp); //Irp堆栈
IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;
InputData = Irp->AssociatedIrp.SystemBuffer;
OutputData = Irp->AssociatedIrp.SystemBuffer;
InputDataLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
OutputDataLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
switch (IoControlCode)
{
case CTL_EVENT:
{ if (InputData != NULL&&InputDataLength == sizeof(HANDLE)*2)
{ Status = Ring3EventHandleToRing0KernelEvent((HANDLE*)InputData, InputDataLength / sizeof(HANDLE)); } Information = 0; break; } case CTL_SET_EVENT:
{ DbgPrint("Ring0触发Ring3\r\n");
KeSetEvent(__KernelEvent[0], IO_NO_INCREMENT, FALSE); //Ring3层线程中WaitForMultipleObjects响应 DbgPrint("Ring0等待\r\n");
Status = KeWaitForSingleObject(__KernelEvent[1],
Executive, KernelMode, FALSE, NULL); //注意这里的最后一个参数NULL 是永久等待 DbgPrint("Ring3触发Ring0\r\n"); Information = 0;
break; } default:
{ Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = 0; break;
}
} Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = Information;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} NTSTATUS Ring3EventHandleToRing0KernelEvent(HANDLE* EventHandle, ULONG_PTR EventHandleCount)
{
NTSTATUS Status = STATUS_SUCCESS;
PULONG_PTR HandleArray = NULL;
ULONG i = 0; if (EventHandle==NULL)
{
return STATUS_UNSUCCESSFUL;
}
__KernelEventCount = EventHandleCount;
for (i = 0; i < EventHandleCount; i++)
{
//把句柄转化为KEvent结构
Status = ObReferenceObjectByHandle(
(HANDLE)EventHandle[i], //Irp->AssociatedIrp.SystemBuffer 句柄
SYNCHRONIZE, //权限
*ExEventObjectType, //对象类型,对象类型
KernelMode, //访问模式分KernelMode
&__KernelEvent[i], //指向映射句柄对象的指针
NULL);
if (!NT_SUCCESS(Status))
{
break;
}
} if (Status != STATUS_SUCCESS)
{
for (i = 0; i < EventHandleCount; i++)
{
if (__KernelEvent[i] != NULL)
{
//递减计数
ObDereferenceObject(__KernelEvent[i]); __KernelEvent[i] = NULL;
}
}
}
return Status;
} NTSTATUS PassThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
Irp->IoStatus.Status = STATUS_SUCCESS; //LastError()
Irp->IoStatus.Information = 0; //ReturnLength
IoCompleteRequest(Irp, IO_NO_INCREMENT); //将Irp返回给Io管理器
return STATUS_SUCCESS;
} VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
int i = 0;
UNICODE_STRING DeviceLinkName;
PDEVICE_OBJECT v1 = NULL;
PDEVICE_OBJECT DeleteDeviceObject = NULL; RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
IoDeleteSymbolicLink(&DeviceLinkName); DeleteDeviceObject = DriverObject->DeviceObject;
while (DeleteDeviceObject != NULL)
{
v1 = DeleteDeviceObject->NextDevice;
IoDeleteDevice(DeleteDeviceObject);
DeleteDeviceObject = v1;
}
for (i = 0; i < __KernelEventCount; i++)
{
if (__KernelEvent[i] != NULL)
{
ObDereferenceObject(__KernelEvent[i]); __KernelEvent[i] = NULL;
}
}
}

  

内核事件KEVENT(同步)的更多相关文章

  1. Windows API学习---线程与内核对象的同步

    前言 若干种内核对象,包括进程,线程和作业.可以将所有这些内核对象用于同步目的.对于线程同步来说,这些内核对象中的每种对象都可以说是处于已通知或未通知的状态之中.这种状态的切换是由Microsoft为 ...

  2. C#异步编程(三)内核模式线程同步

    其实,在开发过程中,无论是用户模式的同步构造还是内核模式,都应该尽量避免.因为线程同步都会造成阻塞,这就影响了我们的并发量,也影响整个应用的效率.不过有些情况,我们不得不进行线程同步. 内核模式 wi ...

  3. Windows核心编程 第九章 线程与内核对象的同步(上)

    第9章 线程与内核对象的同步 上一章介绍了如何使用允许线程保留在用户方式中的机制来实现线程同步的方法.用户方式同步的优点是它的同步速度非常快.如果强调线程的运行速度,那么首先应该确定用户方式的线程同步 ...

  4. Windows核心编程 第九章 线程与内核对象的同步(下)

    9.4 等待定时器内核对象 等待定时器是在某个时间或按规定的间隔时间发出自己的信号通知的内核对象.它们通常用来在某个时间执行某个操作. 若要创建等待定时器,只需要调用C r e a t e Wa i ...

  5. 在Spring中使用异步事件实现同步事务

    结合Scala+Spring,我们将采取一个很简单的场景:下订单,然后发送一封电子邮件. 编制一个服务: @Serviceclass OrderService @Autowired() (orderD ...

  6. ie低版本内核事件兼容问题(事件绑定,绑定事件自动执行,文档模式问题)

    问题情况 搜狗等,兼容模式下,以前前端写的点击事件的代码没有, 后来一看是因为兼容模式为9,导致点击事件失效 解决办法,步骤 1,处理绑定事件兼容问题 ie低版本绑定事件只支持attactevent, ...

  7. C# 委托 、事件、同步、异步知识点归纳

    一.委托 基本用法: 1.声明一个委托类型.委托就像是‘类'一样,声明了一种委托之后就可以创建多个具有此种特征的委托.(特征,指的是返回值.参数类型) public delegate void Som ...

  8. linx 内核 并发与同步 1

    内核并发来源: 1.硬件中断和异常:中断服务程序和被中断的进程可能发生并发访问资源 2.软中断和tasklet,软中断和taklet随时都可能倍调度执行,从而打断当前正在执行 进程的上下文. 3.内核 ...

  9. python-Event事件线程同步和互斥

    #!/usr/bin/python #coding=utf-8 #用于线程间通信,通过事件标识控制 import threading from time import sleep,ctime def ...

随机推荐

  1. Ubuntu 16.04设置IP、网关、DNS

    说明:在网上给的教程上面通常会有这样的一个误导思路,按照配置文件设置后会不生效的问题,甚至没有一点效果,经过排查发现Linux下设置IP这个话题的入口线索应该分为两种:1为Server版,2为Desk ...

  2. BZOJ3300: [USACO2011 Feb]Best Parenthesis 模拟

    Description Recently, the cows have been competing with strings of balanced  parentheses and compari ...

  3. FAST:通过Floodlight控制器下发流表

    参考: Floodlight+Mininet搭建OpenFlow(四):流表操作 通过Floodlight控制器下发流表 下发流表的方式有两种: 1.借助Floodlight的北向API,利用curl ...

  4. UVa 11235 频繁出现的数值

    https://vjudge.net/problem/UVA-11235 题意: 给出一个非降序排列的整数数组a1,a2,...,an,你的任务是对于一系列询问(i,j),回答ai,ai+1,...a ...

  5. UVa 140 带宽

    题意:给出一个n个结点的图G和一个结点的排列,定义结点的带宽为i和相邻结点在排列中的最远距离,求出让带宽最小的结点排列. 思路:用STL的next_permutation来做确实是很方便,适当剪枝一下 ...

  6. redis nginx session tomcat

    Redis解决session共享 http://lyl-zsu.iteye.com/blog/2408292 http://zx10103326.iteye.com/blog/2244195 http ...

  7. ros python 重置位置

    #!/usr/bin/env python import rospy import math import sys import commands import yaml from tf import ...

  8. 更换主机后SSH无法登录的问题

    之前通过SSH远程一台机器(起个名字:cc),某一天把cc重装了一下系统,再SSH时显示密钥验证失败: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ...

  9. LIBS+=

    ZC: “LIBS+=”是要结合“LIBPATH += ”一起使用的?类似下面的用法: ZC: “LIBS+=”指明lib文件的名称,“LIBPATH += ”指明lib文件的路径.最后还要把DLL文 ...

  10. [原][osg][粒子特效]spark粒子特效生成流程