Chaospace

Reshape the world by engineering chaos


  • 首页

  • 分类

  • 归档

  • 站点地图

  • 公益 404

  • 搜索

.designated_initializers = 指派初始化

发表于 2016-05-27 | 分类于 代码

2008-06-16 11:00 在设备驱动中常见下面这种语法格式,首先定义一堆函数指针,并根据不同的设备初始化为不同的实现函数,很像 C++ 里面的成员函数。

struct file_operations scull_fops = {
          .owner = THIS_MODULE,
          .llseek = scull_llseek,
          .read = scull_read,
          .write = scull_write,
          .ioctl = scull_ioctl,
          .open = scull_open,
          .release = scull_release,
};

这种语法使用了指派初始化(designated_initializers ),使程序可以使不同的结构定义间的移植更加容易,代码更加精简易读。而且编译器可以根据需要改变结构成员的顺序,提升程序的性能。

以下内容摘自 gcc 手册

In a structure initializer, specify the name of a field to initialize with `.fieldname =' before the element value. For example, given the following structure,

struct point {int x, y;};

The following initialization

struct point p = { 
    .y = yvalue, 
    .x = xvalue
};

is equivalent to

struct point p = {xvalue, yvalue};

Vim 键盘图

发表于 2016-05-27 | 分类于 技术

picture alt

主板诊断卡

发表于 2016-05-27 | 分类于 软件

主板诊断卡也叫 POST 卡(Power On Self Test 加电自检),其工作原理是利用主板中 BIOS 内部程序的检测结果,通过主板诊断卡代码一一显示出来,结合诊断卡的代码含义速查表就能很快地知道电 脑故障所在。尤其在 PC 机不能引导操作系统、黑屏、喇叭不叫时,使用本卡更能体现其便利,事半功倍。

主板上的 BIOS 在每次开机时,会对系统的电路、存储器、键盘、视 频部分、硬盘、软驱等各个组件时行严格测试,并分析硬盘系统配置,对已配置的基本 I/O 设置进行初始化,一切正常后,再引导操作系统。当计算机出现关键性 故障,屏幕上无显示时,很难判断计算机故障所在,此时可以将本卡插入扩充槽 内,根据卡上显示的代码,参照计算机所所属的 BIOS 种类,再通过主板诊断卡的代码含义速查表查出该代码所表示的故障原因和部位,就可清楚地知道故障所 在。

DEBUG 卡的工作原理

DEBUG 卡的工作原理其实很简单,每个厂家的 BIOS,无论是 AWARD、AMI 还是 PHOENIX 的,都有所谓的 POST CODE,即开机自我侦测代码,当 BIOS 要进行某项测试动作时,首先将该 POST CODE 写入 80h 地址,如果测试顺利完成,再写入下一个 POST CODE,因此,如果发生错误或死机,根据 80H 地址的 POST CODE 值,就可以了解问题出在什么地方。DEBUG 卡的作用就是读取 80H 地址内的 POST CODE,并经译码器译码,最后由数码管显示出来。这样就可以通过 DEBUG 卡上显示的 16 进制代码判断问题出在硬件的那一部分,而不用仅依靠计算机主板 那几声单调的警告声来粗略判断硬件错误了。通过它可知道硬件检测没有通过的是内存还是 CPU,或者是其他硬件,方便直观地解决棘手的主板问题。以此类推, 还可以判断超频的限制硬件是哪一个,做到有的放矢,查障无忧。

HelloWDM

发表于 2016-05-27 | 分类于 代码

2008-08-14 13:47
人生真是会开玩笑啊,作为自由软件的信徒,居然要研究 Windows 的驱动了。不过对比一下还是挺好的,
和 Linux 下面的驱动比起来,还是有许多相似的地方。不过这种函数缩进形式也太难看了,一个参数一行,
看了两天都没反应过来,头晕。


WDM 驱动程序是一种很新的东西,相信很多人都跟我一样,对它很感兴趣,但是又找不到学习的切入 点。究其原因,还是因为 WDM 是一种非常“死板板”的程 序,它一运行就是工作在系统的底层 RING 0 处,提供各种接口给应用程序调用。也正因为如此,它不像普通的应用程序一样,可以很快地上手——更多的时候,你是在阅读它的技术资料和各种接口信息,你 还要非常地熟悉系统底层的工作原理,否则一个不小心,就“蓝屏”了,呵呵——话说回来,写驱动程序的时候,死机是家常便饭。

因 此很多人都对 WDM 望而生畏了。回想一下,我刚开始学 WDM 的情形还历历在目——看书看了整整 3 天,但是看完之后好像跟没看也差不了多少,还是不知道怎么入门,甚至连怎么写一个“Hello World”都不知道——后来才知道其实 WDM 是没有所谓的“Hello World”程序的,唉,真是痛苦啊,这主要还是因为网络上的 WDM 资料太少造成的。为了不让大家重蹈我的覆辙并对 WDM 有个感性的认识,在此我给出一个 最简单的完整的 WDM 框架,并附有注释,姑且可以算是一个入门的“Hello World”吧。

废话少说,让我们马上开始研究,要求读者已安装 DDK 2000。

/***************************************************************
程序名称:Hello World for WDM
文件名称:HelloWDM.cpp
作者:罗聪
日期:2002-8-16
***************************************************************/

// 一定要的头文件,声明了函数模块和变量:
#include "HelloWDM.h"

/***************************************************************
函数名称:DriverEntry()
功能描述:WDM 程序入口
***************************************************************/
//extern "C" 是必须的,表示“用 C 链接”。如果你的文件名是 HelloWDM.c 的话,这句可以省略。
extern "C"
NTSTATUS DriverEntry(     IN PDRIVER_OBJECT DriverObject,
                         IN PUNICODE_STRING RegistryPath)
{// 指定“添加设备”消息由函数“HelloWDMAddDevice()”来处理:
     DriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
    // 指定“即插即用”消息由函数“HelloWDMPnp()”来处理:
     DriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;

    // 返回一个 NTSTATUS 值 STATUS_SUCCESS。几乎所有的驱动程序例程都必须返回一个 NTSTATUS 值,这些值在 NTSTATUS.H DDK 头文件中有详细的定义。
    return STATUS_SUCCESS;
}

/***************************************************************
函数名称:HelloWDMAddDevice()
功能描述:处理“添加设备”消息
***************************************************************/
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
                           IN PDEVICE_OBJECT PhysicalDeviceObject)
{
    // 定义一个 NTSTATUS 类型的返回值:
     NTSTATUS status;
    // 定义一个功能设备对象(Functional Device Object):
     PDEVICE_OBJECT fdo;

    // 创建我们的功能设备对象,并储存到 fdo 中:
     status = IoCreateDevice(
         DriverObject,                // 驱动程序对象
        sizeof(DEVICE_EXTENSION),    // 要求的设备扩展的大小
         NULL,                        // 设备名称,这里为 NULL
         FILE_DEVICE_UNKNOWN,        // 设备的类型,在标准头文件 WDM.H 或 NTDDK.H 中列出的 FILE_DEVICE_xxx 值之一
        0,                            // 各种常量用 OR 组合在一起,指示可删除介质、只读等。
         FALSE,                        // 如果一次只有一个线程可以访问该设备,为 TRUE,否则为 FALSE
        &fdo);                        // 返回的设备对象

    //NT_SUCCESS 宏用于测试 IoCreateDevice 内核是否成功完成。不要忘记检查对内核的所有调用是否成功。NT_ERROR 宏不等同于!NT_SUCCESS,最好使用!NT_SUCCESS,因为除了错误外,它还截获警告信息。
    if(!NT_SUCCESS(status))
        return status;

    // 创建一个设备扩展对象 dx,用于存储指向 fdo 的指针:
     PDEVICE_EXTENSION dx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
     dx->fdo = fdo;

    // 用 IoAttachDeviceToDeviceStack 函数把 HelloWDM 设备挂接到设备栈:
     dx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);

    // 设置 fdo 的 flags。有两个“位”是必须改变的,一个是必须清除 DO_DEVICE_INITIALIZING 标志,如果在 DriverEntry 例 程中调用 IoCreateDevice(),就不需要清除这个标志位。还有一个是必须设置 DO_BUFFER_IO 标志位:
     fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
     fdo->Flags &= ~DO_DEVICE_INITIALIZING;

    // 返回值:
    return STATUS_SUCCESS;
}

/***************************************************************
函数名称:HelloWDMPnp()
功能描述:处理“即插即用”消息
***************************************************************/
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
    // 创建一个设备扩展对象 dx,用于存储指向 fdo 的指针:
     PDEVICE_EXTENSION dx=(PDEVICE_EXTENSION)fdo->DeviceExtension;

    // 首先要通过函数 IoGetCurrentIrpStackLocation() 得到当前的 IRP,并由此得到 Minor Function:
     PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
     ULONG MinorFunction = IrpStack->MinorFunction;

    // 然后把这个 Minor Function 传递给下一个设备栈:
     IoSkipCurrentIrpStackLocation(Irp);
     NTSTATUS status = IoCallDriver(dx->NextStackDevice, Irp);

    // 处理“即插即用”次功能代码:
    // 当 Minor Function 等于 IRP_MN_REMOVE_DEVICE 时,说明有设备被拔出或卸下,这时要取消资源分配并删除设备:
    if(MinorFunction==IRP_MN_REMOVE_DEVICE)
    {
        // 取消设备接口:
         IoSetDeviceInterfaceState(&dx->ifSymLinkName, FALSE);
         RtlFreeUnicodeString(&dx->ifSymLinkName);

        // 调用 IoDetachDevice() 把 fdo 从设备栈中脱开:
        if (dx->NextStackDevice)
             IoDetachDevice(dx->NextStackDevice);
        // 删除 fdo:
         IoDeleteDevice(fdo);
    }

    // 返回值:
    return status;
}

/***************************************************************
程序名称:Hello World for WDM
文件名称:HelloWDM.h
作者:罗聪
日期:2002-8-16
***************************************************************/

// 头文件,只是声明一些函数和变量,比较简单就不多说了,请读者自行研究:

#ifdef __cplusplus

extern "C"
{
#endif

#include "ntddk.h"

#ifdef __cplusplus
}
#endif

typedef struct _DEVICE_EXTENSION
{
     PDEVICE_OBJECT     fdo;
     PDEVICE_OBJECT     NextStackDevice;
     UNICODE_STRING     ifSymLinkName;

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
                           IN PDEVICE_OBJECT PhysicalDeviceObject);

NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,
                         IN PIRP Irp);

提交代码到上游内核的好处

发表于 2016-05-27 | 分类于 开源
  1. 所有 Linux 用户都可以使用加入主内核的代码,在不同的发行版中自动启用。所以就没有必要发布驱动盘,免去了支持不同发行版及其不同版本的痛苦(亲身感受,非常痛苦)。解决了大量的发布和支持问题。

  2. 尽管开发者尽量保持内核空间与用户空间的接口稳定性,内核内部的调用接口却经常变化。这是有意而为之,使得任何时间都可以对内核进行基础修正,以获得更加高质量的代码。但是这一策略的另一个结果就是独立分支必须经常进行大量的修改以跟的上内核前进的脚步。

而内核主树中的代码则不必担心这个问题,因为改变调用接口的开发者必须修正由此引发的问题。所以主树中代码的维护成本很低。

  1. 除此之外,其他开发者会经常对内核中的代码进行提升。

  2. 只有经过审阅的代码才会进入内核。不管代码开发者有多牛,这个审阅过程总会提高代码的质量。

function-call-convention

发表于 2016-05-22 | 分类于 代码

在 C 语言中,假设我们有这样的一个函数:

int function(int a,int b)

调用时只要用 result = function(1,2)这样的方式就可以使用这个函数。但是,当高级语言被编译成计算机可以识别的机器码时,有一个问题就凸现出来:在 CPU 中,计算 机没有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数。也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须 由函数调用者和函数本身来协调。为此,计算机提供了一种被称为栈的数据结构来支持参数传递。

栈是一种先进后出的数据结构,栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项(被称为栈顶)。用户可以在栈顶上方向栈中加入 数据,这个操作被称为压栈 (Push),压栈以后,栈顶自动变成新加入数据项的位置,栈顶指针也随之修改。用户也可以从堆栈中取走栈顶,称为弹出栈 (pop),弹出栈后,栈顶下的一个元素变成栈顶,栈顶指针随之修改。

函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得数据,并进行计算。函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈恢复原装。

在参数传递中,有两个很重要的问题必须得到明确说明:

  1. 当参数个数多于一个时,按照什么顺序把参数压入堆栈
  2. 函数调用后,由谁来把堆栈恢复原装

在高级语言中,通过函数调用约定来说明这两个问题。常见的调用约定有:

  • stdcall
  • cdecl
  • fastcall
  • thiscall
  • naked call

stdcall 调用约定

stdcall 很多时候被称为 pascal 调用约定,因为 pascal 是早期很常见的一种教学用计算机程序设计语言,其语法严谨,使用的函数调用约 定就是 stdcall。在 Microsoft C++ 系列的 C/C++ 编译器中,常常用 PASCAL 宏来声明这个调用约定,类似的宏还有 WINAPI 和 CALLBACK。

stdcall 调用约定声明的语法为(以前文的那个函数为例):

int __stdcall function(int a,int b)

stdcall 的调用约定意味着:

  1. 参数从右向左压入堆栈
  2. 函数自身修改堆栈
  3. 函数名自动加前导的下划线,后面紧跟一个 @符号,其后紧跟着参数的尺寸

以上述这个函数为例,参数 b 首先被压栈,然后是参数 a,函数调用 function(1,2)调用处翻译成汇编语言将变成:

push 2 第二个参数入栈
push 1 第一个参数入栈
call function 调用参数,注意此时自动把 cs:eip 入栈

而对于函数自身,则可以翻译为:

push ebp 保存 ebp 寄存器,该寄存器将用来保存堆栈的栈顶指针,可以在函数退出时恢复
mov ebp,esp 保存堆栈指针
mov eax,[ebp + 8H] 堆栈中 ebp 指向位置之前依次保存有 ebp,cs:eip,a,b,ebp +8 指向 a
add eax,[ebp + 0CH] 堆栈中 ebp + 12 处保存了 b
mov esp,ebp 恢复 esp
pop ebp
ret 8

而在编译时,这个函数的名字被翻译成_function@8

注意不同编译器会插入自己的汇编代码以提供编译的通用性,但是大体代码如此。其中在函数开始处保留 esp 到 ebp 中,在函数结束恢复是编译器常用的方法。

从函数调用看,2 和 1 依次被 push 进堆栈,而在函数中又通过相对于 ebp(即刚进函数时的堆栈指针)的偏移量存取参数。函数结束后,ret 8 表示清理 8 个字节的堆栈,函数自己恢复了堆栈。

cdecl 调用约定

cdecl 调用约定又称为 C 调用约定,是 C 语言缺省的调用约定,它的定义语法是:

int function (int a ,int b) // 不加修饰就是 C 调用约定
int __cdecl function(int a,int b)// 明确指出 C 调用约定

在写本文时,出乎我的意料,发现 cdecl 调用约定的参数压栈顺序是和 stdcall 是一样的,参数首先由有向左压入堆栈。所不同的是,函数本身不 清理堆栈,调用者负责清理堆栈。由于这种变化,C 调用约定允许函数的参数的个数是不固定的,这也是 C 语言的一大特色。对于前面的 function 函数,使 用 cdecl 后的汇编码变成:

调用处
push 1
push 2
call _function
add esp,8 注意:这里调用者在恢复堆栈

被调用函数_function 处

push ebp 保存 ebp 寄存器,该寄存器将用来保存堆栈的栈顶指针,可以在函数退出时恢复
mov ebp,esp 保存堆栈指针
mov eax,[ebp + 8H] 堆栈中 ebp 指向位置之前依次保存有 ebp,cs:eip,a,b,ebp +8 指向 a
add eax,[ebp + 0CH] 堆栈中 ebp + 12 处保存了 b
mov esp,ebp 恢复 esp
pop ebp
ret 注意,这里没有修改堆栈

由于参数按照从右向左顺序压栈,因此最开始的参数在最接近栈顶的位置,因此当采用不定个数参数时,第一个参数在栈中的位置肯定能知道,只要不定的参数个数能够根据第一个后者后续的明确的参数确定下来,就可以使用不定参数,例如对于 CRT 中的 sprintf 函数,定义为:

int sprintf(char* buffer,const char* format,...)

由于所有的不定参数都可以通过 format 确定,因此使用不定个数的参数是没有问题的。

fastcall
fastcall 调用约定和 stdcall 类似,它意味着:

函数的第一个和第二个 DWORD 参数(或者尺寸更小的)通过 ecx 和 edx 传递,其他参数通过从右向左的顺序压栈
被调用函数清理堆栈
函数名修改规则同 stdcall
其声明语法为:int fastcall function(int a,int b)

thiscall

thiscall 是唯一一个不能明确指明的函数修饰,因为 thiscall 不是关键字。它是 C++ 类成员函数缺省的调用约定。由于成员函数调用还有一个 this 指针,因此必须特殊处理,thiscall 意味着:

参数从右向左入栈
如果参数个数确定,this 指针通过 ecx 传递给被调用者;如果参数个数不确定,this 指针在所有参数压栈后被压入堆栈。
对参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈
为了说明这个调用约定,定义如下类和使用代码:

class A 
{ 
public:    
    int function1(int a,int b);    
    int function2(int a,...); 
}; 

int A::function1 (int a,int b) 
{return a+b;}

#include

callee 函数被翻译成汇编后就变成:

// 函数 function1 调用
0401C1D    push        2
00401C1F   push        1
00401C21   lea         ecx,[ebp-8]
00401C24   call function1           注意,这里 this 没有被入栈
// 函数 function2 调用
00401C29   push        3
00401C2B   push        2
00401C2D   push        1
00401C2F   push        3
00401C31   lea         eax,[ebp-8]   这里引入 this 指针
00401C34   push        eax
00401C35   call function2
00401C3A   add         esp,14h

可见,对于参数个数固定情况下,它类似于 stdcall,不定时则类似 cdecl

naked call

这是一个很少见的调用约定,一般程序设计者建议不要使用。编译器不会给这种函数增加初始化和清理代码,更特殊的是,你不能用 return 返回返回值,只能用插入汇编返回结果。这一般用于实模式驱动程序设计,假设定义一个求和的加法程序,可以定义为:

__declspec(naked) int add(int a,int b)
{
  __asm mov eax,a
   __asm add eax,b
   __asm ret 
}

注意,这个函数没有显式的 return 返回值,返回通过修改 eax 寄存器实现,而且连退出函数的 ret 指令都必须显式插入。上面代码被翻译成汇编以后变成:

mov eax,[ebp+8]
add eax,[ebp+12]
ret 8

注意这个修饰是和 __stdcall 及 cdecl 结合使用的,前面是它和 cdecl 结合使用的代码,对于和 stdcall 结合的代码,则变成:

__declspec(naked) int __stdcall function(int a,int b)
{
    __asm mov eax,a
    __asm add eax,b
    __asm ret 8        // 注意后面的 8
}

至于这种函数被调用,则和普通的 cdecl 及 stdcall 调用函数一致。

函数调用约定导致的常见问题
如果定义的约定和使用的约定不一致,则将导致堆栈被破坏,导致严重问题,下面是两种常见的问题:

函数原型声明和函数体定义不一致
DLL 导入函数时声明了不同的函数约定
以后者为例,假设我们在 dll 种声明了一种函数为:

__declspec(dllexport) int func(int a,int b);// 注意,这里没有 stdcall,使用的是 cdecl

使用时代码为:

typedef int (*WINAPI DLLFUNC)func(int a,int b);
hLib = LoadLibrary(...);
DLLFUNC func = (DLLFUNC)GetProcAddress(...)// 这里修改了调用约定
result = func(1,2);// 导致错误

由于调用者没有理解 WINAPI 的含义错误的增加了这个修饰,上述代码必然导致堆栈被破坏,MFC 在编译时插入的 checkesp 函数将告诉你,堆栈被破坏了。

程序员发展路线图

发表于 2016-05-21 | 分类于 代码

高中

10 PRINT "HELLO WORLD"
 20 END

大一

rogram Hello(input, output)
   begin
    writeln('Hello World')
   end.

大二

(defun hello
       (print 
        (cons 'Hello 
            (list 'World))))

新手

#include <stdio.h>
void main(void)
{char *message[] = {"Hello ", "World"};
      int i;

      for(i = 0; i < 2; ++i)
        printf("%s", message[i]);

   printf("\n");
}

熟练程序员

 #include <iostream.h>
#include <string.h>
class string {
private:
     int size;
     char *ptr;

     string() : size(0), ptr(new char[1]) {ptr[0] = 0; }

    string(const string &s) : size(s.size)
    {ptr = new char[size + 1];
        strcpy(ptr, s.ptr);
    }

    ~string()
    {delete [] ptr;
    }

       friend ostream &operator <<(ostream &, const string &);
       string &operator=(const char *);
 };

 ostream &operator<<(ostream &stream, const string &s)
 {return(stream << s.ptr);
 }

 string &string::operator=(const char *chrs)
 {if (this != &chrs)
   {delete [] ptr;
    size = strlen(chrs);
     ptr = new char[size + 1];
     strcpy(ptr, chrs);
   }
   return(*this);
 }

 int main()
 {
   string str;

   str = "Hello World";
   cout << str << endl;

   return(0);
 }

专家

[uuid(2573F8F4-CFEE-101A-9A9F-00AA00342820)
 ]
 library LHello
 {
     // bring in the master library
     importlib("actimp.tlb");
     importlib("actexp.tlb");

     // bring in my interfaces
     #include "pshlo.idl"

     [uuid(2573F8F5-CFEE-101A-9A9F-00AA00342820)
     ]
     cotype THello
  {
  interface IHello;
  interface IPersistFile;
  };
 };

 [
 exe,
 uuid(2573F890-CFEE-101A-9A9F-00AA00342820)
 ]
 module CHelloLib
 {

     // some code related header files
     importheader(<windows.h>);
     importheader(<ole2.h>);
     importheader(<except.hxx>);
     importheader("pshlo.h");
     importheader("shlo.hxx");
     importheader("mycls.hxx");

     // needed typelibs
     importlib("actimp.tlb");
     importlib("actexp.tlb");
     importlib("thlo.tlb");

     [uuid(2573F891-CFEE-101A-9A9F-00AA00342820),
     aggregatable
     ]
     coclass CHello
  {cotype THello;};
 };


 #include "ipfix.hxx"

 extern HANDLE hEvent;

 class CHello : public CHelloBase
 {
 public:
     IPFIX(CLSID_CHello);

     CHello(IUnknown *pUnk);
     ~CHello();

     HRESULT  __stdcall PrintSz(LPWSTR pwszString);

 private:
     static int cObjRef;
 };


 #include <windows.h>
 #include <ole2.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include "thlo.h"
 #include "pshlo.h"
 #include "shlo.hxx"
 #include "mycls.hxx"

 int CHello::cObjRef = 0;

 CHello::CHello(IUnknown *pUnk) : CHelloBase(pUnk)
 {
     cObjRef++;
     return;
 }

 HRESULT  __stdcall  CHello::PrintSz(LPWSTR pwszString)
 {
     printf("%ws
", pwszString);
     return(ResultFromScode(S_OK));
 }


 CHello::~CHello(void)
 {

 // when the object count goes to zero, stop the server
 cObjRef--;
 if(cObjRef == 0)
     PulseEvent(hEvent);

 return;
 }

 #include <windows.h>
 #include <ole2.h>
 #include "pshlo.h"
 #include "shlo.hxx"
 #include "mycls.hxx"

 HANDLE hEvent;

  int _cdecl main(
 int argc,
 char * argv[]) {
 ULONG ulRef;
 DWORD dwRegistration;
 CHelloCF *pCF = new CHelloCF();

 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

 // Initialize the OLE libraries
 CoInitializeEx(NULL, COINIT_MULTITHREADED);

 CoRegisterClassObject(CLSID_CHello, pCF, CLSCTX_LOCAL_SERVER,
     REGCLS_MULTIPLEUSE, &dwRegistration);

 // wait on an event to stop
 WaitForSingleObject(hEvent, INFINITE);

 // revoke and release the class object
 CoRevokeClassObject(dwRegistration);
 ulRef = pCF->Release();

 // Tell OLE we are going away.
 CoUninitialize();

 return(0); }

 extern CLSID CLSID_CHello;
 extern UUID LIBID_CHelloLib;

 CLSID CLSID_CHello = { /* 2573F891-CFEE-101A-9A9F-00AA00342820 */
     0x2573F891,
     0xCFEE,
     0x101A,
     {0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20}
 };

 UUID LIBID_CHelloLib = { /* 2573F890-CFEE-101A-9A9F-00AA00342820 */
     0x2573F890,
     0xCFEE,
     0x101A,
     {0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20}
 };

 #include <windows.h>
 #include <ole2.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include "pshlo.h"
 #include "shlo.hxx"
 #include "clsid.h"

 int _cdecl main(
 int argc,
 char * argv[]) {
 HRESULT  hRslt;
 IHello        *pHello;
 ULONG  ulCnt;
 IMoniker * pmk;
 WCHAR  wcsT[_MAX_PATH];
 WCHAR  wcsPath[2 * _MAX_PATH];

 // get object path
 wcsPath[0] = '\0';
 wcsT[0] = '\0';
 if(argc > 1) {mbstowcs(wcsPath, argv[1], strlen(argv[1]) + 1);
     wcsupr(wcsPath);
     }
 else {fprintf(stderr, "Object path must be specified\n");
     return(1);
     }

 // get print string
 if(argc > 2)
     mbstowcs(wcsT, argv[2], strlen(argv[2]) + 1);
 else
     wcscpy(wcsT, L"Hello World");

 printf("Linking to object %ws\n", wcsPath);
 printf("Text String %ws\n", wcsT);

 // Initialize the OLE libraries
 hRslt = CoInitializeEx(NULL, COINIT_MULTITHREADED);

 if(SUCCEEDED(hRslt)) {hRslt = CreateFileMoniker(wcsPath, &pmk);
     if(SUCCEEDED(hRslt))
  hRslt = BindMoniker(pmk, 0, IID_IHello, (void **)&pHello);

     if(SUCCEEDED(hRslt)) {

  // print a string out
  pHello->PrintSz(wcsT);

  Sleep(2000);
  ulCnt = pHello->Release();}
     else
  printf("Failure to connect, status: %lx", hRslt);

     // Tell OLE we are going away.
     CoUninitialize();}

 return(0);
 }

见习黑客

#!/usr/local/bin/perl
 $msg="Hello, world.\n";
 if ($#ARGV >= 0) {while(defined($arg=shift(@ARGV))) {
     $outfilename = $arg;
     open(FILE, ">" . $outfilename) || die "Can't write $arg: $!\n";
     print (FILE $msg);
     close(FILE) || die "Can't close $arg: $!\n";
   }
 } else {print ($msg);
 }
 1;

有经验黑客

#include <stdio.h>
 #define S "Hello, World\n"
 main(){exit(printf(S) == strlen(S) ? 0 : 1);}

熟练黑客

% cc -o a.out ~/src/misc/hw/hw.c
 % a.out

黑客领袖

% echo "Hello, world." 初级主管 10 PRINT "HELLO WORLD"
 20 END

中级主管

mail -s "Hello, world." bob@b12
 Bob, could you please write me a program that prints "Hello, world."?
 I need it by tomorrow.
 ^D

高级主管

% zmail jim
 I need a "Hello, world." program by this afternoon.

首席执行官

% letter
 letter: Command not found.
 % mail
 To: ^X ^F ^C
 % help mail
 help: Command not found.
 % damn!
 !: Event unrecognized
 % logout

dbus 进程间通信

发表于 2016-05-21 | 分类于 Linux

简介

通常一个桌面中会有几个不同的程序同时运行,而且它们之间需要相互间进行通讯。在操作系统层,这些不同的程序用不同的进程来实现。而为了安全起见,不同进 程间是不能直接相互调用和内存访问的,也就是一个进程对另外的进程是不可见的。这就需要一个机制来定义一个接口,一个进程通过接口访问另外一个进程,对于 发起访问的进程来说,调用 IPC 接口中的函数与调用自己进程内的函数是没有区别的这就是进程间通讯。进程间通讯有许多不同的实现:D-bus,CORBA,KDE 中的 DCOP 和 Gnome 中的 Bonobo 等。

实践证明, D-bus 是未来一段时间的桌面程序间进行通讯的主要方式。 D-BUS 的目标是将 DCOP 和 Bonobo 替换为简单的 IPC,并集成这两种桌面环境。由于尽可能地减少了 D-BUS 所需的依赖,所以其他可能会使用 D-BUS 的应用程序不用担心引入过多依赖。

与其它通讯方式的对比

其他方式的缺点概括起来就是:

CORBA: 非常复杂,难于实现。看起来接口定义得相当完善,所以 Gnome 和 KDE 都曾经试图用它作为 进程间通讯的方式。但是因为此方式的接口复杂难用,速度慢,耗内存等缺点,结果都以失败告终。
DCOP:轻量级,依赖于 QT,曾经是 KDE 中的默认进程间通信方式,而目前 KDE4 已经完全转移到 Dbus;

Bonobo:基于 CORBA,实现复杂,依赖于 GObject。目前 Gnome 除了极个别的复杂情况下仍然使用 Bonobo 外,大部分已经转移到 D-Bus。

XML-RPC 和 SOAP:所有消息都以复杂的 XML 格式封包,用文本模式发送,接收后再转换回二进制模式。两次转换低效而耗时、

COM/DCOM: 微软早期版本使用,而且只能在微软的视窗系统中使用。众所周知,微软的设计通常包含安全问题,曾经风靡一时的冲击波病毒就是利用了远程进程间调用的漏洞。 COM 当然也不例外,ActiveX 依然是大部分网页病毒的源泉。COM+ 解决了部分问题,不过也实在好不到哪里去。

综合起来,大部分的缺点包括:

(1)有些过于复杂,不适合桌面使用

(2) 有些严重依赖桌面环境,只能在 Gnome 中通讯或只能在 KDE 中通讯。这就造成 Gnome 和 KDE 间的程序难以相互通讯,扩大了 Linux 桌面的分裂。

Dbus 的特点

Dbus 的设计重复考虑了以上缺点, 其主要特点包括:

(1)基于消息的通讯方式,使用二进制传输数据,使得实现起来相对简单,效率高

(2) 独立于 Gnome 和 KDE,在 freedesktop.org 的维护下发展。这样 Gnome 和 KDE 的程序之间就可以进行良好的通讯。

(3)正如其名字显示的那样,Dbus 使用总线系统,这样一个进程就可以同时和一组进程进行通讯。Dbus 中可以使用系统级总线,操作系统可以像总线中发送消息,供用户空间的程序使用。将整个桌面系统更好的整合起来。

(4)典型的 D-BUS 设置将由几个总线构成, 经常使用系统总线和会话总线。一个持久系统总线在引导时就会启动,由操作系统和后台进程使用,安全性非常好,应用程序无法欺骗系统事件。会话总线在用户登录后启动,为用户私有,供用户的自己的应用程序进行通信。

dbus 的调用方式

下面这个例子来自 Dbus 的官方教程,使用了 D-Bus 的 GLib 绑定,展示了如何通过 dbus 调用函数,更详细的例子请参考 http://dbus.freedesktop.org/doc/dbus/libdbus-tutorial.html 。

int main (int argc, char **argv)
{
    DBusGConnection *connection;
    GError *error;
    DBusGProxy *proxy;
    char **name_list;
    char **name_list_ptr;

    g_type_init ();

    error = NULL;

    /* 首先连接到总线 */
    connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
    if (connection == NULL){
        g_printerr ("Failed to open connection to bus: %s\n",
        error->message);
        g_error_free (error);
        exit (1);
    }

    /* 创建代理对象 "bus driver" (name "org.freedesktop.DBus") */

    proxy = dbus_g_proxy_new_for_name (connection,
        DBUS_SERVICE_DBUS,
        DBUS_PATH_DBUS,
        DBUS_INTERFACE_DBUS);

    /* 跨进程调用 ListNames 方法 并等待相应 */
    error = NULL;
    if (!dbus_g_proxy_call (proxy, "ListNames", &error, G_TYPE_INVALID,
    G_TYPE_STRV, &name_list, G_TYPE_INVALID)){
    /* 演示远程异常与本地错误的处理 */
        if (error->domain == DBUS_GERROR && error->code ==  BUS_GERROR_REMOTE_EXCEPTION)
            g_printerr ("Caught remote method exception %s: %s",             dbus_g_error_get_name (error), error->message);
        else {g_printerr ("Error: %s\n", error->message);

        g_error_free (error);
        exit (1);
    }

    /* 打印结果 */

    g_print ("Names on the message bus:\n");

    for (name_list_ptr = name_list; *name_list_ptr; name_list_ptr++){g_print (" %s\n", *name_list_ptr);
    }
    g_strfreev (name_list);

    /* 释放接口 */
    g_object_unref (proxy);

    return 0;
}

testdisk

发表于 2016-05-21 | 分类于 Linux

Testdisk 是修复分区表,恢复误删数据的好工具。

Install(Ubuntu)

$ sudo apt-get install testdisk

启动

$ sudo testdisk

重新扫描分区表

依次选择

[Create] --> 需要修复的硬盘 --> 
[Proceed] --> 
[Intel] Intel/PC partition --> 
[Analyse] --> 
[Proceed] -->
[Write]

reboot

生成和使用代码补丁

发表于 2016-05-21 | 分类于 代码

生成

$ diff -Nur old new > patchfile

使用

$ cd old 
$ patch -p1 < ../patchfile
1…345…18

Feng Chao

178 日志
14 分类
1 标签
RSS
GitHub E-Mail Twitter
© 2006 — 2018 Feng Chao
由 Hexo 强力驱动
|
主题 — NexT.Mist