注意:凡是未标注函数所在头文件的均是unistd.h头文件中声明的函数
1、pid_t fork(void);
(1)pid_t 是一个宏定义,其实质是int
(2)返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1
(3)函数说明:
一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。子进程和父进程都会从调用点继续往下执行。
子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。
linux将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。
(4)总结
fork函数的特点概括起来就是“调用一次,返回两次”,在父进程中调用一次,在父进程和子进程中各返回一次。
fork的另一个特性是所有由父进程打开的描述符都被复制到子进程中。父、子进程中相同编号的文件描述符在内核中指向同一个file结构体,也就是说,file结构体的引用计数要增加。
2、exec函数族
(1)exec和system的区别
system是用shell来调用程序=fork+exec+waitpid,而exec是直接让你的程序代替原来的程序运行。
system是在单独的进程中执行命令,完了还会回到你的程序中。而exec函数是直接在你的进程中执行新的程序,新的程序会把你的程序覆盖(新进程与旧进程的进程号相同),除非调用出错,否则你再也回不到exec后面的代码,就是说你的程序就变成了exec调用的那个程序了。
(2)说明
exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段。有六种不同的exec函数可供使用,它们常常被统称为exec函数。这些exec函数都是UNIX进程控制原语。
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, char *const argv[]); int execvp(const char *file, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]);
其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。
值得一提的是,argv数组和envp数组存放的都是指向字符串的指针,这两个数组都以一个NULL元素表示数组的结尾。
(3)六个函数的比较
留心看一下这6个函数还可以发现,前3个函数都是以execl开头的,后3个都是以execv开头的,它们的区别在于,execv开头的函数是以"char *argv[]"这样的形式传递命令行参数,而execl开头的函数采用了我们更容易习惯的方式,把参数一个一个列出来,然后以一个NULL表示结束。这里的NULL的作用和argv数组里的NULL作用是一样的。
这里建议使用 (char *)0 代替NULL。
在全部6个函数中,只有execle和execve使用了char *envp[]传递环境变量,其它的4个函数都没有这个参数,这并不意味着它们不传递环境变量,这4个函数将把默认的环境变量不做任何修改地传给被执行的应用程序。而execle和execve会用指定的环境变量去替代默认的那些。
还有2个以p结尾的函数execlp和execvp,咋看起来,它们和execl与execv的差别很小,事实也确是如此,除execlp和 execvp之外的4个函数都要求,它们的第1个参数path必须是一个完整的路径,如"/bin/ls";而execlp和execvp的第1个参数 file可以简单到仅仅是一个文件名,如 "ls",这两个函数可以自动到环境变量PATH制定的目录里去寻找。
3、pid_t getpid (void)
(1)说明
getpid函数用来取得目前进程的进程识别码,许多程序利用取到的此值来建立临时文件,以避免临时文件相同带来的问题。
(2)返回值
返回当前进程的ID
4、休眠函数
(1)unsigned int sleep (unsigned int __seconds);
以秒为单位,sleep是会被外部信号中断的,中断后,sleep返回值表示本应该还要休眠多久实际上却没有休眠的。因此,如果程序正常运行应该返回0。
(2)int usleep (int __useconds);
以微秒为单位。同样,不用太在意返回值。
(3)不要忘了select函数
5、pid_t wait(int * status); //<sys/wait.h>
(1)说明
wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。
(2)参数和返回值
子进程的结束状态值会由参数status 返回,而返回值是子进程的进程ID,如果有错误发生则返回-1,失败原因存于errno 中。如果不在意结束状态值,则参数status 可以设成NULL。
(3)pid_t waitpid(pid_t pid,int * status,int options);
可以使用waitpid等待某一个子进程结束。
参数pid 为欲等待的子进程识别码:
- pid<-1 等待进程组识别码为pid绝对值的任何子进程。
- pid=-1 等待任何子进程,相当于wait()。
- pid=0 等待进程组识别码与目前进程相同的任何子进程。
- pid>0 等待任何子进程识别码为pid 的子进程。
参数option 可以为0 或一些宏,具体见百度百科。
如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno 中。