进程操作

进程控制

进程标识

  • 每个进程都有一个非负整数表示的唯一进程ID.
  • 进程ID是可以复用的,当进程退出后它的ID可以被其他进程使用.(大多数Unix实现了延迟复用算法)
    #include<unistd.h>
    pid_t getpid(void);
        //返回值:调用进程的ID
    pid_t getppid(void);
        //返回值:调用进程的父进程ID

fork函数

  • 函数原型:
  #include<unistd.h>
  pid_t fork(void);
      //子进程返回0,父进程返回子进程ID;若出错,返回-1
  • 通过fork创建的新进程被成为子进程.fork函数调用一次返回两次,父进程返回子进程ID,子进程返回0.

  • 父进程和子进程继续执行fork之后的语句.

  • 子进程获得父进程的数据空间,堆,栈的副本.这是子进程拥有的副本而不是与父进程共享.

  • 由于fork之后经常跟着exec,所以很多实现并不执行一个父进程的数据.作为代替,很多实现都使用了Copy-On-Write.
    内核将数据的访问权限改变为只读,如果父进程或者进程试图修改这些区域,则内核会为修改区域的那块内存制作一个副本.

  • 需要注意的是,由于父子进程共享同一份描述符表,而描述符中的文件指针则指向了同一个文件表.也就是说,父子进程共享着同一个文件偏移量.如果没有任何形式的同步,父子进程同时读写会造成乱序问题.

  • fork的两种用法:

    • 一个父进程希望复制自己,使父子进程同时执行不同的代码段.
    • 一个进程要执行不同的程序,配合exec.

wait函数与僵尸/孤儿进程

  • 当父进程先于子进程退出时,内核将子进程的ppid(父进程)改为init进程(pid:1).此时子进程成为孤儿进程,当子进程退出时,由init进程回收子进程的某些信息.

  • 当子进程先于父进程退出时,内核可以释放子进程所使用的存储区,关闭其所打开的文件.但是子进程会等待父进程的wait函数,并返回一定量的信息(pid,终止状态,使用cpu的时间总量),如果父进程一直没有调用wait,
    那么子进程被成为僵死进程并不释放该进程占用的pid.

  • wait和waitpid:

    • 当一个进程正常或者异常终止时,内核就向其父进程发送SIGCHLD信号.

    • 函数原型:

        #include<sys/wait.h>
    
        pid_t wait(int *statloc);
        pid_t waitpid(pid_t pid,int *statloc, int options);
    
    • 如果子进程仍在进行,wait函数会阻塞.waitpid并不等待子进程的终止,它有若干个选项可以控制.

    • 如果子进程已经终止,并且是一个僵死进程,则wait函数立即返回并取得子进程的状态.

    • 如果statloc不是一个空指针,那么退出进程的状态会存在指针所指向的内存中.两个函数的返回值都是退出进程的pid.

  • 如果我们不想调用wait去等待子进程终止,APUE给出了一个巧妙的办法.两次fork生成真正的子进程.

    • 首先fork生成了一个子进程a,子进程a再fork一次生成执行任务的进程b.
    • 子进程a sleep(2*second)后退出,此时进程b的ppid(父进程)变成了init.
    • 我们前文提到,init进程会回收它所有的子进程.所以此时进程b会被init回收而不会编程僵死进程

system函数

  • 函数原型:
  #include<stdlib.h>
  int system(const char *cmdstring);
  • cmdstring参数代表了我们希望在shell中执行的命令.
  • 返回值:
    • fork失败或者waitpid返回除EINTR之外的出错则system返回-1并且设置errno.
    • 如果exec失败不能执行shell则如同得到了exit(127)的返回值
    • 如果成功则返回shell的返回值.
posted @ 2017-03-12 22:58  XLLL  阅读(135)  评论(0编辑  收藏  举报