linux终端关闭软件运行

linux终端关闭软件运行
linux终端关闭软件运行

Unix/Linux下一般想让某个程序在后台运行,很多都是使用&在程序结尾来让程序自动运行;但如果要想在退出终端后,程序依然还在后台运行,则要用nohup与&组合来实现。

nohup 命令

用途:不挂断地运行命令

语法:nohup Command [ Arg ... ] [& ]

描述:nohup 命令运行由Command参数和任何相关的Arg参数指定的命令,忽略所有挂断(SIGHUP)信号。在注销后使用nohup 命令运行后台中的程序:要运行后台中的nohup 命令,需要添加&到命令的尾部。

日志记录:

无论是否将nohup 命令的输出重定向到终端,输出都将附加到当前目录的nohup.out 文件中。如果当前目录的nohup.out文件不可写,输出重定向到$HOME/nohup.out文件中。如果没有文件能创建或打开以用于追加,那么Command 参数指定的命令不可调用。

使用时注意:

在当shell中提示了nohup成功后,还需要按终端上键盘任意键退回到shell输入命令窗口,然后通过在shell中输入exit来退出终端;如果在nohup执行成功后直接点关闭程序按钮关闭终端的话,这时候会断掉该命令所对应的session,导致nohup对应的进程被通知需要一起shutdown,起不到关掉终端后调用程序继续后台运行的作用。

例:

$nohup DW_BATCH.sh &

4 动态库移植方法

如果要编制在两个系统中都能使用的动态链接库,通常会先选择在Windows的VC++提供的调试环境中完成初始的开发,毕竟VC++提供的图形化编辑和调试界面比vi和gcc方便许多。完成测试之后,再进行动态库的程序移植。通常gcc默认的编译规则比VC++默认的编译规则严格,即使在VC++下面没有任何警告错误的程序在gcc调试中也会出现许多警告错误,可以在gcc中采用-w选项关闭警告错误。

下面给出程序移植需要遵循的规则以及经验。

(1)尽量不要改变原有动态库头文件的顺序。通常在C/C++语言中,头文件的顺序有相当的关系。另外虽然C/C++语言区分大小写,但在包含头文件时,Linux必须与头文件的大小写相同,因为ext2文件系统对文件名是大小写敏感,否则不能正确编译,而在Windows下面,头文件大小写可以正确编译。

(2)不同系统独有的头文件。在Windows系统中,通常会包括windows.h头文件,如果调用底层的通信函数,则会包含winsock..h头文件。因此在移植到Linux系统时,要注释掉这些Windows系统独有的头文件以及一些windows系统的常量定义说明,增加Linux都底层通信的支持的头文件等。

(3)数据类型。VC++具有许多独有的数据类型,如__int16,__int32,TRUE,SOCKET等,gcc编译器不支持它们。通常做法是需要将windows.h和basetypes.h中对这些数据进行定义的语句复制到一个头文件中,再在Linux中包含这个头文件。例如将套接字的类型为SOCKET改为int。

(4)关键字。VC++中具有许多标准C中所没有采用的关键字,如BOOL,BYTE,DWord,

__asm等,通常在为了移植方便,尽量不使用它们,如果实在无法避免可以采用#ifdef 和#endif为LINUX和WINDOWS编写两个版本。

(5)函数原型的修改。通常如果采用标准的C/C++语言编写的动态库,基本上不用再重新编写函数,但对于系统调用函数,由于两种系统的区别,需要改变函数的调用方式等,如在Linux编制的网络通信动态库中,用close()函数代替windows操作系统下的closesocket()函数来关闭套接字。另外在Linux下没有文件句柄,要打开文件可用open和fopen函数,(6)makefile的编写。在windows下面通常由VC++编译器来负责调试,但gcc需要自己动手编写makefile文件,也可以参照VC++生成的makefile文件。对于动态库移植,编译动态库时需要加入-shared选项。对于采用数学函数,如幂级数的程序,在调用动态库是,需要加入-lm。

(7)其它一些需要注意的地方

①程序设计结构分析,对于移植他人编写的动态库程序,程序结构分析是必不可少的步骤,通常在动态库程序中,不会包含界面等操作,所以相对容易一些。

②在Linux中,对文件或目录的权限分为拥有者、群组、其它。所以在存取文件时,要注意对文件是读还是写操作,如果是对文件进行写操作,要注意修改文件或目录的权限,否则无法对文件进行写。

③指针的使用,定义一个指针只给它分配四个字节的内存,如果要对指针所指向的变量赋值,必须用malloc函数为它分配内存或不把它定义为指针而定义为变量即可,这点在linux下面比windows编译严格。同样结构不能在函数中传值,如果要在函数中进行结构传值,必须把函数中的结构定义为结构指针。

④路径标识符,在Linux下是“/”,在Windows下是“\”,注意windows和Linux的对动态库搜索路径的不同。

linux下的共享对象(动态链接库)(源于网络,归于网络)

2012-09-07 10:16:34| 分类:linux|举报|字号订阅

nm libswapi.a | grep "swNewtran"

查询库里面是否有这个函数

1 简介

大家都知道,在WINDOWS系统中有很多的动态链接库(以.DLL为后缀的文件,

DLL即Dynamic Link Library)。这种动态链接库,和静态函数库不同,它里面的函数并不是执行程序本身的一部分,而是根据执行程序需要按需装入,同时其执行代码可在多个执行程序间共享,节省了空间,提高了效率,具备很高的灵活性,得到越来越多程序员和用户的青睐。那么,在LINUX系统中有无这样的函数库呢?答案是肯定的,LINUX的动态链接库不仅有,而且为数不少。在/lib目录下,就有许多以.so作后缀的文件,这就是LINUX系统应用的动态链接库,只不过与WINDOWS叫法不同,它叫so,即Shared Object,共享对象。

-

2 如何创建动态链接库

生成动态链接库:

如:

1.gcc -fpic -shared -o example.so example1.c example

2.c

-fpic 使输出的对象模块是按照可重定位地址方式生成的。

-shared指定把对应的源文件生成对应的动态链接库文件libstr.so文件。

3 如何使用动态链接库

3.1 dlfcn.h头文件

Linux下使用动态链接库,源程序需要包含dlfcn.h头文件,此文件定义了调用动态链接库的函数的原型。下面详细说明一下这些函数。

3.1.1 dlerror

原型:

1.const char *dlerror(void);

作用:

当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL 时表示操作函数执行成功。

3.1.2 dlopen

原型:

1.void *dlopen (const char *filename, int flag);

作用:

dlopen用于打开指定名字(filename)的动态链接库,并返回操作句柄。

参数说明:

filename: so文件名.如果名字不以/开头,则非绝对路径名,将按下列先后顺序查找该文件。

(1) 用户环境变量中的LD_LIBRARY值;

(2) 动态链接缓冲文件/etc/ld.so.cache

(3) 目录/lib,/usr/lib.

flag:表示在什么时候解决未定义的符号(调用)。取值有两个:

1) RTLD_LAZY : 表明在动态链接库的函数代码执行时解决。

2) RTLD_NOW : 表明在dlopen返回前就解决所有未定义的符号,一旦未解决,dlopen将返回错误。

dlopen调用失败时,将返回NULL值,否则返回的是操作句柄。

3.1.3 dlsym

原型:

1.void *dlsym(void *handle, char *symbol);

作用:

dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。由此地址,可以带参数执行相应的函数。

举例:

1.void handle =NULL;

2.void (*add)(int x,int y);

3.

4.handle =dlopen("xxx.so",RTLD_LAZY);

5.if (!handler) {

6. printf( "加载模块错误%s\n", dlerror() );

7. return;

8.}

9.add=dlsym(handle,"add"); 10.if(add) 11. add(89,369);

12.dlclose(handle);

3.1.4 dlclose

原型:

1.int dlclose (void *handle);

作用:

dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。

充分利用共享内存并不总是容易的。在本文中,IBM 的Sachin Agrawal 与我们共享了他的C++ 专门技术,展示了面向对象如何去利用一个独特而实用的进程间通信通道的关键优势。

就时间和空间而言,共享内存可能是所有现代操作系统都具备的最高效的进程间通信通道。共享内存同时将地址空间映射到多个进程:一个进程只需依附到共享内存并像使用普通内存一样使用它,就可以开始与其他进程进行通信。

不过,在面向对象编程领域中,进程更倾向于使用共享对象而不是原始的信息。通过对象,不需要再对对象中容纳的信息进行序列化、传输和反序列化。共享对象也驻留在共享内存中,尽管这种对象“属于”创建它们的进程,但是系统中的所有进程都可以访问它们。因此,共享对象中的所有信息都应该是严格与特定进程无关的。

这与当前所有流行的编译器所采用的C++ 模型是直接矛盾的:C++ 对象中总是包含指向各种Vee-Table 和子对象的指针,这些是与特定进程相关的。要让这种对象可以共享,您需要确保在所有进程中这些指针的目标都驻留在相同的地址。

在一个小的示例的帮助下,本文展示了在哪些情况下C++ 模型可以成功使用共享内存模型,哪些情况下不能,以及可能从哪里着手。讨论和示例程序都只限于非静态数据成员和虚函数。其他情形不像它们这样与C++ 对象模型关系密切:静态的和非静态非虚拟的成员函数在共享环境中没有任何问题。每个进程的静态成员不驻留在共享内存中(因此没有问题),而共享的静态成员的问题与这里讨论到的问题类似。

环境假定

本文仅局限于用于32 位x86 Interl 体系结构的Red Hat Linux 7.1,使用版本2.95 的GNU C++ 编译器及相关工具来编译和测试程序。不过,您同样可以将所有的思想应用到任意的机器体系结构、操作系统和编译器组合。

示例程序

示例程序由两个客户机构成:shm_client1 和shm_client2,使用由共享对象库shm_server 提供的共享对象服务。对象定义在common.h 中:清单1. common.h 中的定义

#ifndef __COMMON_H__

#define __COMMON_H__

class A {

public:

int m_nA;

virtual void WhoAmI();

static void * m_sArena;

void * operator new (unsigned int);

};

class B : public A {

public:

int m_nB;

virtual void WhoAmI();

};

class C : virtual public A {

public:

int m_nC;

virtual void WhoAmI();

};

void GetObjects(A ** pA, B ** pB, C ** pC);

#endif //__COMMON_H__

清单1 定义了三个类(A、B 和C),它们有一个共同的虚函数WhoAmI()。基类A 有一个名为m_nA 的成员。定义静态成员m_sArena 和重载操作

new() 是为了可以在共享内存中构造对象。类B 简单地从A 继承,类C 从A 虚拟地继承。为了确保A、B 和C 的大小明显不同,定义了B::m_nB 和C::m_nC。这样就简化了A::operator new() 的实现。GetObjects() 接口返回共享对象的指针。

共享库的实现在shm_server.cpp 中:

清单2. 库- shm_server.cpp

#include

#include

#include

#include

#include

#include

#include "common.h"

void * A::m_sArena = NULL;

void * A::operator new (unsigned int size)

{

switch (size)

{

case sizeof(A):

return m_sArena;

case sizeof(B):

return (void *)((int)m_sArena + 1024);

case sizeof(C):

return (void *)((int)m_sArena + 2048);

default:

cerr << __FILE__ << ":" << __LINE__ << " Critical error" << endl;

}

}

void A::WhoAmI() {

cout << "Object type: A" << endl;

}

void B::WhoAmI() {

cout << "Object type: B" << endl;

}

void C::WhoAmI() {

cout << "Object type: C" << endl;

}

void GetObjects(A ** pA, B ** pB, C ** pC) { *pA = (A *)A::m_sArena;

*pB = (B *)((int)A::m_sArena + 1024);

*pC = (C *)((int)A::m_sArena + 2048); }

class Initializer {

public:

int m_shmid;

Initializer();

static Initializer m_sInitializer;

};

Initializer Initializer::m_sInitializer; Initializer::Initializer()

{

key_t key = 1234;

bool bCreated = false;

m_shmid = shmget(key, 3*1024, 0666);

if (-1 == m_shmid) {

if (ENOENT != errno) {

cerr << __FILE__ << ":" << __LINE__ << " Critical error" << endl;

return;

}

m_shmid = shmget(key, 3*1024, IPC_CREAT | 0666);

if (-1 == m_shmid) {

cerr << __FILE__ << ":" << __LINE__ << " Critical error" << endl;

return;

}

cout << "Created the shared memory" << endl;

bCreated = true;

}

A::m_sArena = shmat(m_shmid, NULL, 0);

if (-1 == (int)A::m_sArena) {

cerr << __FILE__ << ":" << __LINE__ << " Critical error" << endl;

return;

}

if (bCreated) {

// Construct objects on the shared memory

A * pA;

pA = new A;

pA->m_nA = 1;

pA = new B;

pA->m_nA = 2;

pA = new C;

pA->m_nA = 3;

}

return;

}

让我们更详细地研究清单2:

第9-25 行:operator new ()

同一个重载的操作符让您可以在共享内存中构造类A、B 和C 的对象。对象A 直接从共享内存的起始处开始。对象B 从偏移量1024 处开始,C 从偏移量2048 处开始。

第26-34 行:虚函数

虚函数简单地向标准输出写一行文本。

第35-39 行:GetObjects

GetObjects() 返回指向共享对象的指针。

第40-46 行:初始化器(Initializer)

这个类存储共享内存标识符。它的构造函数创建共享内存及其中的对象。如果共享内存已经存在,它就只是依附到现有的共享内存。静态成员

m_sInitializer 确保在使用共享库的客户机模块的main() 函数之前调用构造函数。

第48-82 行:Initializer::Initializer()

如果原来没有共享内存,则创建,并在其中创建共享对象。如果共享内存已经存在,对象的构造就会被跳过。Initializer::m_shmid 记录标识符,

A::m_sArena 记录共享内存地址。

即使所有进程都不再使用它了,共享内存也不会被销毁。这样就让您可以显式地使用ipcs 命令来销毁它,或者使用ipcs 命令进行一些速查。

客户机进程的实现在shm_client.cpp 中:

清单3. 客户机- shm_client.cpp

#include "common.h"

#include

#include

int main (int argc, char * argv[])

{

int jumpTo = 0;

if (1 < argc) {

jumpTo = strtol(argv[1], NULL, 10);

}

if ((1 > jumpTo) || (6 < jumpTo)) {

jumpTo = 1;

}

A * pA;

B * pB;

C * pC;

GetObjects(&pA, &pB, &pC);

cout << (int)pA << "\t";

cout << (int)pB << "\t";

cout << (int)pC << "\n";

switch (jumpTo) {

case 1:

cout << pA->m_nA << endl;

case 2:

pA->WhoAmI();

case 3:

cout << pB->m_nA << endl;

case 4:

pB->WhoAmI();

case 5:

cout << pC->m_nA << endl;

case 6:

pC->WhoAmI();

}

return 0;

}

#include

void DoNothingCode() {

pthread_create(NULL, NULL, NULL, NULL); }

第6-35 行

客户机进程获得指向三个共享对象的指针,建立对它们的数据成员的三个引用,并且——依赖于命令行的输入——调用三个虚函数。

第36-39 行

没有被调用的pthread_create() 函数用来强制链接到另一个共享库。来自所有共享库的任何方法都可以满足这一目的。

共享库和客户机可执行文件的两个实例的编译方法如下:

gcc shared g shm_server.cpp o libshm_server.so lstdc++

gcc -g shm_client.cpp -o shm_client1 -lpthread -lshm_server -L .

gcc -g shm_client.cpp -o shm_client2 -lshm_server -L . lpthread

注意,交换了shm_client1 和shm_client2 中shm_server 和pthread 的链接顺序,以确保shm_server 共享库在两个可执行文件中的基址不同。可以使用ldd 命令进一步验证这一点。示例输出通常如下所示:

清单4. shm_client1 的库映射

ldd shm_client1

libpthread.so.0 => (0x4002d000)

libshm_server.so => (0x40042000)

libc.so.6 => (0x4005b000)

ld-linux.so.2 => (0x40000000)

清单5. shm_client2 的库映射

bordercolorlight = "black" bordercolordark = "#FFFFFF"

align="center">

 ldd shm_client2

libshm_server.so => (0x40018000)

libpthread.so.0 => (0x40046000)

libc.so.6 => (0x4005b000)

ld-linux.so.2 => (0x40000000)

这里的主要目的是使构建的两个客户机二进制文件具有不同的服务器库基址。在这个示例程序的上下文中,使用不被调用的pthread_create() 函数和不同的共享库链接顺序来达到这一目标。不过,没有具体规则或统一步骤可以作用于所有链接;需要根据不同的情况采取不同的方法。

例1:shm_client1 与shm_client1

在下面的输出中,首先在shell 中调用shm_client1。由于现在没有共享对象,于是shm_client1 创建了它们,引用它们的数据成员,调用它们的虚函数,然后退出——将对象留在了内存中。第二次,进程只是引用数据成员和虚函数。

清单6. shm_client1 与shm_client1 的输出日志$ ./shm_client1

Created the shared memory

1073844224 1073845248 1073846272

1

Object type: A

2

Object type: B

3

Object type: C

$ ipcs

------ Shared Memory Segments --------

key shmid owner perms bytes nattch status

0x000004d2 2260997 sachin 666 3072 0 $ ./shm_client1

1073840128 1073841152 1073842176

1

Object type: A

2

-> 0

-> Segmentation fault (core dumped)

当第二个进程试图通过类型C * 的指针去引用数据成员A::m_nA 时(您会记得C 虚拟继承自A),共享对象内的基子对象(base sub-object)指针会被读取。共享对象是在现在不存在的进程的上下文中构造的。因此,读取A::m_nA 和C::WhoAmI() 时读入的是内存垃圾。

因为Vee-Table 和虚函数位于shm_server 共享库内部,恰巧在同一虚拟地址被重新加载,所以,再次引用类型A * 和B * 的指针时不会观察到任何问题。

因此,GNU 所采用的C++ 对象模型没有成功地处理虚拟继承。

例2:shm_client1 与shm_client2

在下一个示例输出中,在命令行中首先执行shm_client1,然后执行

shm_client2:

清单7. shm_client1 与shm_client2 的输出日志

$ ./shm_client1

Created the shared memory

1073844224 1073845248 1073846272

1

相关主题