进程的重要函数:
fork函数
了解fork函数
通过调用fork()函数,则会产生一个新的进程。调用fork()函数的进程叫做 父进程,产生的新进程则为子进程。
其编码过程:
1.函数功能:
函数头文件
#include <sys/types.h>
#include <unistd.h>
函数原型
pid_t fork(void);
函数功能
创建一个子进程
函数返回值
成功:返回给父进程是子进程的pid,返回给子进程的是0
失败:返回-1,并设置errno
2.代码实例:
// 创建一个子进程,并打印 Hello fork
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid = fork();
if(pid==-1)
{
perror("fork");
return -1;
}
printf("Hello fork.\n");
return 0;
}
fork创造的父子进程的特点
通过 fork() 函数创建子进程,有如下特点:
- 父子进程并发执行,子进程从 fork() 函数之后开始执行
- 父子进程的执行顺序由操作系统算法决定的,不是由程序本身决定
- 子进程会拷贝父进程地址空间的内容,包括缓冲区、文件描述符等
代码示例:
// 父子进程数据空间拷贝,缓冲区的拷贝
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
// 标准IO
write(1,"write hello.",12);
// 文件IO自带缓冲区
fputs("fputs hello.",stdout); // 注意没有换行符,stdout的缓冲区属于行
缓冲
pid_t pid = fork();
if(pid==-1)
{
perror("fork");
return -1;
}
printf("pid = %d,Hello fork.\n",getpid());
return 0;
}
运行结果:
1.write hello.fputs hello.pid = 4711,Hello fork.
2.fputs hello.pid = 4712,Hello fork.
创建多个进程
在创建多个进程时,最主要的原则是由父进程统一创建,统一管理,不能进行递归创建
代码公示列:
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
#include <stdlib.h>
//一定要创建子进程一定要在父进程中进行
int main(){
printf("hellow process\n");
pid_t pid1 = fork();//先创建第一个子进程
if(pid1 == 0)//如果创建返回为0,则为子进程,如果大于0,则为为父进程
{
printf("son1 start\n");
printf("son2 end\n");
}else if(pid1 == -1){
perror("创建失败");
exit(EXIT_FAILURE);
}else if(pid1 > 0){
pid_t pid2 = fork();//创建第二个子进程
if(pid2 == 0){
printf("son2 start\n");
printf("son2 end\n");
}else if(pid2 == -1){
perror("创建失败");
exit(EXIT_FAILURE);
}else{
printf("parent start\n");
printf("parent end\n");
}
}
}
题目结果:
wait函数和waitpid函数
wait函数:
函数功能及引用头文件:
1.函数头文件
#include <sys/types.h>
#include <sys/wait.h>
2.函数原型
pid_t wait(int *wstatus);
3.函数功能
让函数调用者进程进入到睡眠状态,等待子进程进入僵死状态后,释放相关资源并返回
4.函数参数
wstatus:保存子进程退出状态值变量的指针
*****获取具体值需要使用宏定义*****
5.函数返回值
wait(): on success, returns the process ID of the terminated child; on error,
-1 is returned.
成功:返回退出子进程的pid
失败:返回-1
2.函数代码示例(以下拿c++代码来实现):
using namespace std;
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
pid_t pid = fork();
if(pid == -1){
cout << "创建失败" << endl;
exit(EXIT_FAILURE);
}else if(pid == 0){
cout << "son start" << endl;
sleep(2);
cout << "son end" << endl;
exit(1000);
}else if(pid > 0){
int wat = 0;
wait(&wat);
}
return 0;
}
运行结果:
由此可得等待功能是多么强大,它能让父进程休眠,等子进程完成后,父进程才开始,但waitpid更强大
waitpid函数
函数功能及引用头文件:
waitpid函数的功能与wait函数一样,但比wait()函数功能更强大,可以理解成 wait() 底层调用waitpid()函数
1.函数头文件
#include <sys/types.h>
#include <sys/wait.h>
2.函数原型
pid_t waitpid(pid_t pid, int *wstatus, int options);
3.函数参数
pid:进程id
-1 可以等待任意子进程
>0 等待id为pid的进程
wstatus:保存子进程退出状态值的指针
options:选项
WNOHANG 非阻塞选项
0 阻塞式与wait等同
4.函数返回值
成功
>0 退出进程的pid
=0 在非阻塞模式下,没有进程退出
失败
-1,并设置errno
函数代码:
using namespace std;
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
pid_t pid = fork();
if(pid == -1){
cout << "创建失败" << endl;
exit(EXIT_FAILURE);
}else if(pid == 0){
cout << "son start" << endl;
sleep(3);
cout << "son end" << endl;
exit(1000);
}else if(pid > 0){
pid_t pid1 = fork();
if(pid1 == 0){
cout << "son1 start" << endl;
sleep(3);
cout << "son2 end" << endl;
exit(50);
}else if(pid1 == -1){
cout << "创建pid1失败" << endl;
}else if(pid1 > 0){
int cpid = 0;
//waitpid(-1,NULL,0);//这是waitpid用的最多的形式
while((pid = waitpid(-1,NULL,0)) != -1);//此方式可以阻挡多线程
cout << "parent start" << endl;
cout << "parent end" << endl;
}
}
return 0;
}
重点! 重点! 重点!:
int cpid = 0;
while((cpid=waitpid(-1,&status,WNOHANG))==0);//这种方式可以阻绝任意进程是必须要掌握等待函数
与进程替换有关的函数(重点)
应用场景:
- Linux 终端应用程序,在执行命令时,通过创建一个进程,然后替换成对应命令的可执行程序,在执行。
exec函数族(重点) :
int execl(const char *pathname, const char *arg, .../* (char *) NULL */);
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
int execle(const char *pathname, const char *arg, .../*, (char *) NULL, char
*const envp[] */);
int execv(const char *pathname, char *const argv[]);//最简单,最方便
int execvp(const char *file, char *const argv[]);//最简单,最方便
int execvpe(const char *file, char *const argv[],char *const envp[]);
参数说明:
path:可执行文件的路径名
file:可执行文件名,可以通过PATH环境变量指定的路径
arg:参数列表,以NULL结尾
argv[]:参数数组
envp[]:环境变量数组
返回值:
The exec() functions return only if an error has occurred. The return value
is -1, and errno is set to indicate the error.
其中一个示例代码(c语言):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define SIZE 10
int main(){
char* param[SIZE];
char command[128] = { 0 };
int pipefd[2];
int ret = pipe(pipefd);
if(ret == -1){
perror("pipe");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if(pid == -1){
perror("pid");
exit(EXIT_FAILURE);
}else if(pid == 0){
close(pipefd[1]);
char buf[128] = { 0 };
size_t rtype = read(pipefd[0],buf,sizeof(buf));
printf("read buf:%s\n",buf);
if(rtype == 0){
printf("1\n");
close(pipefd[0]);
exit(EXIT_SUCCESS);
}else if(rtype == -1){
perror("rtype");
close(pipefd[0]);
exit(EXIT_FAILURE);
}
int index = 0;
//用strtok函数将命令分割开装入数组中,以便进行进程替换
char* cmd_name = strtok(buf," ");
param[index] = cmd_name;
index++;
char* ret = NULL;
while((ret = strtok(NULL," ")) != NULL){
param[index] = ret;
index++;
}
param[index] = NULL;
int ret_value = execvp(cmd_name,param);//这是exec族的一个函数用来进行进行替换
if(ret_value == -1){
perror("ret_value");
exit(EXIT_FAILURE);
}
close(pipefd[0]);
}else{
close(pipefd[0]);
memset(command,0,sizeof(command));
printf("请输入你要实现的命令:\n");
fgets(command,sizeof(command),stdin);
command[strlen(command) - 1] = '\0';
if(strcmp(command,"quit") == 0){
printf("master\n");
exit(EXIT_SUCCESS);
}
printf("command=%s\n",command);
size_t wtype = write(pipefd[1],command,strlen(command));
printf("wtype=%ld\n",wtype);
if(wtype == -1){
perror("wtype");
close(pipefd[1]);
exit(EXIT_FAILURE);
}
sleep(1);
close(pipefd[1]);
waitpid(-1,NULL,0);
}
}
所以exec族的函数可以进行进程替换,让系统响应: