1、进程是一个程序的一次执行过程,同时也是资源分配的最小单元,根据它的生命周期可分为三种状态:执行态、就绪态和等待态。Linux中的进程包含三个段:数据段、代码段和堆栈段。
2、Linux进程控制编程
@1、fork函数创建,在unistd.h库中定义,函数原型为pid_t fork(void)。fork函数调用一次却返回两次;在父进程中返回子进程的ID,在子进程中返回0,出错返回负值。Fork子进程是对父进程的拷贝,即子进程从父进程得到数据段和堆栈段(有些linux操作系统不是完全拷贝),需要分配内存;对于只读代码段是共享方式访问,子进程和父进程的执行顺序是不确定的,由调度方法决定。
@2、vfork函数创建,vfork函数区别于fork有以下三点:
a、vfork()子进程与父进程共享数据段;
b、fork()父子进程的执行次序不确定,而vfork()保证子进程先运行
c、vfork()保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行,如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。
@3、exac函数族,根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,执行完之后,原调用进程的内容除了进程号外,其余都被新进程替换。它的六个成员为:
int execl(const char *path,const char *arg0,...);int execv(const char *path,char *const argv[ ]);
int execle(const char *path,const char *arg0,...,char *const envp[ ]);
int execve(const char *path,char *const argv[ ],char *const envp[ ]);
int execlp(const char *filename,const char *arg0,...);
int execvp(const char *filename,char *const argv[ ]);
可以做一个简单归纳,如下图:
使用exec函数族注意几点:找不到文件或路径;数组argv和envp忘记用NULL结束;没有对应的执行文件的运行权限。
@4、exit()和_exit()
exit()函数定义在stdlib.h中,原型为void exit(int status),而_exit()定义在unistd.h中,原型为void _exit(int status);
_exit()函数的作用最为简单:直接使进程停止运行,清除其使用的内存空间,并清除其在内核中的各种数据结构;exit()函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序,exit()函数与_exit()函数最大的区别在于exit()函数在调用exit系统之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,就是“清理I/O缓冲”,因此,若要保证数据的完整性,就一定要用exit()函数。
@5、system函数
#include <stdlib.h>
int system(const char *string)
调用fork产生子进程,由子进程来调用/bin/sh -c string(shell)来执行参数string所代表的命令,在编写具有SUID/SGID权限的程序时请勿使用system(),system()会继承环境变量,通过环境变量可能会造成系统安全的问题。
@6、wait()和waitpid()进程等待
wait()原型:pid_t wait (int *status);它会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一起返回。如果不在意结束状态值,则参数status可以设成NULL。
waitpid()原型:pid_t waitpid(pid_t pid,int *status,int options);wait会令调用者阻塞直至某个子进程终止,而waitpid则可以通过设置一个选项来设置为非阻塞,另外waitpid并不是等待第一个结束的进程而是等待参数中pid指定的进程。
waitpid()参数意义如下图: