LInux进程与线程学习笔记

浪子不回头ぞ 提交于 2019-12-03 11:47:46

进程与线程

1、进程的相关概念

(1)、进程与程序:

1、进程是动态的,程序是静态的;
2、进程有生命周期,程序没有生命周期;
3、一个进程只能对应一个程序,一个程序却可以对应多个进程,没有建立进程的程序不能作为一个独立的单位获得操作系统的认可;

(2)、进程控制块(PCB)

每个进程在内核中都有一个进程控制块来维护进程的相关信息,linux内核的进程控制块是task-struct结构体。内部成员很多,主要有:

a、进程ID:系统中每个进程都有唯一的id,用pid_t类型表示。
b、进程状态:有初始、就绪、运行、挂起、终止五个状态。
c、进程切换需要保存和恢复的CPU寄存器。
d、描述虚拟地址空间的信息。
e、描述控制终端的信息。
f、当前工作目录。
g、文件描述符表:包含很多指向file结构体的指针。
h、和信号相关的信息。
i、用户组id和组id。
j、会话(session)和进程组。
k、umask掩码。
l、进程可以使用的资源上限。

2、进程控制

(1)、fork函数

pid_t fork(void)

问题:
a、fork函数的返回值?
答:当fork函数创建子进程成功后,会返回两个,一个数为0:代表子进程的返回值;当返回值大于0时:父进程返回值,代表子进程的id。

b、子进程创建成功后,代码的执行位置?
答:父进程执行到哪,子进程就从哪里开始执行。

c、父子进程的执行顺序?
答:随机执行,谁先抢到cpu,谁先执行。

d、如何区分父子进程?
答:通过fork函数的返回值

// 通过判断for循环的打印结果即可知道问题b与问题c答案
#include <stdio.h>   
#include <stdlib.h>    
#include <unistd.h>
#include <sys/types.h>  
#include <sys/stat.h>  
int main(int argc,const char* argv[])
{
        pid_t pid;
        for(int i=0;i<4;i++)
        {
            printf("----i-----=%d\n",i);
        }
        pid = fork();
        //父进程
        if(pid>0)
        {
            printf("parent process,pid=%d"\n",getpid());
        }
        // 子进程
        else if(pid == 0)
        {
            printf("child process,pid=%d,ppid=%d\n"\n",getpid(),getppid());
        }
        for(int i=0;i<4;i++)
        {
            printf("i=%d\n",i);
        }
        return 0;
}

(2)、循环创建多个子进程

#include <stdio.h>   
#include <stdlib.h>    
#include <unistd.h>
#include <sys/types.h>  
#include <sys/stat.h>  
int main(int argc,const char* argv[])
{
    int number=3;//要创建的子进程个数
    pid_t pid;
    for(int i=0;i<number;i++)
    {
        pid = fork();
        if(pid==0)//子进程不再进程fork()
        {
            break;
        }
    }
    //如何判断是第几个孩子
    if(i==0)
    {
        printf("first process,pid=%d\n",getpid());
    }
    if(i==1)
    {
        printf("second process,pid=%d\n",getpid());
    }
    if(i==2)
    {
        printf("third process,pid=%d\n",getpid());
    }
    if(i==number)
    {
        printf("father process,pid=%d\n",getpid());
    }
    return 0;
}

2、进程相关命令

1、查询某个进程的id

ps aux | grep XXX
ps ajx | grep XXX

2、kill 向指定的进程发送信号
查看信号:

kill -l

杀死某个进程:

kill -9(SIGKILL) pid

3、进程之间的数据共享

父子进程之间的数据:
读时共享,写时复制。

4、exec函数蔟

  • (1)、exec函数:
    • 让父子进程执行不相干的操作;
    • 能够替换进程地址空间中的源代码.txt段;
    • 在当前程序中调用另外一个程序;
      • 在执行exec之前需要fork();
    • 返回值:
      • 如果函数执行成功,不返回;
      • 如果执行失败,打印错误信息,退出当前进程;
  • (2)、执行指定目录下的程序:
    int execl(cnst char* path,const char* arg,...)
    • path:要执行的程序的绝对路径;
    • 变参arg:要执行的程序所需要的参数;
    • 第一arg:占位;
    • 后边的arg:命令的参数;
    • 参数写完以后:NULL;
    • 一般执行自己写的程序;
      ```c
      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      #include <sys/types.h>
      #include <sys/stat.h>
      int main(int argc,const char* argv[])
      {

      pid_t pid = fork();
      for(int i=0;i<4;i++)
      {
      printf("it is parent process\n");
      }
      //子进程执行ls命令
      if(pid==0)
      {
      execl("/bin/ls","ls","-l",NULL);
      }
      //运行程序后,会发现下面的代码只有父进程打印了
      for(int i=0;i<4;i++)
      {
      printf("=====it is parent process====\n");
      }
      return 0;
      }

* **(3)、执行PATH环境变量能够搜索到的程序:**int execlp(cnst char file,const char arg,...)```
* file:要执行的命令的位置
* 变参arg:要执行的程序所需要的参数
* 第一arg:占位
* 后边的arg:命令的参数
* 参数写完以后:NULL
* 执行系统自带的程序

    #include <stdio.h>   
    #include <stdlib.h>    
    #include <unistd.h>
    #include <sys/types.h>  
    #include <sys/stat.h>  
    int main(int argc,const char* argv[])
    {
        pid_t pid = fork();
        for(int i=0;i<4;i++)
        {   
            printf("it is parent process\n");
        }
        //子进程执行ps命令
        if(pid==0)
        {   //这里不需要指定路径,系统会在PATH中找到该命令
            int ret = execlp("ps","ps","aux",NULL);.
            perror("Execlp:");//如果execlp执行错误(返回-1),系统才会执行该代码。(所以可以不写ret)
            exit(1);
        }
        //运行程序后,会发现下面的代码只有父进程打印了
        for(int i=0;i<4;i++)
        {   
            printf("=====it is parent process====\n");
        }
        return 0;
    }

5、进程回收

1. 孤儿进程

  • 爹生孩子;
  • 爹先死,孩子还活着,孩子叫孤儿进程;
  • 孤儿被init进程领养,init进程变为孤儿进程的父亲;
  • 为了释放子进程占用的系统资源;
    • 进程结束后,能够释放用户去空间;
    • 释放不了pcb,必须由父进程释放;
    #include <stdio.h>   
    #include <stdlib.h>    
    #include <unistd.h>
    #include <sys/types.h>  
    #include <sys/stat.h>  
    int main(int argc,const char* argv[])
    {
      pid_t pid = fork();
      if(pid==0)
      {
          sleep(1);//子进程睡一秒,保证父进程先死
          printf("child pid = %d,ppid = %d\n",getpid(),getppid());
      }
      else if(pid > 0)
      {
          printf("==========parent process==========");
          printf("parent pid = %d,ppid = %d\n",getpid(),getppid());
      }
      // 可以看到,程序运行完以后,两个parent pid不一样,因为父亲先挂,子进程被干爹init进程收养
      return 0;
    }
    2. 僵尸进程
  • 孩子死了,爹还活着,爹不去释放子进程的pcb,孩子变为僵尸进程;
#include <stdio.h>   
#include <stdlib.h>    
#include <unistd.h>
#include <sys/types.h>  
#include <sys/stat.h>  
int main(int argc,const char* argv[])
{
    pid_t pid = fork();
    if(pid==0)
    {
        printf("child pid = %d,ppid = %d\n",getpid(),getppid());
    }
    else if(pid > 0)
    {   //父进程死循环,子进程挂掉后,父进程没办法去回收子进程pcb,孩子变为僵尸进程
        while(1)
        {
            printf("==========parent process==========");
            printf("parent pid = %d,ppid = %d\n",getpid(),getppid());
        }
    }
    // 假设程序名字为app,则执行完以后 去查 ps aux | grep app,可以看到有个app的状态为Z+(zombie:僵尸)
    return 0;
}


** 3. 进程回收**
编程过程中,有时需要让一个进程等待另一个进程,最常见的是父进程等待自己的子进程,或者父进程回收自己的子进程资源包括僵尸进程。
  • (1)、wait(阻塞函数)
    • pid_t wait(int *status);
      • 头文件:
        • #include <sys/types.h> /* 提供类型pid_t的定义*/
        • include <wait.h>

      • 函数作用:
        1. 阻塞并等待子进程退出;
        2. 回收子进程残留资源;
        3. 回去子进程结束的原因;
      • 返回值:
        • -1:回收失败:已经没有子进程了;
        • >0:回收的子进程对应的的pid;
      • 参数:status,子进程的退出状态
        • 判断子进程是如何挂掉的(需要四个宏)
        1. WIFEXITED(status):是否正常退出,如果该宏为真,则使用下面这个宏可以获取进程退出的状态(exit/return)的参数;
          • WEXITSTATUS(status);
        2. WIFSIGNALED(status):是否被某个信号杀死,如果该宏结果为非零,则使用下面的宏可以取得使进程终结的那个信号的编号
          • WTERMSIG(status);
      • 调用一次wait函数,只能回收一个子进程;
      #include <stdio.h>   
      #include <stdlib.h>    
      #include <unistd.h>
      #include <sys/types.h>  
      #include <sys/stat.h>  
      #include <wait.h>
      int main(int argc,const char* argv[])
      {
        pid_t pid = fork();
        if(pid >0)//父进程回收子进程
        {
        printf("parent process,pid = %d,ppid = %d\n",getpid(),getpid());
        int status;
        pit_t wpid = wait(&status); 
        //判断是否是正常退出的
        if(WIFEXITED(status))
        {   // 打印退出状态参数
            printf("exit value:%d\n",WEXITSTATUS(status));
        }
        //判断是否是被信号杀死的
        if(WIFEXITED(status))
        {   // 查看是被那个信号杀死的
            printf("killed   by signal:%d\n",WTEMSIG(status));
        }
        printf("died child pid = %d\n",wpid);
        }
        else if
        {
        sleep(2);
        printf("child process,pid = %d,ppid = %d\n",getpid(),getpid());
        }
        return 0;
      }
  • (2)、 waitpid
    • pid_t waitpid(pid_t pid,int *status,int options);
      • 头文件:
        • #include <sys/types.h> /* 提供类型pid_t的定义*/
        • include <wait.h>

      • 函数作用:
        • 同wait函数;
      • 返回值:
        • -1:回收失败:已经没有子进程了;
        • >0:回收的子进程对应的的pid;
        • =0:参数3为WNOHANG,且子进程正在运行;
      • 参数:
        1. pid:
          1. pid==-1:等待任一个子进程,与wait函数等效;
          2. pid>0:等待其的进程ID与pid相等的子进程;
          3. pid==0:等待其组ID等于调用进程的组ID的任一子进程;
          4. pid<-1:等待其组ID等于pid的绝对值的任一子进程;
        2. status:子进程的退出状态,用法同wait函数;
        3. options:设置为WNOHANG,函数为非阻塞,设置为0,函数阻塞;
      • 调用一次wait函数,只能回收一个子进程。可以选择回收那个子进程;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!