目录

1、进程

2、进程的状态

3、进程的组成

4、进程的创建fork()

练习:父子进程的拷贝

5、进程结束的两个特殊状态

孤儿进程

僵尸态

stat函数

功能

参数

返回值

fork之后文件共享的问题

6、进程的执行

exec函数

exec l / exec v

功能

参数

返回值

exec l p / exec v p

参数

exec l p e / exec v p e

strtok函数

功能

参数

返回值

注意

7、进程的结束

(1)正常结束

exit函数

功能

参数

库函数

atexit函数(注册函数)

功能

参数

返回值

注意

_exit函数

功能

exit和_exit的区别

(2)异常结束

8、进程资源的回收

(1)wait

功能

参数

返回值


1、进程

进程:程序的一次执行过程

进程:动态   保存在内存

程序:静态   保存在硬盘

一个程序可以对应多个进程

2、进程的状态

3、进程的组成

PCB+堆|栈|bss|data|text

4、进程的创建fork()

复制调用进程,创建子进程

练习:父子进程的拷贝

5、进程结束的两个特殊状态

孤儿进程

子进程还在,但是父进程结束了------------此时子进程会成为孤儿进程------------会成为后台进程,此时ctrl+c无法停止,因为ctrl+c本质是一个发信号,一般是发给前台进程的

当一个进程成为孤儿进程后,会被系统的systemd(早期为init(1)进程)进程收养。因为子进程最终的资源一定要有人回收,如果没有人回收就会出现僵尸态。

僵尸态

子进程结束了,父进程没有做收尸操作(资源回收),那么子进程的PCB块就一直存在。

僵尸态的进程是有危害的,一般需要做处理-----------做收尸操作(回收资源)

stat函数

#include <unistd.h>

int stat(const char *pathname, struct stat *statbuf);

功能
  • 获得文件状态信息
参数
  • @pathname:文件名
  • @ststbuf:文件状态信息的结构体
返回值
  • 成功返回0
  • 失败返回-1而且errno会被设置

fork之后文件共享的问题

1、如果是fork之前打开文件,子进程会继承父进程中已经打开的文件描述符;此时父子进程各有各的文字描述符,但是他们共享了文件表项(文件状态和文件偏移量),此时,操作文件会相互影响偏移量。

2、如果不想相互影响,可以将打开文件的操作放在fork之后

6、进程的执行

与父进程做独立的事

fork+exec

exec函数
  • int execl(const char *path, const char *arg, .../* (char  *) NULL */);
       
  • int execlp(const char *file, const char *arg, .../* (char  *) NULL */);                       
  • int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
  • int execv(const char *path, char *const argv[]);
  • int execvp(const char *file, char *const argv[]);
  • int execvpe(const char *file, char *const argv[],char *const envp[]);       
exec l / exec v
  • int execl(const char *path, const char *arg, .../* (char  *) NULL */);
  • int execv(const char *path, char *const argv[]); (紫色部分为指针数组)

l------------------------list:表示从第二个参数开始,传参时把参数逐个列举出来

  • execl(“/bin/ls”,“ls”,“-l”,“.”,NULL)
#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{

	printf("exec  start\n");
	execl("/bin/ls","ls",".",NULL);
	printf("exec end\n");
	return 0;
}

v------------------------vector:向量(数组),表示传参时,将要传的参数组织成指针数组的方式

  • char *const args[]={"ls","-l",".",NULL};
  • execv("/bin/ls",args);
#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{

	printf("exec  start\n");
	char *const args[]={"ls","-l",".",NULL};
	execv("/bin/ls",args);
	printf("exec end\n");
	return 0;
}
功能

镜像(进程的组成部分)

exec函数 用新的进程的各个段 替换了 当前进程的各个段,但是PCB还是之前的PCB

参数

@path:表示要执行的新程序的路径。eg:"/bin/ls"

@arg:代表要执行的文件的名字

返回值
  • 成功返回0
  • 失败返回-1而且erno会被设置

查看cp在哪里:which cp

exec l p / exec v p

p--------------------------PATH 系统的环境变量--------------------------echo $PATH

  • int execlp(const char *file, const char *arg, .../* (char  *) NULL */);       
  • int execvp(const char *file, char *const argv[]);
参数

file:表示可执行的文件(名字)

这个文件不需要指定路径,当程序运行时,默认会到path环境变量中寻找

PATH-----------系统的 环境变量-------用来记录当前系统的各个二进制文件(可执行文件)的路径

execlp

#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{

	printf("exec  start\n");
	if(execlp("ls","ls","-l",".",NULL)<0)
	{
		perror("execlp fail");
		return -1;
	}

	printf("exec end\n");
	return 0;
}

execvp

#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{

	printf("exec  start\n");
	char *const args[]={"ls","-l",".",NULL};
	execvp("ls",args);
	printf("exec end\n");
	return 0;
}
exec l p e / exec v p e
  • int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
  • int execvpe(const char *file, char *const argv[],char *const envp[]);       

带e,表示environment----------------环境变量

extern char **environ;  表示environ变量定义在外部,如果要用请到外部查找。是系统中定义好的环境变量指针。

PATH = /bin,系统的环境变量都是这种形式 键值对,key=value

带e的exec函数主要的用途可以给要运行的程序传递环境变量

eg:env  这个程序用到了环境变量-------打印所有环境变量

execlpe

#include <stdio.h>
#include <unistd.h>
extern char **environ;
int main(int argc, const char *argv[])
{	
	char *ev[]={"USER=hello","PASSWORD=12345",NULL};
	//系统
	execle("/usr/bin/env","env",NULL,environ);//表示要运行的这个env使用时系统的环境变量
	//自己组织的环境变量
	//execle("/usr/bin/env","env",NULL,ev);//表示要运行的这个env使用时系统的环境变量

	return 0;
}

execvpe

这个编译的时候 要加     -D _GNU_SOURCE

#include <stdio.h>
#include <unistd.h>
extern char **environ;
int main(int argc, const char *argv[])
{	
	char *ev[]={"USER=hello","PASSWORD=12345",NULL};
	char *const args[]={"env",NULL};
	//execvpe("env",args,environ);
	execvpe("env",args,ev);
	return 0;
}

strtok函数

#include <string.h>

char *strtok(char* str,const char delim)

功能
  • 拆分字符串
参数
  • @str:要拆分的字符串
  • @delim:表示拆分的标志,也是字符串形式
返回值
  • 成功返回 拆好的字符串 的 首地址
  • 失败返回NULL
注意
  • 如果想接着之前的字符串往下拆分,后续调用时,第一个参数必须写成NULL,strtok(NULL,";:")
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
	char buf[10]="ls;-l:."; 
	char* s1=strtok(buf,";:");//遇到:;分割
	printf("s1:%s\n",s1);
	char* s2=strtok(NULL,";:");
	printf("s2:%s\n",s2);
	char* s3=strtok(NULL,";:");
	printf("s3:%s\n",s3);

	char buf1[10]="cp/-l,?";
	char* s4=strtok(buf1,"/,");//遇到/,分割
	printf("s4:%s\n",s4);
	char* s5=strtok(NULL,"/,");
	printf("s5:%s\n",s5);
	char* s6=strtok(NULL,"/,");
	printf("s6:%s\n",s6);
	return 0;
}

实现ls 

#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, const char *argv[])
{
	//读入字符串
	char buf[100]={0};
	printf("myshell$ ");
	while(1)
	{
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf)-1] = '\0';

		//判断
		if (strcmp(buf,"quit")==0 ||(strcmp(buf,"exit"))==0) 
		{
			printf("程序停止\n");
			return -1;
		}
		else
		{
			//strtok分割
			char *s[10]={NULL};
			int i=0;
			s[i++] = strtok(buf," ");
			while(s[i++] = strtok(NULL," "))
				;
			//fork创建子进程
			pid_t pid=fork();
			if(pid<0)
			{
				perror("fork fail");
				return -1;
			}
#if 1
			//父进程
			if(pid>0)
			{
				sleep(1);
			}
#endif
			//子进程
			if(0==pid)
			{
				if(execvp(s[0],s)<0)
				{
					perror("exec fali");
					return -1;
				}
			}

		}
	}
	return 0;
}

7、进程的结束

(1)正常结束
  • 从main函数返回
  • exit函数 ---------------正常结束
  • _exit函数 --------------正常结束

exit函数

  #include <stdlib.h>

  void exit(int status);

功能
  • 造成进程正常结束
参数
  • @status:是传递给父进程的 状态信息,status & 0377 --------只有最低8位有效
库函数
  • 退出之前会刷新IO缓存
  • 调用退出处理函数
atexit函数(注册函数)

#include <stdlib.h>

int atexit(void (*function)(void));

功能
  • 用来注册一个退出清理函数
参数
  • function:函数指针,指向一个函数的------调用函数
返回值
  • 成功返回0
  • 失败返回非0值
注意

void clean_up(void)   退出清理函数

{

}

多次注册时,注册顺序和调用顺序是反着的    //栈

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char* p=NULL;

void clean_up1(void)
{
	printf("clean_up 1--------\n");
	free(p);
}
void clean_up2(void)
{
	printf("clean_up 2--------\n");
	free(p);
}
int main(int argc, const char *argv[])
{
	atexit(clean_up1);
	atexit(clean_up2);
	p=malloc(10);
	strcpy(p,"hello!");
	printf("p:%s\n",p);

	sleep(3);
	exit(0);
	return 0;
}
_exit函数

#include <unistd.h>

void _exit(int status);

#include <stdlib.h>

void _Exit(int status);

功能
  • 立即结束调用进程,不会有 处理IO缓存 和 退出清理函数。
exit和_exit的区别

第一个执行是exit,会刷新缓存和调用处理函数

第二个是_exit,不会刷新缓存

(2)异常结束
  • abort函数:中止 ------------ 异常结束
  • signal:发信号结束的进程

8、进程资源的回收

(1)wait

#include <sys/wait.h>

pid_t wait(int *wstatus);

功能
  • 等待子进程状态改变,但是这里我们研究的是子进程的退出
参数
  • @wstatus:用来保存子进程退出的状态值的
返回值
  • 成功返回 结束了的子进程的PID
  • 失败返回 -1
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐