gdb动态库延迟断点及线程进程创建相关事件处理(上)

gdb动态库延迟断点及线程进程创建相关事件处理(上)
gdb动态库延迟断点及线程进程创建相关事件处理(上)

gdb动态库延迟断点及线程进程创建相关事件处理(上)

一、gdb对共享库符号的支持

当使用gdb调试一些动态链接生成的可执行文件时,我们可能会有意或者无意的在一些不存在的函数上打断点,此时gdb并不是提示错误,而是提示是否在之后加载的动态库中添加该断点,也就是pending断点,下面是一个典型的提示:(gdb) b noexisting

Function "noexisting" not defined.

Make breakpoint pending on future shared library load? (y or [n])

此时我们可以想一下,对于动态加载的共享库文件,它可能是在可执行文件中已经确定好的,例如大家通过ldd可以看到一个可执行文件静态依赖的共享库文件;还有一种是通过dlopen在代码中主动随机打开的一个so文件。

无论是哪种情况,有一点是相同而确定的,那就是在设置断点的时候,gdb是不知道这个符号的位置。这一点其实也不难,难点是gdb怎么知道一个so文件被加载(包括代码中通过dlopen打开的),并且这个被加载的so文件中包含了pending的断点符号?这里有一个实实在在的限制:gdb必须第一时间知道所有so文件的加载,在该so文件中任何一个函数都没有开始执行的时候就提前打上断点,否则就可能错

误唯一的一次执行机会,例如一些so文件中init节的函数。大家可以先思考一下gdb将如何实现这个功能,此时建议先理一下思路,不要还没有理解问题本身就开始继续看下面内容。二、gdb第一时间感知动态库加载方法

1、什么样的模式和思路

这个问题我其实是想了一下,觉得应该有比较巧妙的方法,只是我不知道。但是看了一下gdb的实现,发现此处的方法并没有巧妙之处,但是可以实实在在解决问题。这可能就是做工程和做科学的区别,很多时候,我们要让一个工程联动流畅的运行,中间可以使用协议、妥协、适配、模式等各种方法,最终把一个产品实现,这就是我们的目的。当一个产品实现之后,大家就可以在这个基础上进行优化,扩展,兼容等各种操作,这就是工程。也就是说,实现的方法可能很朴素,但是只要能很好的解决问题,它就是一个好的工程。例如,java,它效率可能没有C++高,但是它便于跨平台、C++虽然没有C那么底层、但是它可以更好的支持大规模项目协作开发,这些都是一些应用和场景决定的一些实现。

这里说gdb对SO文件加载的第一时间感知并不是自己独立完成的,而是需要动态链接器的支持,甚至是dl库本身的支持,gdb本身可能的确没有自己完成这个功能的能力(不太确定,但是当前的Linux实现是依赖了动态链接库本身),它需要动态库操作本身的支持。这一点对于WIndows系统同样适

用,windows系统下对于调试器来说,它可以通过WaitForDebugEvent来获得被调试任务的一些事件,而动态链接库的加载就在这个通知范围内(通知类型为

LOAD_DLL_DEBUG_EVENT,可参考

gdb-6.0\gdb\win32-nat.c get_child_debug_event)。

2、linux下实现

①、动态链接库本身支持

_dl_debug_state动态库和调试器约定好的一个接口,这个接口事实上是一个空函数,定义于glibc-2.7\elf\dl-debug.c:

/* This function exists solely to have a breakpoint set on it by the

debugger. The debugger is supposed to find this function's address by

examining the r_brk member of struct r_debug, but GDB 4.15 in fact looks

for this particular symbol name in the PT_INTERP file. */ void

_dl_debug_state (void)

{

}

上面的注释已经说明了这个函数的用处,可能有些同学看这个代码的时候没有在意这个空函数,更不要说注释了。它的

意思就是说,这个函数单独放在这里就是为了给调试器一个下断点的机会,调试器可以在这个约定好的地方设置断点,在该函数断点命中之后,调试器可以通过搜索_r_debug符号来找到被调试任务主动反映的一些状态。大家可以在glibc 中搜索一下对这个_dl_debug_state函数的调用。在调用这个函数之前,C库都会重新的给_r_debug结构赋值。例如glibc-2.7\elf\dl-load.c _dl_map_object_from_fd

struct r_debug *r = _dl_debug_initialize (0, nsid);

……

/* Notify the debugger we have added some objects. We need to

call _dl_debug_initialize in a static program in case dynamic

linking has not been used before. */

r->r_state = RT_ADD;

_dl_debug_state ();

而函数就是通过

struct r_debug *

internal_function

_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)

{

struct r_debug *r; if (ns == LM_ID_BASE)

r = &_r_debug;也就是这个函数返回的就是这个全局的_r_debug变量。

else

r = &GL(dl_ns)[ns]._ns_debug;

……

return r;

}

这种模式在NPTL库中也存在,该库中定义的

__nptl_create_event和__nptl_death_event就是为了让调试器方便的打断点,但是当前的gdb并没有使用这个接口功能,这是后话,具体怎么实现本文最后再描述一下。

②、gdb对该接口的使用

gdb-6.0\gdb\solib-svr4.c

该文件中包含了一些符号信息,其中包含了和外部符号联动的协约式接口

static char *solib_break_names[] =

{

"r_debug_state",

"_r_debug_state",

"_dl_debug_state",这个就是之前和动态库约定好的

_dl_debug_state 接口,在该文件初始化的开始就会给该函数打断点。

"rtld_db_dlactivity",

"_rtld_debug_state",

……

NULL

};

在gdb-6.0\gdb\solib-legacy.c

legacy_svr4_fetch_link_map_offsets函数中,其中设置了

r_debug、link_map结构之间的一些相对关系及结构信息(实不相瞒,这些结构具体细节有待详细分析,我也没有完全分析完整,只是看个大概)。

然后在文件初始化函数中会调用

gdb-6.0\gdb\solib-svr4.c:enable_break (void)

/* Now try to set a breakpoint in the dynamic linker. */

for (bkpt_namep = solib_break_names; *bkpt_namep != NULL; bkpt_namep++) 这个数组中就包含了我们之前说的那个_r_debug_state函数,

{

sym_addr = bfd_lookup_symbol (tmp_bfd,

*bkpt_namep);

if (sym_addr != 0)

break;

}

/* We're done with the temporary bfd. */

bfd_close (tmp_bfd); if (sym_addr != 0)

{

create_solib_event_breakpoint (load_addr + sym_addr);这个函数实现非常简单,只是简单转发给

create_internal_breakpoint (address, bp_shlib_event)函数,注意其中的类型bp_shlib_event,后面将会用到。

return 1;

}

③、当_r_debug_state命中时

明显地,当使能动态so断点之后,系统并不会在加载一个文件之后就让程序停下来,虽然gdb在其中设置了断点。所以gdb要能够识别这个断点类型并自己默默的消化掉这个断点,然后读取新加载(卸载时删除)文件中的符号表,并判断pending断点是否存在其中,如果存在则使能断点。

gdb-6.0\gdb\breakpoint.c:bpstat_what (bpstat bs)#define shl BPSTAT_WHAT_CHECK_SHLIBS 这个类型将会决定调试器对新接收事件的处理方式,这里就是

BPSTAT_WHAT_CHECK_SHLIBS static const enum

bpstat_what_main_action

table[(int) class_last][(int) BPSTAT_WHAT_LAST] = {

/*shlib */

{shl, shl, shl, shl, shl, shl, shl, shl, ts, shl, shlr}, case bp_shlib_event:

bs_class = shlib_event;

……

current_action = table[(int) bs_class][(int)

current_action];调试器对之上类型判断的调用位置

gdb-6.0\gdb\infrun.c:handle_inferior_event (struct execution_control_state *ecs)

what = bpstat_what (stop_bpstat);

switch (what.main_action)

{

case BPSTAT_WHAT_CHECK_SHLIBS:

case

BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOO K:

#ifdef SOLIB_ADD

{

……

SOLIB_ADD (NULL, 0, NULL, auto_solib_add); ……

} }

其中的

SOLIB_ADD--->>>solib_add--->>>update_ solib_list--->>>TARGET_SO_CURRENT_SOS---> ;>>svr4_current_sos

其中的svr4_current_sos函数将会遍历被调试任务中所有的so文件链表,对于被调试任务来说,它的所有so文件通过link_map的指针域连接在一起,下面是glibc中结构

glibc-2.7\include\link.h

struct link_map

{

/* These first few members are part of the protocol with the debugger.

This is the same format used in SVR4. */

ElfW(Addr) l_addr; /* Base address shared object is loaded at. */

char *l_name; /* Absolute file name object was found in. */

ElfW(Dyn) *l_ld; /* Dynamic section of the shared object. */

struct link_map *l_next, *l_prev; /* Chain of loaded objects. */

……

}

所以当gdb知道了动态链接库对应的link_map实例,它就可以通过该链表遍历被调试任务的所有link_map,由于每个link_map都和一个加载的so文件对应,所以可以知道被调试任务所有已经加载的动态库。

④、读取符号后使能断点

前面的步骤之后,gdb就可以得到了so文件加载的事件消息,然后读入被调试任务中所有的so文件的符号信息。前面的行为也说明了要忽略此次断点,继续运行。

handle_inferior_event--->>>keep_going--->>ins ert_breakpoints函数中完成对所有断点的使能,如果新加载的so文件中包含了之前的一个pending断点,对于

insert_breakpoints函数的调用将会使这个断点生效。

3、说明

这里只是描述了一个大致的思路,里面的有些细节可能比较模糊,而且不一定完全准确,但是大致的流程和思路是没有问题的,这一点我还是能够保证的。

三、进程/线程/系统调用相关事件处理

新的内核中添加了一些自动调试子进程、枚举系统调用之类的功能,这些功能对动态链接库要求不多,转而依赖内核实现。可以通过

set follow-fork-mode parent/child 来设置调试跟踪模式,这样对子进程的调试比较方便,因为Unix中进程创建时

fork+exec模式,所以fork之后的代码如果出问题,当前的调试是不好使的。

还有就是一些catch命令来跟踪系统调用等

(gdb) show version

GNU gdb (GDB) Fedora (7.0-3.fc12)

Copyright (C) 2009 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later

<https://www.360docs.net/doc/dc13548179.html,/licenses/gpl.html>

This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying"

and "show warranty" for details.

This GDB was configured as "i686-redhat-linux-gnu".

For bug reporting instructions, please see:

<https://www.360docs.net/doc/dc13548179.html,/software/gdb/bugs/>.

(gdb) help catch

Set catchpoints to catch events.List of catch subcommands:catch assert -- Catch failed Ada assertions

catch catch -- Catch an exception

catch exception -- Catch Ada exceptions

catch exec -- Catch calls to exec

catch fork -- Catch calls to fork

catch syscall -- Catch system calls by their names and/or numbers 这些功能不清楚是什么版本开始支持的,上面显示我用的是gdb7.0

catch throw -- Catch an exception

catch vfork -- Catch calls to vfork这些很多都需要内核支持,所以看一下内核实现。

1、进程、线程创建及删除

这些主要是通过ptrace的PTRACE_SETOPTIONS选项来实现的(该文件中还有ptrace_getsiginfo借口,说明子进程信号信息也是容易被父进程获得和修改的)

linux-2.6.21\kernel\ptrace.c

static int ptrace_setoptions(struct task_struct *child, long data) {

child->ptrace &= ~PT_TRACE_MASK; if (data & PTRACE_O_TRACESYSGOOD)

child->ptrace |= PT_TRACESYSGOOD; if (data & PTRACE_O_TRACEFORK)

child->ptrace |= PT_TRACE_FORK; if (data & PTRACE_O_TRACEVFORK)

child->ptrace |= PT_TRACE_VFORK; if (data

& PTRACE_O_TRACECLONE)

child->ptrace |= PT_TRACE_CLONE; if (data & PTRACE_O_TRACEEXEC)

child->ptrace |= PT_TRACE_EXEC; if (data & PTRACE_O_TRACEVFORKDONE)

child->ptrace |= PT_TRACE_VFORK_DONE;

if (data & PTRACE_O_TRACEEXIT)

child->ptrace |= PT_TRACE_EXIT; return (data & ~PTRACE_O_MASK) ? -EINV AL : 0;

}

在进程fork时:long do_fork(unsigned long clone_flags,……) if (unlikely(current->ptrace)) {

trace = fork_traceflag (clone_flags);

if (trace)

clone_flags |= CLONE_PTRACE;

}

……

if (unlikely (trace)) {

current->ptrace_message = nr;

ptrace_notify ((trace << 8) | SIGTRAP);

}

由于这篇文章粘贴的代码已经很多了,所以就不再粘贴

fork_traceflag和ptrace_notify的实现了,但是大家通过这个名字应该就可以知道这些信息是发送给了父进程。大家注意一下ptrace_notify中返回值,低8bits为SIGTRAP信号,而高8bits为trace类型,这些类型可以为PT_TRACE_VFORK、PT_TRACE_CLONE、PTRACE_EVENT_FORK类型,大家可以在gdb中搜索一下对这些事件的处理位置。其它处理,例如PT_TRACE_EXEC、PT_TRACE_EXIT实现和该实现类似,这里省略,大家搜索一下内核这些关键字即可。

2、系统调用枚举

这个主要是在汇编代码中设置跟踪点:

linux-2.6.21\arch\i386\kernel\entry.S

syscall_trace_entry:

movl $-ENOSYS,PT_EAX(%esp)

movl %esp, %eax

xorl %edx,%edx

call do_syscall_trace 系统调用前调用do_syscall_trace,其中edx参数清零,表示是进入系统调用。

cmpl $0, %eax

jne resume_userspace # ret != 0 -> running under PTRACE_SYSEMU,

# so must skip actual syscall

movl PT_ORIG_EAX(%esp), %eax

cmpl $(nr_syscalls), %eax

jnae syscall_call

jmp syscall_exit

END(syscall_trace_entry) # perform syscall exit tracing ALIGN

syscall_exit_work:

testb

$(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SI NGLESTEP), %cl

jz work_pending

TRACE_IRQS_ON

ENABLE_INTERRUPTS(CLBR_ANY) # could let do_syscall_trace() call

# schedule() instead

movl %esp, %eax

movl $1, %edx

call do_syscall_trace 系统调用退出执行

do_syscall_trace,参数为1,表示是推出系统调用。

jmp resume_userspace

END(syscall_exit_work)通知调试器代码

linux-2.6.21\arch\i386\kernel\ptrace.c

__attribute__((regparm(3)))

int do_syscall_trace(struct pt_regs *regs, int entryexit)

{

int is_sysemu = test_thread_flag(TIF_SYSCALL_EMU);

/*

* With TIF_SYSCALL_EMU set we want to ignore

TIF_SINGLESTEP for syscall

* interception

*/

int is_singlestep = !is_sysemu &&

test_thread_flag(TIF_SINGLESTEP);

int ret = 0;

……

/* the 0x80 provides a way for the tracing parent to distinguish

between a syscall stop and SIGTRAP delivery */ /* Note that the debugger could change the result of

test_thread_flag!*/

ptrace_notify(SIGTRAP | ((current->ptrace &

PT_TRACESYSGOOD) ? 0x80:0));

}

可以看到,这里并没有区分是系统调用进入还是退出,我想可能是需要调试器自己记录是什么,并且进入和退出不能同

时跟踪,PTRACE_CONT之后两者都失效。

linux-2.6.21\arch\i386\kernel\ptrace.c

long arch_ptrace(struct task_struct *child, long request, long addr, long data)

case PTRACE_SYSEMU: /* continue and stop at next syscall, which will not be executed */

case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */

case PTRACE_CONT: /* restart after signal. */ ret = -EIO;

if (!valid_signal(data))

break;

if (request == PTRACE_SYSEMU) {

set_tsk_thread_flag(child,

TIF_SYSCALL_EMU);

clear_tsk_thread_flag(child,

TIF_SYSCALL_TRACE);

} else if (request == PTRACE_SYSCALL) {

set_tsk_thread_flag(child,

TIF_SYSCALL_TRACE);

clear_tsk_thread_flag(child,

TIF_SYSCALL_EMU);

} else {

clear_tsk_thread_flag(child,

TIF_SYSCALL_EMU);

clear_tsk_thread_flag(child,

TIF_SYSCALL_TRACE);

}

这么看来,watch还是比较耗费CPU的,如果系统调用比较多的话。

四、和NPTL库比较

1、线程创建、删除

glibc-2.7\nptl\events.c

void

__nptl_create_event (void)

{

}

hidden_def (__nptl_create_event)void

__nptl_death_event (void)

{

}

hidden_def (__nptl_death_event)

它们被C库创建和删除线程时调用,调试器同样可以设置此处为断点。

2、glibc-2.7\nptl\allocatestack.c/* List of queued stack frames. */

static LIST_HEAD (stack_cache);/* List of the stacks in use. */

static LIST_HEAD (stack_used);

struct pthread

{

……

/* This descriptor's link on the `stack_used' or `__stack_user' list. */

list_t list;

……

}

所有的线程通过list连接在一起,所以调试器可以动态获得被调试任务的所有线程列表。

进程与线程的区别 进程的通信方式 线程的通信方式

进程与线程的区别进程的通信方式线 程的通信方式 进程与线程的区别进程的通信方式线程的通信方式2011-03-15 01:04 进程与线程的区别: 通俗的解释 一个系统运行着很多进程,可以比喻为一条马路上有很多马车 不同的进程可以理解为不同的马车 而同一辆马车可以有很多匹马来拉--这些马就是线程 假设道路的宽度恰好可以通过一辆马车 道路可以认为是临界资源 那么马车成为分配资源的最小单位(进程) 而同一个马车被很多匹马驱动(线程)--即最小的运行单位 每辆马车马匹数=1 所以马匹数=1的时候进程和线程没有严格界限,只存在一个概念上的区分度 马匹数1的时候才可以严格区分进程和线程 专业的解释: 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

线程的划分尺度小于进程,使得多线程程序的并发性高。另外,进程在执 行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序 的运行效率。 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行 的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在 应用程序中,由应用程序提供多个线程执行控制。 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可 以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程 的调度和管理以及资源分配。这就是进程和线程的重要区别。 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的 能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中 必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的 其他的线程共享进程所拥有的全部资源. 一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以 并发执行 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有 独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响, 而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线 程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程 的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。如果有兴趣深入的话,我建议你们看看《现代操作系统》或者 《操作系统的设计与实现》。对就个问题说得比较清楚。 +++ 进程概念

静态链接库lib和动态链接库dll区别

1.什么是静态连接库,什么是动态链接库 静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的EXE 文件中了。但是若使用DLL,该DLL 不必被包含在最终EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与EXE 独立的DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。静态链接库与静态链接库调用规则总体比较如下。 对于静态链接库(比较简单): 首先,静态链接库的使用需要库的开发者提供生成库的.h头文件和.lib文件。 生成库的.h头文件中的声明格式如下: extern "C" 函数返回类型函数名(参数表); 在调用程序的.cpp源代码文件中如下: #include "..\lib.h" #pragma comment(lib,"..\\debug\\libTest.lib") //指定与静态库一起链接 第二,因为静态链接库是将全部指令都包含入调用程序生成的EXE文件中。因此如果用的是静态链接库,那么也就不存在“导出某个函数提供给用户使用”的情况,要想用就得全要!要不就都别要!:) 对于动态链接库: 动态链接库的使用,根据不同的调用方法,需要提供不同的资源: 1. 静态加载------程序静态编译的时候就静态导入dll,这样的话就需要提供给库 使用者(C客户)如下文件:*.lib文件和.dll文件和*.h。其有2个坏处: 1 程序一开始运行就需要载入整个dll,无法载入程序就不能开始运行; 2 由于载入的是整个dll,需要耗费资源较多 其调用方法如下: #include "..\lib.h" #pragma comment(lib,"..\\debug\\libTest.lib") 但是这种方式的话可以调用Class method. 2.动态加载-----那么只需要提供dll文件。 因此调用程序若想调用DLL中的某个函数就要以某种形式或方式指明它到底想调用哪一个函数。但是无法调用Class method了。 如果要调用Dll中的function,需要经历3个步骤: Handle h=LoadLibrary(dllName) --> GetProcAddress(h,functionName) 返回函数指针,通过函指针调用其function-->FreeLibrary(h) 例如:Another.dll有一个int Add(int x,int y)函数。则完整的调用过程如下:

实验一 进程与线程

实验:进程与线程 一、实验目的 通过函数调用掌握进程之间的通信。 体会线程的存在,了解线程与进程的关系。 二、实验环境 PC+Win7操作系统 三、实验方法和实验步骤 1.准备工作 打开VC++6.0环境。 2.在程序编辑区内输入程序,实现两个数互换。 3. 在VC环境下建立一个控制台应用程序P1。系统启动一个进程(因为支持线程,OS会在进程中主动创建一个主线程)来运行该程序。输出该进程的ID号、以及该进程下面主线程的ID号。多运行几次,观察结果。 四、实验结果

补充:在VC环境下建立一个控制台应用程序P1。系统启动一个进程(因为支持线程,OS会在进程中主动创建一个主线程)来运行该程序。 在进程中,我们自己再创建一个子线程(子线程1),该子线程做的事情很简单,就是让它不停地输出如下信息: 子线程1正在运行第1次,其进程的ID号=~, 子线程1的ID号=~ 子线程1正在运行第2次,其进程的ID号=~, 子线程1的ID号=~ 。。。。。。 。。。。。。 子线程1正在运行第20次,其进程的ID号=~, 子线程1的ID号=~ 只要启动了一个子线程,实际上系统中是主线程和子线程1在并发执行。 主线程的功能是输出这样形式的内容: 主线程正在运行第1次,其进程的ID号=~,主线程的ID号=~ 主线程正在运行第2次,其进程ID号=~, 主线程的ID号=~ 。。。。。。 。。。。。。 主线程正在运行第20次,其进程ID号=~, 主线程的ID号=~ 多运行几次,观察主线程和子线程并发调动的次序。每次调度都一样吗?为什么?进程ID、主线程ID和子线程ID每次都一样吗? 体会操作系统中并发的异步性。 程序代码如下: #include #include DWORD WINAPI Thread1(LPVOID lpparameter){ int i; for(i=1;i<=20;i++){ printf("子线程1在运行中,它正在运行第%d times,所属进程的ID号=%ld, 本线程的ID号=%ld\n",i,GetCurrentProcessId(),GetCurrentThreadId());} return 0;} int main(){ int j; printf("一个进程在运行中\n"); printf("主线程在运行中\n"); HANDLE hThread1=CreateThread(NULL,0,Thread1,NULL,0,NULL); for(j=1;j<=20;j++){ printf("主线程正在运行第%d次;进程的ID号=%ld,线程ID号=%ld\n", j,GetCurrentProcessId(),GetCurrentThreadId()); Sleep(500); } return 0; } 多次运行的结果显示,每次调度是不一样的,因为操作系统中程序并发运行时的异步性原则,进程ID、主线程ID和子线程ID每次也都是不一样的。

lib和dll文件的区别和联系

(1)lib是编译时需要的,dll是运行时需要的。 如果要完成源代码的编译,有lib就够了。 如果也使动态连接的程序运行起来,有dll就够了。 在开发和调试阶段,当然最好都有。 (2)一般的动态库程序有lib文件和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。 (3)在动态库的情况下,有两个文件,一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。 一、开发和使用dll需注意三种文件 1、 dll头文件 它是指dll中说明输出的类或符号原型或数据结构的.h文件。当其它应用程序调用dll时,需要将该文件包含入应用程序的源文件中。 2、 dll的引入库文件 它是dll在编译、链接成功后生成的文件。主要作用是当其它应用程序调用dll时,需要将该文件引入应用程序。否则,dll无法引入。 3、 dll文件(.dll) 它是应用程序调用dll运行时,真正的可执行文件。dll应用在编译、链接成功后,.dll文件即存在。开发成功后的应用程序在发布时,只需要有.exe文件和.dll文件,不必有.lib文件和dll头文件。 动态链接库 (DLL) 是作为共享函数库的可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。 动态链接与静态链接的不同之处在于:动态链接允许可执行模块(.dll 文件或 .exe 文件)仅包含在运行时定位 DLL 函数的可执行代码所需的信息。在静态链接中,链接器从静态链接库获取所有被引用的函数,并将库同代码一起放到可执行文件中。 使用动态链接代替静态链接有若干优点。DLL 节省内存,减少交换操作,节省磁盘空间,更易于升级,提供售后支持,提供扩展 MFC 库类的机制,支持多语言程序,并使国际版本的创建轻松完成。 lib与dll文件最大区别在调用方面 dll可以静态陷入 lib与DLL 从这一章起,我讲述的内容将特定于windows平台。其实这篇文章也可看作是我在windows下的开发经验总结,因为以后我决定转unix了。 前面有一章说编译与链接的,说得很简略,其实应该放到这一章一块儿来说的。许多单讲

VC++ MFC DLL动态链接库编写详解

VC++ MFC DLL动态链接库编写详解(2008-07-10 17:38:40) 标签:it分类:com技术然能用DLL实现的功能都可以用COM来替代,但DLL的优点确实不少,它更容易创建。本文将讨论如何利用VC MFC来创建不同类型的DLL,以及如何使用他们。 一、DLL的不同类型 使用VC++可以生成两种类型的DLL:MFC扩展DLL和常规DLL。常规DLL有可以分为动态连接和静态连接。Visual C++还可以生成WIN32 DLL,但不是这里讨论的主要对象。 1、MFC扩展DLL 每个DLL都有某种类型的接口:变量、指针、函数、客户程序访问的类。它们的作用是让客户程序使用DLL,MFC扩展DLL可以有C++的接口。也就是它可以导出C++类给客户端。导出的函数可以使用C++/MFC数据类型做参数或返回值,导出一个类时客户端能创建类对象或者派生这个类。同时,在DLL中也可以使用DLL和MFC。 Visual C++使用的MFC类库也是保存在一个DLL中,MFC扩展DLL动态连接到MFC代码库的DLL,客户程序也必须要动态连接到MFC代码库的DLL。(这里谈到的两个DLL,一个是我们自己编写的DLL,一个装MFC类库的DLL)现在MFC代码库的DLL也存在多个版本,客户程序和扩展DLL都必须使用相同版本的MFC代码DLL。所以为了让MFC扩展DLL能很好的工作,扩展DLL和客户程序都必须动态连接到MFC代码库DLL。而这个DLL必须在客户程序运行的计算机上。 2、常规DLL 使用MFC扩展DLL的一个问题就是DLL仅能和MFC客户程序一起工作,如果需要一个使用更广泛的DLL,最好采用常规DLL,因为它不受MFC的某些限制。常规DLL也有缺点:它不能和客户程序发送指针或MFC派生类和对象的引用。一句话就是常规DLL和客户程序的接口不能使用MFC,但在DLL和客户程序的内部还是可以使用MFC。 当在常规DLL的内部使用MFC代码库的DLL时,可以是动态连接/静态连接。如果是动态连接,也就是常规DLL需要的MFC代码没有构建到DLL中,这种情况有点和扩展DLL类似,在DLL运行的计算机上必须要MFC代码库的DLL。如果是静态连接,常规DLL里面已经包含了需要的MFC代码,这样DLL的体积将比较大,但它可以在没有MFC代码库DLL的计算机上正常运行。 二、建立DLL 利用Visual C++提供的向导功能可以很容易建立一个不完成任何实质任务的DLL,这里就不多讲了,主要的任务是如何给DLL添加功能,以及在客户程序中利用这个DLL 1、导出类 用向导建立好框架后,就可以添加需要导出类的.cpp .h文件到DLL中来,或者用向导创建C++ Herder File/C++ Source File。为了能导出这个类,在类声明的时候要加“_declspe c(dllexport)”,如:

查看程序的进程和线程实验报告

查看程序的进程和线程实验报告 篇一:程序实验2:11-多线程编程---实验报告 程序实验二:11-多线程编程实验 专业班级实验日期 5.21 姓名学号实验一(p284:11-thread.c) 1、软件功能描述 创建3个线程,让3个线程重用同一个执行函数,每个线程都有5次循环,可以看成5个小任务,每次循环之间会有随即等待时间(1-10s)意义在于模拟每个任务到达的时间是随机的没有任何的特定规律。 2、程序流程设计 3.部分程序代码注释(关键函数或代码) #include #include #include #define T_NUMBER 3 #define P_NUMBER 5 #define TIME 10.0

void *thrd_func(void *arg ) { (本文来自:https://www.360docs.net/doc/dc13548179.html, 小草范文网:查看程序的进程和线程实验报告) int thrd_num=(int)arg; int delay_time =0; int count =0; printf("Thread %d is staraing\n",thrd_num); for(count=0;count { delay_time =(int)(rand()*TIME/(RAND_MAX))+1; sleep(delay_time); printf("\tTH%d:job%d delay =%d\n",thrd_num,count,delay_time); } printf("%d finished\n",thrd_num); pthread_exit(NULL); } int main()

VC++动态链接库(DLL)编程深入浅出

VC++动态链接库(DLL)编程深入浅出 作者:宋宝华 联系作者:e-mail:21cnbao@https://www.360docs.net/doc/dc13548179.html, 出处:Pconline 由于本文篇幅较长,内容较多,势必需要先对阅读本文的有关事项进行说明,下面以问答形式给出。 问:本文主要讲解什么内容? 答:本文详细介绍了DLL编程的方方面面,努力学完本文应可以对DLL有较全面的掌握,并能编写大多数DLL程序。 问:如何看本文? 答:本文每一个主题的讲解都附带了源代码例程,可以随文下载(每个工程都经WINRAR压缩)。所有这些例程都由笔者编写并在VC++6.0中调试通过。 当然看懂本文不是读者的最终目的,读者应亲自动手实践才能真正掌握DLL的奥妙。 问:学习本文需要什么样的基础知识? 答:如果你掌握了C,并大致掌握了C++,了解一点MFC的知识,就可以轻松地看懂本文。 目录 1、概论 2、静态链接库 3、库的调试与查看 4、非MFC DLL 5、MFC规则DLL 6、MFC扩展DLL的创建

1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。在仓库的发展史上经历了“无库-静态链接库-动态链接库”的时代。 静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。但是若使用DLL,该DLL不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。 对动态链接库,我们还需建立如下概念: (1)DLL 的编制与具体的编程语言及编译器无关 只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL都可以相互调用。譬如Windows提供的系统DLL(其中包括了Windows的API),在任何开发环境中都能被调用,不在乎其是Visual Basic、Visual C++还是Delphi。 (2)动态链接库随处可见 我们在Windows目录下的system32文件夹中会看到kernel32.dll、user32.dll和gdi32.dll,windows的大多数API都包含在这些DLL中。kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。 一般的程序员都用过类似MessageBox的函数,其实它就包含在user32.dll这个动态链接库中。由此可见DLL对我们来说其实并不陌生。 (3)VC动态链接库的分类 Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。 非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;MFC规则DLL 包含一个继承自CWinApp的类,但其无消息循环;MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。

操作系统--进程和线程实验报告

一.进程的创建 1.编辑源程序。 2. 编辑结果如下。 3.编译和运行程序。 4.运行解释结果 在语句p1=fork()之前,只有一个进程在执行这段代码,但在这条语句之后,就变成两个进程在执行了.这两个进程的几乎完全相同,将要执行的下一条语句都是if(p1==0). 而fork函数有三种返回值。(1)在父进程中,fork返回新创建子进程的进程ID; (2)在子进程中,fork返回0; (3)如果出现错误,fork返回一个负值; 所以,子进程中p1==0,输出I am child。父进程p1>0,输出I am parent。

1.编辑源程序。 2.编辑结果如下。 3.编译和运行程序。 4. 运行解释结果 在语句p1=fork()之前,只有父进程执行,putchar(‘x’)语句将x放入父进程的缓冲区。当成功创建子进程后,子进程复制父进程的缓冲区。接着子进程运行输出xby,父进程输出xay。

1.编辑源程序。 2.编辑结果如下。 3.编译和运行程序。 4. 运行解释结果 在语句p1=fork()之前,只有父进程执行,putchar(‘x’)语句将x放入父进程的缓冲区。当成功创建子进程后,子进程复制父进程的缓冲区。接着子进程输出b后,执行exit(0)系统调用终止执行。父进程输出a 后继续输出y。所以父进程输出xay而子进程输出xb。

1.编辑源程序。 2.编辑结果如下。 3.编译和运行程序。 4. 运行解释结果 语句while(p1=fork()==-1)创建了子进程和父进程。父进程执行到wait()时,等待子进程的终止信号,当子进程执行完exit(0)后,父进程才继续执行。实现了父进程等待子进程。

操作系统_第二章_进程和线程复习题

第二章练习题 一、单项选择题 1.某进程在运行过程中需要等待从磁盘上读入数据,此时该进程的状态将(C )。 A. 从就绪变为运行;B.从运行变为就绪; C.从运行变为阻塞;D.从阻塞变为就绪2.进程控制块是描述进程状态和特性的数据结构,一个进程(D )。 A.可以有多个进程控制块; B.可以和其他进程共用一个进程控制块; C.可以没有进程控制块; D.只能有惟一的进程控制块。 3.临界区是指并发进程中访问共享变量的(D)段。 A、管理信息 B、信息存储 C、数据 D、程序 4. 当__ B__时,进程从执行状态转变为就绪状态。 A. 进程被调度程序选中 B. 时间片到 C. 等待某一事件 D. 等待的事件发生 5. 信箱通信是一种(B )通信方式。 A. 直接通信 B. 高级通信 C. 低级通信 D. 信号量 6. 原语是(B)。 A、一条机器指令 B、若干条机器指令组成

C、一条特定指令 D、中途能打断的指令 7. 进程和程序的一个本质区别是(A)。 A.前者为动态的,后者为静态的; B.前者存储在内存,后者存储在外存; C.前者在一个文件中,后者在多个文件中; D.前者分时使用CPU,后者独占CPU。 8. 任何两个并发进程之间存在着(D)的关系。 A.各自完全独立 B.拥有共享变量 C.必须互斥 D.可能相互制约 9. 进程从运行态变为等待态可能由于(B )。 A.执行了V操作B.执行了P操作 C.时间片用完D.有高优先级进程就绪 10. 用PV操作管理互斥使用的资源时,信号量的初值应定义为(B)。 A.任意整数B.1 C.0 D.-1 11. 现有n个具有相关临界区的并发进程,如果某进程调用P操作后变为等待状态,则调用P操作时信号量的值必定为(A)。 A.≤0B.1 C.n-1 D.n 12. 用PV操作管理临界区时把信号量的初值定义为1,现已有一个进程在临界区,但有n个进程在等待进入临界区,这时信号量的值为(C)。 A.-1 B.1 C.-n D.n 13. 用V操作唤醒一个等待进程时,被唤醒进程的状态应变成(B)状态。

windowsapi编程之动态链接库(dll)

Windows API编程之动态链接库(DLL) 本文总结Windows API编程之动态链接库(DLL),内容涉及DLL的制作、发布、使用及相关技术以供大家参考。 作者:tyc611,2007-05-26 链接库分为静态链接库和动态链接库,而动态链接库在使用时,又进一步分为装载时链接和运行时链接。装载时链接是指该动态链接库是在程序装入时进行加载链接的,而运行时链接是指该动态链接库是在程序运行时执行LoadLibrary(或LoadLibraryEx,下同)函数动态加载的。因此,由于动态链接库有这两种链接方式,所以在编写使用DLL的程序时,就有了两种可选方案。 可能有人会问“为什么需要装载时链接?直接静态链接 不就行了吗?”,这是模块化程序设计的需要。试想,如果你开发一个很大的程序,并且经常需要更新。如果你选择静态链接,那么每次更新就必须更新整个exe文件,而如果你把需要经常更新的模块做成dll,那么只需要更新这个文件即可,每次程序运行时加载这个更新的文件即可。 在进入编写DLL程序之前,先介绍一些相关知识。 VC支持三种DLL,它们分别是Non-MFC DLL、MFC Regular DLL、MFC Extension DLL。由于本文只讲解API 编程,所以这里只对第一种DLL进行介绍,后面两种DLL 将在另外的文章中介绍。

动态链接库的标准后缀是.DLL,当然也可以使用其它任意后缀名。但使用.DLL后缀的好处是:一是,很直观表明该文件的性质;二是,只有后缀为.DLL的动态链接库才能被Windows自动地加载,而其它后缀的动态链接库只能通过LoadLibrary显示式加载。 动态链接库的用途:一是作为动态函数库使用,另一个常用的方式是作为动态资源库。当然,没有绝对的划分,比如你的动态函数库时也可能有资源,但动态资源库一般不会有函数。 另两个重要的、需要区分的概念是:对象库(Object Library)和导入库(Import Library)。对象库是指普通的库文件,比如C运行时库libc.lib;而导入库是一种比较特殊的对象库文件,与一个动态链接库相对应。它们都有后缀.lib,并且都仅在程序编译链接时使用,被链接器用来解析函数调用。然而,导入库不包含代码,它只为链接器提供动态链接库的信息,以便于链接器对动态链接库中的对象作恰当地链接。 动态链接库的查找规则。如果在使用时没有指定动态链接库的路径,则Windows系统按如下顺序搜索该动态链接库:使用该动态链接库的.exe文件所在目录、当前目录、Windows系统目录、Windows目录、环境变量%PATH%中的路径下的目录。

动态链接库的使用方法

动态链接库的使用方法 动态链接库是一堆变量,函数和类的集合体,供其它函数调用。 为什么要使用动态链接库,原因很多,其中三条1.可跨平台调用2.方便二次开发3.方便项目管理。 动态链接库的使用有两个方面,一是把原来的源代码做成动态链接库文件(即生成DLL和lib 格式的文件),二是在其它源代码中使用动态链接库。 把源代码做成动态链接库文件 可以使用vc6.0及其以上的版本来做,直接建一个动态链接库工程,这个工程和其它的工程类似,有头文件和源文件,不同之处是在为了让DLL导出函数,需在每一个要导出的函数前添加标识符_declspec(dllexport)或declspec(dllimport)。 在头文件中,申明要导出的函数,类,以及一些全局变量。在源文件中,定义或实现头文件中要导出的函数,类以及变量。 做单独的头文件,该头文件可以同时用于动态链接库和使用动态链接库的程序。 为了方便的添加标识符,我们一般在头文件中使用宏定义,示例如下: #ifdef DLL1_API #else #define DLL1_API _declspec(dllimport) #endif 这样在后面的使用中,可以用DLL1_API来代替_declspec(dllimport),如下: DLL1_API int add (int a,int b); DLL1_API int subtract(int a,int b); class DLL1_API Point { public: void output(int x,int y); }; 在动态链接库的源文件中包含动态链接库头文件和一条宏定义 在源文件中,要重新定义标识符,使用_declspec(dllexport),所以在源文件中也要添加一条宏定义#define DLL1_API _declspec(dllexport),之后再包含头文件#include "Dll1.h" 二.在目标程序中调用动态链接库 在使用动态链接库的程序中隐式的调用动态链接库: 1.把动态链接库的头文件拷贝到目标程序目录下 2.在使用动态链接库的源文件中包含该头文件 3.拷贝动态链接库的两个文件到目标程序目录下 4.在vc6.0的工程/设置/连接中的对象/库模块中输入动态链接库文件名,如DLL1.lib 三.其它 1.关于DLL中全局变量的定义 在头文件中声明,在源文件中定义,如: 在头文件中extern _declspec(dllimport) int num_cai1; 在源文件中则是int num_cai1=200;

Linux下查看进程和线程

在Linux中查看线程数的三种方法 1、top -H 手册中说:-H : Threads toggle 加上这个选项启动top,top一行显示一个线程。否则,它一行显示一个进程。 2、ps xH 手册中说:H Show threads as if they were processes 这样可以查看所有存在的线程。 3、ps -mp 手册中说:m Show threads after processes 这样可以查看一个进程起的线程数。 查看进程 1. top 命令 top命令查看系统的资源状况 load average表示在过去的一段时间内有多少个进程企图独占CPU zombie 进程:不是异常情况。一个进程从创建到结束在最后那一段时间遍是僵尸。留在内存中等待父进程取的东西便是僵尸。任何程序都有僵尸状态,它占用一点内存资源,仅仅是表象而已不必害怕。如果程序有问题有机会遇见,解决大批量僵尸简单有效的办法是重起。kill是无任何效果的stop模式:与sleep进程应区别,sleep会主动放弃cpu,而stop 是被动放弃cpu ,例单步跟踪,stop(暂停)的进程是无法自己回到运行状态的。 cpu states: nice:让出百分比irq:中断处理占用 idle:空间占用百分比iowait:输入输出等待(如果它很大说明外存有瓶颈,需要升级硬盘(SCSI)) Mem:内存情况 设计思想:把资源省下来不用便是浪费,如添加内存后free值会不变,buff值会增大。判断物理内存够不够,看交换分区的使用状态。 交互命令: [Space]立即刷新显示 [h]显示帮助屏幕

C#调用动态链接库

C#程序实现动态调用DLL的研究 摘要:在《csdn开发高手》2004年第03期中的《化功大法——将DLL嵌入EXE》一文,介绍了如何把一个动态链接库作为一个资源嵌入到可执行文件,在可执行文件运行时,自动从资源中释放出来,通过静态加载延迟实现DLL函数的动态加载,程序退出后实现临时文件的自动删除,从而为解决“DLL Hell”提供了一种解决方案。这是一个很好的设计思想,而且该作者也用C++实现了,在Internet上也有相似的VB程序,但在某一技术论坛上提起这种设计方法时,有网友提出:“这种方法好是好,但就是启动速度太慢”。这是因为程序启动时实现DLL释放,然后再加载释放出来的DLL,这个过程会耗费一定的时间。鉴于此问题,经过思索,提出另一个设计方案:DLL作为资源文件嵌入程序,但不需进行DLL释放及其重新加载。本文就是对该设计方案的原理分析及使用C#编程来实现该设计方案。 关键词:动态调用DLL,嵌入DLL,C# 正文: 一、DLL与应用程序 动态链接库(也称为DLL,即为“Dynamic Link Library”的缩写)是Microsoft Windows最重要的组成要素之一,打开Windows系统文件夹,你会发现文件夹中有很多DLL文件,Windows就是将一些主要的系统功能以DLL模块的形式实现。 动态链接库是不能直接执行的,也不能接收消息,它只是一个独立的文件,其中包含能被程序或其它DLL调用来完成一定操作的函数(方法。注:C#中一般称为“方法”),但这些函数不是执行程序本身的一部分,而是根据进程的需要按需载入,此时才能发挥作用。 DLL只有在应用程序需要时才被系统加载到进程的虚拟空间中,成为调用进程的一部分,此时该DLL也只能被该进程的线程访问,它的句柄可以被调用进程所使用,而调用进程的句柄也可以被该DLL所使用。在内存中,一个DLL只有一个实例,且它的编制与具体的编程语言和编译器都没有关系,所以可以通过DLL来实现混合语言编程。DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。 下面列出了当程序使用DLL时提供的一些优点:[1] 1)使用较少的资源 当多个程序使用同一个函数库时,DLL可以减少在磁盘和物理内存中加载的代码的重复量。这不仅可以大大影响在前台运行的程序,而且可以大大影响其他在Windows操作系统上运行的程序。 2)推广模块式体系结构 DLL有助于促进模块式程序的开发。这可以帮助您开发要求提供多个语言版本的大型程序或要求具有模块式体系结构的程序。模块式程序的一个示例是具有多个可以在运行时动态加载的模块的计帐程序。 3)简化部署和安装 当DLL中的函数需要更新或修复时,部署和安装DLL不要求重新建立程序与该DLL的链接。此外,如果多个程序使用同一个DLL,那么多个程序都将从该更新或修复中获益。当您使用定期更新或修复的第三方DLL时,此问题可能会更频繁地出现。 二、DLL的调用

DLL的制作过程

一、前言 自从微软推出16位的Windows操作系统起,此后每种版本的Windows操作系统都非常依赖于动态链接库(DLL)中的函数和数据,实际上Windows操作系统中几乎所有的内容都由DLL以一种或另外一种形式代表着,例如显示的字体和图标存储在GDI DLL中、显示Windows桌面和处理用户的输入所需要的代码被存储在一个User DLL中、Windows编程所需要的大量的API函数也被包含在Kernel DLL中。 在Windows操作系统中使用DLL有很多优点,最主要的一点是多个应用程序、甚至是不同语言编写的应用程序可以共享一个DLL文件,真正实现了资源"共享",大大缩小了应用程序的执行代码,更加有效的利用了内存;使用DLL的另一个优点是DLL文件作为一个单独的程序模块,封装性、独立性好,在软件需要升级的时候,开发人员只需要修改相应的DLL文件就可以了,而且,当DLL中的函数改变后,只要不是参数的改变,程序代码并不需要重新编译。这在编程时十分有用,大大提高了软件开发和维护的效率。 既然DLL那么重要,所以搞清楚什么是DLL、如何在Windows操作系统中开发使用DLL是程序开发人员不得不解决的一个问题。本文针对这些问题,通过一个简单的例子,即在一个DLL中实现比较最大、最小整数这两个简单函数,全面地解析了在Visual C++编译环境下编程实现DLL的过程,文章中所用到的程序代码在Windows98系统、Visual C++6.0编译环境下通过。 二、DLL的概念 DLL是建立在客户/服务器通信的概念上,包含若干函数、类或资源的库文件,函数和数据被存储在一个DLL(服务器)上并由一个或多个客户导出而使用,这些客户可以是应用程序或者是其它的DLL。 DLL库不同于静态库,在静态库情况下,函数和数据被编译进一个二进制文件(通常扩展名为*.LIB),Visual C++的编译器在处理程序代码时将从静态库中恢复这些函数和数据并把他们和应用程序中的其他模块组合在一起生成可执行文件。这个过程称为"静态链接",此时因为应用程序所需的全部内容都是从库中复制了出来,所以静态库本身并不需要与可执行文件一起发行。 在动态库的情况下,有两个文件,一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL 和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。 微软的Visual C++支持三种DLL,它们分别是Non-MFC Dll(非MFC动态库)、Regular Dll(常规DLL)、Extension Dll(扩展DLL)。Non-MFC DLL指的是不用MFC的类库结构,直接用C语言写的DLL,其导出的函数是标准的C接口,能被非MFC或MFC编写的应用程序所调用。Regular DLL:和下述的Extension Dlls一样,是用MFC类库编写的,它的一个明显的特点是在源文件里有一个继承CWinApp的类(注意:此类DLL虽然从CWinApp派生,但没有消息循环),被导出的函数是C函数、C++类或者C++成员函数(注意不要把术语C++类与MFC的微软基础C++类相混淆),调用常规DLL的应用程序不必是MFC应用程序,只要是能调用类C函数的应用程序就可以,它们可以是在Visual C++、Dephi、Visual Basic、Borland C等编译环境下利用DLL开发应用程序。

C 调用外部dll

C# 调用外部dll 一、DLL与应用程序 动态链接库(也称为DLL,即为“Dynamic Link Library”的缩写)是Microsoft Windows 最重要的组成要素之一,打开Windows系统文件夹,你会发现文件夹中有很多DLL文件,Windows就是将一些主要的系统功能以DLL模块的形式实现。 动态链接库是不能直接执行的,也不能接收消息,它只是一个独立的文件,其中包含能被程序或其它DLL调用来完成一定操作的函数(方法。注:C#中一般称为“方法”),但这些函数不是执行程序本身的一部分,而是根据进程的需要按需载入,此时才能发挥作用。 DLL只有在应用程序需要时才被系统加载到进程的虚拟空间中,成为调用进程的一部分,此时该DLL也只能被该进程的线程访问,它的句柄可以被调用进程所使用,而调用进程的句柄也可以被该DLL所使用。在内存中,一个DLL只有一个实例,且它的编制与具体的编程语言和编译器都没有关系,所以可以通过DLL来实现混合语言编程。DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。 下面列出了当程序使用DLL 时提供的一些优点:[1] 1)使用较少的资源 当多个程序使用同一个函数库时,DLL 可以减少在磁盘和物理内存中加载的代码的重复量。 这不仅可以大大影响在前台运行的程序,而且可以大大影响其他在Windows 操作系统上运行的程序。 2)推广模块式体系结构 DLL 有助于促进模块式程序的开发。这可以帮助您开发要求提供多个语言版本的大型程序或要求具有模块式体系结构的程序。模块式程序的一个示例是具有多个可以在运行时动态加载的模块的计帐程序。 3)简化部署和安装 当DLL 中的函数需要更新或修复时,部署和安装DLL 不要求重新建立程序与该DLL 的链接。此外,如果多个程序使用同一个DLL,那么多个程序都将从该更新或修复中获益。当您使用定期更新或修复的第三方DLL 时,此问题可能会更频繁地出现。 二、DLL的调用 每种编程语言调用DLL的方法都不尽相同,在此只对用C#调用DLL的方法进行介绍。首先,您需要了解什么是托管,什么是非托管。一般可以认为:非托管代码主要是基于win 32平台开发的DLL,activeX的组件,托管代码是基于.net平台开发的。如果您想深入了解托管与非托管的关系与区别,及它们的运行机制,请您自行查找资料,本文件在此不作讨论。(一)调用DLL中的非托管函数一般方法 首先,应该在C#语言源程序中声明外部方法,其基本形式是: [DLLImport(“DLL文件”)] 修饰符extern 返回变量类型方法名称(参数列表) 其中: DLL文件:包含定义外部方法的库文件。 修饰符:访问修饰符,除了abstract以外在声明方法时可以使用的修饰符。 返回变量类型:在DLL文件中你需调用方法的返回变量类型。 方法名称:在DLL文件中你需调用方法的名称。

进程和线程的选择

鱼还是熊掌:浅谈多进程多线程的选择 关于多进程和多线程,教科书上最经典的一句话是“进程是资源分配的最小单位,线程是CPU调度的最小单位”,这句话应付考试基本上够了,但如果在工作中遇到类似的选择问题,那就没有这么简单了,选的不好,会让你深受其害。 经常在网络上看到有的XDJM问“多进程好还是多线程好?”、“Linux下用多进程还是多线程?”等等期望一劳永逸的问题,我只能说:没有最好,只有更好。根据实际情况来判断,哪个更加合适就是哪个好。 我们按照多个不同的维度,来看看多线程和多进程的对比(注:因为是感性的比较,因此都是相对的,不是说一个好得不得了,另外一个差的无法忍受) 看起来比较简单,优势对比上是“线程 3.5 v 2.5 进程”,我们只管选线程就是了? 呵呵,有这么简单我就不用在这里浪费口舌了,还是那句话,没有绝对的好与坏,只有哪个更加合适的问题。我们来看实际应用中究竟如何判断更加合适。 1)需要频繁创建销毁的优先用线程 原因请看上面的对比。 这种原则最常见的应用就是Web服务器了,来一个连接建立一个线程,断了就销毁线程,要是用进程,创建和销毁的代价是很难承受的

2)需要进行大量计算的优先使用线程 所谓大量计算,当然就是要耗费很多CPU,切换频繁了,这种情况下线程是最合适的。 这种原则最常见的是图像处理、算法处理。 3)强相关的处理用线程,弱相关的处理用进程 什么叫强相关、弱相关?理论上很难定义,给个简单的例子就明白了。 一般的Server需要完成如下任务:消息收发、消息处理。“消息收发”和“消息处理”就是弱相关的任务,而“消息处理”里面可能又分为“消息解码”、“业务处理”,这两个任务相对来说相关性就要强多了。因此“消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。 当然这种划分方式不是一成不变的,也可以根据实际情况进行调整。 4)可能要扩展到多机分布的用进程,多核分布的用线程 原因请看上面对比。 5)都满足需求的情况下,用你最熟悉、最拿手的方式 至于“数据共享、同步”、“编程、调试”、“可靠性”这几个维度的所谓的“复杂、简单”应该怎么取舍,我只能说:没有明确的选择方法。但我可以告诉你一个选择原则:如果多进程和多线程都能够满足要求,那么选择你最熟悉、最拿手的那个。 需要提醒的是:虽然我给了这么多的选择原则,但实际应用中基本上都是“进程+线程”的结合方式,千万不要真的陷入一种非此即彼的误区。

Psim中动态链接库DLL文件的生成和使用(实例)

在Psim中可以调用动态链接库DLL文件,使用C、C++等编程语言实现一些复杂控制算法。例如,使用VC++等C编译器建立.C文件,编译成DLL文件,使用Psim中的DLL功能块调用,实现数值输入、运算、输出到Psim中,完成控制算法。 本例在Psim中建立一个最简单的DLL功能块,演示DLL文件的生成和调用方法。 在Psim中新建如图的电路图,其中DLL模块在Elenments->Other->Function Blocks工具栏下。 双击DLL,在File name下输入你要生成的DLL文件名称。这里输入test3.dll。

在VC++6.0中新建工程,工程类型为"Win32Dynamic-Link Library",工程名称为要生成的DLL文件名称,这里为test3。 点击确定,选择“一个空的DLL工程”选项,确定,完成。 然后在工程Source中添加一个.c文件,在这里将写入你的执行代码。下面是一个.c模版:

#include __declspec(dllexport)void simuser(t,delt,in,out) //Note that all the variables must be defined as"double" double t,delt; double*in,*out; { //Place your code here............begin double x; x=in[0]; //Output out[0]=(x/10); //Place your code here............end } 这个.c文件完成将in0输入口上的数据除以10后在out0端口输出的功能。t,delt,in,out一定要有,t为系统仿真时间,delt为仿真步长,in 为输入口指针,out为输出口指针,都为double型。在Psim电路中,不用的输入口一定要接地,不用的输出口悬空即可。

相关文档
最新文档