1. fork

CSAPP中对这个比喻很形象,就是从master线中分叉出来了一条子进程,与父进程共享文件描述符和所有其他资源。

#include <unistd.h>

pid_t fork(void);

2. exec

这是一个函数族,包含了几个函数

#include <unistd.h>
 
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
 
#参数说明:
#path:可执行文件的路径名字
#arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
#file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。

后缀的意思:
l:表示 "list",这意味着参数以可变参数列表(variable argument list)的形式传递,最后一个参数为 NULL,用于标识参数列表的结束。
p:表示 "path",这意味着在系统的 PATH 环境变量中搜索可执行文件。
e:表示 "environment",这意味着可以指定环境变量。
v:表示 "vector",这意味着参数以数组的形式传递,数组中每个元素都是一个字符串指针。

不管哪种exec,都有一个共性:会用一个程序完全取代当前的程序。
换句话说,这是一个有去无回的动作,可以将当前进程完全转变成另一个进程。

fork和exec是其他的基础。

3. system

参考man-pages,system实际上就是fork和exec的结合。

#include <unistd.h>

pid_t fork(void);

简而言之就是用fork生成一个新进程,然后父进程wait,等待子进程结束。子进程用exec执行命令。

int system(const char * cmdstring)
{
  pid_t pid;
  int status;
 
  if(cmdstring == NULL){
      
      return (1);
  }
  if((pid = fork())<0){
        status = -1;
  }
  else if(pid == 0){
    execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
    -exit(127); //子进程正常执行则不会执行此语句
    }
  else{
        while(waitpid(pid, &status, 0) < 0){
          if(errno != EINTER){
            status = -1;
            break;
          }
        }
    }
    return status;
}

4. spawn

#include <spawn.h>

int posix_spawn(pid_t *restrict pid, const char *restrict path,
                const posix_spawn_file_actions_t *restrict file_actions,
                const posix_spawnattr_t *restrict attrp,
                char *const argv[restrict],
                char *const envp[restrict]);
int posix_spawnp(pid_t *restrict pid, const char *restrict file,
                const posix_spawn_file_actions_t *restrict file_actions,
                const posix_spawnattr_t *restrict attrp,
                char *const argv[restrict],
                char *const envp[restrict]);

按照man-pages的说法,spawn是为了给不支持fork的系统提供标准化方法(standardized method),主要针对小型嵌入式系统。并且文中提到,spawn是syscall实现功能的子集。

两种spawn有什么区别?

With posix_spawnp(), the executable file is specified as a simple filename; the system searches for this file in the list of directories specified by PATH (in the same way as for execvp(3).
也就是说posix_spawnp后缀的p也是指代PATH

那么spawn和system,fork有什么区别呢?
根据多处文档(BlackBerry|qnxOracle Solaris BlogThe Open Group)以及《理解Unix进程》- Jesse Storimer中描述:

system会阻塞到命令完成,而spawn会直接返回。

fork子进程有两个特点:1.获得父进程的文件描述符 2.获得父进程所有内存。而spawn只保留了1,没有保留2。也就是说spawn比fork更轻便,性能更好,在进程切换的过程中开销更小。但也因为没有内存的内容,缺少了灵活性。

从实现层面上,spawn也是通过fork和exec实现,但是和system不同的是在fork之后,子进程中在exec之前做了其他事,而不是像system一样直接调用exec。所以spawn有很多参数,能指定更改环境变量。

5. popen

pipe open
创建一个管道,并启动一个子进程进行命令的执行。它返回一个文件指针,该文件指针指向子进程的标准输入或标准输出,具体取决于 popen 函数的第二个参数。注意,无法一次访问所有的流。
对应还有pclose。

#include <stdio.h>

FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);

popen 函数返回的文件指针可以像普通文件指针一样使用标准的文件 I/O 函数,如 fread、fwrite、fgets、fputs 等。

标签: none

评论已关闭