Linux——进程介绍

2019-07-14 08:09发布

一、进程1.基本概念    课本概念:程序的一个执行实例,正在执行的程序等
     内核观点:担当分配系统资源的实体
描述进程——PCB    进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合
    课本上之所以称之为PCB(process control block),Linux操作系统下的PCB是task_struct
task_struct——PCB的一种(是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息)task_struct内容分类    标识符:描述本进程的唯一标识符,用来区别其他进程    状态:任务状态,退出代码,退出信息等    优先级:相对于其他进程的优先级    程序计数器:程序中即将被执行的下一条指令的地址
    内存指针:包含程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
    上下文数据:进程执行时处理器的寄存器中的数据
    I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表
    记账信息:可能包含处理器时间总和,使用的时钟数总和,时间限制,记账号等
    其他信息
2.组织进程     可以在内核源代码里找到它,所有运行在系统里的进程都以task_struct链表的形式存在内核里
3.查看进程    进程的信息可以通过/proc系统文件夹查看
    如要获取PID为1的进程信息,需要查看/proc/1这个文件夹:


通过系统调用来获取进程标识符    系统为每一个进程分配一个唯一的标识号,这个标识号就称为进程的ID(process identifer)
    进程id(PID)
    父进程id(PPID) #include #include #include int main() { printf("pid:%d ",getpid()); printf("ppid:%d ",getppid()); return 0; }
4.通过系统调用创建进程——fork()初识进程创建的一般过程:    1.分配PID
    2.分配PCB
    3.将父进程环境复制过来
    4.将父进程地址空间的内容拷贝到自己的地址空间
    5.放入就绪队列
    使用man手册认识fork():

    返回值:

    父子进程代码共享,数据各自开辟空间 #include #include #include int main() { int ret=fork(); printf("hello proc:%d,ret:%d ",getpid(),ret); return 0; }
    fork()之后通常要用if分流: #include #include #include int main() { int ret=fork(); if(ret<0){ perror("fork"); return 1; } else if(ret==0){ //child printf("I am child:%d,ret:%d ",getpid(),ret); } else{ //father printf("I am father:%d,ret:%d ",getppid(),ret); } sleep(2); return 0; }
二、程序和进程    程序:完成特定任务的一系列指令集合    进程:从用户角度看,进程是程序的一次动态执行过程;从操作系统角度看,进程是操作系统分配资源的基本单位,也是最小单位   每个进程都有自己的状态和独立的地址空间    进程需要有相关的数据结构来管理,操作系统通过PCB感知一个进程的存在
    程序:数据+代码
    进程:数据+代码+堆栈+PCB
进程和程序的区别:    进程是动态,程序是静态
    进程的生命周期短暂,程序永久
    进程有重要的数据结构PCB
    一个进程只能对应一个程序,一个程序可以对应多个进程
三、进程状态
1.进程的几种状态    R 运行状态(running):并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里
    S 睡眠状态(sleeping):意味着进程在等待事件完成(也可叫做可中断睡眠 interruptible sleep)
   D 磁盘休眠状态(Disk sleep):有时也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待 I/O的结束
    T 停止状态(stopped):可以通过发送SIGSTOP信号给进程来停止(T)进程,这个被暂停的进程可以通过发送SIGCONT信号让进程继续运行
    X 死亡状态(dead):这个状态只是一种返回状态,你不会在任务列表里看到这个状态

2.进程状态的修改    通过kill演示一下T、R状态:
    · kill -l:查看系统支持的信号列表
    · ps -l:查看进程信息
    · kill -SIGSTOP pid:将该进程状态改为T状态
    · kill -SIGCONT pid:将该进程状态改为R状态

Z(zombie)僵尸进程    · 僵尸状态(zombie)是一个比较特殊的状态,当进程退出并且父进程(使用wait()系统调用)没有读到子进程退出的返回代码时就会产生僵尸进程
    · 僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态码
    · 所以,只要子进程退出父进程还在运行,但父进程中没有读取子进程状态,子进程进入Z状态
举例:维持30秒僵尸进程 #include #include int main() { pid_t id=fork(); if(id<0){ perror("fork"); return 1; } else if(id>0){ printf("parent [%d] is sleeping... ",getpid()); sleep(30); } else{ printf("child [%d] is begin z... ",getpid()); sleep(5); exit(EXIT_SUCCESS); } return 0; }

编译运行并在另一个终端下启动监控:

测试结果:
可以看到子进程5秒后由S状态进入僵尸状态僵尸进程危害    · 进程的退出状态必须被维持下去,父进程若一直不读取,子进程将一直处于Z状态
    · 维护退出状态本身就是要用数据维护,也属于进程的基本信息,所以保存在PCB中,即Z状态一直不退出,PCB一直都要维护
    · 若一个父进程创建了很多子进程,就是不回收,就会造成资源的浪费,因为数据结构本身就要占用内存,就像C语言中定义一个结构体变量,是要在内存的某个位置开辟空间
    · 造成内存泄露
孤儿进程    父进程如果提前退出,那么子进程后退出,进入Z状态之后,该如何处理?
    父进程先退出,子进程就被称为孤儿进程
    孤儿进程被1号init进程“领养”(当然要有init进程回收)举例: #include #include #include int main() { pid_t id=fork(); if(id<0){ perror("fork"); return 1; } else if(id==0){ printf("I am child,pid:%d ",getpid()); sleep(10); } else{ printf("I am father,pid:%d ",getpid()); sleep(3); exit(0); } return 0; }
测试结果: