fork 和 exec 函数

  1. /* 函数功能:创建子进程;  
  2.  * 返回值:  
  3.  * 1)在子进程中,返回0;  
  4.  * 2)在父进程中,返回新创建子进程的进程ID;  
  5.  * 3)若出错,则范回-1;  
  6.  * 函数原型:  
  7.  */   
  8. #include <unistd.h>   
  9. pid_t fork(void);   
  10. /* 说明:  
  11.  * 该函数调用一次若成功则返回两个值:  
  12.  * 在调用进程即父进程)中,返回新创建进程即子进程)的进程ID;  
  13.  * 在子进程返回值是0;  
  14.  * 因此,可以根据返回值判断进程是子进程还是父进程;  
  15.  */   
  16.    
  17. /* exec 序列函数 */   
  18.    
  19. /*  
  20.  * 函数功能:把当前进程替换为一个新的进程,新进程与原进程ID相同;  
  21.  * 返回值:若出错则返回-1,若成功则不返回;  
  22.  * 函数原型:  
  23.  */   
  24. #include <unistd.h>   
  25. int execl(const char *pathname, const char *arg, ...);   
  26. int execv(const char *pathnam, char *const argv[]);   
  27. int execle(const char *pathname, const char *arg, ... , char *const envp[]);   
  28. int execve(const char *pathnam, char *const argv[], char *const envp[]);   
  29. int execlp(const char *filename, const char *arg, ...);   
  30. int execvp(const char *filename, char *const argv[]);   
  31. /*  6 个函数的区别如下:  
  32.  * 1)待执行的程序文件是 文件名 还是由 路径名 指定;  
  33.  * 2)新程序的参数是 一一列出 还是由一个 指针数组 来引用;  
  34.  * 3)把调用进程的环境传递给新程序 还是 给新程序指定新的环境;  
  35.  */   

exec 6个函数在函数名和使用语法的规则上都有细微的区别,下面就从可执行文件查找方式、参数传递方式及环境变量这几个方面进行比较。

查找方式:前4个函数的查找方式都是完整的文件目录路径 pathname ,而最后两个函数(也就是以p结尾的两个函数)可以只给出文件名 filename,系统就会自动按照环境变量 “$PATH” 所指定的路径进行查找。

参数传递方式:exec 序列函数的参数传递有两种方式:一种是逐个列举的方式,而另一种则是将所有参数整体构造指针数组传递。在这里是以函数名的第5位字母来区分的,字母为 “l”(list)的表示逐个列举参数的方式,其语法为 const char *arg;字母为 “v”(vertor)的表示将所有参数整体构造指针数组传递,其语法为 char *const argv[]。读者可以观察 execl()、execle()、execlp() 的语法与 execv()、execve()、execvp() 的区别。这里的参数实际上就是用户在使用这个可执行文件时所需的全部命令选项字符串(包括该可执行程序命令本身)。要注意的是,这些参数必须以NULL结束。

环境变量:exec 序列函数可以默认系统的环境变量,也可以传入指定的环境变量。这里以 “e”(environment)结尾的两个函数 execle() 和 execve() 就可以在 envp[] 中指定当前进程所使用的环境变量。

  1.  表 1 exec 序列函数的总结       
  2. 前4位 统一为:exec       
  3. 第5位 l:参数传递为逐个列举方式    execl、execle、execlp   
  4.      v:参数传递为构造指针数组方式     execv、execve、execvp   
  5. 第6位 e:可传递新进程环境变量     execle、execve   
  6.      p:可执行文件查找方式为文件名     execlp、execvp   

其关系如下图:

并发服务器

当要求一个服务器同时为多个客户服务时,需要并发服务器。TCP 并发服务器,它们为每个待处理的客户端连接调用 fork 函数派生一个子进程。当一个连接建立时,accept 返回,服务器接着调用 fork 函数,然后由子进程服务客户端,父进程则等待另一个连接,此时,父进程必须关闭已连接套接字。

close 和 shutdown 函数

当要关闭套接字时,可使用 close 和 shutdown 函数,其描述如下:

  1. /* 函数功能:关闭套接字,若是在 TCP 协议中,并终止 TCP 连接;  
  2.  * 返回值:若成功则返回0,若出错则返回-1;  
  3.  * 函数原型:  
  4.  */   
  5. #include <unistd.h>   
  6. int close(int sockfd);   
  7.    
  8. /*  
  9.  * 函数功能:关闭套接字上的输入或输出;  
  10.  * 返回值:若成功则返回0,若出错返回-1;  
  11.  * 函数原型:  
  12.  */   
  13. #include <sys/socket.h>   
  14. int shutdown(int sockfd, int how);   
  15. /*  
  16.  * 说明:  
  17.  * sockfd表示待操作的套接字描述符;  
  18.  * how表示具体操作,取值如下:  
  19.  * 1)SHUT_RD     关闭读端,即不能接收数据  
  20.  * 2)SHUT_WR     关闭写端,即不能发送数据  
  21.  * 3)SHUT_RDWR   关闭读、写端,即不能发送和接收数据  
  22.  *  
  23.  */   

getsockname 和 getpeername 函数

为了获取已绑定到套接字的地址,我们可以调用函数 getsockname 来实现:

  1. /*  
  2.  * 函数功能:获取已绑定到一个套接字的地址;  
  3.  * 返回值:若成功则返回0,若出错则返回-1;  
  4.  * 函数原型:  
  5.  */   
  6. #include <sys/socket.h>   
  7.    
  8. int getsockname(int sockfd, struct sockaddr *addr, socklen_t *alenp);   
  9. /*  
  10.  * 说明:  
  11.  * 调用该函数之前,设置alenp为一个指向整数的指针,该整数指定缓冲区sockaddr的大小;  
  12.  * 返回时,该整数会被设置成返回地址的大小,如果该地址和提供的缓冲区长度不匹配,则将其截断而不报错;  
  13.  */   
  14. /*  
  15.  * 函数功能:获取套接字对方连接的地址;  
  16.  * 返回值:若成功则返回0,若出错则返回-1;  
  17.  * 函数原型:  
  18.  */   
  19. #include <sys/socket.h>   
  20.    
  21. int getpeername(int sockfd, struct sockaddr *addr, socklen_t *alenp);   
  22. /*  
  23.  * 说明:  
  24.  * 该函数除了返回对方的地址之外,其他功能和getsockname一样;  
  25.  */   




相关内容