Linux进程学习笔记

进程与线程

1、进程的相关概念

(1)、进程与程序:

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

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

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

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

2、进程控制

(1)、fork函数

pid_t fork(void)

问题:

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

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

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

  4. 如何区分父子进程?
    答:通过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;
    • 一般执行自己写的程序;
#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,函数阻塞;
          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函数,只能回收一个子进程。可以选择回收那个子进程;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值