Linux下进程特性总结:工作目录,环境变量,标准输出转命令行参数,O_CLOEXEC标志作用,读写锁控制进程互斥

news2024/9/25 3:22:05

       进程是运行中的程序,是资源分配的最小单位,其有一些特性对于实际开发很有帮助,本篇博客将进程的相关特性进行梳理总结,包含工作目录,环境变量,标准输出转命令行参数,读写锁控制进程互斥。

目录

1.进程工作目录

 1.1获取CWD

1.2修改CWD

1.3代码演示

2.环境变量

2.1操作效果演示

3.标准输出转命令行参数

 4.O_CLOEXEC标志作用

5.读写锁控制进程互斥


1.进程工作目录

        每个进程都有一个与之对应的工作目录(CWD),进程相对路径都是基于CWD,关于CWD有获取和修改CWD。

 

 1.1获取CWD

 #include <unistd.h>
char *getcwd(char *buf, size_t size);

//不安全,不建议使用
char *getwd(char *buf);
char *get_current_dir_name(void);

1.2修改CWD

#include <unistd.h>
int chdir(const char *path);
int fchdir(int fd);

1.3代码演示

ppath.c

// 如果是gcc编译器,需要使用该宏,否则gcc -E之后发现找不到get_current_dir_name声明,运行时程序崩溃
//#define  _GNU_SOURCE   
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

#define MAX_LEN (1024)

int main(void)
{
    char buffer[MAX_LEN] = {0};
    char *path = getcwd(buffer, MAX_LEN);
    if (path) {
        printf("buffer: %s path: %s size: %ld\n", buffer, path, strlen(buffer));
    }
    
    char* curdir = get_current_dir_name();
    if (curdir) {
        printf("curdir: %s\n" , curdir);
        free(curdir);
    }
 
    //不安全函数,不建议使用
    char *twd = getwd(buffer);  
    if (twd) {
        printf("buffer:%s  twd:%s \n", buffer, twd);
    }  

    //修改进程工作目录
    chdir("otherpath");
    curdir = get_current_dir_name();
    if (curdir) {
        printf("after curdir: %s\n" , curdir);
        free(curdir);
    }

    return 0 ; 
}

g++ -o ppath ppath.c

 

2.环境变量

        操作系统有系统环境变量,每个进程也有自己的环境变量,进程启动之后会拷贝一份环境变量到进程空间中,进程运行过程中可以增删改查自己的环境变量,而不会影响系统的环境变量。

2.1操作效果演示

penv.c

#include<stdio.h>
#include<stdlib.h>

extern char **environ;

int main(int argc,char *argv[],char *envp[]) {
    int i=0;

    // 通过envp获取系统所有环境变量
    for(; envp[i] != NULL; i++) {
        printf("%s\n", envp[i]);
    }
    
    printf("=========================================================\n");

    // 使用系统全局变量environ获取系统所有环境变量
    i = 0;
    for(; environ[i] != NULL; i++) {
        printf("%s\n", environ[i]);
    }

    char *pEnv = getenv("XDG_DATA_DIRS");
    if (pEnv) {
        printf("XDG_DATA_DIRS: %s\n", pEnv);
    }

    //设置的环境变量只在当前进程生效,不会影响系统的环境变量
    putenv("XDG_DATA_DIRS=Hello");
    pEnv = getenv("XDG_DATA_DIRS");
    if (pEnv) {
        printf("XDG_DATA_DIRS: %s\n", pEnv);
    }

    //修改或者增加环境变量
    setenv("XDG_DATA_DIRS", "world", 1);
    pEnv = getenv("XDG_DATA_DIRS");
    if (pEnv) {
        printf("XDG_DATA_DIRS: %s\n", pEnv);
    }

    //删除环境变量
    unsetenv("XDG_DATA_DIRS");
    pEnv = getenv("XDG_DATA_DIRS");
    if (pEnv) {
        printf("XDG_DATA_DIRS: %s\n", pEnv);
    }
    else {
        printf("no XDG_DATA_DIRS\n");
    }

    setenv("XDG_DATA_DIRS", "china", 1);
    pEnv = getenv("XDG_DATA_DIRS");
    if (pEnv) {
        printf("XDG_DATA_DIRS: %s\n", pEnv);
    }

    // 清除所有环境变量
    clearenv();
    pEnv = getenv("XDG_DATA_DIRS");
    if (pEnv) {
        printf("XDG_DATA_DIRS: %s\n", pEnv);
    }
    else {
        printf("clear XDG_DATA_DIRS\n");
    }
    pEnv = getenv("PATH");
    if (pEnv) {
        printf("PATH: %s\n", pEnv);
    }

    return 0;
}

gcc -o penv penv.c 

 

execle启动进程penv并修改其环境变量。

testenv.c

#include<stdio.h>
#include<unistd.h>

int main(void){

    const char* ps_env[] = {"PATH=hello:world", NULL};

    execle("./penv", "penv", NULL, ps_env);

    return 0;
}

gcc -o testenv testenv.c 

 

3.标准输出转命令行参数

        每个进程可以命令行参数输入参数,如何将一个标准输出数据转换为进程的命令行参数呢?不错大家应该会想到xargs命令,这个命令就是将标准输出参数转换为进程的命令行参数,例如

find ./* -name "*.c" | xargs grep main
xargs到底是怎么实现的呢?以下代码将实现一个自己的xargs

args.cpp

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

#include <iostream>
#include <string>
#include <vector>

using namespace std;

string getCommand(const vector<string> &cmd) {
    string command = "";
        for (int i = 0; i < cmd.size(); i++) {
        command = command + cmd[i] + " ";
    }
    return command;
}

void execCmd(vector<string> &cmd) {
    if (cmd.empty()) {
        return;
    }
    string file = cmd[0];
    pid_t pid = fork();

    if (pid < 0) {
        perror("fork error.");
        return;
    }
    if (pid == 0) {
    execl("/bin/bash", "bash", "-c", getCommand(cmd).c_str(), nullptr);
    exit(1);
  }
  int status = 0;
  int ret = waitpid(pid, &status, 0);
  if (ret != pid) {
    perror("waitpid failed.");
  }
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        cout << "arguments is invalid, use xargs cmd" << endl;
        return -1;
    }
    string line;
    string argument;
    vector<string> cmd{argv[1]};

    auto add_arg = [&](string &arg) {
        if (arg != "") {
            cmd.push_back(arg);
        }
        arg = "";
    };

    while (cin >> line) {
        for (size_t i = 0; i < line.size(); i++) {
            if (isblank(line[i])) {
                add_arg(argument);
                continue;
            }
            argument += line[i];
        }
        add_arg(argument);
    }
    execCmd(cmd);

    return 0;
}

g++ -std=c++11 -o exargs args.cpp 

 4.O_CLOEXEC标志作用

O_CLOEXEC标志作用
    当执行exec (3)函数族时, 被设置close_on_exec标志的文件被关闭;

Linux下父进程打开文件得到的fd,将会被子进程继承,子进程可对fd的读写操作。实际业务开发中,fork出子进程中会调用exec执行另一个程序,此时会用全新的程序替换子进程的正文,数据,堆和栈等。此时保存文件描述符的变量当然也不存在了,导致无法关闭无用的文件描述符了。
另外在复杂系统中,有时我们fork子进程时已经不知道打开了多少个文件描述符(包括socket句柄等),这此时进行逐一清理确实有很大难度,这时父进程打开文件可以使用标志O_CLOEXEC,当fork子进程后仍然可以使用fd。但执行exec后系统就会字段关闭子进程中的fd了。

fd设置标志O_CLOEXEC方法

// 方法一: 注意: O_CLOEXEC标志只在内核>=2.6.23和glibc≥2.7以上版本使用 Linux查看系统内核版本: uname -r / uname -a
int fd = open("xxx", O_RDWR | O_CLOEXEC);

// 方法二:
int fd=open("foo.txt", O_RDWR);
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);

// 方法三: Linux内核≥2.6.24且glibc≥2.7,fcntl接受新参数F_DUPFD_CLOEXEC:
#include <fcntl.h>
newfd = fcntl(oldfd, F_DUPFD_CLOEXEC);

// 方法四:inux内核≥2.6.27和glibc≥2.9
#define _GNU_SOURCE
#include <unistd.h>
pipe2(pipefds, O_CLOEXEC);
dup3(oldfd, newfd, O_CLOEXEC);

5.读写锁控制进程互斥

fcntl_lock.c

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

int set_lock(int fd, int type)
{
    struct flock lock;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;
    lock.l_type = type;
    lock.l_pid = -1;

    fcntl(fd, F_GETLK, &lock);

    if(lock.l_type != F_UNLCK)
    {
        if (lock.l_type == F_RDLCK)  
        {
            printf("read lock already set by %d\n", lock.l_pid);
        }
        else if (lock.l_type == F_WRLCK) 
        {
            printf("write lock already set by %d\n", lock.l_pid);
        }           
    }

    lock.l_type = type;

    if ((fcntl(fd, F_SETLKW, &lock)) < 0)
    {
        printf("lock failed : type = %d\n", lock.l_type);
        return 1;
    }

    switch (lock.l_type)
    {
        case F_RDLCK:
            printf("read lock set by %d\n", getpid());
            break;
        case F_WRLCK:
            printf("write lock set by %d\n", getpid());
            break;
        case F_UNLCK:
            printf("unlock by %d\n", getpid());
            break;
        default:
            break;
    }

    return 0;
}

int main(int argc, char** argv)
{  
    int fd;
    if (argc < 2) {
        printf("a.out [1 / 2]\n");  
        return -1;
    }

    fd = open("test.txt", O_RDWR | O_CREAT, 0644);
    if(fd < 0)
    {     
        printf("opem file error\n");  
        return -1; 
    }   
    
    if (1 == atoi(argv[1])) {
        // 上读锁
        set_lock(fd, F_RDLCK);
    }
    else {
        // 上写锁
        set_lock(fd, F_WRLCK);
    }
    getchar(); 
    set_lock(fd, F_UNLCK);  
    close(fd);   

    return 0;
}

gcc -o fcntl_lock fcntl_lock.c 

读锁 -> 读锁

 读锁 -> 写锁

 写锁  ->  读锁

 

写锁 -> 写锁

 可以看到两个进程同时加读锁,互不相互影响。加写锁时,要是有进程已经加了读锁或者写锁,则会阻塞,直到另外的进程将读锁或者写锁进行解锁。写锁是排他的。

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

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

相关文章

vo 2 输出helloworld

vo 2 输出helloworld 目录概述需求&#xff1a; 设计思路实现思路分析1.code 拓展实现性能参数测试&#xff1a; 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better r…

HCIP重发布实验

目录 实验要求&#xff1a; 步骤一&#xff1a;拓扑设计IP地址规划 拓扑设计 R1 R2 R3 R4 发布路由 R1 R2 R3 R4 双向重发布 在R2和R4 上进行 R2 R4 检查R1 修改开销值选路 择优选择去4.0网段的路径 测试&#xff1a;​编辑 择优选择去32网段的路径 测试&…

redis(四)—— java如何操作redis、springboot集成redis

一、java如何操作redis——Jedis jedis的“j”就是javajedis是java官方推荐的java操作redis工具&#xff0c;是一个非可视化的客户端redis-clientspringboot的redisTemplate对象就相当于这里的jedis对象&#xff08;redisTemplate去调用一系列方法不就相当于jedis这个client去…

使用java将个人微信打造成得力助手

本文提供一个通过java编程将微信打造成得力助手的方案, 先看效果&#xff1a; 查看支持的功能与对该功能开放的用户 接入人工智能 下载BiliBili 视频 原理 这个方案最难的地方就是如何把个人账号安全的接入微信&#xff0c;不会被封号。网上主流的有逆向web端微信&#xff0c…

KY258 日期累加

一、题目 二、代码 #include <iostream> using namespace std; class Date {public:Date(int year 0, int month 0, int day 0) {_year year;_month month;_day day;}Date(const Date& _d);int GetDay(int year, int month);Date& operator(int d);Date o…

this is incompatible with sql_mode=only_full_group_by

查看配置 select global.sql_mode 在sql命令行中输入select sql_mode 能够看到sql_mode配置,如果有ONLY_FULL_GROUP_BY&#xff0c;则需要修改 在mysql5.7.5后&#xff0c;ONLY_FULL_GROUP_BY是默认选项&#xff0c;所以就会导致group by的问题 set sql_mode‘复制去掉ONLY_F…

Python(五十)获取列表中指定的元素

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

嵌入式linux之OLED显示屏SPI驱动实现(SH1106,ssd1306)

周日业余时间太无聊&#xff0c;又不喜欢玩游戏&#xff0c;大家的兴趣爱好都是啥&#xff1f;我觉得敲代码也是一种兴趣爱好。正巧手边有一块儿0.96寸的OLED显示屏&#xff0c;一直在吃灰&#xff0c;何不把玩一把&#xff1f;于是说干就干&#xff0c;最后在我的imax6ul的lin…

BUU [BJDCTF2020]The mystery of ip

BUU [BJDCTF2020]The mystery of ip 再hint的源码里面找到这个东西。 这题一定和IP有关系&#xff0c;试了一下伪造IP还真是。 分析一下&#xff0c;这题可能存在SSTI漏洞&#xff0c;先用模板算式子{{9*‘9’}}测一下 那SSTI稳了&#xff0c;应该是Twig模板。 但是报错测出来是…

Android 面试题 线程间通信 六

&#x1f525; 主线程向子线程发送消息 Threadhandler&#x1f525; 子线程中定义Handler&#xff0c;Handler定义在哪个线程中&#xff0c;就跟那个线程绑定&#xff0c;在线程中绑定Handler需要调用Looper.prepare(); 方法&#xff0c;主线程中不调用是因为主线程默认帮你调用…

编写脚本,使用mysqldump实现分库分表备份。

一、实现分库备份&#xff1a; #!/bin/bash #分库备份 bak_userroot-----------备份用户 bak_password513721ykp--------备份密码 bak_path/backup/db_bak---------备份路径 bak_cmd"-u$bak_user -p$bak_password"-------登录命令&#xff0c;以便后面重复编写 exc_…

分布式锁漫谈

简单解释一下个人理解的分布式锁以及主要的实现手段。 文章目录 什么是分布式锁常用分布式锁实现 什么是分布式锁 以java应用举例&#xff0c;如果是单应用的情况下&#xff0c;我们通常使用synchronized或者lock进行线程锁&#xff0c;主要为了解决多线程或者高并发场景下的共…

3ds MAX绘制摄像机动画

之前&#xff0c;我们已经绘制了山地、山间小路、以及树林&#xff1a; 这里我们添加一个自由摄像机&#xff1a;&#xff08;前视图&#xff09; 在动作窗口&#xff0c;给摄像机添加一个按路径移动的设定&#xff1a; 这样&#xff0c;我们只要把指定的路径绘制出来&#xff…

UE4/5C++多线程插件制作(0.简介)

目录 插件介绍 插件效果 插件使用 English 插件介绍 该插件制作&#xff0c;将从零开始&#xff0c;由一个空白插件一点点的制作&#xff0c;从写一个效果到封装&#xff0c;层层封装插件&#xff0c;简单粗暴的对插件进行了制作&#xff1a; 插件效果 更多的是在cpp中去…

Cpp04 — 默认成员函数

前言&#xff1a;本文章主要用于个人复习&#xff0c;追求简洁&#xff0c;感谢大家的参考、交流和搬运&#xff0c;后续可能会继续修改和完善。 因为是个人复习&#xff0c;会有部分压缩和省略。 一、默认成员函数 当类里面成员函数什么都不写的时候&#xff0c;编译器会自动…

AutoSAR系列讲解(实践篇)10.3-BswM配置

目录 一、ECU State Handing(ESH) 二、Module Initialization 三、Communication Control 说起BswM的配置,其实博主问过很多朋友了,大家基本都只用自动配置;很少有用到手动配置的时候,对于刚刚入门的大家来说,掌握自动配置基 本也就足够了。 一、ECU State Handing(…

【雕爷学编程】MicroPython动手做(12)——掌控板之Hello World

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

内网隧道代理技术(十四)之 Earthworm的使用(一级代理)

Earthworm的使用(一级代理) ew 全称是EarchWorm,是一套轻量便携且功能强大的网络穿透工具,基于标准C开发,具有socks5代理、端口转发和端口映射三大功能,可在复杂网络环境下完成网络穿透,且支持全平台(Windows/Linux/Mac)。该工具能够以“正向”、“反向”、“多级级联”…

谷粒商城第七天-商品服务之分类管理下的删除、新增以及修改商品分类

目录 一、总述 1.1 前端思路 1.2 后端思路 二、前端部分 2.1 删除功能 2.2 新增功能 2.3 修改功能 三、后端部分 3.1 删除接口 3.2 新增接口 3.3 修改接口 四、总结 一、总述 1.1 前端思路 删除和新增以及修改的前端无非就是点击按钮&#xff0c;就向后端发送请求…

动脑学院Jetpack Compose学习笔记

最近b站学习了一下Compose相关内容&#xff0c;整理了相关笔记&#xff0c;仅供大家参考。 资源链接如下&#xff0c;象征性收取1个积分 https://download.csdn.net/download/juliantem/88125198