精华内容
下载资源
问答
  • D,一般删除都是文件索引,如果两个文件同时打开同一个文件,一个线程执行删除操作,只要另一个线程不退出,就可以继续对该文件进行操作,一旦退出才找不到该文件的索引节点而报错。

    这里写图片描述

    D,一般删除都是文件索引,如果两个文件同时打开同一个文件,一个线程执行删除操作,只要另一个线程不退出,就可以继续对该文件进行操作,一旦退出才找不到该文件的索引节点而报错。

    这里写图片描述

    展开全文
  • 进程错误问题查找

    千次阅读 2010-12-10 22:41:00
    近期接手了一个服务器项目,服务器程序已经上线将近2个月,期间有过几次崩溃,崩溃时没有生成coredump,从服务器的进程的监管deamon进程的反应来看,服务器进程是收到了SIGSEGV导致崩溃。而日志输出上仅仅能查到崩溃...

          近期接手了一个服务器项目,服务器程序已经上线将近2个月,期间有过几次崩溃,崩溃时没有生成coredump,从服务器进程的监管deamon的反应来看,服务器进程是收到了SIGSEGV导致崩溃。而日志输出上仅仅能查到崩溃位置位于代码中使用的公司的基础代码库中,而这个库已经被证明是正确的。

          由于崩溃问题并不严重,曾经负责调查的人认为是服务器程序收到了异常报文,这个问题也随着这位同事的离职而不了了之。

          在我接手之后,由于需要增加功能,就进行了后续开发,开发结束在内网测试没有任何问题,然后一旦上线,运行1-2个小时必然崩溃,崩溃依旧没有core文件生成,从日志输出来看,曾经的崩溃问题再次浮出水面。

          由于这个崩溃比较严重,导致后续工作无法进行,于是上面下了期限,必须在2周内找出崩溃原因,然后进入后续功能的开发,悲剧的是开发这个代码的同事已经走了,最有可能崩溃的地方这位同事应该是比较清楚的,于是乎我不得不read the fucking code。

          比较奇怪的是如果我新增加的功能单独运行,进程完全不会崩溃。而旧的功能单独运行也看起来很稳定,可一旦同时启用两个功能,必定在两个小时后崩溃。起初我怀疑是我新增加的代码导致了内存写越界,于是我把新增加代码所有涉及内存写操作的代码注掉,然而问题依旧会发生。于是我考虑是否新增加代码在和旧功能交互的时候导致内存写越界,于是我把我所修改代码所有可能影响到的代码都分析了一遍,结果没有任何问题,其间也做过几次假设,把边界条件全部严加判断,结果也没有解决问题。

           随后我的精力集中在,判断是否线程的栈空间不足,进程的同时打开文件数目限制以及进程中使用的非线程安全的系统调用(strerror),以及线程所使用的一个第三方日志库上,最终这些地方的疑问都被排除了。

           时间经过了一周,问题没有丝毫的进展。      

           在一度绝望中,想到了coredump,由于进程崩溃不生成core文件,于是我想到是不是进程的信号处理有问题,于是分析了代码中的信号处理问题。可以说,信号处理写的很乱,在一个多线程的程序,各个线程分别设置自己的屏障字,有些线程还使用sigwait,不同的线程又修改信号处理函数。更要命的是,这个多线程的进程居然在一个地方执行了system("curl "),在某个线程中fork了一个子进程shell,又这个shell里fork子进程去执行curl的一个命令。这个东西在退出的时候会发送SIGCHLD信号,这里是整个代码最阴暗的地方,我在重写了整个进程的信号处理部分后,屏障了SIGCHLD和SIGPIPE,因为我曾经认为这两个信号会打断我在进程中的不可重入的new或malloc操作,导致后续的内存写操作出错,但结果证明并不是这两个信号的问题。

           在这个时候有转机的是在修正了信号处理部分之后,进程可以正常的生成coredump了,服务进程上线后可以生成比较多的coredump文件让我来分析崩溃时代码内部的状况。在此期间痛苦的在GDB中不停的print和X众多变量和对象的内存值,这时我发现在程序崩溃点确实在公司的基础类库中,而只有我的代码调用这个库的接口,但在库中使用的一个指针的所指向的内存空间却经常无效。这时我又对我新增加的代码产生了疑问,我认为崩溃是由于我的某些代码,导致写越界把栈里保存的指针的值给写错了,可仔细检查代码也没有发现问题。由于每次崩溃这个指针指向的值都不同,有时这个指针指向更好像是其所指对象被析构后的遗所,于是我开始怀疑库中所使用的一个系统调用存在BUG,在网上搜索也没有得到什么有用信息。为此我重写类的析构函数,让他写些特定的值,以验证指针所指的对象是自动析构了,然而在另一个地方却被再次使用,不过证明这个假想也不正确。

            由于我的视线一直停留在崩溃点附近,这让我一直找些不可能的原因来验证,不过在验证过程中我渐渐发现,大多情况下,这个指针所指向的内存对象基本上是完整的,只有部分被改写。而这个指针是在栈上,指针所指的空间是在堆上,问题不是这个指针被改写了,而是指针所指向的堆空间被改写了。此时形势渐渐明朗起来,我要找的是程序中写堆上内存可能出错的地方。

            其间想到借助第三方工具来进行查找,于是详细用了valgrind,挂着程序跑,可是由于服务器程序在运行时分配了大量内存,但在退出时并不释放,因此valgrind报了很多内存泄露的错误。然后据我分析,进程在运行过程中并没有泄露内存,只是在退出时没有释放,让操作系统进行自行回收而已。此外valgrind也报了一些其他的使用未初始化内存的提示,但都被一一排除在导致崩溃的问题之外。传说中的valgrind也不能帮助我找到写内存越界的情况。期间也想到用gdb跑程序,可是程序运行要提供保证10几万用户的在线处理,在gdb中根本无法达到这个性能要求,只得放弃。

             这个时候我对整个程序的框架也渐渐清楚了,对coredump的分析也让我程序的运行状态有了比较清楚的了解。这个时候我观察到一个很常见的现象。这个服务器程序兼容了一个旧版本的协议模块,而这个协议模块还兼容一种更为古老的登录协议,这种登录协议会导致代码执行那个丑陋的system()调用,目前使用这种登录协议的用户很少,在每天登录的几百万用户中,只有几百是用这种登录方式。然而每次崩溃的时候,负责处理这种旧协议的线程必然在执行那个system()调用,函数调用栈的顶端一定是sys_wait的内核调用接口。不过我这时还是很放心system()这个函数的,因为man上说它可以处理其内部执行命令的错误状态。由于我很信任那段阴暗的代码,于是转而在整个代码中查找所有的内存写操作。

            我分析了代码中所有snprintf(),所有会写内存的地方。这个时候我被代码中各处的string对象折磨的要死,由于string特有的写时copy能力,这让我不得我仔细分析所有string对象的使用和引用的地方。本来做为服务器程序,在处理协议报文时,以高效为先,可是代码中处理协议报文全是用string对象来实现,各种的resize,data,c_str调用,把string内部的指针在各处传递,要命的是这个const指针在传递后其所指内存内容也会被修改。虽然string类的设计者一再强调不要修改data(),c_str()返回的内存内容,可在代码里,显然不是这么回事。在分析完所有的内存读写操作后,居然并没有找到写越界的地方,这个时候我失去方向了,不知道下一步改往哪里查找原因了。

            这一阶段我重写程序的内存池模块,简化内存池的分配和写操作,当然主要是为了排除内存池模块的写越界。当没有原因再查时,我只得再把目光转到那个SYSTEM调用上,我怀着试试看的态度,去掉那个system的调用而是使用第三方库在线程中直接实现其功能,重新编译、上线,奇迹居然发生了,程序稳定没有再崩溃。

            到此问题终于解决,如果非要让我对崩溃原因做个解释的话,我要说:(1)不要在多线程的程序中执行与fork相关的操作 (2)不要把一个string的内部指针传到另一个进程中去使用。(3)屏蔽掉大多数信号,不要以为忽略就可以,否则它会中断你的某些系统调用,导致出错。

            在这次问题解决的过程中,也学到很多东西,以前也没有这么痛恨string类,当然也更加理解string这个类的行为,对信号处理也更加得心应手。

            需要反思的地方是:当面对一个无从下手的问题时,你要直面其中最困难的部分,因为你不敢面对的部分往往是你最后才去触碰的部分,而它也是最有可能出问题的地方。如果你不能第一时间去面对它,那么在此之前,你只能在其他的非重点的部分徘徊,只到你无路可走,当你在最困难的地方发现了问题所在时,你会发现之前的徘徊都是在浪费时间和精力。所以解决问题要从最困难也是最有可能出问题的地方去检查,而不是避开它去寻找那此不可能的次要原因。

             在此罗列一些可能导致段错误的地方,以及如何避免相应的错误

             出现段错误时,有的很容易调查,但有的很难调查,比如在一个地方把内存写错,需要过一段时间另一个地方读这个内存时,才出错。这种是非常难定位的。因此在编写代码时一定要小心预防。

             1 使用非法的指针,包括使用未经初始化及已经释放的指针(指针使用之前和释放之后置为NULL)

             2 内存读/写越界。包括数组访问越界,或在使用一些写内存的函数时,长度指定不正确或者这些函数本身不能指定长度,典型的函数有strcpy(strncpy),sprintf(snprint)等等。

             3 对于C++对象,请通过相应类的接口来去内存进行操作,禁止通过其返回的指针对内存进行写操作,典型的如string类的data()和c_str()两个接口。

             4 函数不要返回其中局部对象的引用或地址,当函数返回时,函数栈弹出,局部对象的地址将失效,改写或读这些地址都会造成未知的后果。

             5 避免在栈中定义过大的数组,否则可能导致进程的栈空间不足,此时也会出现段错误。

             6 操作系统的相关限制,如:进程可以分配的最大内存,进程可以打开的最大文件描述符个数等,这些需要通过ulimit或setrlimit或sysctl来解除相关的限制。

             7 多线程的程序,涉及到多个线程同时操作一块内存时必须进行互斥,否则内存中的内存将不可预料

             8 使用非线程安全的函数调用

             9 在有信号的环境中,使用不可重入函数调用,而这些函数内部会读或写某片内存区,当信号中断时,内存写操作将被打断,而下次进入时将不避免的出错。

             10 跨进程传递某个地址

            11 某些有特殊要求的系统调用,例如epool_wait,正常情况下使用close关闭一个套接字后,epool会不再返回这个socket上的事件,但是如果你使用dup或dup2操作,将导致epool无法进行移除操作。

             其它还有很多,以后再进行补充。

     

    展开全文
  • 进程描述与控制

    2013-11-18 23:02:07
    多道程序设计 顺序程序 并发程序 多道程序设计 与时间有关的错误 线程的基本概念 线程的引入 线程的实现机制 Solaris进程线程模型
  • OpenProcess打开进程返回错误的问题

    千次阅读 2014-05-14 14:57:00
    问题描述  项目中需要做一个小功能:能够查看系统中当前正在运行的进程的内存信息,如内存块类型、分配状态、访问权限等。如下图所示:  需要的信息和上图相差无几。说起来也不算太难,毕竟现成的API已经提供...

    问题描述

          项目中需要做一个小功能:能够查看系统中当前正在运行的进程的内存信息,如内存块类型、分配状态、访问权限等。如下图所示:

          需要的信息和上图相差无几。说起来也不算太难,毕竟现成的API已经提供了。我们要做的就是遍历获取每个进程的句柄,然后逐个打开就可以提取信息了。

    排查结论

          但是,当我逐步编写完代码并运行时,发现什么结果也没得到。于是乎,打开调试器下了几个断点跟了进去发现:GetLastError()的返回值在遇到System Process时,会返回错误代码87。回头一查MSDN,人家已然说明:当OpenProcess()给定的进程ID为0时,该函数会失败并且GetLastError()返回的错误代码是ERROR_INVALID_PARAMETER。这个错误代码值就是87。另外还说明了,当给定的进程是空闲进程(Idle Process)或CSRSS进程之一时,GetLastError()返回的错误代码是ERROR_ACCESS_DENIED,其值为5. 出于系统安全性考虑,操作系统禁止用户层代码打开这些进程。

          显然,错误很明显了。我并没有过滤这些特殊进程,而是一股脑的全部调用OpenProcess()打开进程。而刚好,我的系统上第一个遍历的进程就是System Process(进程ID为0)。于是程序直接跳出了而得不到任何结果。另外,我的系统是Windows 7 64位系统,网上有人说在Windows XP系统上不会出现这种错误,不知道真假。

          遍历进程并打开:

    HANDLE proc = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (proc == INVALID_HANDLE_VALUE)
    {
    	assert(proc != INVALID_HANDLE_VALUE);
    	return ;
    }
    ProcessMemory item;
    HANDLE hProcess;
    PROCESS_MEMORY_COUNTERS pmc;
    PROCESSENTRY32 procEntry = { 0 };
    procEntry.dwSize = sizeof(PROCESSENTRY32);
    BOOL bRet = Process32First(proc,&procEntry);
    while (bRet)
    {
    	hProcess = OpenProcess( PROCESS_QUERY_INFORMATION| PROCESS_VM_READ, FALSE, procEntry.th32ProcessID );
    	if (NULL == hProcess) 
    	{
    		int ret = GetLastError();
    		// skip the system process and Idle process or one of CSRSS process
    		if (ret != ERROR_INVALID_PARAMETER && ret != ERROR_ACCESS_DENIED)  
    			return;  // function failed for other errors
    	}
    	else 
    	{
    		if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) )
    		{
    			item.pname = procEntry.szExeFile;
    			item.pid = procEntry.th32ProcessID;
    			item.PageFaultCount = pmc.PageFaultCount;
    			item.PagefileUsage = pmc.PagefileUsage;
    			item.PeakPagefileUsage = pmc.PeakPagefileUsage;
    			item.PeakWorkingSetSize = pmc.PeakWorkingSetSize;
    			item.QuotaNonPagedPoolUsage = pmc.QuotaNonPagedPoolUsage;
    			item.QuotaPagedPoolUsage = pmc.QuotaPagedPoolUsage;
    			item.QuotaPeakNonPagedPoolUsage = pmc.QuotaPeakNonPagedPoolUsage;
    			item.QuotaPeakPagedPoolUsage = pmc.QuotaPeakPagedPoolUsage;
    
    			m_procsmem.push_back(item);
    		}
    	}
    
    	bRet = Process32Next(proc, &procEntry);
    }
    CloseHandle(hProcess);
    CloseHandle(proc);
    

      

     

     

    展开全文
  • 进程与进程描述符(task_struct)

    千次阅读 2017-04-18 18:33:21
    一、 进程进程(Process) 计算机中的程序关于某数据...程序是指令、数据及其组织形式的描述进程是程序的实体。————————————————————————————————————————————————

    一、 进程

    进程(Process)
    计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

    ———————————————————————————————————————————————————————————————

    • 释义:一段程序的执行过程
    • 特征:动态、独立、异步、并发
    • 结构特征:程序、数据和进程控制块
    • 相关概念:线程,管程

    定义

    狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。

    广义定义:
    进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。
    它是是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

    进程的概念主要有两点:

    第一,进程是一个实体

    '每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。'

    第二,进程是一个“执行中的程序”

    '程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。'

    特征

    动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。

    并发性:任何进程都可以同其他进程一起并发执行

    独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;

    异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进

    结构特征:进程由程序、数据和进程控制块三部分组成。
    多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变。

    内容

    一个计算机系统进程包括(或者说“拥有”)下列数据:
    那个程序的可运行机器码的一个在存储器的映像。 分配到的存储器(通常包括虚拟内存的一个区域)。存储器的内容包括可运行代码、特定于进程的数据(输入、输出)、调用堆栈、堆栈(用于保存运行时运数中途产生的数据)。 分配给该进程的资源的操作系统描述符,诸如文件描述符(Unix术语)或文件句柄(Windows)、数据源和数据终端。 安全特性,诸如进程拥有者和进程的权限集(可以容许的操作)。 处理器状态(内文),诸如寄存器内容、物理存储器寻址等。当进程正在运行时,状态通常储存在寄存器,其他情况在存储器。

    切换

    进行进程切换就是从正在运行的进程中收回处理器,然后再使待运行进程来占用处理器。
    这里所说的从某个进程收回处理器,实质上就是把进程存放在处理器的寄存器中的中间数据找个地方存起来,从而把处理器的寄存器腾出来让其他进程使用。那么被中止运行进程的中间数据存在何处好呢?当然这个地方应该是进程的私有堆栈。
    让进程来占用处理器,实质上是把某个进程存放在私有堆栈中寄存器的数据(前一次本进程被中止时的中间数据)再恢复到处理器的寄存器中去,并把待运行进程的断点送入处理器的程序指针PC,于是待运行进程就开始被处理器运行了,也就是这个进程已经占有处理器的使用权了。
    这就像多个同学要分时使用同一张课桌一样,所谓要收回正在使用课桌同学的课桌使用权,实质上就是让他把属于他的东西拿走;而赋予某个同学课桌使用权,只不过就是让他把他的东西放到课桌上罢了。
    在切换时,一个进程存储在处理器各寄存器中的中间数据叫做进程的上下文,所以进程的 切换实质上就是被中止运行进程与待运行进程上下文的切换。在进程未占用处理器时,进程 的上下文是存储在进程的私有堆栈中的。

    状态

    进程执行时的间断性,决定了进程可能具有多种状态。事实上,运行中的进程可能具有以下三种基本状态。

    1)就绪状态(Ready):
    进程已获得除处理器外的所需资源,等待分配处理器资源;只要分配了处理器进程就可执行。就绪进程可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。

    2)运行状态(Running):
    进程占用处理器资源;处于此状态的进程的数目小于等于处理器的数目。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。

    3)阻塞状态(Blocked):
    由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,也无法运行。
    区别

    程序

    程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
    程序可以作为一种软件资料长期存在,而进程是有一定生命期的。程序是永久的,进程是暂时的。
    进程更能真实地描述并发,而程序不能;
    进程是由进程控制块、程序段、数据段三部分组成;
    进程具有创建其他进程的功能,而程序没有。
    同一程序同时运行于若干个数据集合上,它将属于若干个不同的进程,也就是说同一程序可以对应多个进程。
    在传统的操作系统中,程序并不能独立运行,作为资源分配和独立运行的基本单元都是进程。

    线程

    进程和线程关系

    通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。
    当下推出的通用操作系统都引入了线程,以便进一步提高系统的并发性,并把它视为现代操作系统的一个重要指标。

    控制

    进程控制是进程管理中最基本的功能。它用于创建一个新进程,终止一个已完成的进程,或者去终止一个因出现某事件而使其无法运行下去的进程,还可负责进程运行中的状态转换。

    创建进程

    1.引起创建进程的事件
    在多道程序环境中,只有(作为)进程(时)才能在系统中运行。因此,为使程序能运行,就必须为它创建进程。导致一个进程去创建另一个进程的典型事件,可以有以下四类:

    1) 用户登录
    在分时系统中,用户在终端键入登录命令后,如果是合法用户,系统将为该终端建立一个进程,并把它插入到就绪队列中。

    2)作业调度
    在批处理系统中,当作业调度程序按照一定的算法调度到某作业时,便将该作业装入到内存,为它分配必要的资源,并立即为它创建进程,再插入到就绪队列中。

    3) 提供服务
    当运行中的用户程序提出某种请求后,系统将专门创建一个进程来提供用户所需要的服务,例如,用户程序要求进行文件打印,操作系统将为它创建一个打印进程,这样,不仅可以使打印进程与该用户进程并发执行,而且还便于计算出为完成打印任务所花费的时间。

    4) 应用请求
    在上述三种情况中,都是由系统内核为它创建一个新进程,而这一类事件则是基于应用进程的需求,由它创建一个新的进程,以便使新进程以并发的运行方式完成特定任务。
    2.进程的创建过程
    这里写图片描述
    一旦操作系统发现了要求创建新进程的事件后,便调用进程创建原语create()按下述步骤创建一个新进程。

    1) 申请空白PCB。为新进程申请获得唯一的数字标识符,并从PCB集合中索取一个空白PCB。

    2) 为新进程分配资源。为新进程的程序和数据以及用户栈分配必要的内存空间。显然,此时操作系统必须知道新进程所需要的内存大小。

    3) 初始化进程控制块。PCB的初始化包括:
    ①初始化标识信息,将系统分配的标识符和父进程标识符,填入新的PCB中。
    ②初始化处理机状态信息,使程序计数器指向程序的入口地址,使栈指针指向栈顶。
    ③初始化处理机控制信息,将进程的状态设置为就绪状态或静止就绪状态,对于优先级,通常是将它设置为最低优先级,除非用户以显式的方式提出高优先级要求。

    4) 将新进程插入就绪队列,如果进程就绪队列能够接纳新进程,便将新进程插入到就绪队列中。

    进程终止
    1.引起进程终止的事件

    1)正常结束
    在任何计算机系统中,都应该有一个表示进程已经运行完成的指示。例如,在批处理系统中,通常在程序的最后安排一条Hold指令或终止的系统调用。当程序运行到Hold指令时,将产生一个中断,去通知OS本进程已经完成。

    2)异常结束
    在进程运行期间,由于出现某些错误和故障而迫使进程终止。这类异常事件很多,常见的有:越界错误,保护错,非法指令,特权指令错,运行超时,等待超时,算术运算错,I/O故障。

    3)外界干预
    外界干预并非指在本进程运行中出现了异常事件,而是指进程应外界的请求而终止运行。这些干预有:操作员或操作系统干预,父进程请求,父进程终止。

    1. 进程的终止过程
      如果系统发生了上述要求终止进程的某事件后,OS便调用进程终止原语,按下述过程去终止指定的进程。

    1)根据被终止进程的标识符,从PCB集合中检索出该进程的PCB,从中读出该进程状态。
    2)若被终止进程正处于执行状态,应立即终止该进程的执行,并置调度标志为真。用于指示该进程被终止后应重新进行调度。
    3)若该进程还有子孙进程,还应将其所有子孙进程予以终止,以防他们成为不可控的进程。
    4)将被终止的进程所拥有的全部资源,或者归还给其父进程,或者归还给系统。
    5)将被终止进程(它的PCB)从所在队列(或链表)中移出,等待其它程序来搜集信息。

    阻塞唤醒

    1.引起进程阻塞和唤醒的事件

    1)请求系统服务
    当正在执行的进程请求操作系统提供服务时,由于某种原因,操作系统并不立即满足该进程的要求时,该进程只能转变为阻塞状态来等待,一旦要求得到满足后,进程被唤醒。

    2)启动某种操作
    当进程启动某种操作后,如果该进程必须在该操作完成之后才能继续执行,则必须先使该进程阻塞,以等待该操作完成,该操作完成后,将该进程唤醒。

    3)新数据尚未到达
    对于相互合作的进程,如果其中一个进程需要先获得另一(合作)进程提供的数据才能运行以对数据进行处理,则是要其所需数据尚未到达,该进程只有(等待)阻塞,等到数据到达后,该进程被唤醒。

    4)无新工作可做
    系统往往设置一些具有某特定功能的系统进程,每当这种进程完成任务后,便把自己阻塞起来以等待新任务到来,新任务到达后,该进程被唤醒。

    2.进程阻塞过程

    正在执行的进程,当发现上述某事件后,由于无法继续执行,于是进程便通过调用阻塞原语block()把自己阻塞。可见,进程的阻塞是进程自身的一种主动行为。进入block过程后,由于此时该进程还处于执行状态,所以应先立即停止执行,把进程控制块中的现行状态由执行改为阻塞,并将PCB插入阻塞队列。如果系统中设置了因不同事件而阻塞的多个阻塞队列,则应将本进程插入到具有相同事件的阻塞(等待)队列。最后,转调度程序进行重新调度,将处理机分配给另一就绪进程,并进行切换,亦即,保留被阻塞进程的处理机状态(在PCB中),再按新进程的PCB中的处理机状态设置CPU环境。

    1. 进程唤醒过程
      当被阻塞的进程所期待的事件出现时,如I/O完成或者其所期待的数据已经到达,则由有关进程(比如,用完并释放了该I/O设备的进程)调用唤醒原语wakeup(),将等待该事件的进程唤醒。唤醒原语执行的过程是:首先把被阻塞的进程从等待该事件的阻塞队列中移出,将其PCB中的现行状态由阻塞改为就绪,然后再将该PCB插入到就绪队列中。

    调度算法

    进程的调度算法包括:
    实时系统中:FIFO(First Input First Output,先进先出算法),SJF(Shortest Job First,最短作业优先算法),SRTF(Shortest Remaining Time First,最短剩余时间优先算法)。

    交互式系统中:RR(Round Robin,时间片轮转算法),HPF(Highest Priority First,最高优先级算法),多级队列,最短进程优先,保证调度,彩票调度,公平分享调度。

    阶段
    进程是由进程控制块、程序段、数据段三部分组成。一个进程可以包含若干线程(Thread),线程可以帮助应用程序同时做几件事(比如一个线程向磁盘写入文件,另一个则接收用户的按键操作并及时做出反应,互相不干扰),在程序被运行后,系统首先要做的就是为该程序进程建立一个默认线程,然后程序可以根据需要自行添加或删除相关的线程。是可并发执行的程序。在一个数据集合上的运行过程,是系统进行资源分配和调度的一个独立单位,也是称活动、路径或任务,它有两方面性质:活动性、并发性。进程可以划分为运行、阻塞、就绪三种状态,并随一定条件而相互转化:就绪–运行,运行–阻塞,阻塞–就绪。

    进程为应用程序的运行实例,是应用程序的一次动态执行。看似高深,我们可以简单地理解为:它是操作系统当前运行的执行程序。在系统当前运行的执行程序里包括:系统管理计算机个体和完成各种操作所必需的程序;用户开启、执行的额外程序,当然也包括用户不知道,而自动运行的非法程序(它们就有可能是病毒程序)。


    二、进程描述符

    1.概念

    在linux中,每一个进程都有一个进程描述符,这个”进程描述符”是一个结构体名字叫做task_struct,在task_struct里面保存了许多关于进程控制的信息。
    task_struct是Linux内核的一种数据结构,它会被装载到RAM里并包含进程的信息。每个进程都把它的信息放在task_struct这个数据结构里面,而

    2.task_struct内容

    标示符:描述本进程的唯一标示符,用来区别其他进程。

    状态:任务状态,退出代码,退出信号等。

    优先级:相对于其他进程的优先级。

    程序计数器:程序中即将被执行的下一条指令的地址。

    内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。

    上下文数据:进程执行时处理器的寄存器中的数据。

    I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和正在被进程使用的文件列表。

    记账信息:可能包括处理器时间总和,使用的时钟总数,时间限制,记账号等。

    3.task_struct的分类

    调度数据成员

    (1) volatile long states; //进程状态
    (2) unsigned long flags; //进程标记符
    (3) long priority; //用来保存动态优先级
    (4) unsigned long rt_priority; //用来保存实时优先级,取值范围为0~99
    (5) long counter;
    (6) unsigned long policy;

    信号处理

    (1) unsigned long signal;
    (2) unsigned long blocked;
    (3) struct signal_struct *sig;

    进程队列指针

    (1) struct task_struct *next_task,*prev_task;
    (2) struct task_struct *next_run,*prev_run;
    (3) struct task_struct *p_opptr,*p_pptr;和struct task_struct *p_cptr,*p_ysptr,*p_osptr;

    进程标识

    (1) unsigned short uid,gid;
    (2) int groups[NGROUPS];
    (3) unsigned short euid,egid;
    (4) unsigned short fsuid,fsgid;
    (5) unsigned short suid,sgid;
    (6) int pid,pgrp,session;
    (7) int leader;

    时间数据成员

    (1) unsigned long timeout;
    (2) unsigned long it_real_value,it_real_iner;
    (3) struct timer_list real_timer;
    (4) unsigned long it_virt_value,it_virt_incr;
    (5) unsigned long it_prof_value,it_prof_incr;
    (6) long utime,stime,cutime,cstime,start_time;

    信号量数据成员

    (1) struct sem_undo *semundo;
    (2) struct sem_queue *semsleeping;

    进程上下文环境

    (1) struct desc_struct *ldt;
    (2) struct thread_struct tss;
    (3) unsigned long saved_kernel_stack;
    (4) unsigned long kernel_stack_page;

    文件系统数据成员

    (1) struct fs_struct *fs;
    (2) struct files_struct *files;
    (3) int link_count;

    内存数据成员

    (1) struct mm_struct *mm;

    页面管理

    (1) int swappable:1;
    (2) unsigned long swap_address;
    (3) unsigned long min_flt,maj_flt;
    (4) unsigned long nswap;
    (5) unsigned long cmin_flt,cmaj_flt,cnswap;
    (6) unsigned long old_maj_flt,dec_flt;
    (7) unsigned long swap_cnt;

    支持对称多处理器方式(SMP)时的数据成员

    (1) int processor;
    (2) int last_processor;
    (3) int lock_depth;

    其它数据成员

    (1) unsigned short used_math;
    (2) char comm[16];
    (3) struct rlimit rlim[RLIM_NLIMITS];
    (4) int errno;
    (5) long debugreg[8];
    (6) struct exec_domain *exec_domain;
    (7) unsigned long personality;
    (8) struct linux_binfmt *binfmt;
    (9) int exit_code,exit_signal;
    (10) int dumpable:1;
    (11) int did_exec:1;
    (12) int tty_old_pgrp;
    (13) struct tty_struct *tty;
    (14) struct wait_queue *wait_chldexit;

    进程队列的全局变量

    (1) current;
    (2) struct task_struct init_task;
    (3) struct task_struct *task[NR_TASKS];
    (4) unsigned long volatile jiffies;
    (5) int need_resched;
    (6) unsigned long intr_count;

    task_struct的定义

    truct task_struct {
    volatile long state;  //说明了该进程是否可以执行,还是可中断等信息
    unsigned long flags;  //Flage 是进程号,在调用fork()时给出
    int sigpending;    //进程上是否有待处理的信号
    mm_segment_t addr_limit; //进程地址空间,区分内核进程与普通进程在内存存放的位置不同
                            //0-0xBFFFFFFF for user-thead
                            //0-0xFFFFFFFF for kernel-thread
    //调度标志,表示该进程是否需要重新调度,若非0,则当从内核态返回到用户态,会发生调度
    volatile long need_resched;
    int lock_depth;  //锁深度
    long nice;       //进程的基本时间片
    //进程的调度策略,有三种,实时进程:SCHED_FIFO,SCHED_RR, 分时进程:SCHED_OTHER
    unsigned long policy;
    struct mm_struct *mm; //进程内存管理信息
    int processor;
    //若进程不在任何CPU上运行, cpus_runnable 的值是0,否则是1 这个值在运行队列被锁时更新
    unsigned long cpus_runnable, cpus_allowed;
    struct list_head run_list; //指向运行队列的指针
    unsigned long sleep_time;  //进程的睡眠时间
    //用于将系统中所有的进程连成一个双向循环链表, 其根是init_task
    struct task_struct *next_task, *prev_task;
    struct mm_struct *active_mm;
    struct list_head local_pages;       //指向本地页面      
    unsigned int allocation_order, nr_local_pages;
    struct linux_binfmt *binfmt;  //进程所运行的可执行文件的格式
    int exit_code, exit_signal;
    int pdeath_signal;     //父进程终止时向子进程发送的信号
    unsigned long personality;
    //Linux可以运行由其他UNIX操作系统生成的符合iBCS2标准的程序
    int did_exec:1; 
    pid_t pid;    //进程标识符,用来代表一个进程
    pid_t pgrp;   //进程组标识,表示进程所属的进程组
    pid_t tty_old_pgrp;  //进程控制终端所在的组标识
    pid_t session;  //进程的会话标识
    pid_t tgid;
    int leader;     //表示进程是否为会话主管
    struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr;
    struct list_head thread_group;   //线程链表
    struct task_struct *pidhash_next; //用于将进程链入HASH表
    struct task_struct **pidhash_pprev;
    wait_queue_head_t wait_chldexit;  //供wait4()使用
    struct completion *vfork_done;  //供vfork() 使用
    unsigned long rt_priority; //实时优先级,用它计算实时进程调度时的weight值
    
    //it_real_value,it_real_incr用于REAL定时器,单位为jiffies, 系统根据it_real_value
    //设置定时器的第一个终止时间. 在定时器到期时,向进程发送SIGALRM信号,同时根据
    //it_real_incr重置终止时间,it_prof_value,it_prof_incr用于Profile定时器,单位为jiffies。
    //当进程运行时,不管在何种状态下,每个tick都使it_prof_value值减一,当减到0时,向进程发送
    //信号SIGPROF,并根据it_prof_incr重置时间.
    //it_virt_value,it_virt_value用于Virtual定时器,单位为jiffies。当进程运行时,不管在何种
    //状态下,每个tick都使it_virt_value值减一当减到0时,向进程发送信号SIGVTALRM,根据
    //it_virt_incr重置初值。
    unsigned long it_real_value, it_prof_value, it_virt_value;
    unsigned long it_real_incr, it_prof_incr, it_virt_value;
    struct timer_list real_timer;   //指向实时定时器的指针
    struct tms times;      //记录进程消耗的时间
    unsigned long start_time;  //进程创建的时间
    //记录进程在每个CPU上所消耗的用户态时间和核心态时间
    long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS]; 
    //内存缺页和交换信息:
    //min_flt, maj_flt累计进程的次缺页数(Copy on Write页和匿名页)和主缺页数(从映射文件或交换
    //设备读入的页面数); nswap记录进程累计换出的页面数,即写到交换设备上的页面数。
    //cmin_flt, cmaj_flt, cnswap记录本进程为祖先的所有子孙进程的累计次缺页数,主缺页数和换出页面数。
    //在父进程回收终止的子进程时,父进程会将子进程的这些信息累计到自己结构的这些域中
    unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
    int swappable:1; //表示进程的虚拟地址空间是否允许换出
    //进程认证信息
    //uid,gid为运行该进程的用户的用户标识符和组标识符,通常是进程创建者的uid,gid
    //euid,egid为有效uid,gid
    //fsuid,fsgid为文件系统uid,gid,这两个ID号通常与有效uid,gid相等,在检查对于文件
    //系统的访问权限时使用他们。
    //suid,sgid为备份uid,gid
    uid_t uid,euid,suid,fsuid;
    gid_t gid,egid,sgid,fsgid;
    int ngroups; //记录进程在多少个用户组中
    gid_t groups[NGROUPS]; //记录进程所在的组
    //进程的权能,分别是有效位集合,继承位集合,允许位集合
    kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
    int keep_capabilities:1;
    struct user_struct *user;
    struct rlimit rlim[RLIM_NLIMITS];  //与进程相关的资源限制信息
    unsigned short used_math;   //是否使用FPU
    char comm[16];   //进程正在运行的可执行文件名
     //文件系统信息
    int link_count, total_link_count;
    //NULL if no tty 进程所在的控制终端,如果不需要控制终端,则该指针为空
    struct tty_struct *tty;
    unsigned int locks;
    //进程间通信信息
    struct sem_undo *semundo;  //进程在信号灯上的所有undo操作
    struct sem_queue *semsleeping; //当进程因为信号灯操作而挂起时,他在该队列中记录等待的操作
    //进程的CPU状态,切换时,要保存到停止进程的task_struct中
    struct thread_struct thread;
      //文件系统信息
    struct fs_struct *fs;
      //打开文件信息
    struct files_struct *files;
      //信号处理函数
    spinlock_t sigmask_lock;
    struct signal_struct *sig; //信号处理函数
    sigset_t blocked;  //进程当前要阻塞的信号,每个信号对应一位
    struct sigpending pending;  //进程上是否有待处理的信号
    unsigned long sas_ss_sp;
    size_t sas_ss_size;
    int (*notifier)(void *priv);
    void *notifier_data;
    sigset_t *notifier_mask;
    u32 parent_exec_id;
    u32 self_exec_id;
    
    spinlock_t alloc_lock;
    void *journal_info;
    };

    博客参照于:http://blog.csdn.net/lf_2016/article/details/54347820
    进程百度百科:http://baike.baidu.com/item/%E8%BF%9B%E7%A8%8B#2_1

    展开全文
  • 关于守护进程

    千次阅读 2019-01-24 14:54:17
    看了几篇别人的关于守护进程的博客发现大多就是代码一贴(看着代码就是一脸蒙蔽),然后对我的理解也是毫无作用,仍旧不知道啥是守护进程,最后突然看到了百度百科,不得不说百度上这个解释的还是很清楚的。...
  • 关于进程的理解及监控

    千次阅读 2018-01-29 18:30:38
    进程的概念 什么是进程 进程(Process)是计算机中程序执的实体。程序通常是由指令和相关数据组成的,在 Linux 系统中,程序的运行通常是由用户通过一个命令行解释器(例如 bash shell)发起执行,或者由其他...
  • 已安装 pre-removal 脚本 返回了错误号 1或2 与 子进程 已安装 post-installation 脚本 返回了错误号 1或2 | 学步园   http://www.xuebuyuan.com/2153374.html 子进程 已安装 pre-removal 脚本 返回了...
  • 进程控制块PCB(进程描述符)

    千次阅读 2019-03-24 19:35:48
    每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块是task_struct结构体。grep -r “task_struct” / 可以查找根目录下,包含task_struct的文件文件。或者 find /usr -name ...
  • 每一个Android应用程序都在它自己的进程中运行,不一定拥有一个独立 的Dalvik虚拟机实例.而每一个DVM都是在Linux中的一个进程,所以说可以认为是同一个概念.b DVM指dalvik的虚拟机.每一个Android应用程序都在它自己...
  • TCP服务器中,多进程并发模式算是比较常见的服务器范式,然而关于这个范式,自己却在很长时间里存在这样两个误区:  误区一:TCP服务器accept得到一个新的套接字描述符,这个新的套接字描述符需要占用一个新的TCP...
  • Linux 计算需求的表现是以进程的通用抽象为中心的。进程可以是短期的(从命令行执行的一个命令),也可以是长期的(一种网络服务)。因此,对进程及其调度进行一般管理就显得极为重要。  在用户空间,进程是由进程...
  • 进程描述和控制

    千次阅读 2013-03-21 21:52:14
    ①通常那些事件会导致创建一个进程? 答:新的批处理作业;交互登录;操作系统因为提供一项服务而创建;由现有的进程派生。 1、明确两个概念:进程控制块、进程映像。  在处理进程定位问题和进程属性问题...
  • tornado多进程log日志切分错误的解决方式
  • 关于进程helpsvc.exe这个进程!

    千次阅读 2006-05-30 01:40:00
    于是上网搜了一下,对于这个进程描述是这样的:进程文件: helpsvc or helpsvc.exe 进程名称: Microsoft Helpsvc 进程类别:存在安全风险的进程中文参考: helpsvc.exe是Windows 2000以及更高版本Windo
  • 1、错误描述 2、错误原因 今天,我在摸索如何利用命令查看MySQL日志,查了很多资料,大多数是通过修改my.ini文件配置。我修改了配置后,准备重启MySQL服务器,先执行了net stop mysql,发现服务还是没停;...
  • 日期 内核版本 架构 作者 GitHub CSDN 2016-05-12 Linux-4.5 X86 & arm gatieme LinuxDeviceDrivers Linux-进程管理与调度 ...注意,程序并不是进程,实际上两个或多个进程不仅有可能执行同一程序
  • 系统进程 csrss.exe 进程文件: csrss or csrss.exe 进程名称: Client/Server Runtime Server Subsystem 描述: 客户端服务子系统,用以控制Windows图形相关子系统。 是否为系统进程: 是
  • 依稀记得去年年中时,有个同事也问过我如何获取被调用进程的输出结果,当时还研究了一番,只是没有做整理。今天花点时间,将该方法整理成文。(转载请指明出于breaksoftware的csdn博客)  在信息化非常发达的今天,...
  • 关于进程wdfmgr.exe

    千次阅读 2006-06-02 07:12:00
    进程文件:wdfmgr或者wdfmgr.exe进程名称:windows driver foundation manager描述:wdfmgr.exe是微软microsoftwindowsmediaplayer10播放器的相关程序。该进程用于减少兼容性问题。这不是纯粹的系统程序,但是如果...
  •  一是如何实现一个守护进程,二是如何检测一个进程是否活着,三是保证某一执行文件只有一个实例在运行。 /*  * 1.守护进程  */ 守护进程的最大特点就是脱离了中断,Linux提供了一个系统调用daemon(),要...
  • 看到这个日志,刚开始还以为是执行数据库SQL语句相关的程序有问题,因为错误描述中有“数据字段包含错误号”嘛,又逐一检查数据存储层的代码,但仔细一想,本次升了修改也没有修改任何与数据库交互相关的代码呀。...
  • 系统进程描述

    千次阅读 2004-11-24 12:18:00
    系统进程 [system process] alg.exe csrss.exe ddhelp.exe dllhost.exe explorer.exe inetinfo.exe internat.exe kernel32.dll lsass.exe mdm.exe mmtask.tsk mprexe.exe msgsrv32.exe mstask.exe regsvc.exe rpcss....
  • 详解Linux中的进程描述符task_struct

    千次阅读 2016-06-05 17:15:45
    进程是处于执行期的程序以及它所管理的资源(如打开的文件...Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程,这个结构体包含了一个进程所需的所有信息。它定义在linux-2.6.38.8/include/linux/sche
  • 创建进程,等待进程进程终止
  • 进程间共享文件描述

    千次阅读 2015-10-26 15:30:01
    进程间传递文件描述符unix一个传统的服务器模型就是一连接一进程模型。进程对地址空间的保护作用是显而易见的,为某个连接服务的进程出现错误导致崩溃,其它的进程还能正常的运行。但是,当连接数大时,创建过多的...
  • 一、系统环境 操作系统:Windows10专业版 64位 Redis版本:redis-64.3.2 二、问题描述 cmd 进入redis 3.2目录 执行 redis-server.exe redis.windows.conf 可以启动redis服务, ...错误1067:进程意外终止。...
  • 一、子进程 已安装 pre-removal 脚本 返回了错误号 1或2 解决办法: sudo gedit /var/lib/dpkg/status 找到Package: ,比如Package: vsftpd 删除这部分的描述,删到下个Package: XXX前就好。存档后,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 325,452
精华内容 130,180
关键字:

关于进程的描述错误的是