嵌入式学习系统编程(四)进程

目录

一、进程

        1.程序和进程

        2.进程的八种状态

        3.     几个状态

        4.关于进程常用命令

二、关于进程的函数

        1.fork

        2.面问

        3.孤儿进程  后台进程

        2.   exec函数族 (只保留父子关系,做新的事情)  

  strtok函数 

三、进程的结束

1.分类

exit和_exit的区别 

wait函数 

 waitpid函数 

四、总结


一、进程

        1.程序和进程

        内存中正在进行(运行)中的程序。

        ./a.out跑起来就是a.out对应的进程。

         程序 静态 硬盘

         进程 动态 内存

       

        程序= 代码 + 数据= 代码区(text段)+栈区+堆区+BSS+Data

        进程 = 代码区(text段)+栈区+堆区+BSS+Data + PCB(Process Control Block)

        BSS:存放程序中未初始化的全局变量的一块内存区域。

        DATA:存放程序中已初始化的全局变量的一块内存区域。

        2.进程的八种状态

        *D  不可中断态的睡眠态

        *R  正在运行态    或者   就绪态  

        *S  可终端的睡眠态

        *T  stopped by job control signal

         t  stopped by debugger during the tracing

         X  死亡态

        *Z  僵尸态

        3.     几个状态

 ​​​​​

                                                                 通用三态图 

                                                          linux系统的状态

        4.关于进程常用命令

        top   动态查看系统中进程

        pstree -sp [pid]     查看进程树(可指定pid号)

        kill   发出信号       

                kill -l  //查看可以发送的信号

              18) SIGCONT  //继续信号

              19) SIGSTOP  //暂停

               9) SIGKILL  //死亡信号,杀死进程

    kill -19 [pid] //给指定pid号的进程发送 信号

        ps aux | grep a.out     查看pid号进程的状态信息

        ps -eLf | grep a.out      查看pid和ppid号

二、关于进程的函数

        1.fork

           pid_t fork(void);    通过复制主调进程创建子进程。

        一次fork返回了两次        typedef   int    pid_t 

   成功 在父进程空间返回子进程pid,在子进程空间返回 0

   失败    父进程返回值-1       errno会被设置 

        2.面问

        eg1如果两次fork同时前后执行,会生成几个进程?

                4个进程

        eg2   fork()&&fork()||fork();

                5个进程

            fork()

          /           \

     fork()        fork1()

     /  \                /    \

  fork  fork2  fork1 fork3

           /   \

      fork2  fork4

        3.孤儿进程  后台进程

        子进程没有父进程就是孤儿进程,将会有 init(1)-进程 收养子进程。变成 后台进程 ( ctrl + c) 这个信号只能发给前台进程。  结束后台进程要用kill

        子进程结束,但是父进程,没有对子进程收尸。那就是僵尸进程。僵尸态是有危害的,消耗内存。

   eg1      fork创建子进程,分别打印pid

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main(void)
{pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid > 0){printf("father pid = %d ---\n",getpid());printf("pid = %d",getpid());}else if (pid == 0){printf("child pid = %d ---\n",getpid());}printf("--pid = %d --end---\n",getpid());return 0;
}

        2.   exec函数族 (只保留父子关系,做新的事情)  

                通过将新进程的各个段替换当前进程的各个段来实现,用来执行一个新的功能

                int execl(const char *path, const char *arg, .../* (char  *) NULL */);
               int execv(const char *path, char *const argv[]); 

                   @path   代表要运行的新程序的名字 要包含路径

                          eg:

                     "/home/linux/fileio/mycp"

                   @arg   表示要运行的程序的名字

                          eg:

                     "mycp"

                   @...  可变参数(要执行程序用到的参数)

                         eg:

                   "src.txt","dest.txt",NULL结尾

        l(list)和v(vector)的区别  传参的方式不同

          execl( "/home/linux/fileio/mycp","mycp", "src.txt","dest.txt",NULL);

          char * const args[] = {"mycp", "src.txt","dest.txt",NULL};

          int execv( "/home/linux/fileio/mycp", args);

           printf("---exec---code---");后续代码不执行,因为段被替换

               int execlp(const char *file, char *const argv[]);
               int execvp(const char *file, char *const argv[],char *const envp[]);

                p 表示 PATH系统环境变量(系统运行时侯需要的一些变量)

                表示要执行的可执行文件到PATH环境变量中去寻找

                execlp("ls","ls","-l","/",NULL);

                char *const args[] = {"ls","-l","/",NULL};

                execlp("ls","ls","-l","/",NULL);

        

           int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);   看要运行的程序需不需要环境变量,需要就可以传。不需要的话用之前的方式运行起来就行。

           int execvpe(const char *file, char *const argv[],char *const envp[]);

          e 表示用户环境变量 ---提供了一种方式,可以给要运行的程序传递环境变量

           extern char **environ;

            int main{

                   execle("/usr/bin/env","env",NULL,environ);

                   char *const my_env[] = {"USERNAME=linux","PSWD=123456 ",NULL};

                   execle("/usr/bin/env","env",NULL,my_env);

                   execvpe("env",my_env,NULL);

                   return 0;

}

              

  strtok函数 

        作用,提取字符串

        char *strtok(char *str, const char *delim);

        

            @str   --- 要提取完整字符串   -- buf

               如果连续的分割  填NULL

            @delim --- 分隔标志 // " ;,"可以有多个分割标志

   

            返回值:

              成功 返回提取到的字符串的地址

              失败 NULL

eg    strtok的使用 

#include<stdio.h>
#include<string.h>
int main(int argc, char const *argv[])
{char buf[1024]={"ls -l ;/"};
#if 0	char *s1 = strtok(buf," ;");printf("s1 = %s\n",s1);char *s2 = strtok(NULL," ;");printf("s2 = %s\n",s2);char *s3 = strtok(NULL," ;");printf("s3 = %s\n",s3);
#endifchar *s[5] = {NULL};int i= 0;s[i] = strtok(buf," ;");while (s[++i] = strtok(NULL," ;"));for ( i = 0; i < 5; ++i){printf("s[%d]=%s\n",i,s[i]);}return 0;
}

eg1   实现一个shell程序

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>int main(int argc,const char *argv[])
{char buf[1024] = {0};while (1){printf("myshell$ ");fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = '\0';if (strncmp(buf,"exit",4) == 0 || strncmp(buf,"quit",4) == 0){printf("exit---myshll---\n");return 0;}int i = 0;char *arg[10] = {NULL};arg[i] = strtok(buf," ");while (arg[++i] = strtok(NULL," "));
#if 0for (i = 0; i < 10; ++i){printf("%d:%s\n",i,arg[i]);}
#endif pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid > 0){wait(NULL);continue;}else if (pid == 0){if (execvp(arg[0],arg) < 0){perror("execvp fail");return -1;}}}return 0;
}

 

三、进程的结束

1.分类

正常结束:

        1)main 中 return

        2)exit() //库函数

           c库函数,会执行io库的清理工作,关闭所有 的流,以及所有打开的文件。

           注册清理函数(atexit)。

        3)_exit,_Exit 会关闭所有的已经打开的文件,不执行清理函数。 //系统调用

        //4) 主线程退出  

        //5)主线程调用pthread_exit

              异常终止:

        6)abort() //发送一个SIGABRT

        7)signal  //发信号 结束了进程  kill pid  -9

        //8) 最后一个线程被pthread_cancle

     exit函数

      #include <stdlib.h>

       void exit(int status);

         功能:

            造成进程正常结束

         参数:

           @status   带出一个状态值给到父进程  结合wait和宏才能查看状态值

return 0 自动调取一个exit函数,从而调用atexit函数,_exit不调用atexit函数

_exit

        #include <unistd.h>

       void _exit(int status);

         功能:

            造成进程正常结束 ,立刻结束

         参数:

           @status   带出一个状态值给到父进程   结合wait和宏才能查看状态值

exit和_exit的区别 

区别:

   exit 是库函数

      退出前,

      1.先清理IO缓存

      2.调用清理函数

   _exit 是系统调用

      立即结束进程

atexit

       #include <stdlib.h>

       int atexit(void (*function)(void));

       

       功能:

          注册一个退出清理函数

       参数:

        @function ---- 函数指针

                       函数类型  void func1(void)

       

       返回值:

        成功 返回0

        失败 非0

        ps注册顺序和调用顺序相反(有栈的结构)

        

状态值:

   status & 0377  => 一个字节的数据

   

    1 1 1 1 1 1 1 1  //0377 二进制

   

    0 0 0 0 0 0 0 0

   

    //数值 个数 256 个值

   

wait函数 

        pid_t wait(int *wstatus);

         用于等待子进程的状态的变化,并获取一些该子进程的信息。

        三种变化

        1.子进程结束 

        2.子进程被信号暂停(kill   SIGSTOP)

        3.子进程因信号被回恢复(SIGCONT)

      参数:

        @status  获取子进程退出时的状态信息(要用宏才能提取出来)   //被调修改主调的方式

      返回值:

        成功 返回状态改变了的子进程的pid

        失败 -1

         如果不关心其退出状态一般用NULL表示

   

    注意:

       wait 本身是个阻塞操作(只有有子进程时,才阻塞,没有子进程时,立即返回wait调用失败) //子进程结束

        父进程在子进程结束后用wait进行资源回收和状态的获取称为"关心"。

        不进行资源回收的结束的子进程是僵尸态。

       

    宏:

   

    //正常结束

       WIFEXITED(wstatus)   //判断子进程是否是正常结束

                            //正常结束 则为真                        

       WEXITSTATUS(wstatus) //使用这个宏去那返回值

       

    //异常结束

       WIFSIGNALED(wstatus) //判断子进程是否是被信号结束

       WTERMSIG(wstatus) //获得 结束子进程的那个信号编号

eg 1    WIFEXITED(wstatus)和  WEXITSTATUS(wstatus)

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>int main(void)
{pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid > 0){sleep(3);printf("---wait----child---\n");int status;wait(&status);printf("status = %d\n",status);if (WIFEXITED(status)){printf("child status = %d\n",WEXITSTATUS(status));}}else if (pid == 0){printf("---child----exit---\n");exit(99);}return 0;
}

 eg 1    WIFSIGNALED(wstatus) 和 WTERMSIG(wstatus)

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>int main(void)
{pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid > 0){sleep(3);printf("---wait----child---\n");int status;//wait(&status);//waitpid(-1,&status,WNOHANG);waitpid(-1,&status,0);printf("status = %d\n",status);if (WIFEXITED(status)){printf("child status = %d\n",WEXITSTATUS(status));}}else if (pid == 0){printf("---child----exit---\n");exit(99);}return 0;
}

eg3     wait的使用

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>int main(void)
{pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid > 0){   sleep(3);printf("---wait----child---\n");int status;wait(&status);printf("status=%d\n",status);if(WIFEXITED(status)){printf("child status = %d\n",WEXITSTATUS(status));}}else if (pid == 0){printf("---child----exit---\n");exit(99);}return 0;
}

 eg4   无人机状态模拟

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>void do_fly(void)
{int i = 0;while (i < 5){printf("---%s---\n",__FUNCTION__);++i;sleep(1);}exit(88);
}void do_video(void)
{int i = 0;while (i < 3){printf("---%s---\n",__FUNCTION__);++i;sleep(1);}exit(77);
}
void do_transmit(void)
{int i = 0;while (i < 4){printf("---%s---\n",__FUNCTION__);++i;sleep(1);}exit(66);
}
void do_store(void)
{int i = 0;while (i < 6){printf("---%s---\n",__FUNCTION__);++i;sleep(1);}exit(55);
}int main(void)
{int i = 0;pid_t pid = 0;for (i = 0; i < 4; ++i){pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid == 0)break;}if (pid > 0){int status;for (i = 0; i < 4; ++i){wait(&status);if (WIFEXITED(status)){switch(WEXITSTATUS(status)){case 55:printf("do_store exit---\n");break;case 66:printf("do_transmit exit---\n");break;case 77:printf("do_video exit---\n");break;case 88:printf("do_fly exit---\n");break;}}}}else if (pid == 0){switch(i){case 0:do_fly();break;case 1:do_video();break;case 2:do_transmit();break;case 3:do_store();break;}}return 0;
}

 waitpid函数 

     pid_t  waitpid(pid_t pid, int *wstatus, int options);

       功能:

          等待子进程状态改变

       参数:

         @pid        

               < -1   meaning wait for any child process whose process group ID is equal to the absolute value of pid.

                 eg: -100

                   表示等待 进程组ID 为 |-100| 这个进程组中的任意子进程

               -1     meaning wait for any child process.

                   表示等待 当前父进程的任意子进程

               0      meaning wait for any child process whose process group ID is equal to that of the calling process.

                   等待 进程组ID 等于 父进程pid的那个进程组中的任意子进程            

               > 0    meaning wait for the child whose process ID is equal to the value of pid.

                  eg: 100

                      等待 进程pid 为 100的这个子进程状态改变

         @wstatus  //与wait的参数类似    如果不关心其退出状态一般用NULL表示

        获取准确状态值也是用

         @options  //

                   0         //阻塞调用

                   WNOHANG   //非阻塞   不断的看子进程状态有没有改变,没有改变就返回了。有改变了,将资源回收,并进行if判断下面的操作。

       

       

       waitpid(-1, &wstatus, 0);  //等价于 wait(&wstatus)

四、总结

//进程

创建  --- fork

运行

   //1.跟父进程类似 事情  

   //2.独立运行一个新程序 ---exec函数

   //3.运行多个不同任务

结束

   正常结束  

     //1.return //main

     //2.exit

     //3._exit

   异常结束

     //4.abort  //信号

     //5.signal //发其它信号

进程结束是两种特殊状态

   孤儿进程  

   僵尸进程   --- wait/waitpid  

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux中添加重定向(Redirection)功能到minishell

前言&#xff1a;在谈论添加minishell之前&#xff0c;我再重谈一下重定向的具体实现等大概思想&#xff01;&#xff01;&#xff01;方便自己回顾&#xff01;&#xff01;&#xff01; 目录 一、重定向&#xff08;Redirection&#xff09;原理详解 1、文件描述符基础 2、…

Django由于数据库版本原因导致数据库迁移失败解决办法

在django开发中&#xff0c;一般我们初始化一个项目之后&#xff0c;创建应用一般就会生成如下的目录&#xff1a;django-admin startproject myproject python manage.py startapp blogmyproject/ ├── manage.py └── myproject/ | ├── __init__.py | ├── se…

C++STL系列之vector

前言 vector是变长数组&#xff0c;有点像数据结构中的顺序表&#xff0c;它和list也是经常被拿出作对比的&#xff0c; vector使用动态分配数组来存储它的元素。当新元素插入时候&#xff0c;这个数组需要被重新分配大小&#xff0c;如果扩容&#xff0c;因为要开一个新数组把…

Functional C++ for Fun Profit

Lambda Conf上有人讲C函数式编程。在Functional Conf 2019上&#xff0c;就有主题为“Lambdas: The Functional Programming Companion of Modern C”的演讲。演讲者介绍了现代C中函数式编程相关内容&#xff0c;讲解了如何使用Lambda表达式编写符合函数式编程原则的C代码&…

Python基础理论与实践:从零到爬虫实战

引言Python如轻舟&#xff0c;载你探寻数据宝藏&#xff01;本文从基础理论&#xff08;变量、循环、函数、模块&#xff09;启航&#xff0c;结合requests和BeautifulSoup实战爬取Quotes to Scrape&#xff0c;适合零基础到进阶者。文章聚焦Python基础&#xff08;变量、循环、…

ThingJS开发从入门到精通:构建三维物联网可视化应用的完整指南

文章目录第一部分&#xff1a;ThingJS基础入门第一章 ThingJS概述与技术架构1.1 ThingJS平台简介1.2 技术架构解析1.3 开发环境配置第二章 基础概念与核心API2.1 核心对象模型2.2 场景创建与管理2.3 对象操作基础第三章 基础开发实战3.1 第一个ThingJS应用3.2 事件系统详解3.3 …

关于list

1、什么是listlist是一个带头结点的双向循环链表模版容器&#xff0c;可以存放任意类型&#xff0c;需要显式定义2、list的使用有了前面学习string和vector的基础&#xff0c;学习和使用list会方便很多&#xff0c;因为大部分的内容依然是高度重合的。与顺序表不同&#xff0c;…

Mysql 查看当前事务锁

在 MySQL 中查看事务锁&#xff08;锁等待、锁持有等&#xff09;&#xff0c;可以使用以下方法&#xff1a; 一、查看当前锁等待情况&#xff08;推荐&#xff09; SELECTr.trx_id AS waiting_trx_id,r.trx_mysql_thread_id AS waiting_thread,r.trx_query AS waiting_query,b…

【Keil5-map文件】

Keil5-map文件■ map文件■ map文件

k8s 基本架构

基于Kubernetes(K8s)的核心设计&#xff0c;以下是其关键基本概念的详细解析。这些概念构成了K8s容器编排系统的基石&#xff0c;用于自动化部署、扩展和管理容器化应用。### 一、K8s核心概念概览 K8s的核心对象围绕容器生命周期管理、资源调度和服务发现展开&#xff0c;主要包…

Bell不等式赋能机器学习:微算法科技MLGO一种基于量子纠缠的监督量子分类器训练算法技术

近年来&#xff0c;量子计算&#xff08;Quantum Computing&#xff09; 和 机器学习&#xff08;Machine Learning&#xff09; 的融合成为人工智能和计算科学领域的重要研究方向。随着经典计算机在某些复杂任务上接近计算极限&#xff0c;研究人员开始探索量子计算的独特优势…

Edge浏览器设置网页自动翻译

一.浏览网页自动翻译设置->扩展->获取Microsoft Edge扩展->搜索“沉浸式翻译”->获取 。提示&#xff1a;如果采用其他的翻译扩展没找自动翻译功能&#xff0c;所以这里选择“沉浸式翻译”二.基于Java WebElement时自动翻译Java关键代码&#xff1a;提示&#xff1…

TCP/UDP协议深度解析(四):TCP的粘包问题以及异常情况处理

&#x1f50d; 开发者资源导航 &#x1f50d;&#x1f3f7;️ 博客主页&#xff1a; 个人主页&#x1f4da; 专栏订阅&#xff1a; JavaEE全栈专栏 本系列往期内容~ TCP/UDP协议深度解析&#xff08;一&#xff09;&#xff1a;UDP特性与TCP确认应答以及重传机制 TCP/UDP协议深…

R 基础语法

R 基础语法 R 语言是一种针对统计计算和图形表示而设计的编程语言&#xff0c;广泛应用于数据分析、统计学习、生物信息学等领域。本文将为您介绍 R 语言的基础语法&#xff0c;帮助您快速入门。 1. R 语言环境搭建 在开始学习 R 语言之前&#xff0c;您需要安装并配置 R 语言环…

语义熵怎么增强LLM自信心的

语义熵怎么增强LLM自信心的 一、传统Token熵的问题(先理解“痛点”) 比如模型回答“阿司匹林是否治疗头痛?”→ 输出“是” 传统Token熵:只看“词的概率”,比如“是”这个词的概率特别高(Token熵0.2,数值低说明确定性强 )。 但实际风险:医学场景里,“是”的字面肯定…

javaweb的几大常见漏洞

CTF javaweb中几大常见漏洞(基于java-security靶场) 对于CTF而言&#xff0c;java类型的题目基本都是白盒代码审计&#xff0c;在java类型的web题目增长的今天&#xff0c;java代码审计能力在ctf比赛中尤为重要。 这篇博客主要是给大家介绍一下一些常见漏洞在java代码里面大概是…

【设计模式C#】外观模式(用于解决客户端对系统的许多类进行频繁沟通)

一种结构性设计模式。特点是将复杂的子系统调用逻辑封装到一个外观类&#xff0c;从而使客户端更容易与系统交互。优点&#xff1a;简化了接口的调用&#xff1b;降低了客户端与子系统的耦合度&#xff1b;封装了子系统的逻辑。缺点&#xff1a;引入了额外的类&#xff0c;可能…

【PTA数据结构 | C语言版】二叉堆的快速建堆操作

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 请编写程序&#xff0c;将 n 个顺序存储的数据用快速建堆操作调整为最小堆&#xff1b;最后顺次输出堆中元素以检验操作的正确性。 输入格式&#xff1a; 输入首先给出一个正整数 c&#xff08;≤1…

【数据结构初阶】--双向链表(二)

&#x1f525;个人主页&#xff1a;草莓熊Lotso &#x1f3ac;作者简介&#xff1a;C研发方向学习者 &#x1f4d6;个人专栏&#xff1a; 《C语言》 《数据结构与算法》《C语言刷题集》《Leetcode刷题指南》 ⭐️人生格言&#xff1a;生活是默默的坚持&#xff0c;毅力是永久的…

vue-cli 模式下安装 uni-ui

目录 easycom 自定义easycom配置的示例 npm安装 uni-ui 准备 sass 安装 uni-ui 注意 easycom 传统vue组件&#xff0c;需要安装、引用、注册&#xff0c;三个步骤后才能使用组件。easycom将其精简为一步。 只要组件路径符合规范&#xff08;具体见下&#xff09;&#…