《windows核心编程系列》四谈谈进程的建立和终止_windows中进程的创建、运行和终止的操作视频-程序员宅基地

技术标签: null  dll  谈谈Windows核心编程系列  编程  attributes  windows  数据结构  谈谈windows 核心编程系列  

http://blog.csdn.net/ithzhang/article/details/7927682转载请注明出处!


第二部分:工作机理

第一章:进程



上一章介绍了内核对象,这一节开始就要不断接触各种内核对象了。首先要给大家介绍的是进程内核对象。进程大家都不陌生,它是资源和分配的基本单位,而进程内核对象就是与进程相关联的一个数据结构。操作系统内核通过它管理进程,也就是操作系统原理上介绍的进程控制块(PCB)。举个例子,它就相当于每个学生都有的学籍,学校管理我们都是通过学籍,什么记过了,处分了,开除学籍了,都是在学籍上做文章。

 

    进程一般被定义为一个正在运行的程序的一个实例,它由两部分组成:

 

    1:内核对象,操作系统用它来管理进程。内核对象也是系统保存进程统计信息的地方。

 

2:一个地址空间,其中包含所有可执行文件或DLL模块的代码和数据。

 

Windows支持两种类型的应用程序:GUI程序和CUI程序。前者是我们经常接触的,具有窗口外观的窗口应用程序。后者是控制台应用程序。在使用vc来开发应用程序时,会设置各种链接器开关。链接器根据这些开关将子系统的正确类型嵌入最终生成的可执行文件。对于CUI程序这个开关是/SUBSYSTEM:CONSOLE。对于GUI程序,则是/SUBSYSTEM:WINDOWS

 

这些开关会告诉链接器在链接时链接什么入口函数。对于GUI程序它的入口点函数时WinMainCUI程序是main

 

 

有人以为入口函数就是程序执行的开始,其实这是不正确的。在入口点函数之前还有一个被称为启动函数的函数。该函数用来初始化C/C++运行库、构造全局和静态的C++对象等。

 

根据应用程序类型的不同,启动函数也不一样。ANSI字符集下,GUI程序的启动函数是WinMainCRTStartup,入口函数是WinMainCUI的启动函数是mainCRTStartup,入口函数是mainUnicode字符集下,GUI程序的启动函数是wWinMainCRTStartup,入口函数是wWinMainCUI的启动函数是wmainCRTStartup,入口点函数时wmain

 

我们在写控制台下的应用程序时,可以通过argv来引用命令行参数,当时也很疑惑,为什么可以直接用呢?原来都是启动函数的功劳。它会在进入入口函数之前帮我们做其他工作:

 

1:获取命令行指针。

 

2:获取指向环境变量的指针

 

3:初始化C/C++运行库的全局变量。

 

4:初始化C运行库内存分配函数。

 

5:调用所有全局和静态C++类对象的构造函数。

 

 

 

完成所有这些工作后,启动函数就会调用应用程序的入口点函数。入口点函数返回后启动函数获得入口点函数返回值,并将其传递给C运行库函数exitExit函数将调用所有全局和静态C++类对象的析构函数和其他清理工作。然后将入口函数的返回值传递给ExitProcess函数,结束进程并设置返回值为退出代码。

 

加载到进程地址空间的可执行文件或是DLL都有一个实例句柄。用以标识它在进程地址空间的位置。可执行文件的实例句柄被当做WinMain函数的第一个参数传入。它实际上是一个内存基地址。系统将可执行文件的映像加载到进程地址空间中的这个位置。映像加载到哪个地址是由链接器决定的。不同的链接器使用不同的首先基地址。exe文件和dll都会有一个默认的首选基地址。exe文件是400000dll10000000

 

为了获得一个可执行文件或dll文件被加载到进程地址空间的位置,可以使用GetModuleHandle函数。它需要一个以/0结尾标示可执行文件或dll的名字字符串为参数。当传入NULL时,此时将会返回主调进程可执行文件的基地址,即使此时代码在一个dll文件中仍然是这样。如果此时代码在dll中执行,我们想何知道此时代码正在什么模块中运行,这可以通过GetModuleHandleEx得到。将GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS作为它的第一个参数,再将当前函数的地址作为它的第二个参数,函数执行完毕,最后一个参数将保存出入的函数所在dll的基地址。

 

     系统在创建进程时会传给他一个命令行,这个命令行总是非空,因为它至少存储有可执行文件的名称。C运行库的启动代码在执行一个GUI应用程序时,会调用windows函数GetCommandLine来获得进程的完整命令行,它忽略可执行文件名称,然后将剩余部分传给WinMainpszCmdLine参数。

 

     每个进程都有一个与它相关联的环境块。用以定义工作环境、保存有用信息,使系统获得相关设置。应用程序经常利用环境变量让用户精细其行为。用户创建一个环境变量并进行初始化,此后应用程序运行时会正在环境块中查找变量,如果找到变量就会解析变量的值,并调整自己的行为。它所占用的内存是在进程地址空间内分配的。同样调用GetEnvironmentStrings函数可以获得完整的环境块。通常子进程会继承一组环境变量,这些环境变量和父进程的环境变量相同,父进程可以控制那些环境变量允许子进程继承。注意子进程继承的仅仅是父进程环境变量的副本,它们不共享同一个环境块。GetEnvironmentVariable函数可以用来判断一个环境变量是否存在。

在下图中我们可以看到形如%USERPROFILE%的字符串,它表示两个%之间的这部分内容是一个可替换的变量。该变量在环境变量中已经被定义。

 

 

    可以使用SetEnvironmentVariable来添加、删除或修改一个变量。

Windows不建议使用入口函数的参数来访问命令行或是环境变量,而应该使用以上介绍的各种函数。应该将它们当做只读变量,不要对它们进行修改。

     在多处理器的系统中,可以强迫线程在某个cpu上运行,这成为处理器关联性。子进程继承了其父进程的关联性。

     如果不提供完整的路径名,windows函数就会在当前驱动器的当前目录查找文件和目录。如:调用CreateFile打开一个文件时,如果仅指定文件名,系统将在当前驱动器和目录查找该文件。

 

系统在内部跟踪记录这一个进程当前驱动器和目录,这些信息是以进程为单位来维护的,如果该进程的一个线程更改了当前驱动器和目录,则只影响本进程的所有线程。

 

     一个线程可以使用GetCurrentDirectorySetCurrentDirectory来获得和设置当前驱动器和目录。子进程的当前目录默认为每个驱动器的根目录。如果父进程希望子进程继承它的当前目录,就必须在生成子进程之前,添加环境变量。

 

使用GetVersionEx可以获得window系统的版本号。

 

 

CreateProcess函数

 

 

    接下来进入到本章最重要的知识点:CreateProcess函数。

该函数用以创建一个进程:

 

Bool CreateProcess(

PCTSTR pszApplicationName,

PTSTR pszCommandLine,

PSECURTITY_ATTRIBUTES psaProcess,

PSECUTRITY_ATTRIBUTE psaThread,

Bool hInheritHandles,

DWROD fdwCreate,

PVOID pvEnvironment,

PCTSTR pszCurDir,

PSTARTUPINFO psiStartInfo,

PPROCESS_INFORMATION ppiProcInfo

);


 

 

当此函数被调用时,系统将首先创建一个进程对象,其初始使用计数为1。进程内核对象并不是进程本身,而是操作系统用来管理这个进程的一个数据结构。此后系统为新进程创建一个虚拟地址空间,并将可执行文件的代码及数据加载到进程的地址空间。然后系统会新进程的主线程创建一个线程内核对象,也将其使用计数设为1。和进程内核对象一样它也是操作系统用以管理线程的数据结构。主线程首先会执行C/C++运行时的启动函数,启动函数调用入口函数。进程被创建成功后CreateProcess返回true,函数返回前CreateProcess可能还没有完全初始化好。

 

 

     psaApplicationNamepszCommandLine分别指定新进程要使用的可执行文件称和要传给新进程的命令行字符串。

 

注意此处的pszCommandLine非常量字符串。传入常量字符串将会导致访问违规,因为在内部CreateProcess会修改传入的命令行字符串,返回时再将这个字符串还原。

 

    所以一下代码是错误的:

 

    STARTUPINFO si={ sizeof(si)};

PROCESS_INFORMATION pi;

CreateProcess(NULL,TEXT        

                  (“NOTEPAD”),NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);

 

 

因为TEXT"NOTEPAD")是常量字符串,当CreateProcess试图修改字符串会引起访问违规。解决方法是将TEXT"NOTEPAD")放在一个缓冲区内:

 

   

 STARTUPINFO si={sizeof(si)};

 PROCESS_INFORMATION pi;

 TCHAR cmdLine[200]=TEXT("NOTEPAD");

 CreateProcess(NULL,cmdLine,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);





  


 

   这一点要特别注意,很容易出错!!!!这也有个例外,windows vista以及win7ANSI版本以上是不会发生访问违规的,因为它们会为命令行创建一个临时副本。

 

在解析命令行时,CreateProcess会检查字符串中第一个标记,假定此标记就是我们想运行的可执行文件名称。如果可执行文件没有扩展名,就会默认是.exe扩展名。CreateProcess会在以下目录下搜索可执行文件:

1:主调进程.exe文件所在目录。

 

2:主调进程的当前目录。

 

3windows系统目录。即System32目录。

 

4windows目录。

 

5PATH环境变量中列出的目录。

 

如果为可执行文件制定完整路径,系统就会按指定路径寻找。

 

    以上情况在pszApplicationNameNULL时才发生。当然也可以在pszApplicationName传递可执行文件名称,此时必须指定扩展名,否则进程不会被创建。系统会按照此处指定的路径寻找,如没有指定完整路径,系统会假定文件位于当前目录。如找不到,函数调用失败。

 

pszApplicationName指定文件名,pszCommandLine参数中的内容也会作为新进程的命令行传给它。

如:


    STARTUPINFO si={sizeof(si)};

 PROCESS_INFORMATION pi;

 TCHAR cmdLine[200]=TEXT("WORDPAD a.txt");

 CreateProcess(TEXT("C:\\windows\\system32\\NOTEPAD.exe"),

 cmdLine,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);


 

 

命令行是“WORDPAD a.txt”,记事本程序将询问:a.txt不存在是否创建a.txt文件。第一个参数WORDPAD应该是作为程序名称传入的。

 

为了创建一个新的进程,系统必须创建一个进程内核对象和一个线程内核对象,由于这些都是内核对象,所以父进程必须将安全属性关联到这两个对象上。可以通过psaProcesspsaThread为进程和线程安全对象指定默认的安全描述符。可以都为它们指定NULL,使用默认的安全属性,也可以分配并初始化两个SECURITY_ATTRIBUTES结构,以便创建安全权限并将它们分配给进程和线程对象。

 

fdwCreate参数影响新进程创建的方式。如:指定CREATE_SUSPENDEd标识让系统在创建新进程时挂起其主线程。这样父进程就可以修改子进程地址空间的内存、更改主线程优先级或是将其添加到作业中。修改完后可以调用ResumeThread来允许子进程执行代码。传入表示创建进程后立即运行。多个标志位可以组合使用。

 

pvEnvironment参数指定一块内存,包含新进程要使用的环境字符串。但多数情况下都是传入NULL,表示子进程要继承父进程使用的一组环境字符串。也可以使用GetEnvironmentStrings函数,此函数获取主调进程正在使用的环境字符串地址,当我们为pvEnvirtonment传入NULL时,CreateProcess就是调用这个函数。不使用这块内存时应该使用FreeEnvironmentStrings函数。

 

 

 

pszCurDir允许父进程设置子进程的当前驱动器和目录。如果为NULL,新进程的工作目录与父进程的一样。

psiStartInfo参数指向一个STARTUPINFO结构。该结构包含很多成员。Windows在创建新进程的时候使用它们,但是大多数应用程序仅仅使用它们的默认值。因此我们要做的最起码的工作就是将此结构的所有成员都初始化为0,将cb成员设为此结构的大小,如:

 

STARTUPINFO si={sizeof(si)};

 

此时除cb成员外,其他成员均为0。不能仅仅si.cb=sizeof(si);因为此时其他成员的值都是垃圾数据。

 

ppiProcInfo参数指向PROCESS_INFORMATION结构,CreateProcess在返回时会初始化这个结构。

 

Typedef struct _PROCESS_INFORMATION

{

   HANDLE hProcess;

   HANDLE hThread;

   HANDLE dwProcessId;

   HANDLE dwThreadId;

}PROCESS_INFORMATION;

 

     CreateProcess创建的进程和线程对象将通过它返回。创建时系统会为每个对象指定一个初始的使用计数1CreateProcess返回时由于PROCESS_INFORMATION结构中再次引用了进程和线程内核对象 此时它们的使用计数都变为2。可以理解为进程和线程实例本身也占有一个计数。当它们结束运行时这个使用计数被递减1

 

此时如果系统要释放进程对象,1:进程必须终止,此时使用计数递减12:父进程必须调用CloseHandle,使用计数再次减去1。线程类似。

 

因此为了使不再使用的内核对象能够得到释放,一定要在不使用时调用CloseHandle关闭对句柄的引用。所有对该句柄的引用都被关闭后,当进程或线程终止时它们关联度的内核对象才能够被释放。

CreateProcess还会为进程和线程分配一个ID号。进程和线程分享同一个号码池。这意味着它们不可能相同。一个对象的ID不可能分配到0,因为windows任务管理器将进程ID 0与系统空闲进程关联。该进程代表未被真实使用的cpu使用率。

    dwProcessIddwThreadId成员就是存储进程和线程的ID。使用GetCurrentProcessId可以获得当前进程的IDGetCurrentThreadId来获得当前正在运行的线程的ID。另外使用GetProcessIdGetThreadId可以获得指定句柄对应的进程和线程的ID。使用GetProcessIDOfThread可以获得某句柄关联的线程所在进程的ID

由于ID可能会立即重用。也就是说当我们获得某个进程的ID并保存后,此后在使用时有可能出现它已经被释放了。此时此ID就对应着其他进程了。避免这种情况的唯一方法就是:保证进程或线程对象不被销毁。

 

 

进程终止

 

 

进程可以通过三种方式终止:

 

1:主线程从入口函数返回。

 

2:进程中的一个线程调用ExitProcess

 

3:另一个进程中的线程调用TerminateProcess

 

在以上介绍的三种方式中仅有第一种,当主线程从入口函数返回才保证主线程的所有资源都会被正确清理。

清理操作包括:

 

1:调用所有在本进程内使用的任何C++对象的析构函数。

 

2:释放各个线程线程栈使用的内存。

 

3:进程的退出代码被设为入口函数的返回值。

 

4:进程内核对象使用计数递减1

 

   正常情况下入口点函数会返回到启动函数,启动函数将正确清理进程使用的所有C运行时资源,清理之后启动代码显式调用ExitProcess并将入口函数返回值传给它。这也是为什么只需从入口函数返回却可以终止整个进程的原因。

 

进程的一个线程调用ExitProcess可以终止本进程。其后的别的代码将不会被执行。

 

ExitProcess相类似的还有ExitThread,它会导致一个线程终止。在创建线程时常出现这种情况:子线程还没有怎么执行程序就已经结束了,这有可能是在创建完线程后,主线程没有调用WaitForSingleObject之类的函数,主线程创建完其他线程后就返回到启动函数函数返回整个进程被终止。这一点很容易出错。

 

 

调用ExitProcess或是ExitThread会导致进程或线程当场终止运行,再也不会返回到启动函数,清理工作(C++对象的析构)当然没法执行。虽然最终随着进程的结束,该进程内所有线程所使用的资源都会被释放,但是应该避免调用这些函数,它们阻止了C++对象析构函数对善后工作的处理。顺便提下,如果在主线程调用ExitThread,虽然主线程当场终止,但是如果进程内还有其他线程,则进程不会终止。

 

如以下代码:

 

#include<windows.h>

#include<iostream>

DWORD ThreadProc(PVOID)

{

 int i=0;

 int j=0;

 

while(i<1000000)

 {

 i++;

 while(j<10000)

  j++;

 std::cout<<i<<","<<j<<std::endl;

 }

 return 0;

}

int main(int argc,char**argv)

{

 DWORD id;

 CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadProc,NULL,0,&id);

 ExitThread(0);

 return 0;

}



 

 

ExitProcess,ExitThread只能由本进程的其他线程调用。而TerminateProcessTerminateThread却可以由任何其他进程的线程调用。它的第一个参数指定要终止进程的句柄。这种情况下应用程序得不到自己要被杀死的通知,也不能阻止自己被杀死,当然也无法为自己准备好后事(得不到清理)。例如已经修改的文件没有刷新到磁盘上。

 

但要明白进程终止后属于它的任何东西都会被释放。TerminateProcess是异步的,此函数调用后我们并不能保证进程已经被强行终止了。要确定进程是否终止可以调用WaitForSingleObject函数,并传入进程句柄。

 

 

进程终止时进行的操作。

 

 

一个进程终止时,系统会依次执行以下操作:

 

1:终止进程中遗留的任何线程。

 

 

2:释放进程分配的所有用户对象,关闭所有内核对象。如果它们的使用计数变为0,内核对象将会释放。

 

 

3:将进程的退出代码从STILL_ACTIVE变为传给ExitProcess或是TerminateProcess的参数存储在内核对象中。

 

 

4:进程内核对象变为一触发状态。这也是为什么其他线程可以挂起他们自己直至另一个进程终止运行。

 

 

5:进程内核对象的使用计数递减1

 

 

   进程内核对象的生命期至少能像进程本身一样长。当进程终止时如果系统中还有另一个进程打开了这个进程的内核对象的句柄,进程内核对象的使用计数就不会变为0。当父进程忘记关闭子进程的句柄时往往发生这种情况。

 

进程终止了内核对象还没有被释放,这样做有用吗?当然有用!!即使进程终止了,存储在内核对象的信息也有可能被使用,如我们想知道进程占用了多少Cpu时间,或是想获得它的退出代码。GetExitCodeProcess此函数会查找进程内核对象并从内核对象的数据结构中取出退出代码。任何时候都可以调用此函数。如此时进程正在运行那么将会得到STILL_ALIVE

 

    WaitForSingleObject将会挂起当前线程,知道它所等待的对象变为已触发状态。进程或线程对象在终止时就会变成已触发状态。

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ithzhang/article/details/7927682

智能推荐

苹果https java_apple登录 后端java实现最终版-程序员宅基地

文章浏览阅读298次。import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;import com.auth0.jwk.Jwk;import com.helijia.appuser.modules.user.vo.AppleCredential;import com.helijia.common.api.model.Api..._com.auth0.jwk.jwk

NLP学习记录(六)最大熵模型MaxEnt_顺序潜在最大熵强化学习(maxent rl)-程序员宅基地

文章浏览阅读4.7k次。原理在叧掌握关于未知分布的部分信息的情况下,符合已知知识的概率分布可能有夗个,但使熵值最大的概率分布最真实地反映了事件的的分布情况,因为熵定义了随机变量的不确定性,弼熵值最大时,随机变量最不确定,最难预测其行为。最大熵模型介绍我们通过一个简单的例子来介绍最大熵概念。假设我们模拟一个翻译专家的决策过程,关于英文单词in到法语单词的翻译。我们的翻译决策模型p给每一个单词或短语分配一..._顺序潜在最大熵强化学习(maxent rl)

计算机毕业设计ssm科研成果管理系统p57gs系统+程序+源码+lw+远程部署-程序员宅基地

文章浏览阅读107次。计算机毕业设计ssm科研成果管理系统p57gs系统+程序+源码+lw+远程部署。springboot基于springboot的影视资讯管理系统。ssm基于SSM高校教师个人主页网站的设计与实现。ssm基于JAVA的求职招聘网站的设计与实现。springboot校园头条新闻管理系统。ssm基于SSM框架的毕业生离校管理系统。ssm预装箱式净水站可视化信息管理系统。ssm基于SSM的网络饮品销售管理系统。

Caused by: org.xml.sax.SAXParseException; lineNumber: 38; columnNumber: 9; cvc-complex-type.2.3: 元素_saxparseexception; linenumber: 35; columnnumber: 9-程序员宅基地

文章浏览阅读1.6w次。不知道大家有没有遇到过与我类似的报错情况,今天发生了此错误后就黏贴复制了报错信息“Caused by: org.xml.sax.SAXParseException; lineNumber: 38; columnNumber: 9; cvc-complex-type.2.3: 元素 'beans' 必须不含字符 [子级], 因为该类型的内容类型为“仅元素”。”然后就是一顿的百度啊, 可一直都没有找到..._saxparseexception; linenumber: 35; columnnumber: 9; cvc-complex-type.2.3:

计算机科学与技术创新创业意见,计算机科学与技术学院大学生创新创业工作会议成功举行...-程序员宅基地

文章浏览阅读156次。(通讯员 粟坤萍 2018-04-19)4月19日,湖北师范大学计算机科学与技术学院于教育大楼学院会议室1110成功召开大学生创新创业工作会议。参与本次会议的人员有党总支副书记黄海军老师,创新创业学院吴杉老师,计算机科学与技术学院创新创业活动指导老师,15、16、17级各班班主任及学生代表。首先吴杉老师介绍了“互联网+”全国大学生创新创业大赛的相关工作进度,动员各级班主任充分做好“大学生创新创业大..._湖北师范 吴杉

【Android逆向】爬虫进阶实战应用必知必会-程序员宅基地

文章浏览阅读1.1w次,点赞69次,收藏76次。安卓逆向技术是一门深奥且充满挑战的领域。通过本文的介绍,我们了解了安卓逆向的基本概念、常用工具、进阶技术以及实战案例分析。然而,逆向工程的世界仍然在不断发展和变化,新的技术和方法不断涌现。展望未来,随着安卓系统的不断更新和加固,逆向工程将面临更大的挑战。同时,随着人工智能和机器学习技术的发展,我们也许能够看到更智能、更高效的逆向工具和方法的出现。由于篇幅限制,本文仅对安卓逆向技术进行了介绍和案例分析。

随便推点

Python数据可视化之环形饼图_数据可视化绘制饼图或圆环图-程序员宅基地

文章浏览阅读1.1k次。制作饼图还需要下载pyecharts库,Echarts 是一个由百度开源的数据可视化,凭借着良好的交互性,精巧的图表设计,得到了众多开发者的认可。随着学习python的热潮不断增加,Python数据可视化也不停的被使用,那我今天就介绍一下Python数据可视化中的饼图。在我们的生活和学习中,编程是一项非常有用的技能,能够丰富我们的视野,为各行各业的领域提供了新的角度。环形饼图的制作并不难,主要是在于数据的打包和分组这里会有点问题,属性的标签可以去 这个网站进行修改。图中的zip压缩函数,并分组打包。_数据可视化绘制饼图或圆环图

SpringMVC开发技术~5~基于注解的控制器_jsp/servlet到controller到基于注解的控制器-程序员宅基地

文章浏览阅读325次。1 Spring MVC注解类型Controller和RequestMapping注释类型是SpringMVC API最重要的两个注释类型。基于注解的控制器的几个优点:一个控制器类可以控制几个动作,而一个实现了Controller接口的控制器只能处理一个动作。这就允许将相关操作写在一个控制器类内,从而减少应用类的数量基于注解的控制器的请求映射不需要存储在配置文件中,而是使用RequestM..._jsp/servlet到controller到基于注解的控制器

利用波特图来满足动态控制行为的要求-程序员宅基地

文章浏览阅读260次,点赞3次,收藏4次。相位裕量可以从增益图中的交越频率处读取(参见图2)。使用的开关频率、选择的外部元件(例如电感和输出电容),以及各自的工作条件(例如输入电压、输出电压和负载电流)都会产生巨大影响。图2所示为波特图中控制环路的增益曲线,其中提供了两条重要信息。对于图2所示的控制环路,这个所谓的交越频率出现在约80 kHz处。通过使用波特图,您可以查看控制环路的速度,特别是其调节稳定性。图2. 显示控制环路增益的波特图(约80 kHz时,达到0 dB交越点)。图3. 控制环路的相位曲线,相位裕量为60°。

Glibc Error: `_obstack@GLIBC_2.2.5‘ can‘t be versioned to common symbol ‘_obstack_compat‘_`_obstack@glibc_2.2.5' can't be versioned to commo-程序员宅基地

文章浏览阅读1.8k次。Error: `_obstack@GLIBC_2.2.5’ can’t be versioned to common symbol '_obstack_compat’原因:https://www.lordaro.co.uk/posts/2018-08-26-compiling-glibc.htmlThis was another issue relating to the newer binutils install. Turns out that all was needed was to initi_`_obstack@glibc_2.2.5' can't be versioned to common symbol '_obstack_compat

基于javaweb+mysql的电影院售票购票电影票管理系统(前台、后台)_电影售票系统javaweb-程序员宅基地

文章浏览阅读3k次。基于javaweb+mysql的电影院售票购票电影票管理系统(前台、后台)运行环境Java≥8、MySQL≥5.7开发工具eclipse/idea/myeclipse/sts等均可配置运行适用课程设计,大作业,毕业设计,项目练习,学习演示等功能说明前台用户:查看电影列表、查看排版、选座购票、查看个人信息后台管理员:管理电影排版,活动,会员,退票,影院,统计等前台:后台:技术框架_电影售票系统javaweb

分分钟拯救监控知识体系-程序员宅基地

文章浏览阅读95次。分分钟拯救监控知识体系本文出自:http://liangweilinux.blog.51cto.com0 监控目标我们先来了解什么是监控,监控的重要性以及监控的目标,当然每个人所在的行业不同、公司不同、业务不同、岗位不同、对监控的理解也不同,但是我们需要注意,监控是需要站在公司的业务角度去考虑,而不是针对某个监控技术的使用。监控目标1.对系统不间断实时监控:实际上是对系统不间..._不属于监控目标范畴的是 实时反馈系统当前状态

推荐文章

热门文章

相关标签