当前位置: 首页> 汽车> 车展 > 杭州seo网站推广软件_郑州软件职业技术学院官网_广西seo搜索引擎优化_内容营销策略有哪些

杭州seo网站推广软件_郑州软件职业技术学院官网_广西seo搜索引擎优化_内容营销策略有哪些

时间:2025/7/13 4:33:58来源:https://blog.csdn.net/2303_80187476/article/details/146507232 浏览次数: 1次
杭州seo网站推广软件_郑州软件职业技术学院官网_广西seo搜索引擎优化_内容营销策略有哪些

1.进程创建

fork函数

pid_t fork()函数就从已存在进程中创建一个进程,新进程为子进程,而原进程就为父进程。
头文件:#include <sys/types.h> #include <unistd.h>
返回值:子进程就返回0,父进程返回当前子进程id,出错返回-1

进程调用 fork ,当控制块转移到内核中 fork 代码后,内核做:

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程的部分数据结构内容拷贝给子进程
  • 添加子进程到进程的系统进程
  • fork返回,父子进程分别从 fork() 之后的代码开始执行,调度器开始调度进程
    在这里插入图片描述
    在这里插入图片描述

写时拷贝

通常,父子代码是共享的,父子不再写入时候,数据也是共享的,进程共享父进程的内存空间,但内核会标记为 只读 ,当任意一方试图写入,内核会为进程复制一份私有副本,防止进程之间相互影响
在这里插入图片描述
因为有了写时拷贝技术的存在,父子进程就可以数据就可以·分离开来,保证了进程之间的独立性
写时拷贝,是一种延时申请内存的技术,可以提高内存的利用率

fork常规用法

  • 父进程创建子进程,父子进程执行不同的代码段,例如:父进程等待客户端请求,子进程来处理请求或者用子进程来执行另一个不同的程序,子进程通过exec()进程替换来执行另一个任务

fork调用失败原因

  • 系统中有太多进程,而进程条目的数量是有限的,就会导致创建子进程失败
  • 实际用户的进程数受到了限制

fork() 行为总结

行为父进程子进程
fork() 返回值子进程 PID(>0)0
执行起点fork() 后的下一行代码fork() 后的下一行代码
内存修改触发写时拷贝(COW)触发写时拷贝(COW)
典型用途管理子进程执行新任务

(注意:父子进程两个执行流谁先执行完取决于调度器)

2.进程终止

进程终止的本质是释放系统资源,就是释放进程申请的相关内核结构和对应的代码和数据

进程退出场景

  1. 代码运行完毕,正确退出(从main返回,return 0)
  2. 代码运行完毕,异常退出 (exit(1))
  3. 代码异常终止·(ctrl + c)

退出码

我们通过 退出码 得到最近一次执行程序的退出状态,来了解程序是否正常退出
Linux 退出码:
在这里插入图片描述
我们可以通过strerror函数来查看对应退出码的描述

进程退出码查看

Linux 进程退出我们可以通过echo $?来查看进程退出码。
比如我正确执行一条指令,它的退出码就为0(Linux中执行指令都是通过bash创建子进程来执行的)
在这里插入图片描述

进程退出方法

  1. _exit函数
    _exit 函数是系统调用函数
#include <unistd.h>
void _exit(int status);

参数:status 定义了进程的终⽌状态,⽗进程通过wait来获取该值

  1. exit函数
    exit最后也会调⽤_exit, 但在调⽤_exit之前,还做了其他工作:
  • 执行用户通过atexiton_exit定义的清理函数。
#include <stdlib.h>
int on_exit(void (*function)(int , void *), void *arg);
#include <stdio.h>
#include <stdlib.h>void cleanup(int status, void *msg) {printf("Exit status: %d, Message: %s\n", status, (char *)msg);
}int main() {on_exit(cleanup, (void *)"Bye!");  // 注册带参数的清理函数printf("Main function\n");exit(42);  // 退出状态 42
}

输出:

Main function
Exit status: 42, Message: Bye!
  • 关闭所有打开的流,所有缓存数据均被写入
  • 调用_exit
    在这里插入图片描述
    exit和_exit函数的区别:exit是对_exit系统调用封装的库函数,它就可以刷新用户缓冲区数据,也就是fflush函数刷新用户缓冲区数据到内核缓冲区中,并且还会调用atexit和on_exit清理函数
int main()
{printf("hello");exit(0);
}
运⾏结果:
[root@localhost linux]# ./a.out
hello[root@localhost linux]#
int main()
{printf("hello");_exit(0);
}
运⾏结果:
[root@localhost linux]# ./a.out
[root@localhost linux]#
  1. return返回
    return 是一种常见的退出进程方法,return n 等同于执行 exit(n),因为调用 main 的运行函数会将 main 的返回值当 exit 的参数。

3.进程等待

僵尸进程

僵尸进程就是已经终止但未被父进程回收的进程。

进程等待

如果想要对僵尸进程进行处理,就需要父进程父进程回收子进程的方式来解决子进程的僵尸状态。

进程等待方法

wait
  • 函数方法:
    #include<sys/types.h>
    #include<sys/wait.h>
    pid_t wait(int* status);
    参数: status:保存子进程退出状态(需用宏解析,如 WEXITSTATUS)。
    返回值: 成功:返回终止的子进程 PID。
    失败:返回 -1(如无子进程)。
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>int main() {pid_t pid = fork();if (pid == 0) {// 子进程printf("Child PID: %d\n", getpid());_exit(42);  // 子进程退出码 42} else {// 父进程int status;pid_t child_pid = wait(&status);  // 阻塞等待printf("Child %d exited with status: %d\n", child_pid, WEXITSTATUS(status));}return 0;
}

输出:

Child PID: 1234
Child 1234 exited with status: 42
waitpid
  1. 函数方法:
    pid_ t waitpid(pid_t pid, int *status, int options);
    返回值:
    当正常返回的时候waitpid返回收集到的⼦进程的进程ID;
    如果设置了选项WNOHANG,⽽调⽤中waitpid发现没有已退出的⼦进程可收集,则返回0;
    如果调⽤中出错,则返回-1,这时errno会被设置成相应的值以指⽰错误所在;
    参数:
    pid:
    Pid=-1: 等待任⼀个⼦进程。与wait等效。
    Pid>0: 等待其进程ID与pid相等的⼦进程。
    status: 输出型参数
    WIFEXITED(status): 若为正常终⽌⼦进程返回的状态,则为真。(查看进程是否是正常退出)
    WEXITSTATUS(status): 若WIFEXITED⾮零,提取⼦进程退出码。(查看进程的退出码)
    options: 默认为0,表⽰阻塞等待
    WNOHANG: 若pid指定的⼦进程没有结束,则waitpid()函数返回0,不予以等
    待。若正常结束,则返回该⼦进程的ID。

等待子进程:

pid_t child_pid = fork();if (child_pid == 0) 
{// 子进程_exit(10);
} 
else 
{int status;// 只等待 child_pid 的子进程waitpid(child_pid, &status, 0);printf("Child %d exited: %d\n", child_pid, WEXITSTATUS(status));
}

非阻塞轮询:

int status;
pid_t pid = waitpid(-1, &status, WNOHANG);if (pid > 0)
{printf("Child %d exited.\n", pid);
}
else if (pid == 0)
{printf("No child exited yet.\n");
} 
else
{perror("waitpid failed");
}
获取子进程status
  1. wait和waitpid,都有⼀个status参数,该参数是⼀个输出型参数,由操作系统填充。

  2. 如果传递NULL,表⽰不关心⼦进程的退出状态信息。

  3. 否则,操作系统会根据该参数,将子进程的退出信息反馈给传递父进程

status 可以当做一个位图来看,其具体实现细节如下图:
在这里插入图片描述

退出状态和终止信号以及core dump表示方式

1.正常终止
退出状态:(status >> 8)&0xFF
2. 被信号所杀
退出信号:status&0x7F
core dump(核心转储):(status>>7)&1

进程替换

替换原理

fork 创建的子进程和父进程执行的是同一个程序,如果子进程想要执行另一个程序,往往需要调用 exec 函数来替换当前程序。当进程调用 exec 程序后,进程代码和数据就完全被新进程替换,从新程序的启动例程开始执行,并且执行完新进程后并不会返回之前子进程执行的代码继续执行,而是直接退出,并且调用exec并不会创建新进程,所以调用exec前后该进程的id并没有改变
​返回值处理: 所有函数调用成功时不返回,失败时返回-1并设置errno。

替换函数

其中有六个exec开头的函数,统称exec函数:

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 execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
函数名参数传递方式路径处理环境变量处理是否系统调用其他特点
execl可变参数列表需完整路径(如/bin/ls继承当前环境变量参数以 NULL 结尾(如 "ls", "-l", NULL
execlp可变参数列表PATH 搜索文件名继承当前环境变量自动搜索可执行文件路径(如 execlp("ls", ...)
execle可变参数列表需完整路径自定义 envp 数组参数列表后需显式传递 envp(如 execle(..., envp)))
execv参数数组 argv[]需完整路径继承当前环境变量参数数组需以 NULL 结尾(如 char *argv[] = {"ls", "-l", NULL}))
execvp参数数组 argv[]PATH 搜索文件名继承当前环境变量结合路径搜索与数组传参(如 execvp("ls", argv))
execve参数数组 argv[]需完整路径自定义 envp 数组唯一直接调用内核的系统调用(其他函数均封装它)

使用方法:

  1. execl
    特点:参数列表形式传参、需完整路径、继承环境变量
#include <unistd.h>
#include <stdio.h>int main() {printf("execl调用示例\n");// 执行/bin/ls,参数列表需以NULL结尾if (execl("/bin/ls", "ls", "-l", NULL) == -1) {perror("execl失败");}return 0;
}
  1. execlp
    特点:参数列表传参、自动搜索PATH环境变量
#include <unistd.h>
#include <stdio.h>int main() {printf("execlp调用示例\n");// 自动搜索PATH中的"ls"可执行文件if (execlp("ls", "ls", "-l", NULL) == -1) {perror("execlp失败");}return 0;
}
  1. execle
    特点:参数列表传参、需完整路径、自定义环境变量
#include <unistd.h>
#include <stdio.h>int main() {char *envp[] = {"CUSTOM_ENV=test", "PATH=/bin", NULL};printf("execle调用示例\n");// 传递自定义环境变量envpif (execle("/bin/ls", "ls", "-l", NULL, envp) == -1) {perror("execle失败");}return 0;
}
  1. execv
    特点:参数数组传参、需完整路径、继承环境变量
#include <unistd.h>
#include <stdio.h>int main() {char *argv[] = {"ls", "-l", NULL};printf("execv调用示例\n");if (execv("/bin/ls", argv) == -1) {perror("execv失败");}return 0;
}
  1. execvp
    特点:参数数组传参、自动搜索PATH环境变量
#include <unistd.h>
#include <stdio.h>int main() {char *argv[] = {"ls", "-l", NULL};printf("execvp调用示例\n");if (execvp("ls", argv) == -1) {perror("execvp失败");}return 0;
}
  1. execve
    特点:参数数组传参、需完整路径、自定义环境变量、唯一系统调用
#include <unistd.h>
#include <stdio.h>int main() {char *argv[] = {"ls", "-l", NULL};char *envp[] = {"CUSTOM_ENV=test", "PATH=/bin", NULL};printf("execve调用示例\n");if (execve("/bin/ls", argv, envp) == -1) {perror("execve失败");}return 0;
}
总结

总结下来就是 是否需要完整路径、自定义环境变量,以及参数是列表还是数组

这些函数原型看起来很容易混,但只要掌握了规律就很好记。

  1. l(list) : 表示参数采用列表
  2. v(vector) : 参数用数组
  3. p(path) : 有p⾃动搜索环境变量PATH
  4. e(env) : 表示自己维护环境变量
    在这里插入图片描述

4,简单shell的实现

**前言:**我们在命令行执行命令时都是由 bash 创建子进程,然后由子进程 exec进程替换 执行对应命令。
在这里插入图片描述
shell脚本的流程:

  1. 获取命令行
  2. 解析命令行
  3. fork()创建子进程
  4. 替换子进程
  5. 父进程等待子进程退出

源码实现:

#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
using namespace std;const int basesize = 1024;
const int gnum = 64;//环境变量表和命令行参数表的大小
char* genv[gnum];
char* gargv[gnum];
char buff[basesize];//辅助数组,存储输入的命令
char pwd[basesize];
char pwdenv[basesize];
int gargc = 0;
int lastcode = 0;string getUsername()
{string username = getenv("USER");return username == "" ? "None" : username;
}
string getHostname()
{char hostname[20];int n = gethostname(hostname,sizeof(hostname));if(n < 0){perror("gethostname");exit(1);}return hostname;
}
string GetPwd()
{if(getcwd(pwd,sizeof(pwd)) == nullptr) {return "Node";}snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);for(int i = 0;genv[i];i++){string str = genv[i];char* before = (char*)str.substr(0,3).c_str();if(strcmp(before,"PWD") == 0){strncpy(genv[i],pwdenv,strlen(pwdenv));genv[i][strlen(pwdenv)] = 0;return pwd;}}}string Lastdir()
{string cur = GetPwd();if(cur == "/" || cur == "None")return cur;size_t pos = cur.rfind('/');if(pos == std::string::npos) return cur;return cur.substr(pos + 1);
}void PrintCommand()
{string username = getUsername();string hostname = getHostname();string pwd  = Lastdir();if(username != "None" && hostname != "None" && pwd != "None"){cout << username << "@" << hostname << ":" << pwd << "$";fflush(stdout);}else exit(1);
}
bool GetCommand()
{memset(buff,0,sizeof buff);char* result = fgets(buff,basesize,stdin);if(result == nullptr){return false;}//cout << result << endl;buff[strlen(buff) - 1] = 0;if(strlen(buff) == 0) return false;return true;
}
void ParseCommand()
{const char* sep = " ";gargc = 0;gargv[gargc++] = strtok(buff,sep);while((bool)(gargv[gargc++] = strtok(nullptr,sep)));//cout << gargc << endl;gargv[gargc] = nullptr;gargc--;
}
bool ExecuteCommand()
{pid_t id = fork();if(id < 0) return false;if(id == 0){//子进程// 1. 执行命令cout << "gragvp[0] = " << gargv[0] << endl;int ret = execvpe(gargv[0], gargv, genv);//char* argv[] = {//    "ls",//    "-l",//    "-a",//    nullptr// };//int ret = execvpe("ls",gargv,genv);cout << errno << endl;lastcode  = 1;// 2. 退出exit(1);}int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){lastcode = WEXITSTATUS(status);return true;}lastcode = 100;return false;//pid_t id = fork();//if(id == 0)//{//    cout << gargv[0] << endl;//    int ret = execvpe(gargv[0],gargv,genv);//    if(ret == -1)//    {//        cout << errno << endl;//        return false;//    }//    return true;//}//int status;//pid_t wid = waitpid(id,&status,0);//if(wid > 0)//{//    cout << "等待子进程成功" << endl;//    return true;//}//return false;}
void AddEnv()
{int index = 0;while(genv[index]){index++;}genv[index] = (char*)malloc(strlen(gargv[1] + 1));strncpy(genv[index],gargv[1],strlen(gargv[1]) + 1);genv[++index] = nullptr;}
bool CheckCommand()
{if(strcmp(gargv[0],"cd") == 0){if(gargc == 2){chdir(gargv[1]);GetPwd();lastcode = 0;}else{lastcode = 2;}return true;}else if(strcmp(gargv[0],"echo") == 0){if(gargc == 2){if(gargv[1][0] == '$'){if(gargv[1][1] == '?'){ printf("%d\n",lastcode);lastcode = 0;}}else {printf("%s\n",gargv[1]);lastcode = 0;}}else lastcode = 3;return true;}else if(strcmp(gargv[0],"env") == 0){for(int i = 0;genv[i];i++)cout << genv[i] << endl;lastcode = 0;return true;}else if(strcmp(gargv[0],"export") == 0){if(gargc == 2){AddEnv();lastcode = 0;return true;}else lastcode = 4;return true;}return false;}void debug()
{for(int i = 0;genv[i];i++){cout << genv[i] << endl;}cout << "//" << endl;for(int i = 0;gargv[i];i++){cout << gargv[i] << endl;}
}
void Initenv()
{extern char** environ;int index = 0;while(environ[index]){genv[index] = (char*)malloc(strlen(environ[index] + 1));strncpy(genv[index],environ[index],strlen(environ[index]));index++;}genv[index] = nullptr;
}
int main()
{Initenv();while(1){PrintCommand();//打印命令提示符//sleep(10);bool ret = GetCommand();//从标准输入获取命令if(ret = true)ParseCommand();//分析命令else continue;// cout << endl;// debug();if(CheckCommand()){continue;}ExecuteCommand();//处理命令}return 0;
}
关键字:杭州seo网站推广软件_郑州软件职业技术学院官网_广西seo搜索引擎优化_内容营销策略有哪些

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: