l6-d1 进程的创建和回收

一、进程概念

1.概念

程序

存放在磁盘上的指令和数据的有序集合(文件)

静态

进程

执行一个程序所分配的资源的总称

进程是程序的一次执行过程

动态的,包括创建、调度、执行和消亡

进程比程序多了堆、栈和进程控制模块。

程序位于硬盘ROM中,进程位于内存RAM中。

BSS段(Block Started by Symbol):用来存放程序中未初始化的全局变量和静态变量的一块内存区域。

数据段:数据段通常是指用来存放程序中已初始化的全局变量的一块内存区域。

代码段:代码段通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

堆(heap):堆是用于存放进程运行中被动态分配的内存段,当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。

栈(stack):栈又称堆栈, 是用户存放程序临时创建局部变量,(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

进程控制块(pcb):

进程标识PID 、进程用户、进程状态、优先级、文件描述符表

2.进程类型

交互进程:在shell下启动。以在前台运行,也可以在后台运行

批处理进程:和在终端无关,被提交到一个作业队列中以便顺序执行

守护进程:和终端无关,一直在后台运行

3.进程状态

运行态:进程正在运行,或者准备运行

等待态:进程在等待一个事件的发生或某种系统资源

可中断

不可中断

cpu没有这么多的资源同时运行这么多的进程,进程需要轮流使用cpu,这也就需要进程进入等待状态

停止态:进程被中止,收到信号后可继续运行

死亡态:已终止的进程,但pcb没有被释放

 二、进程常用命令

1.查看进程信息

ps     查看系统进程快照,只显示当前终端运行的进程。

        ps -e //查看linux下的所有进程

        ps -elf

        ps -elf|grep name

top    查看进程动态信息

        shift+> 后翻页

        shift+< 前翻页

        top -p PID  查看某个进程

/proc  查看进程详细信息

表头

含义

F

进程标志,说明进程的权限,常见的标志有两个:

1:进程可以被复制,但是不能被执行;

4:进程使用超级用户权限;

S

进程状态。进程状态。常见的状态有以下几种:

  1. -D:不可被唤醒的睡眠状态,通常用于 I/O 情况。
  2. -R:该进程正在运行。
  3. -S:该进程处于睡眠状态,可被唤醒。
  4. -T:停止状态,可能是在后台暂停或进程处于除错状态。
  5. -W:内存交互状态(从 2.6 内核开始无效)。
  6. -X:死掉的进程(应该不会出现)。
  7. -Z:僵尸进程。进程已经中止,但是部分程序还在内存当中。
  8. -<:高优先级(以下状态在 BSD 格式中出现)。
  9. -N:低优先级。
  10. -L:被锁入内存。
  11. -s:包含子进程。
  12. -l:多线程(小写 L)。
  13. -+:位于后台。

UID

运行此进程的用户的 ID;

PID

进程的 ID;

PPID

父进程的 ID;

C

该进程的 CPU 使用率,单位是百分比;

PRI

进程的优先级,数值越小,该进程的优先级越高,越早被 CPU 执行;

NI

进程的优先级,数值越小,该进程越早被执行;

ADDR

该进程在内存的哪个位置;

SZ

该进程占用多大内存;

WCHAN

该进程是否运行。"-"代表正在运行;

TTY

该进程由哪个终端产生;

TIME

该进程占用 CPU 的运算时间,注意不是系统时间;

CMD

产生此进程的命令名;

2.进程相关命令

nice   按用户指定的优先级运行进程

     nice [-n NI值] 命令

NI 范围是 -20~19。数值越大优先级越低

普通用户调整 NI 值的范围是 0~19,而且只能调整自己的进程。

普通用户只能调高 NI 值,而不能降低。如原本 NI 值为 0,则只能调整为大于 0。

只有 root 用户才能设定进程 NI 值为负值,而且可以调整任何用户的进程。

renice   改变正在运行进程的优先级

        renice [优先级] PID

jobs   查看后台进程

        程序运行中,按ctrl+z,将程序放到后台并停止,用命令jobs 查看

bg     将挂起的进程在后台运行

fg      把后台运行的进程放到前台运行

ctrl+z  把运行的前台进程转为后台并停止

./test &  把test程序后台运行

三、创建子进程

1.概念

子进程为由另外一个进程(对应称之为父进程)所创建的进程。

2.子进程创建 – fork

#include  <unistd.h>

        pid_t  fork(void);

        创建新的进程,失败时返回-1

        成功时父进程返回子进程的进程号(>0),子进程返回0

        通过fork的返回值区分父进程和子进程

代码演示:

#include <stdio.h>
#include <unistd.h>

int main(int argc,char **argv){
    
    pid_t pid;        
    printf("before fork\n");
    pid = fork();        //创建进程
    if(pid>0){              //父进程
       printf("This is father process\n");
       printf("pid=%d\n",(int)pid);    //(int)pid 可替换为 getpid(),获取当前进程的pid
       printf("father after fork\n");
       while(1){
         sleep(1);
         printf("father sleep\n");
       }
   }else if(pid==0){        //子进程
       printf("This is child process\n");
       printf("pid=%d\n",(int)pid);
       printf("child after fork\n");
       while(1){
          sleep(1);
          printf("child sleep\n");
       }
    }else if(pid<0){        //pid<0,打印错误信息
       perror("fork");
       return 0;
    }
   // printf("pid=%d\n",(int)pid);
  //  printf("after fork\n");
}

注意:

        1)子进程只执行fork之后的代码;

        2)父子进程执行顺序是操作系统决定的;

        3子进程继承了父进程的内容;

        4父子进程有独立的地址空间,互不影响;

        5若父进程先结束,子进程成为孤儿进程,被init进程收养,子进程变成后台进程;

              若子进程先结束,父进程如果没有及时回收,子进程变成僵尸进程。

由父进程生成5个子进程

代码演示:

#include <stdio.h>
#include <unistd.h>

int main(){
    pid_t pid;
    int i;    
    for(i=0;i<5;i++){
        pid = fork();    //子进程运行fork之后的代码,但位于循环体内,循环执行
        if(pid<0){
            perror("fork");
            return 0;
        }else if(pid==0){
            printf("child process\n");
            sleep(5);
            break; //子进程跳出循环,不生成孙进程
        }else{
            printf("Father process\n");
            sleep(5);  //breaK  //若需要单个父进程生成单个子进程,则父进程跳出循环
        }
    }

    sleep(100);
}

五、进程的退出

#include <stdlib.h>

        void  exit(int  status);

#include <unistd.h>

        void  _exit(int  status);

        void  _Exit(int  status);        //_exit和_Exit用法一致

功能:

        结束当前的进程并将status返回

        exit结束进程时会刷新(流)缓冲区,_exit则不会

代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc,char**argv){

   printf("hello world");    //字符串位于缓冲区
   _exit(0);            //直接结束进程,未刷新缓冲区,因此“hello world”不打印
   printf("after exit");
   return 0;
}

return 和exit的区别

        main函数结束时会隐式地调用exit函数,普通函数return是返回上一级。

六、进程的回收

子进程结束时由父进程回收、孤儿进程由init进程回收

若没有及时回收会出现僵尸进程 

1.wait

#include <sys/wait.h>

#include <sys/types.h>

        pid_t wait(int *status);

        成功时返回回收的子进程的进程号;失败时返回EOF

        若子进程没有结束,父进程一直阻塞

        若有多个子进程,哪个先结束就先回收

        status 指定保存子进程返回值结束方式的地址

        status为NULL表示直接释放子进程PCB,不接收返回值

进程返回值和结束方式

        子进程通过exit/ exit / return 返回某个值(0-255)

        父进程调用wait(&status)回收

                WIFEXITED(status)                判断子进程是否正常结束

                WEXITSTATUS(status)                获取子进程返回值

                WIFSIGNALED(status)                判断子进程是否被信号结束

                WTERMSIG(status)                获取结束子进程的信号类型

代码演示:

//省略...
int status;
pid_t pid;

if((pid = fork())<0){
    perror("fork"); exit(-1);
}
else if (pid == 0){
    sleep(1); 
    exit(2);    //退出
}
else {
    wait(&status);     //等待子进程结束,结束后的返回值和结束方式保存在&status中
    printf("%x\n", status);
}

2.waitpid

 #include  <sys/wait.h>

         pid_t waitpid(pid_t pid, int *status, int option);

        成功时返回回收的子进程的pid或0;失败时返回EOF

        pid可用于指定回收哪个子进程或任意子进程

        status指定用于保存子进程返回值和结束方式的地址

        option指定回收方式,0(阻塞,等待进程结束再回收)

                           或 WNOHANG(若进程还未结束,则返回0;若>0,表示进程结束,回收成功)

示例:

        waitpid(pid, &status, 0); 
        waitpid(pid, &status, WNOHANG);
        waitpid(-1, &status, 0);                //-1表示当前进程的任一子进程
        waitpid(-1, &status, WNOHANG);

代码演示:

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char** argv){

   pid_t pid;
   pid_t rpid;
   pid = fork();
   int status;
   if(pid<0){
      perror("fork");
      return 0;
   }
   else if(pid == 0){
       sleep(10);
       printf("child will exit\n");
       exit(2);

   }else if(pid >0){
       //rpid = wait(&status);
       sleep(20);
       waitpid(-1,&status,WNOHANG);
       printf("Get child status=%x\n",WEXITSTATUS(status));
   }

   while(1){
     sleep(1);
   }
}

七、作业

实现一个进程链,父进程->子进程->孙进程->重孙进程->重重孙进程

#include<stdio.h>
#include<unistd.h>
int main(){
	pid_t pid;
	int i;
	for(i = 0;i < 5;i++){
		pid = fork();
		if(pid < 0){
			perror("fork");
			return 0;
		}else if(pid == 0){
			printf("child process\n");
			sleep(5);
		}else{
			printf("father process\n");
			sleep(5);
			break;
		}
	}
	sleep(100);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值