《Linux系统编程》-----DAY3

进程

​ 程序---死的,存在磁盘上,不占用系统资源,编译好的二进制文件

​ 进程---活得,运行的程序,占用系统资源

每个Linux进程都一定有一个唯一的数字标识符,称为进程ID(process ID)。进程ID总是一非负整数。
Linux启动过程有个init进程ID号是1,其它进程都是init派生出来的。 Linux启动过程有个init进程ID号是1,其它进程都是init派生出来的。

Linux下的进程结构

Linux系统是一个多进程的系统,进程之间具有并行性、互不干扰的特点。
Linux中进程包含3个段:代码段、数据段、堆栈段
image

数据段:存放全局变量常数以及动态数据分配的空间(malloc函数取得的空间)

代码段:存放程序代码

堆栈段:存放子程序的返回地址、子程序的参数以及程序的局部变量

image

内存管理单元图

image

虚拟内存和物理内存映射

image

PCB进程控制块

/usr/src/linux-headers-3.16.0-30/include/linux/sched.h

​ 文件中查看 struct task_struct结构体定义

​ 内部成员:

​ 进程id,pid_t

​ 进程状态:就绪,运行,挂起,停止

​ 进程切换时需要保存和恢复的一些cpu寄存器

​ 描述虚拟地址空间的信息

​ 描述控制终端的信息

​ 当前工作目录

​ umask掩码

​ 文件描述符表

​ 和信号相关的信息

​ 用户id和组id

进程控制

fork函数

​ pid_t fork(void)

​ 创建子进程。父进程各自返回,父进程返回子进程pid。子进程返回0

getpid();
getppid()

循环创建5个进程

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


int main()
{
    int i;
    for(i=0;i<5;i++){
        if(fork()==0)
            break;
    }
    if(i==5){
        sleep(5);
        printf("I'm parent 
");
    }
    else{
        sleep(i);
        printf("I'm %d th
",i);
    }
    
    return 0;
}

进程共享

​ 父子进程之间在fork后,有哪些相同,有哪些不同?

​ 刚fork之后

​ 父子相同处:1.全局变量,2..data、3..text、4.堆、5.栈、6.环境变量、7.用户ID、8.宿主目录、9.进程工作目录、10.信号处理方式

​ 父子不同处:1.进程ID 2.fork返回值 3.父进程ID 4.进程运行时间 5.闹钟 6.未决信号集

​ 父子进程共享:

读时共享,写时复制---------全局变量

​ 1.文件描述符

​ 2.nmap映射区

参考书:

​ 《操作系统原理》 谢青松

​ 《计算机硬件及组成原理》 Arnoid S.Berger

exec函数族

重点掌握:

	int execl(const char *path,const char * arg,...);

​	int execlp(const char *file, const har *arg,...);

​ 加载一个进程,借助 PATH 环境变量

int execlp(const char *file, const char *arg, ...);

​ 成功:无返回;

​ 失败:-1

​ 参数 1:要加载的程序的名字。该函数需要配合 PATH 环境变量来使用,当 PATH 中 所有目录搜索后没有参数 1 则出错返回。 该函数通常用来调用系统程序。如:ls、date、cp、cat 等命令。

 #include<stdio.h>                                                                                                                                                         
 #include<unistd.h>
 #include<stdlib.h>
 
 int main(int argc,char*argv[]){
 
     pid_t pid=fork();   //创建子进程
     if(pid==-1){
         perror("fork error");
         exit(1);
     }   
     else if(pid==0){    //子进程
          // execlp("ls","-l","-d","-h",NULL);   错误写法
	     // execlp("ls","ls","-l","-h",NULL);
         //execlp("date","date",NULL);
         //  execlp("./test","./test",NULL);
      
         execl("./test","./test",NULL);
      
         perror("exec error");
         exit(1);
     }   
     else if(pid>0){     //父进程
         sleep(1);
         printf("I'm parent:%d
",getpid());
     }   
 
 
     return 0;
 }

image

回收子进程

孤儿进程(父进程先挂,子进程变成孤儿):

​ 父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程为init进程

​ 称为init进程领养孤儿进程

僵尸进程(子进程挂了,父进程一直不回收,子进程变成僵尸进程)

僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的PCB资源,此时子进程将成为一个僵尸进程

​ 特别注意,僵尸进程是不能使用kill命令清除掉的。因为kill命令是用来终止进程的,而僵尸进程已经终止。用什么办法可以清除僵尸进程?--------------杀死父进程

wait函数

​ 父进程调用 wait 函数可以回收子进程终止信息。该函数有三个功能:

  1. ​ 阻塞等待子进程退出
  2. ​ 回收子进程残留资源
  3. ​ 获取子进程结束状态(退出原因)。

pid_t wait(int *status);

成功:清理掉的子进程 ID;

​ 失败:-1 (没有子进程)


可使用 wait 函数传出参数 status 来保存进程的退出状态。借助宏函数来进一步判断进 程终止的具体原因。宏函数可分为如下三组:

1.WIFEXITED(status) 为非 0 → 进程正常结束 WEXITSTATUS(status)

如上宏为真,使用此宏 → 获取进程退出状态 (exit 的参数)

  1. WIFSIGNALED(status) 为非 0 → 进程异常终止 WTERMSIG(status)
    如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。

  2. WIFSTOPPED(status) 为非 0 → 进程处于暂停状态 WSTOPSIG(status)
    如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。 WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行

waitpid函数 指定某一个进程进行回收

pid_t waitpid(pid_t pid,int *status,int options)

参数:

​ pid:指定回收的子进程id、

​ >0:待回收的子进程pid

​ -1:任意子进程

​ 0:同组的子进程

​ status:(传出)回收进程的状态

​ options:WNOHANG 指定回收方式为:非阻塞

返回值:

​ >0:表示成功回收的子进程pid

​ 0:函数调用时,参3指定了WNOHANG,并且,没有子进程结束。

​ -1:失败errno

总结:

​ wait,waitpid 一次调用,回收一个子进程

​ 想回收多个,循环

​ waitpid(-1,&status,0)==wait(&status);

原文地址:https://www.cnblogs.com/hongweijiang/p/14514210.html