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

Linux应用软件编程---多任务(进程2)(资源回收函数(wait、waitpid)、exec函数族、linux下的命令、const四种位置表示的含义)

一、进程回收资源空间函数

1、wait()

    1)需要包含头文件

       #include <sys/types.h>
#include <sys/wait.h>

    2)解释说明

pid_t wait(int *wstatus);
功能:阻塞等待回收子进程的资源空间
参数:
wstatus :保存子进程消亡状态的变量地址
若为NULL:不保存子进程退出的状态
返回值:
成功:返回回收到的子进程的PID号
失败:-1
wait(NULL);

        可以在子进程结束时,通过 return 或 exit 来实现给 wait 函数的传参操作。

2、waitpid()

    1)需包含头文件

       #include <sys/types.h>
#include <sys/wait.h>

    2)解释说明

 pid_t waitpid(pid_t pid, int *status, int options);
功能:回收指定进程的资源。和wait功能相似,比wait更灵活
参数:
pid:回收范围
<-1   回收指定进程组内的任意子进程 (-100.等待GID=100的进程组中的任意子进程)
-1    回收任意子进程,组内外
0     回收和当前调用waitpid一个组的所有子进程,组内 
> 0   回收指定ID的子进程
status:子进程退出时候的状态,
如果不关注退出状态用NULL;
options 选项:
0             表示回收过程会阻塞等待
WNOHANG    表示非阻塞模式回收资源。
返回值: 成功 返回接收资源的子进程pid
失败  -1
设定为非阻塞且没有回收到子进程返回0 

3、wait函数与waitpid函数的宏

    1)WIFEXITED(status)

        判断子进程是否正常终止,若正常终止返回非零 (真) ,否则返回0 (假)。

    2)WEXITSTATUS(status)

        正常终止时,获取子进程的退出状态码,即子进程退出时返回的值。

    3)WIFSIGNALED(status)

        判断子进程是否被信号终止 (如被 kill 命令发送信号终止),若被信号终止返回非零 (真),否则返回0 (假)。

    4)WTERMSIG(status)

        获取导致子进程终止的信号的编号。

    5)WIFSTOPPED(status)

        判断子进程是否被信号暂停,若子进程被暂停返回非零 (真),否则返回0 (假)。

        *注:仅在使用 waitpid 且指定 WUNTRACED 选项时才可能为真。

    6)WSTOPSIG(status)

        获取导致子进程暂停的信号编号。

4、子进程资源空间回收策略

    1)wait 阻塞回收:一般情况下,父进程专门负责资源回收;
2)waitpid 非阻塞方式回收:搭配轮询方式回收。
3)不回收:子进程的任务需要一直执行
4)异步回收:当子进程结束时通知父进程进行回收

5、例题

    1)wait() 函数

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>int main(int argc, const char *argv[])
{pid_t pid = fork(); //创建子进程if (pid > 0) //父进程{int wstatus;pid_t pidtmp = wait(&wstatus);printf("pidtmp = %d\n", pidtmp);if (WIFEXITED(wstatus)){printf("%d over normall, state : %d\n", pidtmp, WEXITSTATUS(wstatus));}else if (WIFSIGNALED(wstatus)){printf("%d ober by signal,signal num = %d\n", pidtmp, WTERMSIG(wstatus));}while (1){printf("I am father : pid= %d\n", getpid());sleep(1);}}else if (0 == pid) //子进程{int i = 20;while (i--){printf("I am son : pid = %d\n", getpid());sleep(1);}exit(9); //终止信号为 9 号 SIGKILL//return 9;}else{perror("fork error");}return 0;
}

    2)waitpid() 函数

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>int main(int argc, const char *argv[])
{pid_t pid = fork();if (pid > 0){while (1){int wstatus;//pid_t pidtmp = waitpid(-1, &wstatus, 0);  ==>wait(&wstatus)pid_t pidtmp = waitpid(-1, &wstatus, WNOHANG);printf("pidtmp = %d\n", pidtmp);if (0 != pidtmp){if (WIFEXITED(wstatus)){printf("%d over normall, state : %d\n",pidtmp,WEXITSTATUS(wstatus));}else if (WIFSIGNALED(wstatus)){printf("%d ober by signal,signa is= %d\n",pidtmp,WTERMSIG(wstatus));}}printf("I am father : pid= %d\n", getpid());sleep(1);}}//轮询else if (0 == pid){int i = 20;while (i--){printf("I am son : pid = %d\n", getpid());sleep(1);}exit(9);//return 9;}else{perror("fork error");}return 0;
}

 

二、exec 函数族

1、概念

        功能:在一个进程里面执行另外一个文件(可执行文件)。
本质:将文本去的指令代码替换成exec要执行的文件的指令。

2、分类

        exec 函数族分为 execl、execlp、execle、execv、execvp、execvpe六种,下面对这六种中的函数部分进行说明。

    1)execl()

int execl(const char *path, const char *arg, ... , /* (char  *) NULL */);

参数:
path:要执行的可执行文件的路径和名称
arg:执行该可执行文件时需要传递的参数

                省略号部分填写与 arg 同类的参数,直至填写完毕
NULL :参数传递结束标志
返回值:
出错:-1

    2)execlp()

int execlp(const char *file, const char *arg, ... , /* (char  *) NULL */);

功能:从PATH指定的系统路径下寻找该可执行文件

           * 不可执行自己编写的可执行文件。
参数:
file:需要执行的可执行文件的名称(系统路径下已有的文件)
arg: 执行该可执行文件时需要传递的参数

    3)execv()

        execv() 与 execl() 作用一样,只是 execv() 被用于绝对路径中的替换执行。

int execv(const char *path, char *const argv[]);

参数:

        psth:绝对路径
argv:指针数组
举例:实现 ls -l 功能

            char *arg[] = {"ls", "-l", NULL);
execv("/bin/ls", arg);

    4)exec 说明

        l:list 列表

        p:path 路径 (系统路径)

        v:vector 容器

        e:env 环境变量

    5)举例说明

        (1)上述函数的使用格式实例

#include <stdio.h>
#include <unistd.h>int main(int argc, int *argv[])
{printf("exec : pid = %d\n", getpid());execl("./hello", "./hello", NULL);execl("/bin/ls", "ls", "-l", NULL);char *arg[] = {"is", "-l", NULL};execv("/bin/ls", arg);execlp("ls", "ls", "-l", NULL);execvp("ls", arg);return 0;
}

        (2)使用 exec函数族 实现 Linux 中 system 函数的功能。

核心思想:

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>int my_system(char *buff)
{char *arg[10] = {NULL};int i = 0;char cmd[512] = {0};strcpy(cmd, buff);arg[i] = strtok(cmd, " ");while (arg[i] != NULL){i++;arg[i] = strtok(NULL, " ");}pid_t pid = fork();if (pid > 0) //父进程{wait(NULL);}else if (0 == pid) //子进程{execvp(arg[0], arg);}return 0;
}int main(int argc, const char *argv[])
{my_system("ls -l");return 0;
}

三、Linux 下的命令

    1. 内核内置命令:cd
2. 外部命令:ls、rm、 touch、whereis 命令
3. 别名:例如:ll <---> (ls -l)

四、const 四种存放位置的解析

        下述说明中假定指针名为 p。

1、const 类型 *指针名

        含义:指向常量字符的指针。

        说明:指针 p 本身可以被修改,可以指向其他地址,但 p 指向的字符内容 (*p) 不能被修改 (被 const 限定) 。例如:

const char *p = "hello";
p = "word"; //正确(指针可以指向新地址)
//*p = 'H'; //错误(不能修改指向的内容)

2、类型 *const 变量名

        含义:指向字符的常量指针。

        说明:指针 p 本身是常量,不能被修改,不能指向其他地址,但 p 指向的字符内容 (*p) 可以被修改。例如:

char str[] = "hello";
char *const p = str;
// p = "world"; // 错误(指针本身不能指向新地址)

*p = 'H'; //合法(可以修改指向的内容)

3、const 类型 变量名

        含义:修饰普通变量,变量的值不能修改,本身是只读的。

        说明:const 修饰后的变量的值在初始化之后,不允许通过赋值等操作修改。例如:

const char p = 'A';
p = 'B'; //错误(不能修改变量的值)

4、const 类型 *const 指针名

        含义:指向常量字符的常量指针。

        说明:指针 p 本身是常量,不能被修改,不能指向其他地址,同时 p 指向的字符内容 (*p) 也不能被修改。例如:

const char *const p = "hello";
p = "world"; //错误(指针本身不能修改)
*p = 'H'; //错误(指向的内容也不能修改)

五、minshell 功能的实现

1、流程图

2、程序代码

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>int my_system(char *buff)
{if(buff == NULL || strlen(buff) == 0){return 0;}char *arg[10] = {NULL};int i = 0;char cmd[1024] = {0};strcpy(cmd, buff);arg[i] = strtok(cmd, " ");while (arg[i] != NULL){printf("arg[%d] = %s\n", i, arg[i]);i++;arg[i] = strtok(NULL, " ");}pid_t pid = fork();if (pid > 0) //父进程{wait(NULL);}else if (0 == pid) //子进程{execvp(arg[0], arg);perror("execvp failed");exit(1);}else{perror("fork failed");return -1;}return 0;
}int main(int argc, const char *argv[])
{while(1){char buff[1024] = {0};getcwd(buff, sizeof(buff));printf("zmy@ubuntu:%s:", buff);char p[1024] = {0}; fgets(p, sizeof(p), stdin);p[strcspn(p, "\n")] = '\0';if(strcmp(p, "q") == 0){break;}my_system(p);}return 0;
}

3、运行结构示意图

 

【END】

 

http://www.dtcms.com/a/332397.html

相关文章:

  • 一周学会Matplotlib3 Python 数据可视化-绘制树形图
  • Laravel 中解决分表问题
  • ESP32-C3_SMARTCAR
  • 高并发场景下限流算法对比与实践指南
  • 【unity实战】Unity游戏开发:如何用ScriptableObject与序列化多态实现可复用的模块化效果系统?
  • ABP vNext+ WebRTC DataChannel 低延迟传感推送
  • 物联网(IoT)系统中,通信协议如何选择
  • C++——分布式
  • Al大模型-本地私有化部署大模型-大模型微调
  • 图像识别控制技术(Sikuli)深度解析:原理、应用与商业化前景
  • Zabbix【部署 01】Zabbix企业级分布式监控系统部署配置使用实例(在线安装及问题处理)程序安装+数据库初始+前端配置+服务启动+Web登录
  • 後端開發Python篇
  • StarRocks集群部署
  • 从 0 到 1 玩转Claude code(蓝耘UI界面版本):AI 编程助手的服务器部署与实战指南
  • Xget:为您的开发工作流解锁极致速度
  • 清除 pnpm 缓存,解决不同源安装依赖包失败的问题
  • “大模型”技术专栏 | 浅谈基于 Kubernetes 的 LLM 分布式推理框架架构:概览
  • 力扣 hot100 Day74
  • Floyd 判圈算法(龟兔赛跑算法)
  • LeetCode热题100--146.LRU缓存--中等
  • SSL和TLS协议的消息认证码(MAC)
  • Grafana 与 InfluxDB 可视化深度集成(一)
  • Grafana 与 InfluxDB 可视化深度集成(二)
  • LeetCode 刷题【42. 接雨水】
  • RecyclerView 性能优化:从原理到实践的深度优化方案
  • 新手向:Python函数定义与参数传递(位置参数、关键字参数、默认参数)
  • electron之win/mac通知免打扰
  • 什么是接口?PHP如何使用 SessionHandlerInterface 接口实现Session自定义会话数据存储
  • cloudflare缓存配置
  • 【Mysql语句练习】