linxu学习之进程

文章目录

  • 进程
    • 程序和进程
    • 产生进程
    • 销毁进程
    • 多进程高并发设计
    • 孤儿僵尸守护进程
      • 孤儿进程:
      • 守护进程(重点)
    • 僵尸进程:

进程

程序和进程

操作系统可以运行多个程序,那他是如何运行的?实际上,CPU的执行是很快的,而待运行的程序很多,那么为了让操作系统运行多个程序,CPU会把它的执行时间划分成很多段,比如每一段是0.1秒,那么就可以这样A程序运行0.1秒,然后B程序运行0.1,然后C程序运行0.2秒,因为这个切换很快,所以我们感觉程序是同时运行的。

产生进程

  • 创建进程很简单,直接调用fork函数:pid_t fork(void);

创建进程用法举例:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
    int pid = 0;// 父子进程不共享局部变量

    // pid_t fork(void);
    pid_t fpid = fork(); // fpid表示fork函数返回的值


    if(fpid < 0){
        printf("创建子进程失败\n");
        return fpid;
    }else if(fpid == 0){
    	// 创建成功后子进程中fpid == 0
        pid = getpid();
        printf("子进程,子进程pid = %d\n",getpid());
    }else if(fpid > 0){
    	// 创建成功后父进程的fpid 为子进程的pid
        pid = getpid();
        printf("父进程,父进程pid = %d\n",getpid());
        printf("父进程,子进程pid = %d\n",getpid());
    }
    
    // 再创建3个子进程用于观察
    for(int i = 3 ; i > 0 && fpid > 0 ; i--){
        fpid = fork();
    }
    printf("pid = %d\n",pid);

    while (1);
    return 0;
}

在这里插入图片描述

调用fork函数后,会创建一个子进程,并且父子两个进程都从fork处执行,fork函数有两个返回值,对于父进程会返回子进程的pid,此时pid会大于0,对于子进程来说,pid会等于0。

局部变量:写时复制,读时共享

销毁进程

exit - 终止正在执行的进程

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

int main()
{
    int pid = 0;

    // pid_t fork(void);
    pid_t fpid = fork();

    if (fpid < 0)
    {
        printf("创建子进程失败\n");
        return fpid;
    }
    else if (fpid == 0)
    {
        printf("我是子进程,马上就要exit了\n");
        exit(-1);
        printf("子进程还活着\n");
    }
    else if (fpid > 0)
    {
        printf("父进程还活着\n");
    }

    while (1);

    return 0;
}

这里子进程已经退出,但是父进程没有执行完毕,导致子进程没有回收变成僵尸进程
在这里插入图片描述

多进程高并发设计

在这里插入图片描述
优点:

  • 若要用多进程实现高并发那么自己linxu系统cpu有多少核,就产生多少个进程多个核可以并发执行只有多个进程在不同的核上运行才可以充分利用多核系统的并发处理能力

  • 比如:cpu为四核,那么主进程就产生4个子进程,让四个子进程分别在不同的核上运行,

  • 4个子进程在工作的时候,把任务放在一个共享内存中(通过内存映射,使多个进程可以访问一个内存),哪个任务做完了,就接着拿任务给每个子进程的负载均衡

  • 职责明确:父进程管理生死,子进程工作

查看cpu核数

cat /proc/cpuinfo
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

typedef void (*spawn_proc_pt)(void *data);
static void worker_process_cycle(void *data);
static void start_worker_processes(int n);
pid_t spawn_process(spawn_proc_pt proc, void *data, char *name);

int main(int argc, char **argv)
{
    // 子进程用于工作,父进程用于管理子进程

    // 开启工作进程,创建4个子进程
    start_worker_processes(4);
    // 管理子进程
    wait(NULL);
}

// 开启工作进程
void start_worker_processes(int n)
{
    int i = 0;
    for (i = n - 1; i >= 0; i--)
    {
        // 创建子进程
        spawn_process(worker_process_cycle, (void *)(intptr_t)i, "worker process");
    }
}

// 创建子进程
// spawn_proc_pt 类型的函数:void worker_process_cycle(void *data)
pid_t spawn_process(spawn_proc_pt proc, void *data, char *name)
{
    pid_t pid;
    pid = fork(); // 创建子进程

    switch (pid)
    {
    case -1:
        fprintf(stderr, "fork() failed while spawning \"%s\"\n", name);
        return -1;
    case 0:
        proc(data); // 成功创建子进程后让子进程去工作
        return 0;
    default:
        break;
    }
    printf("start %s %ld\n", name, (long int)pid);
    return pid;
}

// 给进程安排工作
void worker_process_cycle(void *data)
{
    // data 其实是一个int*类型
    int worker = (intptr_t)data;
    // 初始化
    worker_process_init(worker);

    // 干活
    for (;;)
    {
        sleep(10);
        printf("pid %ld ,doing ...\n", (long int)getpid());
    }
}

// 初始化进程
void worker_process_init(int worker)
{
    cpu_set_t cpu_affinity;
    // worker = 2;
    // 多核高并发处理  4core  0 - 0 core 1 - 1  2 -2 3 -3
    CPU_ZERO(&cpu_affinity); // 结构体清零
    
    // CPU_SETSIZE:1024 支持cpu最大的数量
    CPU_SET(worker % CPU_SETSIZE, &cpu_affinity); // 0 1 2 3 设置核

    /*
    在多CPU系统中,通过sched_setaffinity ()可以设置进程的CPU亲和力,
    使进程绑定在某一个或几个CPU上运行,避免在CPU之间来回切换,从而提高该进程的实时性能。
    */
    if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_affinity) == -1)
    {
        fprintf(stderr, "sched_setaffinity() failed\n");
    }
}

查看进程对应的核数
在这里插入图片描述

代码中函数指针不太理解的看看下方代码:

// typedef  返回类型(*新类型)(参数表)
typedef char (*PTRFUN)(int); 
PTRFUN pFun; 
char glFun(int a){ return;} 
void main() 
{ 
    pFun = glFun; 
    (*pFun)(2);  //调用函数
} 

查看进程在cpu的核上执行的命令: ps -eLo ruser,pid,lwp,psr,args

孤儿僵尸守护进程

孤儿进程:

一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程所收养,并由init进程对它们完成状态收集工作。
想想我们如何模仿一个孤儿进程? 答案是: kill 父进程!

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
    int fpid = 1;// 父子进程不共享局部变量

    for(int i = 5 ; i > 0 && fpid > 0 ; i--){
        fpid = fork();
    }

    if(fpid < 0){
        printf("创建子进程失败\n");
        return fpid;
    }else if(fpid == 0){
    	while(1);
    }else if(fpid > 0){
        while(1);
    }

    return 0;
}

在这里插入图片描述

守护进程(重点)

把孤儿进程做成守护进程

不与任何终端关联的进程,通常情况下守护进程在系统启动时就在运行,它们以root用户或者其他特殊用户(apache和postfix)运行,并能处理一些系统级的任务。守护进程脱离于终端,是为了避免进程在执行过程中的信息在任何终端上显示,并且进程也不会被任何终端所产生的终端信息所打断(比如关闭终端等)。那如何成为一个守护进程呢? 步骤如下:

  • 1.调用fork(),创建新进程,它会是将来的守护进程.
  • 2.在父进程中调用exit(父进程挂掉),保证子进程不是进程组长
  • 3.调用setsid()创建新的会话区 – 具体先不追究
  • 4.将当前目录改成根目录(如果把当前目录作为守护进程的目录,当前目录不能被卸载他作为守护进程的工作目录)
  • 5.将标准输入,标准输出,标准错误重定向到/dev/null.
    我们来看这个代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

// 一般传入 0 , 0
int daemon(int nochdir, int noclose)
{
    int fd = 0;
    switch (fork())
    {
    case -1:
        return (-1);
    case 0:
        break;
    default:
        _exit(0); // 父进程自杀
    }

    if (setsid() == -1) // 创建会画
        return (-1);
    
    if (!nochdir) // 将当前目录改成根目录
        (void)chdir("/");

    if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1)
    {
        // 重定向 标准输入 标准输出 标准出错
        (void)dup2(fd, STDIN_FILENO);
        (void)dup2(fd, STDOUT_FILENO);
        (void)dup2(fd, STDERR_FILENO);
        if (fd > 2)
            (void)close(fd);
    }

    return (0);
}


int main()
{
    // 创建子进程,并且把父进程杀死
    daemon(0,0);

    printf("hello\n");

    while(1);// 处理事务
    

    return 0;
}

父进程自杀后,守护进程 init 接管 , 一直在后台进行运行
在这里插入图片描述

僵尸进程:

一个进程使用fork创建子进程,如果子进程退出(exit),而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

  1. 僵尸进程怎样产生的:
  • 一个进程使用fork创建子进程,如果子进程退出(exit),而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
  • 它需要它的父进程来为它收尸,如果他的父进程没安装 SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了, 那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。

1.怎么查看僵尸进程:
  利用命令ps,可以看到有标记为的进程就是僵尸进程。

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

int main(){
    pid_t fpid = fork();


    if(fpid < 0){
        printf("创建子进程失败\n");
        return fpid;
    }else if(fpid == 0){
        exit(-1);
    }else if(fpid > 0){
       while(1);
    }

     
    return 0;
}

这里可以用 wait 和 waitpid 函数回收子进程
在这里插入图片描述
2.怎样来清除僵尸进程:

  • 改写父进程,在子进程死后要为它收尸。具体做法是接管SIGCHLD信号。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,执行waitpid()函数为子进程收尸。这是基于这样的原理:就算父进程没有调用 wait,内核也会向它发送SIGCHLD消息,尽管对默认处理是忽略,如果想响应这个消息,可以设置一个处理函数。
  • 把父进程杀掉。父进程死后,僵尸进程成为"孤儿进程",过继给1号进程init,init始终会负责清理僵尸进程。它产生的所有僵尸进程也跟着消失。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/3876.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【FreeRTOS(一)】FreeRTOS新手入门——初识FreeRTOS

初识FreeRTOS一、实时操作系统概述1、概念2、RTOS的必要性3、RTOS与裸机的区别4、FreeRTOS的特点二、FreeRTOS的架构三、FreeRTOS的代码架构一、实时操作系统概述 1、概念 RTOS&#xff1a;根据各个任务的要求&#xff0c;进行资源&#xff08;包括存储器、外设等&#xff09…

【TypeScript入门】TypeScript入门篇——枚举(enum)

TypeScript是一种静态类型、可选的编程语言&#xff0c;它在JavaScript的基础上添加了类型检查、接口、枚举等新特性&#xff0c;可以让开发更加高效、代码更加健壮。在TypeScript中&#xff0c;枚举是一种特殊的数据类型&#xff0c;它可以用来定义一组命名的常量&#xff0c;…

网络通信之应用层协议--Linux

文章目录关于应用层协议的理解应用层协议的制定理论部分代码部分完整代码以及测试HTTP协议代码测试HTTP协议HTTPS协议加密原因基础的加密方式数据摘要&#xff08;数据指纹&#xff09;数字签名HTTPS的加密方式的选择总结关于应用层协议的理解 在之前的一篇关于套接字实现网络…

天梯赛刷题小记 —— L2

最近在重刷 天梯赛&#xff0c;浅浅记录一下&#xff0c;进入L2阶段了 L2-001 紧急救援 解题思路&#xff1a;典型的dijkstra模板题&#xff0c;带路径记录与权重&#xff0c;方案数记录&#xff0c;解析出过 Dijkstra(兼路径) #include <bits/stdc.h> #define inf…

【数据分析之道-基础知识(三)】元组

文章目录专栏导读1、元组简介2、元组创建3、元组查找操作4、元组删除操作5、元组修改操作6、元组增加操作7、元组内置函数专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN Python领域新星创作者&#xff0c;专注于分享python领域知识。 ✍ 本文录入于《数据分析之道》&…

自动驾驶控制概况

文章目录1. 第一章行为决策在自动驾驶系统架构中的位置2. 路径跟踪控制的种类2.1 基于自行车模型的路径跟踪控制算法2.1.1 纯跟踪控制&#xff08;Pure Pursuit&#xff09;算法2.1.2 后轮反馈控制算法&#xff08;Rear wheel feedback&#xff09;2.1.3前轮反馈控制算法&#…

防火墙 NAT地址转换

网络地址转换&#xff08;NAT&#xff09;是一种用于访问Internet访问模式广域网&#xff08;WAN&#xff09;的技术&#xff0c;用于将私有&#xff08;保留&#xff09;地址转换为合法IP地址。NAT不仅能够有效地额抵抗外部网络攻击&#xff0c;还能够在IP地址分配不理想&…

Windows权限提升—令牌窃取、UAC提权、进程注入等提权

Windows权限提升—令牌窃取、UNC提权、进程注入等提权1. 前言2. at本地命令提权2.1. 适用范围2.2. 命令使用2.3. 操作步骤2.3.1. 模拟提权2.3.2. at配合msf提权2.3.2.1. 生成木马文件2.3.2.2. 设置监听2.3.2.3. 设置反弹2.3.2.4. 查看反弹效果3. sc本地命令提权3.1. 适用范围3.…

瑟瑟发抖吧——用了这款软件,我的开发效率提升了50%

一、前言 开发中&#xff0c;一直听到有人讨论是否需要重复造轮子&#xff0c;我觉得有能力的人&#xff0c;轮子得造。但是往往开发周期短&#xff0c;用轮子所节省的时间去更好的理解业务&#xff0c;应用到业务中&#xff0c;也能清晰发现轮子的利弊&#xff0c;一定意义上…

Warshall算法

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;> 算法 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对我…

树莓派Linux源码配置,树莓派Linux内核编译,树莓派Linux内核更换

目录 一 树莓派Linux的源码配置 ① 内核源码下载说明 ② 三种方法配置源码 二 树莓派Linux内核编译 ① 内核编译 ② 编译时报错及解决方案&#xff08;亲测&#xff09; 三 更换树莓派Linux内核 操作步骤说明 ● dmesg报错及解决方案&#xff08;亲测&#xff0…

算法刷题笔记

特定方法 KMP算法&#xff1a;字符串匹配 逆波兰表达式&#xff1a;计算值 斐波那契数&#xff1a;动态规划 强制类型转换&#xff1a;整型->字符串&#xff1a;to_string&#xff0c;字符串->整型&#xff1a;stoi 一、数组 数组&#xff1a;下标从0开始&#xff0c;内存…

蓝桥杯嵌入式--LCD屏幕使用提升

前言之前在专栏里已经介绍过LCD相关库文件的移植&#xff0c;今天来介绍一下对于LCD屏幕的使用技巧。屏幕基本配置与函数一、屏幕初始化使用lcd前的必要步骤就是对LCD屏幕进行初始化操作&#xff0c;这也是一个容易忘记的操作。LCD_Init();\\使用lcd前的必要步骤就是对LCD屏幕进…

蓝桥杯倒计时 | 倒计时17天

作者&#x1f575;️‍♂️&#xff1a;让机器理解语言か 专栏&#x1f387;&#xff1a;蓝桥杯倒计时冲刺 描述&#x1f3a8;&#xff1a;蓝桥杯冲刺阶段&#xff0c;一定要沉住气&#xff0c;一步一个脚印&#xff0c;胜利就在前方&#xff01; 寄语&#x1f493;&#xff1a…

将一段数字转为字符串

将一段数字转为字符串 string turn(long long x){string str;while(x){int tx%10;// 数字0的ascii码为48&#xff01;char ct48;strc;// string类拼接方式x/10;}reverse(str.begin(),str.end()); // 不要忘了反转字符串return str; }例: #include<iostream> #include&l…

使用VS Code 配置Ubuntu远程C++开发环境

使用VS Code 配置Ubuntu远程C开发环境 环境准备 VS CodeWSL Ubuntu 虚拟机 配置步骤 在Ubuntu 中配置ssh远程登录 Ubuntu 配置远程登录 VsCode 安装 Remote-ssh 插件 打开vscode ssh configure ,填入相关信息 ​ Host : 主机名称&#xff0c;在左侧列表中显示的名称 ​ …

【Linux】[万字] Linux下的文件操作 及 Linux文件描述符fd 详解

在Linux操作系统中, 文件描述符是一个至关重要的概念. 理解了文件描述符, 其实就可以相当于理解了Linux系统的关于内存文件系统的整个大致框架和逻辑 但是在介绍文件描述符之前, Linux关于文件还存在许多 概念和文件操作 的知识需要介绍一下, 就当作是为解释文件描述符所做的…

IDEA连接Linux服务器进行文件操作

IDEA连接Linux服务器进行文件操作 文章目录IDEA连接Linux服务器进行文件操作连接的作用和意义安装openssh开启openssh服务验证是否开启服务安装网络工具包查看虚拟机IP地址Idea连接Linux虚拟机打开配置页面配置SFTP配置SSH完成后出现的配置文件安装big data tools插件连接的作用…

【RV1126】调试GT911,1024x600 7寸 MIPI 电容触摸屏

文章目录一、驱动注册失败二、触摸屏可以触摸&#xff0c;但是x轴数据反了三、可以触摸了&#xff0c;但是Y轴数据跳变&#xff0c;几乎只有一半的屏幕是可以正常滑动的三、汇顶触摸屏配置文件解析四、使用新的配置文件4.1 新配置解决问题4.2 测试触摸的方法在kernel增加frame …

【学习经验分享NO.21】学习资料分享(持续更新)

本博客将收集整理人工智能深度学习相关资料&#xff0c;进行整理&#xff0c;供大家学习使用。如果有需要帮忙整理的请留言。将不断更新&#xff0c;请持续关注。 一、深度学习论文资料 链接&#xff1a;https://pan.baidu.com/s/18LO5df0dp9-IE8Z3aFyrPg 提取码&#xff1a;c…