进程
pid & ppid
通过getpid()可以获取进程ID(PID),getppid()可以获取父进程ID(PPID)。
SYNOPSIS
#include <sys/types.h> #include <unistd.h> pid_t getpid(void); pid_t getppid(void);
进程控制编程
fork()
fork函数用于从已存在进程(父进程)中创建一个新进程(子进程)。
父进程的返回值是子进程的进程号,而子进程则返回0。因此,可以通过返回值来判定该进程是父进程还是子进程。
使用fork函数得到的子进程是父进程的一个复制品,所以效率很低。
SYNOPSIS
#include <unistd.h> pid_t fork(void);
实例:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main() { pid_t result = fork(); if(result <= -1){ perror("fork"); exit; } else if(result == 0){ printf("child id is %d, parent id is %d\n", getpid(), getppid()); } else{ printf("parent id is %d\n", getpid()); } return 0; }
运行结果:
xxx@xxx-pc:~/Documents$ ./a.out parent id is 5888 child id is 5889, parent id is 5888
exec函数族
exec 函数族就提供了一个在进程中启动另一个程序执行的方法。
所需头文件 |
#include <unistd.h> |
函数原型 | int execl(const char *path, const char *arg, ...) int execv(const char *path, char *const argv[]) int execle(const char *path, const char *arg, ..., char *const envp[]) int execve(const char *path, char *const argv[], char *const envp[]) int execlp(const char *file, const char *arg, ...) int execvp(const char *file, char *const argv[]) |
函数返回值 |
−1:出错 |
前 4 位 |
统一为:exec |
第 5 位 | l:参数传递为逐个列举方式 execl、execle、execlp v:参数传递为构造指针数组方式 execv、execve、execvp |
第 6 位 |
e:可传递新进程环境变量 execle、execve p:可执行文件查找方式为文件名 execlp、execvp |
实例1:
int main() { if(fork()==0){ /*调用 execlp 函数,这里相当于调用了“ps -ef”命令*/ if(execlp("ps","ps","-ef",NULL)<0) perror("execlp error!"); } }
实例2:
int main() { if(fork()==0){ /*调用 execl 函数,注意这里要给出 ps 程序所在的完整路径*/ if(execl("/bin/ps","ps","-ef",NULL)<0) perror("execl error!"); } }
实例3:
int main() { /*命令参数列表,必须以 NULL 结尾*/ char *envp[]={"PATH=/tmp","USER=sunq",NULL}; if(fork()==0){ /*调用 execle 函数,注意这里也要指出 env 的完整路径*/ if(execle("/usr/bin/env","env",NULL,envp)<0) perror("execle error!"); } }
实例4:
int main() { /*命令参数列表,必须以 NULL 结尾*/ char *arg[]={"env",NULL}; char *envp[]={"PATH=/tmp","USER=sunq",NULL}; if(fork()==0){ if(execve("/usr/bin/env",arg,envp)<0) perror("execve error!"); } }
实例3和4的结果:
xxx@xxx-pc:~/Documents$ ./a.out PATH=/tmp USER=sunq
envp修改的是当前进程的环境变量。
exec 函数族使用注意点:
在使用 exec 函数族时,一定要加上错误判断语句。因为 exec 很容易执行失败,其中最常见的原因有:
• 找不到文件或路径,此时 errno 被设置为 ENOENT;
• 数组 argv 和 envp 忘记用 NULL 结束,此时 errno 被设置为 EFAULT;
• 没有对应可执行文件的运行权限,此时 errno 被设置为 EACCES。
exit 和_exit
exit()函数与_exit()函数最大的区别就在于 exit()函数在调用 exit 系统之前要检查文件的打开情况,把文件缓冲区中的内容写回文件。
SYNOPSIS
#include <unistd.h> void _exit(int status);
实例exit():
int main() { printf("Using exit...\n"); printf("This is the content in buffer"); exit(0); }
结果:
Using exit... This is the content in buffer
实例_exit():
int main() { printf("Using exit...\n"); printf("This is the content in buffer"); _exit(0); }
结果:
Using exit...
wait 和 waitpid
wait 函数和 是用于使父进程(也就是调用 wait 的进程)阻塞,直到一个子进程结束或者该进程接到了一个指定的信号为止。
waitpid 函数功能更强大,还可以实现非阻塞版本。
实际上 wait 函数只是 waitpid 函数的一个特例,在 Linux 内部实现 wait 函数时直接调用的就是 waitpid 函数。
SYNOPSIS
#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options);
实例1:
#include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> int main() { pid_t pc,pr; pc=fork(); if(pc<0) printf("Error fork.\n"); else if(pc==0){ sleep(5); exit(0); } else{ do{ /* father process, so pc value is child id */ pr=waitpid(pc,NULL,WNOHANG); if(pr==0){ printf("The child process has not exited\n"); sleep(1); } }while(pr==0); if(pr==pc) printf("Get child %d\n",pr); else printf("some error occured.\n"); } }
结果如下:
waitpid(pc,NULL,WNOHANG) 提供了非阻塞版本。
xxx@xxx-pc:~/Documents$ ./a.out The child process has not exited The child process has not exited The child process has not exited The child process has not exited The child process has not exited Get child 3366
如果换成waitpid(pc,NULL,0)或wait(NULL)则是阻塞版本。
结果如下:
xxx@xxx-pc:~/Documents$ ./a.out Get child 3440
多进程实例
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> int main(void) { pid_t child1,child2,child; child1 = fork(); child2 = fork(); if(child1 == -1){ perror("child1 fork"); exit(1); }else if(child1 == 0){ printf("In child1: execute 'ls -l'\n"); if(execlp("ls","ls","-l",NULL)<0) perror("child1 execlp"); } if(child2 == -1){ perror("child2 fork"); exit(1); }else if(child2 == 0){ printf("In child2: sleep for 5 seconds and then exit\n"); sleep(5); exit(0); }else{ printf("In father process:\n"); do{ child = waitpid(child2, NULL, WNOHANG); if(child == 0){ printf("The child2 process has not exited!\n"); sleep(1); } }while(child == 0); if(child == child2) printf("Get child2\n"); else printf("Error occured!\n"); } }
运行结果:
多运行几次,结果可能会不太一样。因为,子进程之间存在着竞争关系。
xxx@xxx-pc:~/Documents$ ./a.out In father process: The child2 process has not exited! In child2: sleep for 5 seconds and then exit In child1: execute 'ls -l' In child1: execute 'ls -l' total 36 total 36 -rwxrwxr-x 1 xxx xxx 8824 Apr 27 00:12 a.out -rwxrwxr-x 1 xxx xxx 8824 Apr 27 00:12 a.out -rw-rw-r-- 1 xxx xxx 6 Apr 23 16:49 markd.md -rw-rw-r-- 1 xxx xxx 835 Apr 27 00:12 p2.c -rw-rw-r-- 1 xxx xxx 837 Apr 27 00:11 p2.c~ -rw-rw-r-- 1 xxx xxx 6 Apr 23 16:49 markd.md -rw-rw-r-- 1 xxx xxx 892 Apr 26 23:31 pid.c -rw-rw-r-- 1 xxx xxx 865 Apr 26 23:30 pid.c~ -rw-rw-r-- 1 xxx xxx 835 Apr 27 00:12 p2.c -rw-rw-r-- 1 xxx xxx 156 Apr 23 16:48 piddump.c -rw-rw-r-- 1 xxx xxx 837 Apr 27 00:11 p2.c~ -rw-rw-r-- 1 xxx xxx 892 Apr 26 23:31 pid.c -rw-rw-r-- 1 xxx xxx 865 Apr 26 23:30 pid.c~ -rw-rw-r-- 1 xxx xxx 156 Apr 23 16:48 piddump.c The child2 process has not exited! The child2 process has not exited! The child2 process has not exited! The child2 process has not exited! Get child2