Linux系统应用编程(二)进程
本文主要内容:
一、进程的创建
1.fork( )的理解
-
fork( )函数是Linux/Unix操作系统下的一个系统调用函数,用于创建一个新的进程,该进程是调用fork( )进程的子进程。
-
特点:子进程拷贝复制父进程的内容,包括代码段、数据段、堆和栈的数据,父子进程在单独的内存空间中运行,虽然子进程复制了父进程的大部分内容,但是由于运行的地址空间是独立的,在执行文件写入、映射等操作时不会互相影响。
(man手册所述)
-
另外,man手册中还介绍了一些父子进程的区别和特点:
①子进程拥有自己唯一的进程ID(PID唯一);
②进程资源利用率和CPU时间计数器在子进程中会被重置为0;
③子进程不会继承父进程的锁和信号量;
④子进程不会继承未完成的异步I/O操作;
⑤子进程的终止信号始终为SIGCHLD;
⑥父子进程共享同一个文件描述符(也就是共享一个文件的I/O属性,包括文件指针偏移量、状态标志等);
⑦父子进程共享同一个消息队列。
(更多详细可自行翻译man手册,这里就记个几点够面试掰扯掰扯就行)
2.fork( )的使用
▲ 返回值:fork( )会返回两次,分别在父进程和子进程中返回。
-
在父进程中,fork( )返回子进程的pid(正整数,父进程可以通过该子进程pid来操作子进程,如:发送信号kill( )、等待子进程结束wait( )等)
-
在子进程中,fork( )返回值为0(只作区分父子进程,并不是意味着子进程pid=0)
返回-1表示创建进程失败
3.vfork( )的理解
-
特点:在vfork( )函数中,子进程是在父进程的地址空间上运行的,不同于fork( )函数,vfork( )不拷贝复制父进程的内容。因此,vfork( )在运行效率上也比fork( )高效,但在使用上比fork( )函数更复杂,需要注意以下:
①vfork( )函数中,父子进程共享地址空间,子进程可修改父进程的变量和函数调用,可能会导致出现问题;
②vfork( )函数中,子进程不能使用父进程的栈空间,需要在子进程中重新分配新的栈空间;
③vfork( )函数中,子进程先运行,且需要调用exec函数族中的或者exit( )函数结束子进程,父进程才能执行;
④vfork( )函数中,子进程不继承父进程的文件描述符,因此如果子进程需要使用同一个文件,需要重新在子进程中打开文件。
二、进程的退出
1.进程退出的方式
- main函数中return
- 进程调用标准c库函数exit( )
- 进程调用系统调用函数 _exit( )或 _Exit( )
- 进程中最后一个线程return或者调用pthread_exit( )
简单说就是进程的最后一个线程结束
- 异常退出:调用abort( )、收到退出或终止信号等
2.僵尸进程和孤儿进程
-
僵尸进程:子进程比父进程先结束,且子进程退出状态不被父进程收集,此时子进程将成为一个僵尸进程
-
孤儿进程:父进程先结束,子进程还在运行,此时子进程将成为一个孤儿进程(孤儿进程将被init进程收养)
3.wait( )、waitpid( )函数
三、exec族函数
1.作用
在进程中执行另一个可执行程序(二进制文件、shell脚本等),执行后,原进程除了PID外,其他均被可执行程序进程替代。其中,execlp( )会从系统环境变量中寻找可执行程序
(注意:exec函数并不创建新进程,且调用其他程序结束,原程序不再执行)
2.exec族函数原型
#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
3.参数和返回值
四、system( )函数
五、popen( )函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
FILE *file = NULL;
file = popen("ls -l /","r");
if(file == NULL){
perror("popen");
exit(-1);
}
char *readBuffer = (char *)malloc(128);
fread(readBuffer,128,1,file);
printf("read:%s\n",readBuffer);
pclose(file);
return 0;
}