一、进程标识符。

    1、进程中重要的ID值。

        #include <unistd.h>

          pid_t getpid(void);进程ID

                pid_t getppid(void);父进程ID

                uid_t getuid(void);用户ID

                 uid_t geteuid(void);有效用户ID

                 gid_t  getgid(void);组ID

                gid_t getegid(void); 有效组ID

          对一般进程而言,用户ID与有效用户ID的一样的

 二、进程操作。

           1、创建一个进程。

         #include<unistd.h>            

            pid_t fork(void);

            子进程复制了父进程的数据段,堆栈段。共享了代码段。没有共享空间。

          2、fork函数的出错情况。

               系统中已经有太多的进程存在。调用fork函数的用户的进程太多。

         3、创建一个共享空间的子进程。

            # include<unistd.h>

            pid_t vfork(void);

           子进程和父进程完全共享地址空间。包括代码段,数据段和堆栈段。

          子进程一定比父进程先运行。

           注意在函数内部调用vfork函数的问题。

        4、退出进程。

            退出函数深入内核注销掉进程的内核数据结构,并且释放进程的资源。

            可以利用exit函数检查进程出错信息。

            在头文件中定义#include<errno.h>  在程序中退出时exit(errno);

             exit函数与内核函数的关系:exit是一个标准的函数库。与_exit函数不同的是 exit   会用善后的工作。如清理用户的I/O的缓冲区,写入磁盘文件中。而_exit就不会这样,它只是进入内核释放用户进程地址空间,用户空间的缓冲区内容都将丢失。

       5、设置进程所有者。

          @改变进程的实际用户ID和有效用户ID。用int setuid(uid_t uid);

           @只有根用户或者是该用户的用户ID等于进程的实际用户ID或者保存用户ID。

            @还有只修改有交用户ID的函数。int seteuid(uid_t uid);

                setgid(gid_t gid);

               setegid(gid_t gid);

     6、调试多进程

           在gdb中设置set follow-fork-mode [parent | child]

           如果在选parent 时就是跟踪父进程。

三、执行一个新程序

         1、利用exec函数,不是创建一个进程,而是只是在子进程中的代码段和数据段已经被替换了。

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

         int execle(const char *pathname.const char 

         int execve();

         int execlp();

         int execvp();

       "l"表示List并且以NULL结尾。

       “V”:表示命令行参数要以二维数组的形式提供给新程序。这个数组的每一行是一个命令行参数。

  “e”表示传递给新进程的环境变量表。

 “p”表示第一个参数不是完整的路径名,而是一个程序名。

   @exec只会改变有效用户ID和有效组ID。

2、执行解释器文件。

      执行解释器文件有两种,一:二进制可执行文件,二:为文本文件.例如:(#!/bin/sh )

 3、在程序中执行shell命令。

  #include<stdlibl.h> 

 int system(const char *cmdstring);

例如:system("ls > temp.txt")这样就是表示执行Ls查看当前的目录并保存到temp.tex中.

4、实现system函数。 

      system函数实现的流程分两步走:首先调用system函数的进程创建出一个子进程,并调用wait函数等待子进程执行完毕;然后这个子进程再调用exec函数来加载shell运行cmdstring中指定的命令。

    

 
  1. #include <sys/wait.h>  
  2. #include<unistd.h>  
  3. int system(const char* cmdstring)  
  4. {  
  5.    pid_t pid;  
  6.    int status;  
  7.    if(cmdstring==NULL)  
  8.     return 1;  
  9.    pid= fork();  
  10.    if(pid<0)  
  11.   status = -1;  
  12.   else if(pid==0)  
  13.   {  
  14.     execl("/bin/sh","sh","-c",cmdstring ,NULL);  
  15.     _exit(127);  如果执行失败,子进程退出。
  16.      
  17.    }  
  18.   if(waitpid(pid,&status,0)==-1)  
  19.     status=-1;  
  20.  return status;  
  21.  
   使用shell函数执行shell脚本,要保证shell脚本是有可执权限的。

 四、关系操作。

     1、等待进程退出。

      #include <sys/wait.h>

      pid_t wait(int *statloc);

      调用wait函数的进程会阻塞,直到该进程的任意一个子进程结束,wait函数会取得结束的子进程的信息并且返回该子进程的进程ID。结束信息会保存在statloc中。如果没有子进程则立即出错返回,返回值为-1;

 

状态 判断宏 取值宏
正常结束 WIFEXITED(STATTUS) WEXITSTATUS(STATUS)
异常结束 WIFSIGNALED(STATUS) WTERMSIG(STATUS)
进程暂停 WIFSTOPPED(STATUS) WSTOPSIG(STATUS)

   2、等待指定进程。

 #include <sys/wait.h>

  pid_t waitpid(pid-_t pid, int *status,int options);

   pid=-1时,等待任意进程。

  pid >0时,等待进程ID和pid相等的子进程。

  pid=0时,等待组Id和pid相等的子进程。

 pid<-1时,等待组ID等于pid绝对值的组内的任意子进程。

options有3个参数可选。

WCONTINUED :当子进程在暂停后继续执行,且状态尚未报告,则返回其状态。

WNOHANG :当所等待进程尚未结束运行时不阻塞,waitpid函数直接返回。

WUNTRACED :当子进程暂停时,并且其状态处暂停以来还未报告过,则返回其状态。

与wait不同:

  waitpid可以指定一个子进程;可以不阻塞等待一个子进程;支持作业控制。

3、僵尸进程的概念。

      当子进程退出时,他退出的信息就在内核中,子进程的ID也在系统的进程列表中,这时的进程被称为僵尸进程。

      僵尸进程是有坏处的。因为他占用了进程号。只有调用wait函数才可以解决。

4、产生一个僵尸进程

       当父进程调用fork产生一个子进程,而没有调用wait来获得子进程退出的信息,就会产生一个僵尸进程。

  用ps -ax 可以查找,当STAT为S时,就是僵尸进程。

5、避免僵尸进程的产生。

     当父进程比子进程退出先,子进程就会变成孤儿进程。孤儿进程会被init进程领养的。而init进程总是有调用wait函数。

6、输出进程统计信息。

    #include<sys/types.h>

   #include<sys/wait.h>

   #include<sys/time.h>

   #include<source.h>

  pid_t wait3(int *statloc,int options,struct rusage * rusage);

 pid_t wait4(pid_t pid, int *statloc,int options,struct rusage *rusage);