02-进程和进程之间的通信

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

对于C语言中涉及的进程、线程和进程之间的通信,将会以文章的形式对其进行讲解,也可以充当复习的笔记使用。


提示:以下是本篇文章正文内容,下面案例可供参考

一、进程是什么?

程序:存放在磁盘上的指令和数据的有序集合(文件),是静态的。

进程:执行一个程序所分配资源的总称,进程是程序的一次执行过程,是动态的,生命周期:包括创建、调度、执行和消亡。

二、查看进程信息

ps // 查看系统进程快照
top  // 实时查看系统信息   tip:  shift + >  后翻页   shift + < 前翻页
top -p PID   // 查看PID的进程
nice [-n Ni值] 命令  // 按用户制定的优先级运行进程-
// -NI 范围是-20~19,数值越大,优先级越低。
renice [优先级] PID  // renice 改变正在运行进程的优先级

1.创建子进程

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

代码如下(示例):创建fork_t.c 文件。

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


int main(int argc, char const *argv[])
{
    pid_t pid;
    pid = fork();  // fork 主打复制
    
    // 让父子进程执行不同的代码----执行的先后顺序,没有规律,由操作系统进行调度
    // 通过fork的返回值区分父进程和子进程
    // 子进程只执行fork之后的代码
    if (pid > 0)
    {
        printf("This is a father process\n");
    }else if(pid == 0)
    {
        printf("This is a son process!\n");

    } else if(pid < 0)
    {
        perror("fork");
        return 0;
    }
    printf("pid = %d\n", pid);      // pid=403, pid = 0

    return 0;
}

2.父子进程之间的关系

  1. 子进程继承了父进程的内容
  2. 父子进程有独立的地址空间,互不影响。
  3. 若父进程先结束
    3.1 子进程变为孤儿进程,被init进程收养。
    3.2 子进程变为后台进程。
  4. 若子进程先结束
    4.1 父进程如果没有及时回收,子进程变为僵尸进程。
ps -elf|grep fork_t    // 查看fork_t 的PID 和PPID

在这里插入图片描述

kill -9 490   // 杀死父进程ID 490

在这里插入图片描述

3. 子进程的进阶

3.1 要达到一个父进程产生多个子进程的时候,例如下图。

在这里插入图片描述
代码如下:

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


int main(int argc, char const *argv[])
{
    pid_t pid;
    for (size_t i = 0; i < 5; i++)
    {
        pid = fork();
    if (pid < 0)
    {
        perror("fork");
        return 0;
    } else if(pid == 0)
    {
        printf("This is child process!\n");
        sleep(5);
        break;        // 不让子进程再次复制
    } else {
        printf("This is father process!\n");
        sleep(5);
    }
        sleep(100);      // 避免子进程直接变为僵尸进程。

    }
    
    return 0;
}

3.2 同理要产生孙进程,那么让父进程直接跳出当前for循环,不再进行复制即可。

4. 进程的退出

4.1 创建文件exit_t.c,展示exit在main函数中调用后的作用。

exit (正常结束进程) 
相关函数 _exit, atexit, on_exit 
头文件 #include <stdlib.h> 
定义函数 void exit(int status); 
函数说明 exit()用来正常终结目前进程的执行, 并把参数 status 返回给父进程, 而进程所有的缓冲区数据会自
动写回并关闭未关闭的文件. 
返回值

代码如下:

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

int main(int argc, char const *argv[])
{
    printf("hello world");   // 不加换行符,不会刷新缓冲区,也就是无法在屏幕呈现出这句话。
    exit(0);   // 充当刷新缓冲区的作用。
    printf("after exit");     // 这句话再执行的时候不会被打印。

    return 0;   // 只有在main 函数中,才会隐式调用了exit函数
}

补充: exit函数的作用是结束当前的进程并将status返回,exit结束进程时会刷新(流)缓冲区。

注意: return 和exit的区别

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

5. 进程的回收

子进程是由父进程创造出来的,当子进程结束后,父进程没有及时回收子进程的话,子进程就会变为僵尸进程,占用系统的资源,这节主要将怎么对子进程进行回收。

wait (等待子进程中断或结束) 
相关函数 waitpid, fork 
头文件 #include <sys/types.h> 
 #include <sys/wait.h> 
定义函数 pid_t wait (int * status); 
函数说明 wait()会暂时停止目前进程的执行, 直到有信号来到或子进程结束. 如果在调用 wait()时子进程已经
结束,wait()会立即返回子进程结束状态值. 子进程的结束状态值会由参数 status 返回, 而子进程的进程识
别码也会一快返回. 如果不在意结束状态值, 则 参数 status 可以设成 NULL. 子进程的结束状态值请参考
waitpid(). 
返回值 如果执行成功则返回子进程识别码(PID), 如果有错误发生则返回-1. 失败原因存于 errno 中.

代码演示:

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


int main(int argc, char const *argv[])
{
    pid_t pid;
    pid_t rpid;
    pid = fork();
    int status;

    if (pid < 0)
    {
        perror("fork"); 
        return 0;        // pid < 0 直接退出

    }else if(pid > 0){    // pid > 0 父进程
        rpid = wait(&status);
        printf("Get child status=%x\n", WEXITSTATUS(status));  // 返回自定义的返回值

    } else if(pid == 0) {  // pid == 0 是子进程
        sleep(1);
        printf("child will exit!\n");
        exit(2);
    }
    
    
    return 0;
}
运行 gcc -o wait_t wait_t.c 后,发现没有僵尸进程了,返回了自定义的返回值。

在这里插入图片描述

拓展知识: waitpid函数- -> 可以查看子进程的结束状态值

waitpid (等待子进程中断或结束) 
相关函数 wait, fork 
头文件 #include <sys/types.h> 
 #include <sys/wait.h> 
定义函数 pid_t waitpid(pid_t pid, int * status, int options); 
函数说明 waitpid()会暂时停止目前进程的执行, 直到有信号来到或子进程结束. 如果在调用 wait()时子进程
已经结束,wait()会立即返回子进程结束状态值. 子进程的结束状态值会由参数 status 返回, 而子进程的进
程识别码也会一快返回. 如果不在意结束状态值, 则参数 status 可以设成 NULL. 参数 pid 为欲等待的子进程识
别码, 其他数值意义如下: 
 pid<-1 等待进程组识别码为 pid 绝对值的任何子进程. 
 pid=-1 等待任何子进程, 相当于 wait(). 
 pid=0 等待进程组识别码与目前进程相同的任何子进程. 
 pid>0 等待任何子进程识别码为 pid 的子进程. 
 参数 option 可以为 0 或下面的 OR 组合
 WNOHANG 如果没有任何已经结束的子进程则马上返回, 不予以等待. 
 WUNTRACED 如果子进程进入暂停执行情况则马上返回, 但结束状态不予以理会. 子进
程的结束状态返回后存于 status, 底下有几个宏可判别结束情况
 WIFEXITED(status) 如果子进程正常结束则为非 0. 
 WEXITSTATUS(status) 取得子进程 exit()返回的结束代码, 一般会先用 WIFEXITED 来判断是
否正常结束才能使用此宏. 
 WIFSIGNALED(status) 如果子进程是因为信号而结束则此宏值为真
 WTERMSIG(status) 取得子进程因信号而中止的信号代码, 一般会先用 WIFSIGNALED 来判断
后才使用此宏. 
 WIFSTOPPED(status) 如果子进程处于暂停执行情况则此宏值为真. 一般只有使用 WUNTRACED 
时才会有此情况. 
 WSTOPSIG(status) 取得引发子进程暂停的信号代码, 一般会先用 WIFSTOPPED 来判断后才
使用此宏. 
返回值 如果执行成功则返回子进程识别码(PID), 如果有错误发生则返回-1. 失败原因存于 errno 中.

代码如下:

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


int main(int argc, char const *argv[])
{
    pid_t pid;
    pid_t rpid;
    pid = fork();
    int status;

    if (pid < 0)
    {
        perror("fork"); 
        return 0;        // pid < 0 直接退出

    }else if(pid > 0){    // pid > 0 父进程
        //rpid = wait(&status);
        rpid = waitpid(-1, &status, 0);   // waitpid(任意值=-1, &status, 堵塞的=0)  
        printf("Get child status=%x\n", WEXITSTATUS(status));  // 返回自定义的返回值

    } else if(pid == 0) {  // pid == 0 是子进程
        sleep(1);
        printf("child will exit!\n");
        exit(2);
    }
    
    
    return 0;
}


总结

本篇文章对于进程的概念、进程常用命令、子进程的创建、进阶、退出、回收进行了阐述。代码中所涉及的头文件,在LInux终端下,可以采用以下命令进行查看和阅读详细介绍。

1. man -f fork  
2. man -f wait  
3. man -2 wait   // 基于2的命令后,选择第二个选项进行详细查看,因为命令介绍手册有好几本。
4. ps elf|grep wait_t   // 一般查看wait_t.c 代码执行完后,子进程的情况,看看子进程中是否仍有僵尸进程存在。

上面的命令在LInux系统下,可以多尝试尝试,了解所涉及函数的用法。

  • 19
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值