Linux下进程控制

进程的基本知识 http://www.cnblogs.com/chaguang/p/7818440.html

在linux下init进程是所有进程的父进程

获取进程标识符的相关函数:

  • pid_t getpid()  获取进程id
  • pid_t getppid()     获取父进程id
  • pid_t getpgrp()  获取进程组id

获取进程用户标识号的相关函数:

  • uid_t getuid()   返回进程的用户ID
  • uid_t geteuid()  返回进程的有效用户ID
  • gid_t getgid()         返回进程的组ID
  • gid_t get egid()  返回进程的有效组ID

进程的创建

  • pid_t fork()

  创建子进程,并且复制父进程的数据,堆栈段,进程环境,但是子进程拥有自己的进程环境。若成功则返回两个值,子进程返回0,父进程返回子进程标记;否则,出错返回-1

  头文件:<sys/types.h>以及<unistd.h>

  写时拷贝机制

  由于子进程需要复制父进程的资源,,刚开始创建的时候,可以不需要,于是出现了写时拷贝机制,创建的子进程的时候不复制相关资源,如果是读取的话,访问相同的物理内存,如果有写操作的话,就会先复制资源,然后再写

 1 #include<stdio.h>
 2 #include<sys/types.h>
 3 #include<unistd.h>
 4 
 5 int main()
 6 {
 7     pid_t processId;
 8         //创建一个子进程
 9     processId=fork();
10     printf("processId=%d\n",processId);
11     if(processId==0)
12     {
13         execlp("ls","ls","-al",NULL);
14         printf("this is a child process[%d]\n",getpid());
15     }
16     else
17     {
18         printf("this is a father process[%d]\n",getpid());
19     }
20     sleep(2);
21     printf("the function ending......\n");
22     return 0;
23 }
24 
25 
26 ###########结果############
27 processId=3736
28 this is a father process[3735]
29 processId=0
30 total 84
31 drwxrwxr-x 2 linux linux 4096 Mar  6 22:46 .
32 drwxr-xr-x 6 linux linux 4096 Feb  8 15:45 ..
33 -rwxrwxr-x 1 linux linux 9992 Feb  8 16:25 fork
34 -rwxrwxr-x 1 linux linux 9992 Mar  6 22:27 fork2
35 -rw-rw-r-- 1 linux linux  385 Mar  6 22:27 fork2.c
36 -rw-rw-r-- 1 linux linux 4280 Mar  6 22:27 fork2.o
37 -rw-rw-r-- 1 linux linux  623 Feb  8 16:25 fork.c
38 -rw-rw-r-- 1 linux linux 4640 Feb  8 16:25 fork.o
39 -rw-rw-r-- 1 linux linux  210 Feb  8 16:07 makefile
40 -rwxrwxr-x 1 linux linux 9984 Mar  6 22:46 vfork
41 -rw-rw-r-- 1 linux linux  414 Mar  6 22:46 vfork.c
42 -rw-rw-r-- 1 linux linux 4176 Mar  6 22:46 vfork.o
43 the function ending......
fork

 

  • pid_t vfork()

  vfork共享父进程的资源,调用后,父进程会阻塞,直到子进程调用exec()或者_exit(),注意不能使用return返回或者使用exit()退出

  exit(),return,_exit()的区别

    • exit():终止进程前,关闭文件,清理I/O
    • return:释放句柄,变量,弹出函数调用栈,回到上一级的函数
    • _exit():直接退出,不关闭文件,不清理I/O  

  由上面可以看出,如果使用vfork创建的进程是不可以使用exit和return退出,因为vfork创建的子进程的资源是和父进程的共享,子进程如果调用exit和return就会破坏父进程的资源,从而是父进程不能正常运行。不过由于fork出现了写时拷贝机制,所以我们现在一般用的还是fork

exec函数族

  执行另一个程序作为源程序的子进程,exec函数调用成功之后,系统会把新进程的地址空间替代调用进程的地址空间,并装入新进程内容中

  • 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,const char **argv)
  • int execvp(const char* file,const char **argv)
  • int execve(const char *path,const char **argv,char *const envp[])

  execl:入参是可变参数列表(一级指针),必须使用NULL结尾

  execv:入参是参数数组(二级指针)

  p后缀:搜索系统PATH,寻找可执行文件

  e后缀:显示的传递环境变量

 1    #include<stdio.h>
 2    #include<sys/types.h>
 3    #include<unistd.h>
 4    
 5    int main()
 6    {
 7            pid_t processID;
 8            processID=fork();
 9            //child process
10           if(processID==0)
11           {
12                   printf("this is a child process[%d]\n",getpid());
13           //      execl("/bin/ls","ls","-al",NULL);
14   
15           //      execlp("ls","ls","-al",NULL);
16   
17           //      char *pList[]={"ls","-al"};
18           //      execv("/bin/ls",pList);
19   
20                   char *pList[]={"ls","-al"};
21                   execvp("ls",pList);
22   
23           }
24           //father process
25           else
26           {
27                   printf("this is a father process[%d]\n",getpid());
28           }
29           sleep(2);
30           printf("the process[%d] end~\n",getpid());
31           return 0;
32   }
exec

 

环境变量

  相关的命令:

    • env命令:显示当前用户所有的环境变量
    • export命令:修改或显示当前用户所有环境变量
    • unset:删除某个环境变量

  头文件:<stdlib.h>

  extern char **environ:UNIX系统中环境变量指针数组

  函数:

    • char *getenv(char *name)
      • 获取环境变量值,否则返回NULL      
    • int putenv(const char *expr)  
      • 设置/修改环境变量,name=value
      • 只传name则删除环境变量
 1 #include<stdio.h>
 2 #include<sys/types.h>
 3 #include<unistd.h>
 4 #include<stdlib.h>
 5 //导入环境变量
 6 extern char **environ;
 7 
 8 int main()
 9 {
10     char **pEnv=environ;
11     while(*pEnv)
12     {
13         printf("%s\n",*pEnv);
14         pEnv++;
15     }
16     printf("***************\n");
17         //设置环境变量  
18     putenv("hello=linux");
19         //获取对应的环境变量值
20     printf("hello=%s\n",getenv("hello"));
21     printf("PATH=%s\n",getenv("PATH"));
22     return 0;
23 }    
环境变量

 

 

同步父子进程wait()函数,waitpid()函数

  头文件:<sys/wait.h>

  pid_t wait(int *pState)

    • 第一个字节:*pState & 0xff ----->接受到的信号值
    • 第二个字节:(*pState >> 8 ) & 0xff ------>退出值
    • 第三四个字节保留:0

  一般用在父进程中等待回收子进程的资源,而防止僵尸进程的产生。没有子进程的话马上返回,有子进程的话,等待子进程结束,释放子进程的资源

  waitpid(pid_t pid,int *pState,int option)

    • pid
      • > 0    等待特定的子进程
      • =0     等待父进程对应进程组的所有子进程
      • =-1    等待任意子进程
      • <-1  等待进程组标志位pid的绝对值得所有子进程
    • option
      • 0                     
        • 有子进程的话就阻塞,等待子进程结束
      • 1[WNOHANG]
        • 非阻塞调用,立即返回
        • 搜集已经退出的子进程
        • 如果要搜集多个子进程,需要多次调用
      • 2[WUNTRACED]
        • 有子进程就阻塞
        • 如果waitpid之前有子进程退出,则搜集已退出的子进程并返回       

    waitpid(-1,pState,0)<===>wait(pState)

 1 #include<sys/wait.h>
 2 #include<stdio.h>
 3 #include<unistd.h>
 4 
 5 
 6 int main()
 7 {
 8     pid_t processID;
 9     int state;
10     processID=fork();
11     if(processID==0)
12     {
13         printf("linux and python\n");
14         sleep(4);
15     }
16     else
17     {
18         printf("father process\n");
19         wait(&state);
20         printf("wait subProcess ending\n");
21     }
22     return 0;
23 }
wait

 

杀死进程kill()

  • shell命令kill:

    kill -l    查看kill信号

    kill -[sig] pid   结束进程,可以选择发送的信号sig

  • 函数kill()

    int kill(pid_t pid,int sig)

    pid参数:  

      • pid>0 将信号传给进程识别码为pid 的进程。
      • pid=0 将信号传给和当前进程相同进程组的所有进程
      • pid=-1 将信号广播传送给系统内所有的进程
      • pid<0 将信号传给进程组识别码为pid绝对值的所有进程

    sig参数:可以通过kill -l查看

僵尸进程  

 进程已经终止,但是没有从进程表内删除,因为已经终止,所有不能接受kill信号

产生的原因

  • 子进程终止的时候,会释放资源,并发送SIGCHLD信号通知父进程,父进程接受信号后调用wait返回子进程的状态,并且释放进程表资源
  • 如果子进程结束的时候,父进程没有调用wait接受子进程的消息,则子进程转化为僵尸进程
  • 父进程退出的时候,僵尸进程自动结束

解决方案:

 

  • 利用wait()函数
  • 托管
    • 父进程先于子进程退出,则它的子进程由init进程领养,子进程的父进程ID变为1,由init释放进程表资源
    • 托管技巧:fork出子进程后,在子进程中再次fork,然后退出子进程,则子子进程会被init托管,有init释放资源表
 1 #include<stdio.h>
 2 #include<sys/types.h>
 3 #include<stdlib.h>
 4 #include<unistd.h>
 5 #include<sys/wait.h>
 6 
 7 int main()
 8 {
 9     pid_t pid;
10         //创建一个子进程
11     pid=fork();
12     if(pid==0)
13     {
14                //在子进程下面再创建一个子进程
15         pid=fork();
16         if(pid==0)
17         {
18             printf("this is a child child process[%d]\n",getpid());
19             while(1);
20         }
21         else
22         {    //结束掉子进程,那么子子进程由init进程领养
23             printf("child process[%d] ending ...\n",getpid());
24             exit(0);
25         }
26     }
27     else
28     {
29         int state;
30         printf("this is a father process[%d]\n",getpid());
31                 //接收子进程的信息
32         pid=wait(&state);
33         printf("wait ending....child process[%d] end.\n",pid);
34                 //父进程执行自己的代码
35                  ...
36     }
37     return 0;    
38 }
39             
托管

 

  • 忽略SIGCHLD信号
    • 父进程设置忽略SIGCHLD信号,子进程结束自动释放进程表资源
  • 捕获SIGCHLD信号
    • 父进程捕获SIGCHLD信号,并在捕获函数代码中执行wait()

守护进程daemon

守护进程(精灵进程),是一种运行在后台的特殊进程,不存在控制终端,周期性处理某项任务,守护系统正常运行

例如:

  • 大部分的socket通信服务程序都以守护进程方式执行
  • 以超级用户启动的(uid=0)
  • 父进程为init(ppid=1)
  • 无控制终端(tty=?
  • 终端进程组为-1(TPGID=-1)

守护进程daemon创建步骤:

  • 后台运行
    • 托管法,fork子进程后,父进程退出
  • 脱离控制终端(伪终端)
    • setsid()
    • 创建一个新的session和进程组
    • 并使用进程ID作为进程组ID,返回进程组ID/失败返回-1
  • 把当前工作目录更改为/
    • 防止对当前目录的某些操作不能执行 
  • 关闭文件描述符,重定向stdin,stdout,stderr 
    • fd=open("/dev/null",O_RDWR,0);
    • dup2(fd,STDIN_FILENO);...
  • 设置/清除文件创建掩码<sys/stat.h>
    • umask(xxx)      可以设置进程创建的临时文件不被其他用户查看
    • umask(0)         清除父进程继承的文件创建掩码    

 

posted @ 2018-03-04 22:07  菜鸟也有高飞的时候  阅读(474)  评论(0编辑  收藏  举报