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

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

目录

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 

读锁 -> 读锁

 读锁 -> 写锁

 写锁  ->  读锁

 

写锁 -> 写锁

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

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

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

相关文章

MyBatis学习笔记之缓存

文章目录 一级缓存一级缓存失效 二级缓存二级缓存失效二级缓存相关配置 MyBatis集成EhCache 缓存&#xff1a;cache 缓存的作用&#xff1a;通过减少IO的方式&#xff0c;来提高程序的执行效率 mybatis的缓存&#xff1a;将select语句的查询结果放到缓存&#xff08;内存&…

Pytorch深度学习-----神经网络的卷积操作

系列文章目录 PyTorch深度学习——Anaconda和PyTorch安装 Pytorch深度学习-----数据模块Dataset类 Pytorch深度学习------TensorBoard的使用 Pytorch深度学习------Torchvision中Transforms的使用&#xff08;ToTensor&#xff0c;Normalize&#xff0c;Resize &#xff0c;Co…

IDEA将本地项目上传到码云

一、创建本地仓库并关联 用IDEA打开项目&#xff0c;在菜单栏点击vcs->create git repository创建本地仓库&#xff0c; 选择当前项目所在的文件夹当作仓库目录。 二、将项目提交本地仓库 项目名右键就会出现“GIT”这个选项->Add->Commit Directory, 先将项目add…

【嵌入式学习笔记】嵌入式基础11——STM32常用轮子(SYSTEM)

1.deley文件夹介绍 1.1.delay文件夹介绍 函数名函数功能OSdelay_osschedlockus级延时时,关闭任务调度(防止打断us级延迟)OSdelay_osschedunlockus级延时时,恢复任务调度OSdelay_ostimedlyus级延时时,恢复任务调度OSSysTick_Handlersystick中断服务函数OSdelay_init初始化延迟…

【学习笔记】关于图像YUV格式分类和排布方式的全学习

这里是尼德兰的喵学习笔记相关文章&#xff0c;欢迎您的访问&#xff01; 如果文章对您有所帮助&#xff0c;期待您的点赞收藏 让我们一起为芯片前端全栈工程师而努力 目录 前言 YUV格式导图 YUV444 packed planar I444 YV24 semi-planar NV24 NV42 YUV422 packed …

c++静态代码扫描工具clang-tidy详细介绍

clang-tidy 文章目录 clang-tidy1. 什么是clang-tidy2. clang-tidy可以解决什么问题3. 工作原理4. 如何使用clang-tidy4. 总结5. 举例说明&#xff1a; 1. 什么是clang-tidy Clang-Tidy是一个由LLVM项目提供的开源工具&#xff0c;是一个静态分析工具&#xff0c;用于进行静态…

【雕爷学编程】MicroPython动手做(15)——掌控板之AB按键3

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

win10日程怎么同步到安卓手机?电脑日程同步到手机方法

在如今快节奏的生活中&#xff0c;高效地管理时间变得至关重要。而对于那些经常在电脑上安排日程的人来说&#xff0c;将这些重要的事务同步到手机上成为了一个迫切的需求。因为目前国内使用win10系统电脑、安卓手机的用户较多&#xff0c;所以越来越多的职场人士想要知道&…

macos下安装john the ripper并配置zip2john+破解加密zip文件

为了破解加密的zip文件&#xff0c;需要用到john进行爆破密码。 1、首先使用homebrew安装john&#xff0c;可以安装它的增强版john-jumbo: brew install john-jumbo 2、安装后可以使用 john 命令验证&#xff1a; john 3、配置zip2john的环境——.zshrc下&#xff0c;&#x…

HashMap查找

文章目录 1 哈希表的基本概念1.1 两个例子1.2 如何查找1.3 若干术语 2 哈希函数的构造方法2.1 直接定址法2.2 除留余数法 3 处理冲突的方法3.1 开放地址法3.1.1 线性探测法3.1.2 二次探测法3.1.3 伪随机探测法 3.2 链地址法&#xff08;拉链法&#xff09;3.2.1 创建步骤3.2.2 …

C#..上位机软件的未来是什么?

C#是一种流行的编程语言&#xff0c;广泛应用于桌面应用程序和上位机软件开发。未来&#xff0c;C#上位机软件将继续不断发展和创新&#xff0c;以满足用户日益增长的需求。以下是我认为C#上位机软件未来可能会涉及的一些方向&#xff1a; 更加智能化&#xff1a;随着人工智能…

idea连接远程服务器上传war包文件

idea连接远程服务器&上传war包 文章目录 idea连接远程服务器&上传war包1. 连接服务器2.上传war包 1. 连接服务器 选择Tools -> Start SSH Session 添加配置 连接成功 2.上传war包 Tools -> Deployment -> Browse Remote Host 点击右侧标签&#xff0c;点击&…

Manjaro KDE 22.1.3vmware无法复制文件

Wayland 是 X11 的现代替代品&#xff0c;几十年来 X11 一直是 Linux 上的默认窗口系统。 Wayland 是一种通信协议&#xff0c;定义 X Window 显示服务器和客户端应用程序之间的消息传递。 软件还不兼容 使用X11即可

linux查看服务器系统版本命令

有时我们需要在linux服务器上安装DB、Middleware等&#xff0c;为了保证兼容性&#xff0c;我们需要知晓被提供的linux服务器版本是否满足需求&#xff0c;下面就说一说linux查看服务器系统版本命令。 1.cat /etc/redhat-release 适用于&#xff1a;rhel/centos等 2.cat /etc…

java static修饰的静态成员

静态成员 特点&#xff1a; 1.静态成员可以被本类所有对象共享2.静态成员可以通过类名调用也可以推荐对象调用&#xff0c;但是推荐使用类名调用&#xff01;3.静态成员随着类的加载而加载&#xff0c;优先于对象存在的静态方法的注意事项&#xff1a; 1.非静态方法可以访问任…

大数据Flink(四十九):框架版本介绍和编程语言选择

文章目录 框架版本介绍和编程语言选择 一、框架版本介绍 二、编程语言选择 框架版本介绍和编程语言选择

1-Linux的目录结构

Linux的目录结构是规定好的&#xff0c;不可以随意进行更改&#xff01; Linux的文件系统是采用级层式的树状目录结构&#xff0c;最上层是根目录–/&#xff0c;然后再在根目录下创建其它的目录。 各个目录中主要负责的功能和作用如下&#xff1a;&#xff08;主体的结构一定…

Cisco 路由器配置管理

大多数网络中断的最常见原因是错误的配置更改。对网络设备配置的每一次更改都伴随着造成网络中断、安全问题甚至性能下降的风险。计划外更改使网络容易受到意外中断的影响。 Network Configuration Manager 网络更改和配置管理 &#xff08;NCCM&#xff09;解决方案&#xff…

springboot编写mp4视频播放接口

简单粗暴方式 直接读取指定文件&#xff0c;用文件流读取视频文件&#xff0c;输出到响应中 GetMapping("/display1/{fileName}")public void displayMp41(HttpServletRequest request, HttpServletResponse response,PathVariable("fileName") String fi…

掌握文件锁:使用flock实现多个进程之间的无缝文件同步

使用flock实现多个进程之间的无缝文件同步&#xff1f; 博主简介一、引言二、文件锁的概述2.1、定义文件锁2.2、文件锁的种类2.3、文件锁的作用 三、使用flock实现文件锁3.1、flock的简介3.2、flock的使用方法3.3、flock文件锁命令3.4、flock对文件同步的帮助 四、实现多个进程…