首页 > 代码库 > Linux进程

Linux进程

1、使用命令行启动、撤销进程

1)启动进程

linux启动进程的命令可以用shpython ./文件名

这里我编写的是Python文件作为演示,所以用Python 文件名创建进程

 技术分享

上面,我用vi编辑了一个名为testpy文件,用cat命令查看文件内容,该程序主要是执行3秒钟输出一条“我是一个进程”的字符串,及当前时间戳,一共输出三次。然后,用python 文件名的命令执行该程序,运行结果如上

2)查看进程

我们可以用conky查看进程中是否有py文件在执行

 技术分享

运行程序可以看到,有一个进程号为6622Python文件正在执行

现在我将程序改了一下,让他陷入一个死循环,并且每隔3秒输出指定内容以及他的进程号。

 技术分享

从运行结果可以看出,进程号是6773linux在启动一个进程时,系统会在/proc下创建一个以pid命名的文件夹,该文件夹下存放了进程的信息,其中包括一个名为exe的文件即记录了绝对路径,通过llls l命令即可查看。于是我们进入到/proc/6773文件夹下

 技术分享

cwd符号链接的是进程运行目录,exe符号连接就是执行程序的绝对路径

同样可以查看进程的命令还有topps

top是一个即时显示process动态的命令:

 技术分享

使用方式:top [-] [d delay] [q] [c] [S] [s] [i] [n] [b]

d :改变显示的更新速度,或是在交谈式指令列( interactive command)s

q :没有任何延迟的显示速度,如果使用者是有superuser的权限,则top将会以最高的优先序执行

c :切换显示模式,共有两种模式,一是只显示执行档的名称,另一种是显示完整的路径与名称

S :累积模式,会将己完成或消失的子行程( dead child process )CPU time累积起来

s :安全模式,将交谈式指令取消,避免潜在的危机

i :不显示任何闲置(idle)或无用(zombie)的行程

n :更新的次数,完成后将会退出top

b :批次档模式,搭配"n"参数一起使用,可以用来将top的结果输出到档案内

Ps可以显示瞬间行程 (process) 的动态:

 技术分享

使用方式:ps [options] [--help]
参数:ps的参数非常多, 在此仅列出几个常用的参数并大略介绍含义
-A    列出所有的进程
-w    显示加宽可以显示较多的资讯
-au    显示较详细的资讯
-aux    显示所有包含其他使用者的行程

3)结束进程

进程的退出有两种情况,一种是当程序执行完成后正常退出,还有一种就是由用户强行终止,现在,我们的程序是死循环,是无法正常退出的,于是,我们需要用kill命令强行终止他,kill -9 pid

 技术分享

2、调用系统函数接口

1)创建进程(多个)

fork()函数可以创建进程,他的实质是一个进程的克隆,创建的进程独立于父进程单独存在,父子两个进程同时运行,如果fork()函数返回的是0,则代表该进程是子进程,若返回的值大于0,则代表父进程,若小于0,则代表进程创建失败,且父子进程pid不同

把之前的py文件改一下,在创建子进程前输出一次当前进程的pid,即父进程pid,然后创建子进程,根据fork()返回的子进程pid值输出对应进程的pid,重复执行两次,且每次间隔3

 技术分享

从上面的运行结果可以看出,在创建子进程前的进程pid是父进程的pid7188,创建后不仅存在父进程的pid,还多出了子进程的pid7189,且两个进程相互独立,都在循环中重复了两次,最明显从程序末尾“结束进程”输出了两次可以看出

这里我只是用fork()创建了一个子进程,下面我将用他来创建多个子进程

 技术分享

上面这段程序的意思是,用一个for循环多次(这里是三次)创建子进程,且每次均由父进程创建,若fork()的返回值小于等于0,则退出for循环,执行下面的内容,而父进程则要创建满三次后才能继续执行下面的内容,这里要有一种并行的思维,不能以单一的流水线看待程序的执行,很明显的,从程序末尾输出“进程结束”四次也可以看出创建成功(三个子进程,一个父进程),从fork()的赋值也可以看出,fork()创建出的子进程不会覆盖之前存在的进程,这也体现了一种并行的思维

2)进程的并发执行

进程数量少于空闲cpu数目:

首先来看看怎样查看cpu的数量,方法很多,举几个特别的例子。

1、/proc/cpuinfo中存放了cpu的基本信息,我们可以用过他来查看

 技术分享

 技术分享

可以看到,这里列出的processor01,他是逻辑处理器的id,故共有两个逻辑cpu

2、/proc/cpuinfo通过管道过滤处理一下

 技术分享

可以看到有两个逻辑cpu

3、通过Pythonmultiprocessing库中的cpu_count()函数获取

 技术分享

下面,我来改一下test.py文件,让他执行的大意为创建一个子进程,父子进程并行执行一个二次累乘运算,每次运算后延时2秒,然后输出各自的pid及进程执行时间

 技术分享

上面是两个进程并行的情况,下面我再来改一下,改成只有一个进程跑

 技术分享

进程数量大于空闲cpu数目:

这里我创建三个子进程,也就是四个进程并行,来看下他的执行时间

 技术分享

再来看看cpu的使用情况:

 技术分享

上面这是两个进程的cpu占用率

 技术分享

上面这是多个cpu的占用率,可以看出,进程数大于cpu逻辑数时,单个程序对cpu的占用率下降了,但执行速度还是进程数小于cpu逻辑数的情况下要快些,这是因为,进程数与CPU核数一致的情况下,可以减少不必要的因为系统调度造成的性能损失,在进程与CPU调度的关系中,单个核心处理多个进程的时候,是排队处理的,所以将进程数量设置超过核心数是没有太大意义的。

进程阻塞等待子进程:

下面再用/proc来看看,当用wait()阻塞父进程时,父子进程的状态是怎样的:

这是改了之后的代码,为方便查看状态让父子两个进程循环输出进程号,这里flag的作用是,让wait()函数在while循环中只执行一次,当子进程结束后即可唤醒父进程:

 技术分享

那么一开始执行的时候,只有子进程在运行,父进程备阻塞了,通过命令:/proc/[pid]/status可以看到此时父进程的状态是sleeping的:

 技术分享

而子进程的状态是running

 技术分享

接着,我们kill掉子进程,唤醒父进程,父进程此时的状态就变成Running了:

 技术分享

父子进程执行不同的可执行文件

我把函数改成这样,让子进程在执行时通过execl()函数调用child_test.py文件,输出各自的进程号,可以看到,虽然子进程调用了child_test.py但他们的进程号都是一样的

 技术分享

生成3层或以上的父子进程树

这里我生成三层进程树,为方便查看其关系,每层均为一个进程,flag用来确保第三层只有一个进程,py文件如下:

 技术分享

下面,我们用/proc命令查看,首先来系统的介绍一下/proc文件:

/proc文件系统,不是普通的文件系统,而是系统内核的映像,该目录中的文件时存放在系统内存中的,它以文件系统的形式为访问系统内核数据的操作提供接口。

/proc文件下有根据进程号排列的信息:
之前用到的查看进程详细信息,就是到/proc/pid/status下查看的
/proc/pid/cmdline 进程启动命令
/proc/pid/cwd 链接到进程当前工作目录
/proc/pid/environ 进程环境变量列表
/proc/pid/exe 链接到进程的执行命令文件
/proc/pid/fd 包含进程相关的所有的文件描述符
/proc/pid/maps 与进程相关的内存映射信息
/proc/pid/mem 指代进程持有的内存,不可读
/proc/pid/root 链接到进程的根目录
/proc/pid/stat 进程的状态
/proc/pid/statm 进程使用的内存的状态
/proc/pid/status 进程状态信息,stat/statm更具可读性
/proc/self 链接到当前正在运行的进程

下面我们就用/proc/pid/status来查看父子进程树的关系:

这是查看第三层子进程的status文件结果

 技术分享

这里的Pid是当前进程的id,这里的PPid是当前进程的父进程idThreads表示当前进程的线程数,从上面可以看出该进程的pid19744,他的父进程的pid19743,然后我们又用proc查看pid19743status文件:

 技术分享

可以看到,pid19743的进程,他的父进程pid19742,这和我们运行程序跑出来的结果相同

Linux状态间的转换:

Linux的状态有:

Linux进程状态:R (TASK_RUNNING),可执行状态。
Linux进程状态:S (TASK_INTERRUPTIBLE),可中断的睡眠状态。
Linux进程状态:D (TASK_UNINTERRUPTIBLE),不可中断的睡眠状态。
Linux进程状态:Z (TASK_DEAD - EXIT_ZOMBIE),退出状态,进程成为僵尸进程。
Linux进程状态:T (TASK_STOPPED or TASK_TRACED),暂停状态或跟踪状态。
Linux进程状态:X (TASK_DEAD - EXIT_DEAD),退出状态,进程即将被销毁。
只有当进程从“内核运行态”转移到“睡眠状态”时,内核才会进行进程切换操作。在内核态下运行的进程不能被其它进程抢占,而且一个进程不能改变另一个进程的状态。为了避免进程切换时造成内核数据错误,内核在执行临界区代码时会禁止一切中断。

 

PIDtgid字段:

PID是一个数字,用于标识一个进程,就像学生的学号一样,每个进程都有一个唯一的编号,保存在进程描述符的pid字段中。一般的,在系统运行期间,PID都是被顺序编号,比如进程APID10,那下个创建的进程的PID则为11。不过PID的值有一个上限,当内核使用的PID达到这个上限后就会循环开始找已闲置的小PID号。在缺省状态下,最大PID值为32767(PID_MAX_DEFAULT - 1);可以通过修改/proc/sys/kernel/pid_max这个文件来减小PID上限值。而在64位系统中,PID可扩大到4194303

内核是通过一个叫pidmap的位图来管理已分配的PID号和闲置的PID号。在32位系统中,pidmap的大小就是一个页框的大小(4KB),而一个页框大小为32768位,也就是每一位代表一个PID号,1代表此PID已经被分配,0代表此PID号未被使用;而在64位系统下,pidmap会使用多个页框。

POSIX标准中规定了一个多线程应用程序中所有的线程都必须有相同的PID,在linux内核中,是使用轻量级进程实现线程的功能,但是轻量级进程也是一个进程,他们的PID都不相同,为了实现这一点,内核在进程描述符中引入了tgid字段。在linux的线程组概念中,一个线程组中所有线程使用的该线程组领头线程相同的PID,也就是该组第一个轻量级进程的PID,并保存到进程描述符的tgid字段中

 

进程间关系:

在系统中,除了进程0,一个进程是由另一个进程创建,它们都具有父子关系。如果一个进程创建多个子进程,则子进程之间有兄弟关系。在整个系统启动期间,会初始化系统的第一个进程init_task,这个进程属于内核中的一个进程,它算是所有进程的祖先,之后它会启动PID1init进程和PID2kthreadd,这两个进程之后启动的所有进程,而init_task之后会转变为一个idle进程用于CPU空闲时运行。在进程描述符中,使用real_parentparentchildrensibling这几个指针将进程关系组织在一起

 

以上操作系统作业...不正确的地方请高手纠正

Linux进程