Linux系统编程——进程控制(二)

版权声明:版权归博主所有,转载请注明 https://blog.csdn.net/qq_17808707/article/details/88112478

二 进程的基本操作

相关的函数被定义在系统调用库unistd.h中。

1.fork系统调用

fork系统调用有两个函数,分别是fork()vfork()函数。可创建一个子进程,调用的一般形式是:

pid_t fork(void);
pid_t vfork(void);
  • pid_t:用于保存进程PID信息的结构体
    调用成功时,对父进程返回子进程PID,对子进程返回0。调用失败时返回-1,子进程没有创建。vfork()函数与fork()函数形式相同,区别在于vfork()函数创建子进程时,不复制父进程的上下文。参看如下示例:
#include<sys/types.h>   //提供系统调用标志
#include<sys/stat.h>    //提供系统状态信息和相关函数
#include<sys/uio.h>     //提供进程I/O操作相关函数
#include<unistd.h>
#include<fcntl.h>       //文件操作相关库函数
#include<string.h>      //字符串操作库函数
#include<sys/wait.h>    //wait调用相关库函数
#include<iostream>  
#include<stdlib.h>

using namespace std;

int main()
{
	char buf[100]={0};   //定义缓冲区
	pid_t cld_pid;       //子进程PID
	int fd;
	int status;
	if((fd=open("temp",O_CREAT|O_RDWR|O_TRUNC,0664))==-1)
	{
		cerr<<"创建文件失败"<<endl;
		exit(1);
	}
	strcpy(buf,"父进程数据");
	if((cld_pid=fork())==0)
	{
		strcpy(buf,"子进程数据");
		cout<<"子进程正在工作: "<<endl;
		cout<<"子进程PID是"<<getpid()<<endl;
		cout<<"父进程PID是"<<getppid()<<endl;
		write(fd,buf,strlen(buf));
		close(fd);
		exit(0);
	}
	else
	{
		cout<<"父进程正在工作:"<<endl;
		cout<<"父进程PID是"<<getpid()<<endl;
		cout<<"子进程PID是"<<cld_pid<<endl;
		write(fd,buf,strlen(buf));
		close(fd);
	}
	wait(&status);
	return 0;
}

运行结果如下:
在这里插入图片描述

2.exec系统调用

以新进程替代原有进程,但是PID保持不变。可认为,exec系统调用实际上没有创建新进程,只是替换原有进程上下文内容,共有6个函数,代码如下:

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 execve(const char* path,char* const argv[],char* const envp[]);
int execvp(const char* file,char const* argv[]);

在使用这些函数前,必须在程序中定义全局变量,如下:

extern char** environ;

该变量是预定义用来指向Linux系统全局变量的指针,这样就能在当前工作目录执行系统程序,正如在shell中可不输入路径直接运行VIM,GCC等程序。
exec系统调用的函数中,execve()函数是另外5个函数的基础。这些函数的区别可总结如下:

  1. 执行程序文件是由文件名还是由路径名指定。第一个参数为*file的是文件名,为*path的是路径名。
  2. 新程序的参数时一一列出还是由一个指针数组来引用。
    (1) execl()函数和execlp()函数是将参数一一列出
    (2) execle()函数将参数一一列出,但可以用指针数组引用环境变量
    (3) execv()函数和execvp()函数的参数由一个指针数组来引用
    (4) execve()函数的参数用一个指针数组来引用,还用另一个指针数组引用环境变量
  3. 把调用进程的环境传递给新程序还是给新程序指定新的环境。execle()函数和execve()函数为新程序指定新的环境

这6个函数的执行效果是一样的,在执行成功时,函数的返回值是0,否则返回-1。如下例所示:

//这是第一个文件,被调用者,文件名为beexec.cpp
#include<iostream>
#include<unistd.h>

using namespace std;
extern char **environ;

int main(int argc,char* argv[])
{
	cout<<"输出执行参数:"<<endl;
	for(int i=0;i<=argc;i++)
		cout<<"参数"<<i<<"是:"<<argv[i]<<endl;
	cout<<"输出环境变量:"<<endl;
	for(int i=0;environ[i]!=NULL;i++)
		cout<<environ[i]<<endl;
	return 0;
}
//这是第二个文件,调用者,文件名为doexec.cpp
#include<unistd.h>
#include<iostream>

using namespace std;
extern char **environ;
int main(int argc,char* argv[])
{
	cout<<"此信息可能无法输出"<<endl;
	execve("beexec",argv,environ);
	cout<<"正常情况此信息无法输出"<<endl;
	return 0;
}

两个文件需分开编译。编译运行结果如下:
在这里插入图片描述

3.exit系统调用

exit的功能是终止发出调用的进程,包含两个函数,其一般形式如下:

void _exit(int status);
void exit(int status);

_exit立即终止发出调用的进程。所有属于该进程的文件描述符都关闭。如果该进程拥有子进程,那么父进程关系被转到init进程上。被结束的进程将收到来自进程的僵死信号SIGCHLD。如果被结束的进程在控制台或终端上运行,shell程序将收到SIGHUP信号。
函数中的参数status是返回给父进程的状态值,父进程可通过wait系统调用获得。status只有最低1个字节能被父进程读取,由此可知,实际值域范围为0~255。
系统调用_exit()没有返回值,被终止进程不会知道该调用是否成功。另外,该调用不会刷新输入输出缓冲区,因此进程结束前必须自己刷新缓冲区,或者改用exit()系统调用。exit()系统调用将进行一些上下文清理工作,例如释放所有占用的资源,清空缓冲区等。

4.wait系统调用

用于父进程与子进程同步。父进程调用后,将进入睡眠状态,直到子进程结束或父进程再被其他进程终止。使用wait系统调用需包含头文件sys/types.hsys/wait.h。该调用有如下两个函数:

pid_t wait(int *status);
pit_t waitpid(pid_t pid,int *status,int options);

发出wait系统调用的进程进入睡眠状态,直到它收到一个子进程的僵死信号,或者是收到其他重要信号。如果父进程在wait系统调用的同时子进程进入僵死状态,wait系统调用会立即结束。
参数*status用来获得子进程exit系统调用的参数值,只有最低1个字节能被读取。
wait()函数等待所有子进程的僵死状态,而waitpid()函数等待PID与参数pid相关的子进程僵死状态。其中,参数pid的含义与取值方法如下:

  • pid<-1,且退出的子进程的进程组ID等于绝对值的pid时,结束等待
  • pid=0,且该子进程的进程组ID等于发出调用进程的组ID时,子进程退出
  • pid>0,等待进程ID等于参数pid时,子进程退出
  • pid=-1,等待任何子进程退出,相当于wait()

waitpid()函数中的参数options的取值范围及意义如下:

  • WNOHANG,该选项要求如果没有子进程退出就立即返回
  • WUNTRACED,如果发现已经僵死,但未报告状态的子进程,父进程不进入睡眠状态,立即返回子进程的终止信息

如果status参数不为NULL,可通过该参数获得子进程的信息,下列宏能用来检查子进程的返回状态:

在这里插入图片描述

5.sleep函数调用

系统调用sleep用来使进程主动进入睡眠状态,一般形式是:

sleep(秒数)

执行该系统调用后,进程将进入睡眠状态,直到指定秒数到达。正常情况下,该调用的返回值为0,若是因为被信号所唤醒,则返回值为原始秒数减去已睡眠秒数的差。

猜你喜欢

转载自blog.csdn.net/qq_17808707/article/details/88112478