赞
踩
定义:正在运行的程序及其占用的资源(CPU、内存、系统资源等)叫做进程。
编写出来的c文件称为源码(对于人来说可读性高,但计算机无法识别),通过编译生成CPU可识别的二进制可执行程序保存在存储介质上(一般为外存),注意此时生成的是程序而不是进程。一旦开始运行该程序,则正在运行的该程序及其占用的资源就称为进程了。
进程的概念是针对系统的,而不是针对用户的。
进程会占用四类资源:
在传统的操作系统中,程序不可以独立的运行,作为资源分配和独立运行的基本单位都是进程。
一个程序可以执行多次,这也意味着多个进程可以执行同一个程序。
Linux 进程内存管理的对象都是虚拟内存。
每个进程先天就有 0-4G 的各自互不干涉的虚拟内存空间。0—3G 是用户空间执行用户自己的代码, 高 1GB 的空间是内核空间执行 Linu x 系统调用,这里存放整个内核的代码和所有的内核模块,用户所看到和接触的都是该虚拟地址,并不是实际的物理内存地址。
Linux下,一个进程在内存里有三部分的数据,就是”代码段”、”堆栈段”和”数据段”,一般的CPU都有上述三种段寄存器,以方便操作系统的运行。这三个部分是构成一个完整的执行序列的必要的部 分。
“代码段”:存放了程序代码的数据,假如机器中有数个进程运行相同的一个程序,那么它们就可以使用相同的代码段。
“堆栈段”:存放的就是子程序的返回地址、子程序的参数以及程序的局部变量和malloc()动态申请内存的地址。
“数据段”:存放程序的全局变量,静态变量及常量的内存空间。
PS : linux中的内存分配:
Linux 内存管理的基本思想就是只有在真正访问一个地址的时候才建立这个地址的物理映射。
Linux C/C++语言的分配方式共有 3 种方式:
(1)从静态存储区域分配。就是数据段的内存分配,这段内存在程序编译阶段就已经分配好,在程序的整个运行期间都存在, 例如全局变量,static 变量。
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。 栈内存分配运算内置于处理器的指令集中,效率很高,但是系统栈中分配的内存容量有限,比如大额数组就会把栈空间撑爆导致 段错误。
(3)从堆上分配,亦称动态内存分配。程序在运行的时候用 malloc 或 new 申请任意多少的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多,比如指向某个内存块的指针取值发生了变化又没有其他指针指向这块内存,这块内存就无法访问,发生内存泄露。
1. 结构特征
进程通常是不可以并发执行的,为使程序独立运行,必须配置进程控制块(PCB),由程序段、数据段和PCB共同组成进程实体,我们通常所说的进程就是进程实体,创建进程就是创建进程实体中的PCB。
2. 动态性
进程的实质就是进程实体的一次执行过程,因此动态性是进程最基本的特征,具体表现在:“由创建而产生、调度而执行、撤销而消亡”。
3. 并发性
指的是多个进程实体共同存在内存中且可以在一段时间内同时运行,并发性是进程的重要特征,也是操作系统重要特征,引入进程目的就是为了并发性。
4. 独立性
进程实体是一个独立运行、独立分配资源和独立接受调度的基本单元。
5. 异步性
进程实体按异步方式运行
进程其基本状态有5种,即创建状态、就绪状态、执行状态、阻塞状态、终止状态。
常见的为就绪、执行、阻塞三种。
进程状态是指一个进程的生命周期可以划分为一组状态,这些状态刻画了整个进程,进程状态即体现一个进程的生命状态。
linux下有6种终止进程的方式:
1. 正常返回:
(1)从main函数返回;
(2)调用exit,_exit,_Exit 函数;
(3)最后一个线程从启动历程返回或调用pthread_exit函数;
2. 异常返回:
(1)调用abort函数;
(2)接到一个信号;
(3)最后一个线程对取消请求做出响应。
无论进程如何终止,内核都会关闭所有打开的文件描述符,释放他们所使用的资源。
linux在终端中输入 ps aux 以查看当前时间节点进程信息。
此外,用命令如 ps aux | grep xiao 可查找xiao用户所使用的进程。
ps le \ps ef ( l表示显示详细信息,e表示显示所有进程 )。
此外,输入 top ,表示进入监听模式。进入监听模式后,可输入以下命令进行操作:
该命令可以动态显示进程的信息变化。
Linux内核在启动的最后阶段会创建init进程来执行程序/sbin/init,该进程是系统运行的第一个进程,进程号为 1,称为 Linux 系统的初始化进程,该进程会创建其他子进程来启动不同写系统服务,而每个服务又可能创建不同的子进程来执行不同的 程序。所以init进程是所有其他进程的“祖先”,并且它是由Linux内核创建并以root的权限运行,并不能被杀死。Linux 中维护 着一个数据结构叫做 进程表,保存当前加载在内存中的所有进程的有关信息,其中包括进程的 PID(Process ID)、进程的状态、 命令字符串等,操作系统通过进程的 PID 对它们进行管理,这些 PID 是进程表的索引。
子进程得到的是父进程资源的拷贝,而不是他们本身。
(1)子进程从父进程中继承到:
(2)子进程独有
3. 系统限制
一个服务器不可以给无限多个客户端提供服务,在Linux下每种资源都有相关的软硬限制,譬如单个用户最多能创建的子进程个数有限制,同样一个进程最多能打开的文件描述符也有相应的限制值,这些限制会限制服务器能够提供并发访问的客户端的数量。
在linux下可以使用getrlimit()和setrlimit()两个函数来获取或设置这些限制:
#include <sys/resource.h>
int getrlimit( int resource,struct rlimit *rlim); //获取限制
int setrlimit( int resource, const struct rlimit *rlim); //设置限制
(1)参数resource说明:
- RLIMIT_AS //进程的最大虚内存空间,字节为单位。
- RLIMIT_CORE //内核转存文件的最大长度。
- RLIMIT_CPU //最大允许的CPU使用时间,秒为单位。当进程达到软限制,内核将给其发送SIGXCPU信号,这一信号的默认行为是终止进程的执行。
- RLIMIT DATA //进程数据段的最大值。
- RLIMIT_FSIZE //进程可建立的文件的最大长度。如果进程试图超出这一限制时,核心会给其发送SIGXFSZ信号,默认情况下将终止进程的执行。
- RLIMIT_LOCKS //进程可建立的锁和租赁的最大值。
- RLIMIT_MEMLOCK //进程可锁定在内存中的最大数据量,字节为单位。RLIMIT_MSGQUEUE //进程可为POSIX消息队列分配的最大字节数
- RLIMIT_NICE //进程可通过setpriority)或nice(调用设置的最大完美值。
- RLIMIT_NOFILE //指定比进程可打开的最大文件描述词大一的值,超出此值,将会产生EMFILE错误。
- RLIMIT_NPROC //用户可拥有的最大进程数。
- RLIMIT_RTPRIO //进程可通过sched_setscheduler和sched_setparam设置的最大实时优先级。
- RLIMIT SIGPENDING //用户可拥有的最大挂起信号数。
- RLIMIT_STACK //最大的进程堆栈,以字节为单位。
(2)rlim:描述资源软硬限制的结构体
struct rlimit {
rlim_t rlim_cur;rlim_t rlim_max;
};
一个服务器程序抛开硬件(CPU、内存、带宽)限制以外,还会受到Linux系统的资源限制。所以,如果我们想要增 加Linux服务器并发访问的客户端数量,则需要在服务器程序里通过调用setrlimit()函数来修改这些限制。
(1)fork()系统调用
由于fork()系统调用会创建一个新的进程,这时它会有两次返回。一次返回是给父进程,其返回值是子进程的PID(Process ID),第二次返回是给子进程,其返回值为0。
我们在调用fork()后,需要通过其返回值来判断当前的代码是在父进程还是子进程运行。
返回值为0:子进程
返回值>0:父进程
返回值<0:fork()系统调用出错
fork 函数调用失败的原因主要有两个:
①系统中已经有太多的进程;
② 该实际用户 ID 的进程总数超过了系统限制。
每个子进程只有一个父进程,并且每个进程都可以通过getpid()获取自己的进程PID,也可以通过getppid()获取父进程的 PID,这样在fork()时返回0给子进程是可取的。一个进程可以创建多个子进程,这样对于父进程而言,他并没有一个API函数可 以获取其子进程的进程ID,所以父进程在通过fork()创建子进程的时候,必须通过返回值的形式告诉父进程其创建的子进程PID。 这也是fork()系统调用两次返回值设计的原因。
注意:
fork()系统调用会创建一个新的子进程,这个子进程是父进程的一个副本。这也意味着,系统在创建新的子进程成功后,会将 父进程的文本段、数据段、堆栈都复制一份给子进程,但子进程有自己独立的空间,子进程对这些内存的修改并不会影响父进程 空间的相应内存。这时系统中出现两个基本完全相同的进程(父、子进程),这两个进程执行没有固定的先后顺序,哪个进程先执 行要看系统的进程调度策略。如果需要确保让父进程或子进程先执行,则需要程序员在代码中通过进程间通信的机制来自己实 现。
(2)vfork()系统调用
vfork)是另外一个可以用来创建进程的函数,他与fork()的用法相同,也用于创建一个新进程。
vfork()并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec或exit(),于是也就不会引用该地址空间了。不过子进程再调用exec(或exit()之前,他将在父进程的空间中运行,但如果子进程想尝试修改数据域(数据段、堆、栈)都会带来未知的结果,因为他会影响了父进程空间的数据可能会导致父进程的执行异常。
vfork()会保证子进程先运行,在他调用了exec或exit()之后父进程才可能被调度运行。如果子进程依赖于父进程的进一步动作,则会导致死锁。
使用了写时复制(CopyOnWrite)技术:这些数据区域由父子进程共享,内核将他们的访问权限改成只读,如果父进程和子进程中的任何一个试图修改这些区域的时候,内核再为修改区域的那块内存制作一个副本。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。