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

news/2024/10/23 17:37:09/

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

目录

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.ppmy.cn/news/990022.html

相关文章

数学建模算法应用合辑-AHP层次分析法【电工杯】高校教学资源合理分配

目录 摘 要 一、 问题重述 1.1 问题的背景 1.2 问题的描述 二、 问题的分析 2.1 概述

【算法基础:动态规划】5.4 数位统计DP(计数问题)(数位DP)

文章目录 例题&#xff1a;338. 计数问题解法1——转换成1067. 范围内的数字计数&#xff0c;数位DP模板解法2——分情况讨论&#xff08;TODO&#xff0c;还没理解&#xff09; 相关链接⭐ 例题&#xff1a;338. 计数问题 https://www.acwing.com/problem/content/340/ 解法…

力扣75——队列

总结leetcode75中队列的算法题解题思路。 上一篇&#xff1a;力扣75——哈希表/哈希集合 以下代码大部分为本人所写&#xff0c;少部分为官方示例代码。 力扣75——队列 1 最近的请求次数2 Dota2 参议院1-2 解题总结 1 最近的请求次数 题目&#xff1a; 写一个 RecentCounter…

PP-Matting: AI高精度图像前景Matting,让抠图轻而易举

分割和Matting的一个重要区别是:分割返回的是像素分类标签,其结果是整型数据;而Matting返回的是属于前景或背景的概率P,从而在前景与背景交互区域产生渐变的效果,使得抠图更加自然。Matting分割模型训练完成后,对于原始图像每个位置上的像素,都将生成一个表示其前景透明…

mysql innodb一些知识点

1、事务和锁的关系&#xff1b; 在MySQL事务中&#xff0c;只要开始了一次事务&#xff0c;就会自动加上一个共享锁&#xff08;Shared Lock&#xff09;。这个锁会在事务结束时自动释放。如果在事务中需要更新某个数据对象&#xff0c;那么MySQL会将该数据对象的共享锁升级为…

Echarts 文字太长用省略号代替

xAxis: [{type: category,data: [materialUserEchartsDate.value[0] ? materialUserEchartsDate.value[0].name : ,materialUserEchartsDate.value[1] ? materialUserEchartsDate.value[1].name : ,materialUserEchartsDate.value[2] ? materialUserEchartsDate.value[2].na…

LeetCode刷题笔记-287题寻找重复数

LeetCode 287 寻找重复数 难度&#xff1a;中等 题目&#xff1a; 给定一个包含 n 1 个整数的数组 nums &#xff0c;其数字都在 [1, n] 范围内&#xff08;包括 1 和 n&#xff09;&#xff0c;可知至少存在一个重复的整数。 假设 nums 只有 一个重复的整数 &#xff0c;返回…

css 四角边框移动效果

块是长宽相等的正方形&#xff0c;大小浏览器分辨率变化而变化利用平移变化translate来时实现边框到达鼠标划到的块&#xff0c;坐标是鼠标滑到块的offsetLeft和offsetTop <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8&quo…