做网站推广运营前景,公司营销型网站公司,深圳网站建设seo推广优化,青白江建设网站目录
预备知识
fork
进程等待
wait
waitpid
环境变量
概念
分类
常见的环境变量及其用途
环境变量的查看与设置
exec系列 函数解释
命名理解
简单shell 预备知识
fork
fork 是 Linux 和许多其他类 Unix 系统中的一个重要系统调用#xff0c;它用于创建一个新的…目录
预备知识
fork
进程等待
wait
waitpid
环境变量
概念
分类
常见的环境变量及其用途
环境变量的查看与设置
exec系列 函数解释
命名理解
简单shell 预备知识
fork
fork 是 Linux 和许多其他类 Unix 系统中的一个重要系统调用它用于创建一个新的进程这个新进程是当前进程的复制品称为子进程。子进程会获得父进程当前状态的副本包括父进程的内存布局、环境变量、打开的文件描述符等。但是子进程和父进程是两个独立的执行线程它们可以独立地运行程序的不同部分。
以下是关于 fork 的一些关键点
复制子进程是父进程的一个副本。但是这种复制是“写时复制”Copy-On-Write, COW的。这意味着如果子进程和父进程都不修改它们共享的内存区域那么这些区域实际上不会被复制从而节省内存和 CPU 时间。返回值fork 系统调用在父进程中返回新创建的子进程的进程 IDPID而在子进程中返回 0。这是区分父进程和子进程的一个常用方法。资源分配子进程会获得其父进程所拥有资源的副本如文件描述符、环境变量等。但是这些资源并不一定是物理上独立的它们通常是通过引用计数或其他机制来共享的。并发由于子进程和父进程是两个独立的执行线程因此它们可以并发地执行不同的任务。这使得 fork 成为实现多进程并发和并行处理的重要工具。进程间通信由于子进程和父进程是独立的因此它们之间需要进行通信以共享数据或协调行为。这可以通过各种进程间通信IPC机制来实现如管道、消息队列、信号量、共享内存等。终止和等待当子进程终止时它会留下一个僵尸进程Zombie Process直到其父进程调用 wait 或 waitpid 系统调用来获取其终止状态并释放其占用的资源。
需要注意的是虽然 fork 是一个强大的工具但它也有一些限制和开销。例如由于需要复制父进程的状态因此 fork 的开销通常比创建线程要大。此外由于子进程和父进程是独立的因此它们之间的通信和同步也需要额外的开销和复杂性。因此在选择使用 fork 还是其他并发机制如线程或协程时需要根据具体的应用场景和需求进行权衡。
进程等待
wait
功能wait函数用于使父进程等待其任意一个子进程结束并回收该子进程的资源。当子进程结束时内核会向父进程发送SIGCHLD信号父进程可以通过wait函数来响应这个信号并获取子进程的状态。函数原型pid_t wait(int *wstatus); 参数wstatus是一个指向整数的指针用于存储子进程的退出状态。如果父进程不关心子进程的退出状态可以将此参数设为NULL。返回值是已结束子进程的进程IDPID如果出现错误则返回-1。特点wait函数会阻塞父进程的执行直到有一个子进程结束。如果调用wait时子进程已经结束则wait会立即返回子进程的结束状态值。
waitpid
函数原型#include #include pid_t wait(int*status); 返回值 成功返回被等待进程pid失败返回-1返回值 当正常返回的时候waitpid返回收集到的子进程的进程ID如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在参数 pid Pid-1,等待任一个子进程。与wait等效。Pid0.等待其进程ID与pid相等的子进程。status: WIFEXITED(status): 若为正常终止子进程返回的状态则为真。查看进程是否是正常退出WEXITSTATUS(status): 若WIFEXITED非零提取子进程退出码。查看进程的退出码options: WNOHANG: 若pid指定的子进程没有结束则waitpid()函数返回0不予以等待。若正常结束则返回该子进 程的ID。
环境变量
概念
定义环境变量是系统预定义的参数相当于全局变量存在于所有的shell中具有继承性。它们可存储有关shell会话和工作环境的信息。作用环境变量允许在内存中存储数据以便运行在shell上的程序和脚本访问。这些数据可以用来识别用户、系统、Shell的特性以及任何其它需要存储的数据。
分类
按作用范围分 系统环境变量公共的对全部的用户都生效。用户环境变量用户私有的、自定义的个性化设置只对该用户生效。按生存周期分 永久环境变量在环境变量脚本文件中配置用户每次登录时会自动执行这些脚本相当于永久生效。临时环境变量使用时在Shell中临时定义退出Shell后失效。
常见的环境变量及其用途
PATH指定命令的搜索路径由冒号分隔的目录列表用于告诉系统在哪里可以找到可执行文件。HOME指定用户的主工作目录即用户登录到Linux系统时默认的目录。SHELL当前Shell的路径通常指向用户当前使用的shell解析器如/bin/bash。LD_LIBRARY_PATHC/C语言动态链接库文件搜索的目录对C/C程序员来说非常重要。CLASSPATHJAVA语言库文件搜索的目录对JAVA程序员来说非常重要。
环境变量的查看与设置
查看 使用env命令可以查看当前用户全部的环境变量。使用echo $环境变量名可以查看特定环境变量的值。设置 使用export命令可以设置新的环境变量或修改现有环境变量的值。例如export PATH$PATH:/new/directory。如果希望环境变量永久生效需要在登录脚本文件中如/etc/profile、/etc/profile.d/、~/.bash_profile、~/.bashrc等进行配置
exec系列 #include unistd.h int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); nt execle(const char *path, const char *arg, ...,char *const envp[]); int execv(const char *path, char *const argv[]); i nt execvp(const char *file, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]); 这些函数的主要目的是在当前进程的上下文中执行一个新程序从而替换当前进程的映像。它们允许程序在运行时加载新的可执行文件实现动态性和灵活性。 函数解释
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。如果调用出错则返回-1所以exec函数只有出错的返回值而没有成功的返回值
命名理解
l(list) : 表示参数采用列表 v(vector) : 参数用数组p(path) : 有p自动搜索环境变量PATHe(env) : 表示自己维护环境变量
简单shell
#includestdio.h
#includestdlib.h
#includestring.h
#includectype.h
#includeunistd.h
#includefcntl.h
#includeerrno.h
#includesys/wait.h
#define SIZE 512
#define ZERO \0
#define SEP
#define NUM 32
#define SkipPath(p) do{pstrlen(p)-1;while(*p!/)p--;}while(0)
#define SkipSpace(cmd,pos) do{ \while(1){\if(isspace(cmd[pos]))\pos;\else break;\}}while(0)
#define none_type 0
#define in_type 1
#define out_type 2
#define app_type 3
int redir_typenone_type;
char* filenameNULL;char* gArgv[NUM];
int lastcode0;
void Die()
{exit(1);
}
const char* GetHostname()
{const char* hostnamegetenv(HOSTNAME);if(hostnameNULL)return /;return hostname;
}
const char* GetUsername()
{const char* usergetenv(USER);if(userNULL)return none;return user;
}
const char* Getcwd()
{const char* cwdgetenv(PWD);if(cwdNULL) return none;return cwd;
}
void MakeCommmandLine()
{char line[SIZE];const char* hostnameGetHostname();const char* usernameGetUsername();const char* cwdGetcwd();SkipPath(cwd);snprintf(line,sizeof(line),[%s%s %s] ,username,hostname,strlen(cwd)1?/:cwd1);printf(%s,line);fflush(stdout);
}
int GetUserCommad(char commad[],size_t n)
{char* sfgets(commad,n,stdin);if(sNULL)return -1;commad[strlen(commad)-1]ZERO;return strlen(commad);}
void CheckRedir(char cmd[])
{int pos0;int endstrlen(cmd);while(posend){if(cmd[pos]){if(cmd[pos1]){cmd[pos]0;pos;redir_typeapp_type;SkipSpace(cmd,pos);filenamecmdpos;}else {cmd[pos]0;redir_typeout_type;SkipSpace(cmd,pos);filenamecmdpos; }}else if(cmd[pos]){cmd[pos]0;redir_typein_type;SkipSpace(cmd,pos);filenamecmdpos;}else{pos;}}
}
void SplitCommad(char command[])
{gArgv[0]strtok(command,SEP);int index1;while((gArgv[index]strtok(NULL,SEP)));
}
void ExecuteCommand()
{pid_t idfork();if(id0)Die();else if (id0){//子进程if(filename!NULL){if(redir_typein_type){int fdopen(filename,O_RDONLY);dup2(fd,0);}else if (redir_typeout_type){int fd open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);dup2(fd, 1); }else if (redir_typeapp_type){int fd open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);dup2(fd, 1);}}execvp(gArgv[0],gArgv);exit(errno);}else{//父进程int status 0;pid_t rid waitpid(id, status, 0);if(rid 0){lastcode WEXITSTATUS(status);if(lastcode ! 0) printf(%s:%s:%d\n, gArgv[0], strerror(lastcode), lastcode);} }
}
int main()
{int quit0;while(!quit){redir_typenone_type;filenameNULL;//创建一个自己的命令行MakeCommmandLine();//获取用户命令char usercommad[SIZE];int nGetUserCommad(usercommad,sizeof(usercommad));if(n0)return 1;CheckRedir(usercommad);//测试一下// printf(cmd: %s\n, usercommad);// printf(redir: %d\n, redir_type);// printf(filename: %s\n, filename);//分割字符串SplitCommad(usercommad);//执行命令ExecuteCommand();}return 0;
}