进程相关函数
pid_t fork()
创建一个子进程。
一次调用,会返回两次。
子进程先运行和是父进程先进程,顺序不确定。变量不共享。
子进程复制父进程的0到3g空间和父进程内核中的PCB,但pid号不同。
功能:通过该函数可以从当前进程中克隆一个同名新进程。克隆的进程称为子进程,原有的进程称为 父进程。子进程是父进程的完全拷贝。
子进程的执行过程是从fork函数之后执行。
子进程与父进程具有相同的代码逻辑。
返回值:int 类型的数字。
在父进程中:成功 返回值是子进程的pid号 >0。失败 返回-1;
在子进程中:成功 返回值 0。
fork成功后,父子进程使用相同的代码段。父子进程开始运行的地方有所差异。
获得pid相关的函数
getpid
pid_t getpid(void);
功能:获得调用该函数进程的pid
参数:缺省
返回值:进程的pid
getppid parent
pid_t getppid(void);
功能:获得调用该函数进程的父进程pid号
参数:缺省
返回值:返回父进程id号
父子进程关系
子进程是父进程的一个复制品(副本)。
从linux 2.6 之后 (ubuntu 18 linux 5.4)子进程在复制父进程内存空间的时候,执行写时复制。
刚fork完毕,子进程使用的内存空间全部都是父进程的(子进程共享父进程所以的空间)。随着时间的推移,父子进程对某个变量或内存区域进行修改,这个时候,子进程才会开辟对应内存空间。这个技术就是写时复制。
进程的终止:8中情况
1)main 中return
2)exit(), c库函数,会执行io库的清理工作,关闭所有 的流,以及所有打开的文件。已经清理函数(atexit)。
3)_exit,_Exit 会关闭所有的已经打开的文件,不执行清理函数。
4)主线程退出
5)主线程调用pthread_exit
异常终止
6)abort()
7)signal kill pid
8)最后一个线程被pthread_cancle
退出后的进程
僵尸进程,父进程创建子进程后,子进程先消亡。对应用户内存空间释放,子进程也不会被调度了。但是内核空间中的对应的pcb块,没有释放。
孤儿进程,父进程创建子进程后,父进程先消亡。子进程会寻找新的父进程。孤儿进程不用过多关注。因为,他的回收问题,由新的父进程回收。
退出函数
1.exit 库函数
退出状态,终止的进程会通知父进程,自己使如何终止的。如果是正常结束(终止),则由exit传入的
参数。如果是异常终止,则有内核通知异常终止原因的状态。任何情况下,负进程都能使用wait,waitpid获
得这个状态,以及资源的回收。
void exit(int status)
exit(1);
功能:让进程退出,并刷新缓存区
参数:status:进程退出的状态
返回值:缺省
参数可选值EXIT_SUCCESS 0
EXIT_FAILURE 1
return 当该关键字出现在main函数中时候可以结束进程
如果在其他函数中则表示结束该函数。
exit -> 刷新缓存区 -> atexit注册的退出函数 -> _exit
2._exit 系统调用
void _exit(int status);
功能:
让进程退出,不刷新缓存区
参数:status:进程退出状态
返回值:缺省
进程空间的回收
因为子进程退出后,对应的pcb 没有释放。导致子进程并没有完全退出。如果父进程是一个需要长期运行,并需要多次创建子进程(运行时长相对都比父的短)的进程,那么时间一长,linux内核空间就会有大量的僵尸进程的pcb ,会导致linux 内核内存空间不足,系统就不稳定或崩溃。
可以使用ps 或top 查看
pid_t wait(int *status);
功能:该函数可以阻塞等待任意子进程退出
并回收该进程的状态。
只能父进程回收子进程状态。
参数:status 进程退出时候的状态
如果不关心其退出状态一般用NULL表示
如果要回收进程退出状态,则用WEXITSTATUS回收。
返回值:成功 回收的子进程pid
失败 -1;
WIFEXITED(status) 是不是正常结束
WEXITSTATUS(status) 使用这个宏去那返回值
WIFSIGNALED(status) 是不是收到了信号而终止的
WTERMSIG(status)如果是信号终止的,那么是几号信号。
非阻塞回收
waitpid(-1,status,0)=wait(status);
pid_t waitpid(pid_t pid, int *status, int options);
pid:需要回收的子进程, -1 所有的子进程
status 子进程退出时候的状态,
如果不关注退出状态用NULL;
options 选项:
0 表示回收过程会阻塞等待
WNOHANG 表示非阻塞模式回收资源。函数不会 一直等待,需要判断返回值,来决定是
否回收成功,或继续尝试回收(waitpid外面套一个死循环)
返回值:成功 返回接收资源的子进程pid
0 == 子进程没有退出,需要下次尝试回收
-1 ,错误。
exec 族
能,执行本上的任意一个可执行文件。
一般,exec 会和fork 搭配使用。在子进程执行exec。
从内存角度看, 执行exec的进程的代码段,会被新执行的程序所替换。当新程序执行结束,则整个进程结束。
执行exec之前
执行exec 之后
l ->list 参数被一个个列出
v ->vector 向量,在c中解释为数组,参数被存储在数组中
p ->PATH ,系统的环境变量,
int execl(const char *path, const char *arg, ...);
path,需要执行新程序的路径+文件名,
arg 是新程序所需要的参数列表,结束标志NULL
int execlP(const char *file, const char *arg, ...);
file ,需要执行新程序的文件名(默认在环境变量PATH中查找),
arg 是新程序所需要的参数列表,结束标志NULL
int execv(const char *path, char *const argv[]);
path,需要执行新程序的路径+文件名
argv->存放参数的数组 ,最后一个参数是NULL
int execvp(const char *file, char *const argv[]);
file ,需要执行新程序的文件名(默认在环境变量PATH中查找),
argv->存放参数的数组 ,最后一个参数是NULL
如果这个4个函数,需要调用自己的 可执行程序。
第一个参数 都按照 路径+文件名的形式 填写,就都能调用起来。
system函数
system
功能 执行一个shell 命令 ,注意 ,这个命令不能是需要修改父进程的状态的 相关命令。执行的shell命
令,多为信息输出,文件相关操作。 函数内部实现 使用的是 fork+exec;
int system(const char *command);
command: 需要执行的shell命令
返回值 -1 失败
获得当前工作路径char *getcwd(char *buf, size_t size);
buf,是存储路径的字符数组
size,接收字符数组的最大长度
返回值,指向buf的指针,
NULL 失败
改变当前工作路径
int chdir(const char *path)
path,需要切换的路径
返回值:
-1 失败
0 成功