进程同步:一组并发的进程按照一定的顺序执行的过程称为进程间的同步。
获取ID:
#include<unistd.h>
pid_t getpid(void) //获取本进程ID
pid_t getppid(void) //在子进程中获取父进程ID
进程创建:
#include<unistd.h>
pid_t fork(void)
fork()的奇妙之处在于它被调用一次,却返回两次,它可能有三种不同的返回值:
1. 在父进程中,返回新创建的子进程的PID
2. 在子进程中,返回0
3. 如果出现错误,返回一个负值。
例子:
#include <stdlib.h> #include <stdio.h> #include <sys/types.h> int main(void) { pid_t pid; printf("Before fork .../n"); switch(pid = fork()) { case -1: printf("Fock call fail/n"); exit(1); case 0: printf("The pid of child is: %d/n", getpid()); printf("The pid of child's parent is: %d/n", getppid()); printf("Child exiting.../n"); exit(0); default: printf("The pid of parent is: %d/n", getpid()); printf("the pid of parent's child is: %d/n", pid); } printf("After fork, program exiting.../n"); exit(0); }
#include<unistd.h>
pid_t vfork(void)
fork与vfork的区别:
1. fork:子进程拷贝父进程的数据段
vfork:子进程与父进程共享数据段
2. fork:父、子进程的执行次序不确定
vfork:子进程先运行退出后,父进程再运行。
exec函数族:
exec用被执行的程序替换调用它的程序。与fork的区别:
fork创建一个新的进程,产生一个新的PID
exec启动一个新程序,替换原有的进程,因此进程的PID不会改变。
看代码:
#include<unistd.h> #include<stdio.h> int main(void) { printf("pid in main is %d\n",getpid()); execl("/mnt/hgfs/share/program/process/getpid","getpid",NULL); printf("123\n"); printf("pid in main is %d\n",getpid()); return; }
程序/mnt/hgfs/share/program/process/getpid就是打印出子进程和父进程的pid。
$ #./execl
pid in main is 3724
pid=3724
ppid=3432
$# ps
PID TTY TIME CMD
3432 pts/1 00:00:00 bash
3725 pts/1 00:00:00 ps
分析:可以看到我们的execl程序的进程ID与getpid的进程ID是一样的。而且他们的父进程都是shell命令。注意当getpid程序退出时,整个程序就退出了,所以后面的两个输出语句并没有执行。
#include<unistd.h>
int execl(const char *path,const char *arg1,...)
参数:
path:被执行的程序名(含完整路径)
arg1—argn:被执行程序所需的命令行参数,含程序名。以空指针(NULL)结束。
#include<unistd.h>
int execlp(const char *path,const char *arg1,...)
path:被执行程序名(不含路径,将从path环境变量中查找给程序)
arg1—argn:与上面的相同。
#include<unistd.h>
int execv(const char *path,char * const argv[])
参数:
path:被执行程序名(含完整路径)
argv[]:被执行程序所需的命令行参数数组(包括NULL).argv是指针数组
代码:
#include<unistd.h> #include<stdio.h> #include<string.h> int main(void) { int sel; char *argv[]={"ls","-al","/etc/passwd",(char *)0}; scanf("%d",&sel); switch(sel) { case 1: printf("execl\n"); execl("/bin/ls","ls","-al","/etc/passwd",(char *)0); break; case 2: printf("execlp\n"); execlp("ls","ls","-al","/etc/passwd",(char *)0); break; case 3: printf("execv\n"); execv("/bin/ls",argv); break; case 4: printf("execvp\n"); execvp("ls",argv); break; default: return 0; } }
#include<stdlib.h>
int system(const char *string)
功能:
调用fork产生子进程,由子进程通过exec来执行/bin/sh –c string来执行string所代表的命令。注意:这时在exec函数中,/bin/sh相当于程序名,而string为其参数。所以string命令的父进程是system。
代码:
#include<stdlib.h> #include<stdio.h> int main(void) { printf("pid in main:%d\n",getpid()); printf("ppid in main:%d\n",getppid()); system("/mnt/hgfs/share/program/process/getpid"); printf("test\n"); return 0; }
#./system
pid in main:31192
ppid in main:3432
pid=31193
ppid=31192
test
可以看到getpid这条命令的父进程就是main进程。system就是一个系统调用,在system函数后面的代码必须等到调用所创建的子进程返回后才继续执行。其实在system的实现中其依次调用了fork,exec,waitpid。
进程等待:
#include<sys/wait.h>
pid_t wait(int *staloc);
pid_t waitpid(pid_t pid,int *staloc,int options);
调用wait或waitpid的进程可能:
1. 如果其所有子进程都还在运行,则阻塞。
2. 如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回。
3. 如果它没有任何子进程,则立即出错返回。
其实,可以把wait函数理解为就是获取一个子进程的终止状态,终止状态存在staloc存储单元中。
waitpid中pid参数的作用解释如下:
pid == -1 等待任一子进程。与wait等效。
pid >0 等待其进程ID与pid相等的子进程。
pid =00 等待其组ID等于调用进程组ID的任一子进程。
pid <-1 等待其组ID等于pid绝对值的任一子进程。