当前位置: 首页 > article >正文

linux进程和进程通信编程(1)

What makes the desert beautiful is that somewhere it hides a well.

沙漠之所以美丽,是因为在它的某个角落隐藏着一口井.

linux进程和进程通信编程(1)

    • 1.什么是进程
    • 2.进程id(pid)
    • 3.进程间通信的方法
      • 管道
      • 信号
      • IPC
      • Socket
    • 4.创建进程
      • fork
        • fork有三个返回值
        • 父进程pid为1的原因
    • 5.exec函数族
      • execl
      • 15.c代码
    • 6.ps
    • 7.kill
      • 16.c代码
    • 8.孤儿进程
      • 17.c代码
    • 9.僵尸进程
      • 172.c代码
    • 10.wait
      • 18.c代码
    • 11.守护进程
      • 如何创建守护进程
      • 19.c代码

1.什么是进程

正在运行的程序

进程 : 相当于一个同学

进程组 : 相当于一个班

会话 : 相当于一个年级

进程直接要通过linux内核进行通信
在这里插入图片描述
就绪态: 就差cpu

执行态到阻塞态: 因为某件事给卡住了

2.进程id(pid)

每个进程唯一的标识符

3.进程间通信的方法

管道

有名管道, 无名管道

信号

发送, 结束, 处理

IPC

共享内存, 消息队列, 信号灯

Socket

4.创建进程

用一个进程(父进程)去创建另一个进程(子进程)

子进程相当于父进程的拷贝

fork

参考程序14.c

//头文件
#include <sys/types.h>
#include <unistd.h> 

fork有三个返回值

在父进程里面,返回子进程pid; 在子 进程里返回0, 出错返回负数

原本这整个程序是父进程, 最开始用了fork, 就生成了一个子进程(看不见的), 父子进程都去完整的执行了这个程序(因为可以看见分别执行了两次i++), 只是父进程只能去输出第一个if, 子进程只能去输出第二个if
在这里插入图片描述

父进程pid为1的原因

因为由于父进程先退出了,造成子进程被init(ID=1)接管,所以用getppid出来的是1. 最后在跑父进程的时候加了sleep就能保证父进程后退出。
在这里插入图片描述

5.exec函数族

execl

相关程序15.c

用于让父子进程去执行不同的程序(x.sh)或命令(ls)

execl执行之后, 子进程就不会再往后执行了

execl("要执行的文件的绝对路径", "./要执行的文件", 要执行的文件的参数);

1 . 用法

查找要执行的文件的绝对路径, 记得给需要执行的文件加权限
在这里插入图片描述

execl("/root/amiao/15.1.sh", "./15.1.sh", NULL);

如果执行成功, j就不返回, 不成功 , j就返回-1
在这里插入图片描述
2 . execl执行之后, 子进程就不会再往后执行了

比如这个, 父进程把整个程序执行完了(因为输出了i), 子进程执行了execl之后就没有执行i++了
在这里插入图片描述
这个也可以看出来, 把execl移到前面去, a++也不执行了
在这里插入图片描述
3 . 放父进程也一样, 不执行a++了
在这里插入图片描述
4 . 执行pwd命令

一般系统命令路径就是/bin/xx
在这里插入图片描述

15.c代码

// execl

#include <stdio.h> //用于main函数
#include <stdlib.h>
#include <sys/types.h> //用于pid_t

#include <unistd.h> //用于close,read,write,fork

int main() //不含参数
{
    int j;
    int a = 0;
    int b = 0;
    pid_t pid; //定义一个pid来接收fork的返回值
    pid = fork();
    int i = 0;

    if (pid < 0)
    {
        printf("创建失败");
    }

    //父进程
    if (pid > 0)
    {
        printf("这是父进程,其pid为%d\n", getpid()); // getpid是获得当前进程id
    }

    //子进程
    if (pid == 0)
    {
        printf("a:%d\n", a);
        printf("这是子进程,其pid为%d,", getpid()); // getpid是获得当前进程pid
        printf("其父进程pid为%d\n", getppid());    // getppid是获得当前进程父进程的pid
        // j = execl("/root/amiao/15.1.sh", "./15.1.sh", NULL);  //运行15.1.sh,要用绝对路径
        // printf("j:%d\n", j);
        execl("/bin/pwd", "pwd", NULL);
        printf("b:%d\n", b);
    }
    i++;
    printf("i:%d\n", i);
}

6.ps

用于列出系统中正在运行的进程的各种信息, 比如pid
在这里插入图片描述
查看某一个命令的进程
在这里插入图片描述

7.kill

用于杀死进程

相关程序16.c

要一边运行16.c(编译成hell), 一边查pid才行, 不然pid查的是错的
在这里插入图片描述
如果是按f9运行的(这里我用的VScode远程连接的kali), 就直接查16(VS把16.c编译成16)
在这里插入图片描述

16.c代码

// kill的用法

#include <stdio.h> //用于main函数
#include <stdlib.h>
#include <sys/types.h> //用于pid_t

#include <unistd.h> //用于close,read,write,fork

int main() //不含参数
{
    pid_t pid; //定义一个pid来接收fork的返回值
    pid = fork();
    
    if (pid < 0)
    {
        printf("创建失败");
    }

    //子进程
    if (pid == 0)
    {
        while (1)
        {
            printf("这是子进程,其pid为%d,", getpid()); // getpid是获得当前进程pid
        }              
    }

}

8.孤儿进程

参考进程17.c

就是一个没结束的子进程, 但它的父进程已经结束了

这个子进程的ppid为1(有些特殊的系统可能不是1)

比如这个,先运行完父进程,再运行子进程,就得到父进程为1
在这里插入图片描述

17.c代码

// 孤儿进程

#include <stdio.h> //用于main函数
#include <stdlib.h>
#include <sys/types.h> //用于pid_t

#include <unistd.h> //用于close,read,write,fork

int main() //不含参数
{
    pid_t pid; //定义一个pid来接收fork的返回值
    pid = fork();

    if (pid < 0)
    {
        printf("创建失败");
    }

    //父进程
    if (pid > 0)
    {
        while (1);

        printf("这是父进程,其pid为%d\n", getpid()); // getpid是获得当前进程id
    }

    //子进程
    if (pid == 0)
    {
        // sleep(2);             //停2s,让父进程先结束,再运行子进程
        printf("这是子进程,其pid为%d,", getpid()); // getpid是获得当前进程pid
        printf("其父进程pid为%d\n", getppid());    // getppid是获得当前进程父进程的pid
    }

}

9.僵尸进程

参考进程172.c

是一个没有被释放进程块的子进程

父进程没结束, 子进程结束了, 但是父进程不去释放这个进程控制块
在这里插入图片描述
kill父进程
在这里插入图片描述

172.c代码

// 僵尸进程

#include <stdio.h> //用于main函数
#include <stdlib.h>
#include <sys/types.h> //用于pid_t

#include <unistd.h> //用于close,read,write,fork

int main() //不含参数
{
    pid_t pid; //定义一个pid来接收fork的返回值
    pid = fork();

    if (pid < 0)
    {
        printf("创建失败");
    }

    //父进程
    if (pid > 0)
    {
        while (1);

    }

    //子进程
    if (pid == 0)
    {
        // sleep(2);             //停2s,让父进程先结束,再运行子进程
        printf("这是子进程,其pid为%d,", getpid()); // getpid是获得当前进程pid
        printf("其父进程pid为%d\n", getppid());    // getppid是获得当前进程父进程的pid
    }

}

10.wait

用于减少僵尸进程产生

当父进程调用了wait, 就会直接阻塞父进程, 然后去把父进程的子进程结束之后的空间给释放了

参考程序18.c

//头文件
#include <wait.h>

在这里插入图片描述

18.c代码

//wait,用于减少僵尸进程产生,当父进程调用了wait, 就会直接阻塞父进程, 然后去把父进程的子进程结束之后的空间给释放了

#include <stdio.h> //用于main函数
#include <stdlib.h>
#include <sys/types.h> //用于pid_t

#include <unistd.h> //用于close,read,write,fork
#include <wait.h> //用于wait

    int
    main() //不含参数
{
    pid_t pid; //定义一个pid来接收fork的返回值
    pid = fork();

    if (pid < 0)
    {
        printf("创建失败");
    }

    //父进程
    if (pid > 0)
    {
        sleep(2); //停2s,让子进程先结束,再运行父进程

        int status;
        wait(&status);
        if (WIFEXITED(status) == 1)          // WIFEXITED(status)==1表示子进程是正常运行完退出的,不是被kill的
        { 
            printf("子进程结束的返回:%d\n" ,WEXITSTATUS(status));//返回子进程中的exit里面的数
        }
    }

    //子进程
    if (pid == 0)
    {
        
        printf("这是子进程,其pid为%d,", getpid()); // getpid是获得当前进程pid
        printf("其父进程pid为%d\n", getppid());    // getppid是获得当前进程父进程的pid
        exit(6);
    }
}

11.守护进程

就是后台进程, 与所有终端没有关联, 不能和用户交互, 不能ctrl+c退出

参考程序19.c

如何创建守护进程

其中1 ,2 ,6 是必须的

1 . 必须是init(pid=1的那个) 的子进程

用fork创建一个子进程, 然后让父进程用exit退出, 然后这个子进程就是init的子进程了

2 . 不跟终端交互

用setsid函数创建一个新会话, 会话里面的首进程不和终端交互

3 . 用chdir , 将当前目录改成根目录

因为有时候会把程序写在U盘里, U盘拔了 , 就不能在系统里面运行了, chdir用于避免这种情况

4 . 重设umask文件掩码

父进程和子进程掩码是一样的, 如果不想要一样的, 就用umask改了

5 . 关闭文件描述符

用于节约资源

6 . 执行代码

该输出的还是会输出,只是重启时会开机自启
在这里插入图片描述

19.c代码

// 守护进程

#include <stdio.h> //用于main函数
#include <stdlib.h>
#include <sys/types.h> //用于pid_t
#include <sys/stat.h>

#include <unistd.h> //用于close,read,write,fork
int main()          //不含参数
{
    pid_t pid; //定义一个pid来接收fork的返回值
    pid = fork();

    if (pid < 0)
    {
        printf("创建失败");
    }

    //父进程
    if (pid > 0)
    {
        exit(0); //让父进程直接退了
    }

    //子进程
    if (pid == 0)
    {
        setsid();             //用setsid函数创建一个新会话,不跟终端交互

        chdir("/");          //将当前目录改成根目录

        umask(0);             //重设umask文件掩码

        close(0);            //关闭文件描述符

        while (1)             //用while循环写出一直要执行的代码
        {
            printf("hh");
            printf("其父进程pid为%d\n", getppid());
        }
       
    }
}


http://www.kler.cn/a/1587.html

相关文章:

  • 【mysql】流程控制
  • HTML5 动画效果:淡入淡出(Fade In/Out)详解
  • 【算法】算法大纲
  • Java语法总结
  • 游戏关卡设计的常用模式
  • RK3562编译Android13 ROOT固件教程,触觉智能开发板演示
  • 软件测试 - 非技术常见面试题
  • 【C++学习】日积月累——SLT中stack使用详解(1)
  • 012+limou+C语言深入知识——(4)“结构体”与“枚举体”与“联合体”
  • 011+limou+C语言深入知识——(3)“字符串函数”和“字符分类函数”和“内存操作函数”以及“部分库函数的模拟实现”
  • 【DP动态规划】最长子序列
  • [数据分析与可视化] Python绘制数据地图1-GeoPandas入门指北
  • string类(下)
  • 如何优雅地让谷歌浏览器中的网页旋转90度?掌握这个技巧,让你的网页与众不同!
  • linux kernel 5.0 inline hook框架
  • Mysql常用命令
  • 尚融宝07-前端模块化
  • 2023年网络安全最应该看的书籍,弯道超车,拒绝看烂书
  • 【C++编译】gcc、gdb、make、cmake
  • 论文阅读和分析:Mathematical formula recognition using graph grammar
  • 线程安全(重点)
  • 202304读书笔记|《不被定义的女孩》——做最真实最漂亮的自己,依心而行
  • 2023秋招前端面试必会的面试题
  • 多层多输入的CNN-LSTM时间序列回归预测(卷积神经网络-长短期记忆网络)——附代码
  • STM32开发(九)STM32F103 通信 —— I2C通信编程详解
  • springcloud3 nacos,sentinel,ribbon,openfegin等整合案例4[fallback+blockhandler完美整合]