linux进程之fork 和 exec函数

---恢复内容开始---shell

fork函数服务器

该函数是unix中派生新进程的惟一方法。网络

  #include <unistd.h>并发

  pid_t   fork(void);异步

返回: (调用它一次, 它返回 两次 , 它在调用进程(称为父进程)中返回一次, 返回值是新派生进程(称为子进程)的进程ID号函数

    在子进程又返回一次,返回值为0。 所以,返回值自己告知当前进程是子进程仍是父进程)spa

   在子进程中为0, 在父进程中为子进程ID,操作系统

   若出错则为-1;unix

fork有两个典型的用法:code

  1.一个进程建立一个自身的副本,这样每一个副本都 能够在另外一个副本执行其余任务的同时处理各自的某个操做。 这是网络服务器的典型用法;

  2. 一个进程想要执行另外一个程序。既然建立新进程的惟一办法是调用fork, 该进程因而首先调用fork建立一个自身的副本,而后另外一个副本(一般为子进程)调用exec把自身替换成新的程序。 这是shell之类程序的典型用法;

  exec把当前进程映像替换成新的程序文件,并且该新程序一般从main函数开始执行,进程ID并不改变。咱们称调用exec的进程为调用进程(calling process),称新执行的程序为新程序(new program)

  #include <unistd.h>

  int  execl(const  char *pathname, const char *arg0,  .../);

  int   execv(const  char  *pathname, char *const  *argv[]);

  int    execle(const char *pathname, const char *arg(), ....);

  int   execve(const char  *pathname,  char *const argv[],  char *const envp[]);

  int   execlp(const  char *filename, con)

 并发服务器在listen到链接以后,accept()函数解除阻塞,而后服务器执行fork()函数,在父进程中有listenfd,  和 由 accpet返回的已链接套接字即connfd, 而子进程也存在listenfd, 和由accept返回的已经链接套接字即connfd。 

  下一步: 父进程关闭已链接的套接字connfd,保留listenfd继续监听。 子进程关闭listenfd套接字,保留connfd进行相应操做(read, write等操做)

getsockname  和  getpeername函数

===================================================================================================================

  信号(signal) 就是告知某个进程发生了某个事件的通知,  有时也称为 “ software interrupt “ .  信号一般是异步发生的, 也就是说进程预先不知道信号的准确发生的时间;

  信号能够:

    1. 由一个信号发给另有一个进程(或自身)

    2. 由内核发给某个进程;

  每个信号都有一个与之关联的处置(disposition), 也称为行为(action)。 咱们经过调用sigaction函数来设定一个信号的处置,并有三种选择。

  (1) 咱们提供一个函数, 只要有特定信号发生它就被调用。   这样的函数称为“信号处理函数( signal handler)”, 这种行为称为 捕获信号(catch signal). 

    有两个信号不能被捕获,它们是SIGKILL  和 SIGSTOP。  信号处理函数由信号值这个单一的整数参数来调用, 且没有返回值,

    其函数原型以下:

      void  handler(int  signo);

    对于大多信号来讲,调用signcation函数   并指定信号发生所调用的函数  就是捕获信号所需作的所有工做;此外,SIGIO, SIGPOLL, SIGURG这些个别信号还要求捕获它们的进程 作其它额外 的工做;

  (2)咱们能够把某个信号的处置设定为SIG_IGN来忽略(ignore)它。  SIGKILL和SIGSTOP这两个信号不能被忽略;

  (3)咱们能够把某个信号的处置设定为 SIG_DFL来启用它的默认(default)处理。默认处置一般是在收到信号后终止进程,某些信号的默认default处理不一样;

 

 

---恢复内容结束---

fork函数

该函数是unix中派生新进程的惟一方法。

  #include <unistd.h>

  pid_t   fork(void);

返回: (调用它一次, 它返回 两次 , 它在调用进程(称为父进程)中返回一次, 返回值是新派生进程(称为子进程)的进程ID号

    在子进程又返回一次,返回值为0。 所以,返回值自己告知当前进程是子进程仍是父进程)

   在子进程中为0, 在父进程中为子进程ID,

   若出错则为-1;

fork有两个典型的用法:

  1.一个进程建立一个自身的副本,这样每一个副本都 能够在另外一个副本执行其余任务的同时处理各自的某个操做。 这是网络服务器的典型用法;

  2. 一个进程想要执行另外一个程序。既然建立新进程的惟一办法是调用fork, 该进程因而首先调用fork建立一个自身的副本,而后另外一个副本(一般为子进程)调用exec把自身替换成新的程序。 这是shell之类程序的典型用法;

  exec把当前进程映像替换成新的程序文件,并且该新程序一般从main函数开始执行,进程ID并不改变。咱们称调用exec的进程为调用进程(calling process),称新执行的程序为新程序(new program)

  #include <unistd.h>

  int  execl(const  char *pathname, const char *arg0,  .../);

  int   execv(const  char  *pathname, char *const  *argv[]);

  int    execle(const char *pathname, const char *arg(), ....);

  int   execve(const char  *pathname,  char *const argv[],  char *const envp[]);

  int   execlp(const  char *filename, con)

 并发服务器在listen到链接以后,accept()函数解除阻塞,而后服务器执行fork()函数,在父进程中有listenfd,  和 由 accpet返回的已链接套接字即connfd, 而子进程也存在listenfd, 和由accept返回的已经链接套接字即connfd。 

  下一步: 父进程关闭已链接的套接字connfd,保留listenfd继续监听。 子进程关闭listenfd套接字,保留connfd进行相应操做(read, write等操做)

getsockname  和  getpeername函数

===================================================================================================================

  信号(signal) 就是告知某个进程发生了某个事件的通知,  有时也称为 “ software interrupt “ .  信号一般是异步发生的, 也就是说进程预先不知道信号的准确发生的时间;

  信号能够:

    1. 由一个信号发给另有一个进程(或自身)

    2. 由内核发给某个进程;

  每个信号都有一个与之关联的处置(disposition), 也称为行为(action)。 咱们经过调用sigaction函数来设定一个信号的处置,并有三种选择。

  (1) 咱们提供一个函数, 只要有特定信号发生它就被调用。   这样的函数称为“信号处理函数( signal handler)”, 这种行为称为 捕获信号(catch signal). 

    有两个信号不能被捕获,它们是SIGKILL  和 SIGSTOP。  信号处理函数由信号值这个单一的整数参数来调用, 且没有返回值,

    其函数原型以下:

      void  handler(int  signo);

    对于大多信号来讲,调用signcation函数   并指定信号发生所调用的函数  就是捕获信号所需作的所有工做;此外,SIGIO, SIGPOLL, SIGURG这些个别信号还要求捕获它们的进程 作其它额外 的工做;

  (2)咱们能够把某个信号的处置设定为SIG_IGN来忽略(ignore)它。  SIGKILL和SIGSTOP这两个信号不能被忽略;

  (3)咱们能够把某个信号的处置设定为 SIG_DFL来启用它的默认(default)处理。默认处置一般是在收到信号后终止进程,某些信号的默认default处理不一样;

 =======================================================================================================2.1 Linux下进程的结构

  Linux下一个进程在内存里有三部分的数据,就是"代码段"、"堆栈段"和"数据段"。其实学过汇编语言的人必定知道,通常的CPU都有上述三种段寄存器,以方便操做系统的运行。这三个部分也是构成一个完整的执行序列的必要的部分。

  "代码段",顾名思义,就是存放了程序代码的数据,假如机器中有数个进程运行相同的一个程 序,那么它们就可使用相同的代码段。"堆栈段"存放的就是子程序的返回地址、子程序的参数以及程序的局部变量。而数据段则存放程序的全局变量,常数以及 动态数据分配的数据空间(好比用malloc之类的函数取得的空间)。这其中有许多细节问题,这里限于篇幅就很少介绍了。系统若是同时运行数个相同的程 序,它们之间就不能使用同一个堆栈段和数据段。

  若是一个大程序在运行中,它的数据段和堆栈都很大,一次fork就要复制一次,那么fork的系统开销不是很大吗?其实UNIX自有其解决的办法,你们知 道,通常CPU都是以"页"为单位来分配内存空间的,每个页都是实际物理内存的一个映像,象INTEL的CPU,其一页在一般状况下是 4086字节大小,而不管是数据段仍是堆栈段都是由许多"页"构成的,fork函数复制这两个段,只是"逻辑"上的,并不是"物理"上的,也就是说,实际执 行fork时,物理空间上两个进程的数据段和堆栈段都仍是共享着的,当有一个进程写了某个数据时,这时两个进程之间的数据才有了区别,系统就将有区别的" 页"从物理上也分开。系统在空间上的开销就能够达到最小。

exec( )函数族

下面咱们来看看一个进程如何来启动另外一个程序的执行。在Linux中要使用exec函数族。系统 调用execve()对当前进程进行替换,替换者为一个指定的程序,其参数包括文件名(filename)、参数列表(argv)以及环境变量 (envp)。exec函数族固然不止一个,但它们大体相同,在 Linux中,它们分别是:execl,execlp,execle,execv,execve和execvp,

  以execlp为例,其它函数究 竟与execlp有何区别,请经过manexec命令来了解它们的具体状况。

  一个进程一旦调用exec类函数,它自己就"死亡"了,系统把代码段替换成新的程序的代码, 废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,惟一留下的,就是进程号,也就是说,对系统而言,仍是同一个进程,不过已是另外一个程序 了。(不过exec类函数中有的还容许继承环境变量之类的信息。)

那么若是个人程序想启动另外一程序的执行但本身仍想继续运行的话,怎么办呢?那就是结合fork与exec的使用。下面一段代码显示如何启动运行其它程序:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <errno.h>
 4 #include <string.h>
 5 
 6 char command[256];
 7 int main()
 8 {
 9     int rtn;
10     printf(">>");
11     
12     fgets(command, 256, stdin);
13     command[strlen(command)-1] = 0;
14     if(fork()==0) //子进程
15     {
16         execlp(command, NULL);
17         perror(command);
18         exit(errno);
19     }    
20     else  //父进程
21     {
22         wait(&rtn);
23         printf("child process return %d\n", rtn);
24     }    
25     
26     return 0;
27 }
相关文章
相关标签/搜索