进程参数编程

news2025/1/10 3:26:27

问题

execve(...) 的参数分别是什么?有什么意义?

第一个参数是程序路径,第二个参数是进程参数,第三个参数是环境变量

再论 execve(...)

main 函数 (默认进程入口)

int main(int argc, char* argv[])

  • argc - 命令行参数个数
  • argv - 命令行参数数组

进程空间概要

Linux 进程地址空间的布局大致如上如所示,从高地址到低地址的布局分别为 启动参数和环境变量 (通过 execve() 拷贝父进程得到的)、栈 (用于函数调用)、堆 (用于动态分配内存)、未初始化的数据 (用于存放未初始化的全局变量和静态局部变量)、初始化的数据 (用于存放初始化的全局变量和静态局部变量) 、代码 (用于存放我们定义的函数)

下面我们通过一个程序来查看进程内存地址空间的布局

mem.c


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

static int g_init = 255;
static float g_uninit;

static void text()
{
}

int main(int argc, char* argv[])
{
    static double s_init = 0.255;
    static double s_uninit;
    int i = 0;
    int* p = malloc(4);
    
    printf("argv[0] = %p\n", argv[0]);
    printf("&i = %p\n", &i);
    printf("p = %p\n", p);
    printf("&g_uninit = %p\n", &g_uninit);
    printf("&s_uninit = %p\n", &s_uninit);
    printf("&g_init = %p\n", &g_init);
    printf("&s_init = %p\n", &s_init);
    printf("text = %p\n", text);
    
    free(p);

    return 0;
}

我们按照上面的进程地址空间布局图,按高地址到低地址,打印进程地址空间不同区域的地址值

程序运行结果如下图所示:

通过打印可以看出,进程地址空间的不同区域确实是按照上面的进程地址空间布局图的顺序从高到低排列的 

下面的程序输出什么?为什么?

child.c


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

int main(int argc, char* argv[])
{
    int i = 0;
    
    sleep(3);
    
    for(i=0; i<argc; i++)
        printf("exec = %d, %s\n", 
                       getpid(), argv[i]);
    
    return 0;
}

parent.c 



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

#define EXE "child.out"

int create_process(char* path, char* args[])
{
    int ret = fork();
    
    if( ret == 0 )
    {
        execve(path, args, NULL);
    }
    
    return ret;
}

void zero_str(char* s)
{
    while( s && *s ) *s++ = 0;
}

int main()
{
    char path[] = EXE;
    char arg1[] = "hello";
    char arg2[] = "world";
    char* args[] = {path, arg1, arg2, NULL};
    
    printf("%d begin\n", getpid()); 
    
    printf("%d child = %d\n", getpid(), create_process(EXE, args));
    
    zero_str(path);
    zero_str(arg1);
    zero_str(arg2);
    
    printf("%d path = %s\n", getpid(), path);
    printf("%d arg1 = %s\n", getpid(), arg1);
    printf("%d arg2 = %s\n", getpid(), arg2);
    
    printf("%d end\n", getpid());
    
    return 0;
}

这个程序在子进程 execve() 执行后,将传递给子进程的进程参数都清空

程序运行结果如下图所示:

通过打印可以看出,在父进程执行完 fork() 和 execve() 后,传递给子进程的进程参数已经被清空了;但在子进程中,子进程中的进程参数并没有被清空,这是因为父进程在执行完 fork() 和 execve() 后,子进程会将父进程传递给它的进程参数再复制一份,所以尽管在父进程中修改了传递给子进程的进程参数,也影响不了子进程的进程参数

Linux 启动参数 (命令行规范)

由选项,选项值,操作数组成

选项由短横 (-) 开始,选项名必须是单个字母或数字字符

选项可以有选项值,选项与选项值之间可用空格分隔 (-o test <=> -otest)

如果多个选项均无选项值,可合而为一 (-a -b -c <=> -abc)

既不是选项,也不能作为选项值的参数是操作数

第一次出现的双横线 (--) 用于结束所有选项,后续参数为操作数

Linux 启动参数 (命令行参数)解析

规则:if:s

示例:

if:s 这个规则定义了:有 3 个选项,分别为 -i、-f、和 -s,其中 -i 和 -s 不带选项值,-f 必须带选项值

第 1 个示例:-f 是选项,abc 是它的选项值,def 是操作数

第 2 个示例:-s 和 -i 是选项,-v 是一个不存在的选项

第 3 个示例:abc 是操作数,-f 是选项,gg 是它的选项值,de 是操作数,-s 是选项

第 4 个示例:abc 是操作数,-- 表示后面的参数都是操作数,所以 -- 后面的 -f -s 都是操作数

第 5 个示例:-s 是选项,它不带选项值,所以 abc 是操作数;-i 是选项;-f 是选项,但它后面不带选项值,所以是不合法的

Linux 启动参数 (命令行参数) 编程

getopt(...) 从 argc 和 argv 中获取下一个选项

  • 选项合法:选项值为选项字符,optarg 指向选项值字符串
  • 选项不合法:返回字符 '?' ,optopt 保存当前选项字符 (错误)
  • 选项合法但缺少选项值:返回字符 ':',optopt  保存当前选项字符 (错误)

默认情况下 getopt(...) 对 argv 进行重排,所有操作数位于最后位置

optstring 规则的拓展定义

起始字符可以是 :,+,- 或省略

  • 省略:=> 出现选项错误时,程序中通过 : 或者 ? 进行处理并给出默认错误提示
  • : => 错误提示开关,程序中通过 : 或者 ? 进行处理 (无默认错误提示)
  • + => 提前停止开关,遇见操作数时返回 -1,认为选项处理完毕 (后续都是操作数)
  • - => 不重排开关,遇见操作数时,返回 1,optarg 指向操作数字符串
  • 组合 => +: or -:

进程参数编程

main.c

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

int main(int argc, char* argv[])
{
    int i = 0;
    int c = 0;
    int iflg = 0;
    int fflg = 0;
    int sflg = 0;
    char* fvalue = NULL;
    
    while( (c = getopt(argc, argv, "f:is"))  != -1 )
    {
        switch( c )
        {
            case 'f':
                fflg = 1;
                fvalue = optarg;
                break;
            case 'i':
                iflg = 1;
                break;
            case 's':
                sflg = 1;
                break;
            case '?':
                printf("Unknow option: -%c\n", optopt);
                break;
            case ':':
                printf("-%c missing option argument\n", optopt);
                break;
            case 1:
                printf("inter: %s\n", optarg);
                break;
            default:
                printf("ret = %d\n", c);
        }
    }
    
    printf("fflg = %d, fvalue = %s, iflg = %d, sflg = %d\n", fflg, fvalue, iflg, sflg);
    
    for(i=optind; i<argc; i++)
    {
        printf("parameter: %s\n", argv[i]);
    }
    
    return 0;
}

c 是 getopt 匹配到的选项字符,为 f 时,optarg 指向它对应的选项值;为 ? 时,表示当前选项不合法;为 : 时,表示当前选项没有选项值;为 1 时,表示为操作数,optarg 指向这个操作数

optind 是操作数在 argv 中的起始下标

由于 optstring 的起始字符不是 + 或 -,这种情况下,getopt(...) 会对 argv 进行重排,所有操作数位于最后位置

程序运行结果如下图所示:

下面使用 optstring 的拓展规则进行实验

使用 +:

mian.c

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

int main(int argc, char* argv[])
{
    int i = 0;
    int c = 0;
    int iflg = 0;
    int fflg = 0;
    int sflg = 0;
    char* fvalue = NULL;
    
    while( (c = getopt(argc, argv, "+:f:is"))  != -1 )
    {
        switch( c )
        {
            case 'f':
                fflg = 1;
                fvalue = optarg;
                break;
            case 'i':
                iflg = 1;
                break;
            case 's':
                sflg = 1;
                break;
            case '?':
                printf("Unknow option: -%c\n", optopt);
                break;
            case ':':
                printf("-%c missing option argument\n", optopt);
                break;
            case 1:
                printf("inter: %s\n", optarg);
                break;
            default:
                printf("ret = %d\n", c);
        }
    }
    
    printf("fflg = %d, fvalue = %s, iflg = %d, sflg = %d\n", fflg, fvalue, iflg, sflg);
    
    for(i=optind; i<argc; i++)
    {
        printf("parameter: %s\n", argv[i]);
    }
    
    return 0;
}


+ 会使得 getopt(...) 不会对 argv 进行重排,在碰到操作数以后,getopt(...) 返回 -1,不会再进行匹配了

: 会使得 getopt(...) 匹配失败后,不会有输出错误提示的打印

程序运行结果如下图所示:

使用 -:

mian.c

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

int main(int argc, char* argv[])
{
    int i = 0;
    int c = 0;
    int iflg = 0;
    int fflg = 0;
    int sflg = 0;
    char* fvalue = NULL;
    
    while( (c = getopt(argc, argv, "-:f:is"))  != -1 )
    {
        switch( c )
        {
            case 'f':
                fflg = 1;
                fvalue = optarg;
                break;
            case 'i':
                iflg = 1;
                break;
            case 's':
                sflg = 1;
                break;
            case '?':
                printf("Unknow option: -%c\n", optopt);
                break;
            case ':':
                printf("-%c missing option argument\n", optopt);
                break;
            case 1:
                printf("inter: %s\n", optarg);
                break;
            default:
                printf("ret = %d\n", c);
        }
    }
    
    printf("fflg = %d, fvalue = %s, iflg = %d, sflg = %d\n", fflg, fvalue, iflg, sflg);
    
    for(i=optind; i<argc; i++)
    {
        printf("parameter: %s\n", argv[i]);
    }
    
    return 0;
}


- 会使得 getopt(...) 不会对 argv 进行重排,optind 这个时候等于 argc,在碰到操作数以后,getopt(...) 返回 1,optarg 指向匹配到的操作数

: 会使得 getopt(...) 匹配失败后,不会有输出错误提示的打印

程序运行结果如下图所示:

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

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

相关文章

java——jdbc编程

文章目录 JDBC的概念JDBC的常用APIJDBC示例代码PreparedStatementCallableStatement JDBC&#xff08;Java Database Connectivity&#xff09;是Java的一种数据库访问标准&#xff0c;它提供了一套API&#xff0c;使得我们可以通过Java程序来访问和操作各种关系型数据库。 下面…

从零开始手搓一个STM32与机智云的小项目——GPIO模拟时序控制外设2

文章目录 前言模块简介硬件介绍硬件连接通信时序DHT11的数据帧格式信号时序1. 起始信号2.应答信号(响应信号)3.接收数据0与14.获取数据5结束信号 输入输出切换实际效果 总结 前言 在上一篇中介绍了&#xff0c;使用GPIO模拟WS2812B的控制时序来实现对RGB灯的控制&#xff0c;本…

【开源与项目实战:开源实战】84 | 开源实战四(上):剖析Spring框架中蕴含的经典设计思想或原则

在 Java 世界里&#xff0c;Spring 框架已经几乎成为项目开发的必备框架。作为如此优秀和受欢迎的开源项目&#xff0c;它是我们源码阅读的首选材料之一&#xff0c;不管是设计思想&#xff0c;还是代码实现&#xff0c;都有很多值得我们学习的地方。接下来&#xff0c;我们就详…

Nginx服务的主配置文件 nginx.conf

目录 前言 一、Nginx.con位置 二、Nginx.com相关内容 三、Nginx.conf中配置块和指令 1、I/O时间配置 2、HTTP 配置 日志格式设定 总结 前言 Nginx 的主配置文件是 nginx.conf&#xff0c;它通常位于 Nginx 的安装目录下的 conf 文件夹中。主配置文件 nginx.conf 是 Ngin…

Rust语言从入门到入坑——(6)Rust组织管理

文章目录 0 引入1、组织概念1. 1、箱1. 2、包1. 3、模块 2、组织中权限2.1 权限2.2 模块引用2.2 Use 关键词 3、总结 0 引入 任何一门编程语言如果不能组织代码都是难以深入的&#xff0c;几乎没有一个软件产品是由一个源文件编译而成的。本教程到目前为止所有的程序都是在一个…

java——内部类和异常处理

文章目录 内部类成员内部类局部内部类匿名内部类静态内部类 异常处理异常捕获与处理多重异常捕获和处理抛出异常 内部类 Java内部类&#xff08;Inner Class&#xff09;是嵌套在其他类中的类&#xff0c;它可以访问外部类的成员变量和方法&#xff0c;同时也可以被外部类访问…

模拟电路系列文章-ADC驱动电路(下)

目录 概要 整体架构流程 技术名词解释 技术细节 1.低阻输出&#xff0c;以减小误差 2.抗混叠 3.电源级保护 小结 概要 提示&#xff1a;这里可以添加技术概要 一个模拟电压信号&#xff0c;在进入ADC 的输入端之前&#xff0c;一般都需要增加一级驱动电路(Driver]。但是&#…

C语言基础:指针的使用

本文结合工作经验&#xff0c;研究C语言中指针的用法。 文章目录 1 指针的概念2 用法与使用场景2.1 函数的指针参数2.1.1 基本概念2.1.2 使用场景1-函数返回多个值2.1.3 使用场景2-减少函数参数 2.2 void*指针2.2.1 基本概念2.2.2 使用场景 2.3 空指针2.4 const指针2.4.1 基本…

三分钟学习一个python小知识5-----------我的对python中pandas的理解, 我列举了关于pandas常用的4个例子来深入理解pandas

这里写目录标题 1、Pandas是什么2、Pandas的常用功能&#xff1a;2.1. 读取和写入数据2.2. 数据清洗和转换2.3. 数据分析和计算2.4. 数据可视化总结 1、Pandas是什么 Pandas是Python中一个非常流行的数据处理和分析库&#xff0c;可以使用它对数据进行读取、清洗、转换、分析和…

【动态规划算法练习】day4

文章目录 一、213. 打家劫舍 II1.题目简介2.解题思路3.代码4.运行结果 二、740. 删除并获得点数1.题目简介2.解题思路3.代码4.运行结果 三、剑指 Offer II 091. 粉刷房子1.题目简介2.解题思路3.代码4.运行结果 总结 一、213. 打家劫舍 II 1.题目简介 213. 打家劫舍 II 你是一…

数字IC工程师的护城河是什么?

每个人都希望能够增加⾃⼰的核心竞争⼒&#xff0c;然后延展职业⽣涯。 可能IC研发工程师基本上都会有个40岁危机&#xff1f; 时代背景是最⼤的变数&#xff0c;它改变了⼈才供需和技术⾛向&#xff0c;⽐如做处理器 core曾经是屠⻰术&#xff0c;⽽现在是⻩⾦时代 处理器 cor…

堆和优先队列

文章目录 堆维护堆的性质建堆堆排序算法 优先队列详解cpp标准库 priority_queue 参考文献 堆 虽然“堆”这个词源自堆排序&#xff0c;但是目前它已经被引申为“垃圾存储机制”&#xff0c;例如在Java和Lisp语言中所定义的。强调一下&#xff0c;我们使用的堆不是垃圾收集存储…

设计模式之访问者模式笔记

设计模式之访问者模式笔记 说明Iterator(访问者)目录访问者模式示例类图抽象访问者角色类抽象元素角色类宠物猫类宠物狗类自己类其他人类家类测试类 说明 记录下学习设计模式-访问者模式的写法。JDK使用版本为1.8版本。 Iterator(访问者) 意图:表示一个作用于某对象结构中的…

平面设计师实习报告范文十篇

平面设计师实习报告范文1 经过了四年的系统性理论学习&#xff0c;使我对平面设计有了基本掌握&#xff0c;对于平面设计这个专业也有了一个系统的了解和掌握。我们即将离开大学校园&#xff0c;作为平面设计专业的毕业生&#xff0c;心中想得更多的是如何去做好一个平面设计师…

ETCD集群运维

ETCD集群运维 ETCD扩容节点ETCD参数调优时间参数快照磁盘网络 监控维护压缩历史版本消除碎片化存储配额快照备份 灾难恢复快照恢复 etcd gateway作用什么时候不应该使用网关启动方式 gRPC代理可扩展的watch API租约请求缓存启动gRPC代理 ETCD扩容节点 节点1&#xff1a;10.1.1…

管理类联考——逻辑——知识篇——论证推理——一、削弱——haimian

削弱 考点分析 削弱 年度 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023题量223262132 主要问法 以下哪项如果为真&#xff0c;则能最严重地削弱&#xff08;反驳&#xff09;上述结论?以下哪项如果为真&#xff0c;则最能削弱&#xff08;反驳&#xff09…

文心一言 VS 讯飞星火 VS chatgpt (46)-- 算法导论6.1 4题

四、假设一个最大堆的所有元素都不相同&#xff0c;那么该堆的最小元素应该位于哪里? 文心一言&#xff1a; 最大堆的最小元素位于堆的根结点。由于最大堆的性质&#xff0c;根结点是堆中所有元素的最大值&#xff0c;因此它也是堆中所有元素的最小值。 讯飞星火&#xff1a…

工具及方法 - 如何进行任务管理

如何做好你该做的事&#xff0c;这是你的一生之敌。 没有一步到位的工具和一劳永逸的方法&#xff0c;只有不断变化的需求和自己用起来顺手的感觉。 对于GTD工具&#xff0c;在手机上安装过番茄工作法的工具&#xff0c;在电脑的Excel上也做过任务列表&#xff0c;在免费的项目…

解决 kali换源之后签名无效

报错问题&#xff1a; apt-get update 报错 更新扩展知识&#xff1a;kali更新源 终端输入 vi /etc/apt/sources.list #中科大 deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib deb-src http://mirrors.ustc.edu.cn/kali kali-rolling main …

计算机视觉中,有哪些基于控制点对的图像变换?

这里探讨的所有图像变换&#xff08;二维&#xff09;都是基于控制点对的&#xff0c;它们的主要区别在于&#xff1a; 1、它们是如何通过两张图像的控制点对产生变换场&#xff08;变换矩阵或者变换公式中的参数&#xff09;的 2、控制点之间的对应关系严格程度 这里说的变换…