当前位置:   article > 正文

Unix进程API

Unix进程API

本节内容主要是介绍linux/unix进程API的使用;getpid,fork,exit,atexit,abort,wait/waitpid。

一:进程终止

有8种方式使得进程终止,其中5种为正常终止,它们是:

1.从main函数返回

2.调用exit函数

3.调用_exit或_Exit函数

4.进程的最后一个线程从启动例程返回

5.进程的最后一个线程调用pthread_exit返回

3种异常终止方式是

6.调用abort函数终止

7.收到一个信号终止

8.进程的最后一个线程对取消请求做出响应

 

进程终止函数:

#include<stdlib.h>

void exit(int status);

void _Exit(int status);

#include<unistd.h>

void _exit(int status);

exit使进程退出时,会自动调用预先注册的atexit函数,

#include<stdlib.h>

int atexit(void (*func)(void)); //注意func被调用的顺序跟预先注册的顺序相反

一个例子:

  1. #include "apue.h"
  2. static void my_exit1(void);
  3. static void my_exit2(void);
  4. int
  5. main(void)
  6. {
  7. if (atexit(my_exit2) != 0)
  8. err_sys("can't register my_exit2");
  9. if (atexit(my_exit1) != 0)
  10. err_sys("can't register my_exit1");
  11. if (atexit(my_exit1) != 0)
  12. err_sys("can't register my_exit1");
  13. printf("main is done\n");
  14. return(0);
  15. }
  16. static void
  17. my_exit1(void)
  18. {
  19. printf("first exit handler\n");
  20. }
  21. static void
  22. my_exit2(void)
  23. {
  24. printf("second exit handler\n");
  25. }

运行结果,注意看my_exit1跟myexit2函数的执行顺序:

 

二:进程创建:

进程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

创建进程:

pid_t fork(void); //在子进程中,函数返回值为0,父进程中返回值为子进程的进程ID

子进程会获得父进程的进程空间,文件描述符,缓冲区(如果缓冲区没有冲洗的话)等所有数据的一个副本,一个例子:

  1. #include "apue.h"
  2. int globvar = 6; /* external variable in initialized data */
  3. char buf[] = "a write to stdout\n";
  4. int
  5. main(void)
  6. {
  7. int var; /* automatic variable on the stack */
  8. pid_t pid;
  9. var = 88;
  10. if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
  11. err_sys("write error");
  12. printf("before fork\n"); /* we don't flush stdout */
  13. if ((pid = fork()) < 0) {
  14. err_sys("fork error");
  15. } else if (pid == 0) { /* child */
  16. globvar++; /* modify variables */
  17. var++;
  18. } else {
  19. sleep(2); /* parent */
  20. }
  21. printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar,
  22. var);
  23. exit(0);
  24. }

输出结果:可以看到父子进程各有一个glob,var值,他们是各自独立的。另外当输出重定向到文件后,缓冲区没有被换行符冲洗,子进程会复制父进程的缓冲区,before fork 输出2次。(如果把before fork后的换行符去掉,before fork在终端也会输出2次)

不同之处:子进程不继承父进程的文件锁,子进程的未处理闹钟(alarm函数产生)被清除,子进程的未处理阻塞信号集被清除。

创建进程vfork:

pid_t vfork(void); //在子进程中,函数返回值为0,父进程中返回值为子进程的进程ID

跟fork不同的是,vfork不复制父进程的进程空间,而是直接在父进程的进程空间中运行,其二,父进程会阻塞直到vfork的子进程调用exit或exec函数后才执行。

  1. #include "apue.h"
  2. int globvar = 6; /* external variable in initialized data */
  3. int
  4. main(void)
  5. {
  6. int var; /* automatic variable on the stack */
  7. pid_t pid;
  8. var = 88;
  9. printf("before vfork\n"); /* we don't flush stdio */
  10. if ((pid = vfork()) < 0) {
  11. err_sys("vfork error");
  12. } else if (pid == 0) { /* child */
  13. globvar++; /* modify parent's variables */
  14. var++;
  15. _exit(0); /* child terminates */
  16. }
  17. /* parent continues here */
  18. printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar,
  19. var);
  20. exit(0);
  21. }

输出:可以看到子进程改变了父进程空间的变量

等待子进程的终止:wait/waitpid

在说明wait函数之前,先来说明一下,下列情形:

1.父进程在子进程之前终止:当父进程终止时,内核会遍历该进程的所有子进程,并把子进程的父进程ID改为1(init进程)。

2.子进程在父进程之前终止:子进程(无论是正常还是异常)终止后,内核会保留子进程的相关信息:这些信息包括子进程ID,子进程的终止状态,子进程使用的CPU时间总量。通常表示父进程需要这些信息,如果父进程还未对这些信息做处理,此时这些子进程就变成僵死进程(zombie)。当父进程调用wait相关函数后,内核才会对僵死进程做处理。子进程才真正在内存中消失。

#include<sys/wait.h>

pid_t wait(int *status); //父进程阻塞,等待任意子进程的结束,子进程结束后,返回子进程ID。子进程状态保存在status中。

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

对于waitpid函数:

pid > 0 等待进程ID与pid相等的子进程

pid == 0 等待组ID与父进程组ID相等的任意一个子进程

pid == -1 等待任意一个子进程, 与wait等效

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

options 控制waitpid的操作:

0                                                  waitpid阻塞

WNOHANG                                 waitpid不阻塞

WCONTINUED,WUNTRACED   支持作业控制的选项

三:父子进程之间的同步:一般的同步流程如下

TELL_WAIT(); /*set things up for TELL_xxx & WAIT_xxx*/

if(pid == fork() < 0){

        perror("fork error"); exit(1);

}else if(pid == 0){  /*child execute first*/

        /*child does whatever is necessary*/

        TELL_PARENT(getppid()); /*tell parent we are done*/

        WAIT_PARENT();/*and wait for parent*/

        /*child continues on its way*/

      exit(0);

}

/*parent does whatever is necessary*/

WAIT_CHILD();

TELL_CHILD(pid);

/*parent continues on its way*/

exit(0);

下面代码是基于管道的一种同步实现:

  1. /*父进程读pfd2[0],得到通知;写pfd1[1],在pfd1[1]中写入'c'通知子进程
  2. 子进程读pfd1[0],得到通知;写pfd2[1],在pfd2[1]中写入'p'通知父进程*/
  3. static int pfd1[2], pfd2[2];
  4. void TELL_WAIT(void)
  5. {
  6. if (pipe(pfd1) < 0 || pipe(pfd2) < 0)
  7. err_sys("pipe error");
  8. }
  9. void TELL_PARENT(pid_t pid)
  10. {
  11. if (write(pfd2[1], "c", 1) != 1)
  12. err_sys("write error");
  13. }
  14. void WAIT_PARENT(void)
  15. {
  16. char c;
  17. if (read(pfd1[0], &c, 1) != 1)
  18. err_sys("read error");
  19. if (c != 'p')
  20. err_quit("WAIT_PARENT: incorrect data");
  21. }
  22. void TELL_CHILD(pid_t pid)
  23. {
  24. if (write(pfd1[1], "p", 1) != 1)
  25. err_sys("write error");
  26. }
  27. void WAIT_CHILD(void)
  28. {
  29. char c;
  30. if (read(pfd2[0], &c, 1) != 1)
  31. err_sys("read error");
  32. if (c != 'c')
  33. err_quit("WAIT_CHILD: incorrect data");
  34. }

下面代码是基于信号的一种同步实现:

  1. static volatile sig_atomic_t sigflag; /*set nonzero by sig handler*/
  2. static sigset_t newmask,oldmask,zeromask;
  3. static void sig_handler(int signo)
  4. {
  5. sigflag = 1;
  6. }
  7. void TELL_WAIT()
  8. {
  9. signal(SIGUSR1,sig_handler);
  10. signal(SIGUSR2,sig_handler);
  11. sigemptyset(&zeromak);
  12. sigemptyset(&newmak);
  13. sigaddmask(&newmak,SIGUSR1);
  14. sigaddmask(&newmak,SIGUSR2);
  15. /*阻塞SIGUSR1 & SIGUSR2,并将原信号集存储在oldmask中*/
  16. sigprocmask(SIG_BLOCK,&newmak,&oldmask);
  17. }
  18. void TELL_CHILD(pid_t pid)
  19. {
  20. kill(pid,SIGUSR1);
  21. }
  22. void TELL_PARENT(pid_t pid)
  23. {
  24. kill(pid,SIGUSR2);
  25. }
  26. void wait()
  27. {
  28. while(sigflag == 0)
  29. sigsuspend(&zeromask); //调用进程挂起
  30. sigflag = 0;
  31. /*恢复原信号集的值*/
  32. sigprocmask(SIG_SETMASK,&oldmak,NULL);
  33. }
  34. void WAIT_PARENT()
  35. {
  36. wait();
  37. }
  38. void WAIT_CHILD()
  39. {
  40. wait();
  41. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/210274
推荐阅读
相关标签
  

闽ICP备14008679号