Colderleo's blog Colderleo's blog
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)

Colder Leo

热爱代码的打工人
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)
  • 常见程序性能开销cost、latency延迟耗时的经验值
  • 面试常见问题
  • 静态链接-动态链接-elf详解-elfloader
  • 动态库和静态库的依赖问题
  • glibc和ld的编译调试-为某程序单独设置ld
  • dl_iterate_phdr遍历linkmap头、获取so加载地址
  • shell、bash语法和脚本模板
  • so文件查找路径
  • 逻辑地址-线性地址or虚拟地址-物理地址
  • 通过ELF读取当前进程的符号表并获取符号的版本信息
  • 虚拟内存,cache,TLB,页表
  • 用户内存空间分布和mmap
  • numa网卡绑定
  • 隔核绑核、服务器优化
  • popen底层原理和仿照实现-execl
    • 实现popen输出到标准输出
    • 参考原文
  • tmux用法
  • ASLR机制
  • 程序后台运行、恢复前台nohup
  • 大页内存huge_page
  • 用perf查看page-fault
  • Bash设置显示全部路径
  • 查看socket fd状态,设置nonblock
  • cout输出到屏幕的过程
  • 多进程写同一文件-write原子性-log日志
  • vim用法
  • epoll用法
  • signal信号、软中断、硬中断、alarm
  • 内核模块
  • 读写锁之pthread_rwlock和内核rwlock自旋读写锁
  • systemtap
  • xargs、awk用法
  • openssl libssl.so.10.so缺失问题
  • netstat用法
  • fork函数
  • tcp延迟确认ack
  • 90.centos7上一次std-string编译错误定位
  • docker用法
  • find用法
  • dmesg
  • gcc编译用法
  • avx-sse切换惩罚
  • Centos7防火墙
  • chmod用法
  • kernel-devel安装版本
  • Linux-Centos7系统安装、网络设置、常见报错
  • linux下g++编译c++文件
  • MegaCli 安装及使用
  • mysql
  • mysql忘记密码修改方法
  • set用法
  • crontab
  • ssh传文件scp
  • ssh连接
  • tcpdump、tshark、tcpreplay用法
  • ubantu root登录以及创建新用户
  • ubuntu安装g++和gdb
  • uClibc编译失败解决方法
  • win10安装WSL open-ssh
  • yum升级git
  • 比较so文件版本-md5sum
  • 查看磁盘信息
  • 合并两个硬盘,挂载到一个文件夹下
  • 软件安装目录usr-local-src
  • 下载centos历史版本
  • sh脚本转可执行文件、加密
  • Linux
gaoliu
2022-01-25
目录

popen底层原理和仿照实现-execl

# 实现popen输出到标准输出

pid_t mypopen(const char *cmd) {
    pid_t pid = fork();
    if(pid != 0)
        return pid;
    
    char _cmd[512]{0};
    sprintf(_cmd, "%s", cmd);
    execl("/bin/sh", "sh", "-c", _cmd, NULL);
    exit(0); //will never get here.
}
1
2
3
4
5
6
7
8
9
10

# 参考原文

Linux popen()函数内部实现原理: https://www.joxrays.com/linux-popen/

最近发现了一个函数 popen()/pclose() ,所以现在打算理清下思路…

# 正文

linux下的 popen 函数,大概就是通过 fork 一个子进程来执行命令并返回执行的结果给父进程.

The popen() function shall execute the command specified by the string command. It shall create a pipe between the calling program and the executed command, and shall return a pointer to a stream that can be used to either read from or write to the pipe.

http://pubs.opengroup.org/onlinepubs/009695399/functions/popen.html (opens new window)

函数声明如下

#include<stdio.h>
/* Create a new stream connected to a pipe running the given command.
   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern FILE *popen (const char *__command, const char *__modes) __wur;
/* Close a stream opened by popen and return the status of its child.
   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern int pclose (FILE *__stream);
Copy
1
2
3
4
5
6
7
8
9
10

当然了,更重要的是要知道该函数内部是怎么实现的,大概步骤如下:(此处仅讨论子进程返回结果给父进程!)

  1. 父进程通过 pipe() 创建 读/写匿名管道 ,并关闭写管道pfd[1]
  2. 然后 fork() 创建子进程
  3. 子进程 close() 读管道pfd[0]和标准输出(STDOUT_FILENO=1) ,并 dup() 写管道pfd[1]
  4. 子进程通过 execl() 执行命令,执行的结果为标准输出重定向到了写管道pfd[1]
  5. 父进程 wait() 子进程退出,接着 fdopen() 通过文件描述符返回一个 FILE* . 注意返回文件指针时,不能关闭读管道pfd[0],可以通过后续 fclose() 关闭该描述符
  6. fclose() 关闭该读管道pfd[0]

简单实现代码如下:

FILE* mypopen(const char*cmd){
    //管道文件描述符
    int pfd[2];
    if(pipe(pfd)!=0){
        printf("error: %s\n",strerror(errno));
        exit(EXIT_FAILURE);
    }
    //创建子进程
    pid_t  pid=fork();
    if(pid==0){
        close(pfd[0]);
        
         //重定向 标准输出 到 写管道
        close(STDOUT_FILENO);
        dup(pfd[1]);
        char _cmd[50]{0};
        sprintf(_cmd,"%s",cmd);
        execl("/bin/sh","sh","-c",_cmd,NULL);
        exit(0);
    }
    //关闭写管道
    close(pfd[1]);
    //等待子进程退出
    int p= wait(0);
   //printf("%d terminal\n",p);
    //直接读取数据
    //char buffer[1024*2]={0};
    //read(pfd[0],buffer,1024*2);
    FILE* pfile =fdopen(pfd[0],"r");
    if(!pfile) return NULL;
    return pfile;
}
Copy
int main() {
    FILE *file= mypopen("ls -lSah");
    char buffer[1024];
    while(fgets(buffer,1024,file)){
        printf("%s",buffer);
    }
    //close pfd[0]!
    fclose(file);
    return 0;
}
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

其中,较重要的是在子进程中重定向标准输出,这里给出其他方法重定向标准输出到管道描述符

//方法1
//重定向 标准输出 到 写管道
close(STDOUT_FILENO);
dup(pfd[1]);
//方法2,相当于先close(STDOUT_FILENO) ,再dup(pfd[1])
dup2(pfd[1],STDOUT_FILENO);
//方法3
close(STDOUT_FILENO);
int fd=fcntl(pfd[1],F_DUPFD);
Copy
1
2
3
4
5
6
7
8
9
10

# 结尾

参考文章: http://blog.csdn.net/litingli/article/details/5891726

编辑 (opens new window)
上次更新: 2023/05/07, 17:27:54
隔核绑核、服务器优化
tmux用法

← 隔核绑核、服务器优化 tmux用法→

最近更新
01
通过模板实现结构体成员拷贝-按成员名匹配
05-07
02
c++17通过模板获取类成员的个数
05-01
03
avx-sse切换惩罚
04-30
更多文章>
Theme by Vdoing | Copyright © 2019-2023 Colder Leo | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×