@1405010304geshuaishuai
2016-04-28T13:16:04.000000Z
字数 3940
阅读 589
Linux
进程管理
通过进程的创建、撤销和运行加深对进程概念和并发执行的理解,明确进程和程序之间的区别。
在Linux 中创建子进程要使用fork()函数,执行新的命令要使用exec()系列函数,等待子进程结束使用wait()函数,结束终止进程使用exit()函数。fork()原型如下:pid_t fork(void);
fork 建立一个子进程,父进程继续运行,子进程在同样的位置执行同样的程序。对于父进程,fork()返回子进程的pid,对于子进程,fork()返回0。出错时返回-1。exec 系列有6 个函数,原型如下:
extern char **environ;
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 *filename, char *const argv [], char *const envp[]);
int execvp( const char *file, char *const argv[]);
exec 系列函数用新的进程映象置换当前的进程映象.这些函数的第一个参数是待执行程序的路径名(文件名). 这些函数调用成功后不会返回,其进程的正文(text),数据(data),bss和栈(stack)段被待执行程序程序覆盖。但是进程的PID 和所有打开的文件描述符没有改变,同时悬挂信号被清除,信号重置为缺省行为.
在函数execl,execlp,和execle 中, const char *arg 以及省略号代表的参数可被视为arg0,
arg1, ...,argn.他们合起来描述了指向NULL 结尾的字符串的指针列表,即执行程序的参数列表.作为约定,第一个arg 参数应该指向执行程序名自身,参数列表必须用NULL 指针结束.execv和execvp函数提供指向NULL结尾的字符串的指针数组作为新程序的参数列表.作为约定,指针数组中第一个元素应该指向执行程序名自身.指针数组必须用NULL 指针结束。
execle 函数同时说明了执行进程的环境(environment),他在NULL指针后面要求一个附加参数,NULL 指针用于结束参数列表,或者说,argv数组.这个附加参数是指向NULL结尾的字符串的指针数组,他必须用NULL 指针结束。其他函数从当前进程的environ 外部变量中获取新进程的环境.
execlp和execvp可根据path搜索合适的程序运行,其他则需要给出程序全路径。
execve()类似execv(),但是加上了环境的处理。
wait(), waitpid()可用来等待子进程结束。函数原型:
#include <sys/wait.h>
pid_t wait(int *stat_loc);
pid_t waitpid(pid_tpid,int*stat_loc,intoptions);
当进程调wait,它将进入睡眠状直到有一个子进程结束。wait函数返回子进程的进程id,stat_loc中返回子进程的退出状态。waitpid 的第一个参数pid 的意义:pid > 0: 等待进程id 为pid 的子进程。pid==0:等待与自己同组的任意子进程。
pid == -1: 等待任意一个子进程
pid < -1: 等待进程组号为-pid 的任意子进程。
因此,wait(&stat)等价于waitpid(-1, &stat, 0),waitpid第三个参数option可以是0,WNOHAN,WUNTRACED 或这几者的组合。
(3)实现一个简单的shell(命令行解释器)
你的shell 类似于sh,bash,csh等,必须支持以下内部命令:
cd <目录>更改当前的工作目录到另一个<目录>。如果<目录>未指定,输出当前工作目录。如果<目录>不存在,应当有适当的错误信息提示。这个命令应该也能改变PWD 的环境变量。environ 列出所有环境变量字符串的设置(类似于Unix 系统下的env 命令)。
echo <内容 > 显示echo 后的内容且换行
help 简短概要的输出你的shell的使用方法和基本功能。
jobs 输出shell 当前的一系列子进程,必须提供子进程的命名和PID 号。
quit,exit,bye 退出shell。
#include<stdio.h>
#include<errno.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<pwd.h>
#define NUM 1024
#define SIZE 50
int mystrtok(char *argv[],char *string);
//这个函数做的是对字符串的分割,把命令分割开,用到了字符串的分割函数
int mystrtok(char *argv[],char *string)
{
int i=0;
char delim[]=" ";
char *p;
argv[0]=strtok(string,delim);//字符串分割函数的使用
//printf("%s\n",argv[0]);
while(argv[i]!=NULL)
{
argv[++i]=strtok(NULL,delim);
//printf("%s is %d\n",argv[i],i);
}
return 0;
}
//获得一个命令提示符的字符串
char *getusername(char buffer[NUM])
{
uid_t userid;
char *username;
char *hostname;
char *ptr,*p;
char buf1[1024],buf2[1024];
char *delim=".";
int id;
getwd(buf2);//这段代码说明的是对命令提示的路径获得
username=getenv("USER");//调用getenv函数,来获得shell命令的提示中的用户名
strcpy(buffer,username);
strcat(buffer,"@");
id=gethostname(buf1,sizeof buf1);//调用此函数来获取shell命令的提示中的主机名
p=strtok(buf1,delim);//对主机进行截取
strcat(buffer,p);//字符串的连接函数
ptr=strrchr(buf2,'/');//字符串的从后往前进行截取的函数,讲多得shell命令提示中的路径
//比较字符串,判断用户的路径是在"/","~",还是其他当中
if(strcmp(ptr,"/")==0)
{
ptr="/";
}
else if(strcmp(ptr,"~")==0)
{
ptr="~";
}
else
{
ptr=strtok(ptr,"/");
}
//把获得的 [用户名@主机名 路径]连接在一起
strcat(buffer," ");
strcat(buffer,ptr);
return buffer;
}
int main()
{
pid_t pid;
int status;
char *argv[NUM];
char buffer[NUM];
char str[NUM];
int j=-1;
char *string=NULL;
string=getenv("USER");
//当前用户是root用户还是普通用户,通过字符串的比较,如果是root,则返回0;
if((strcmp(string,"root"))==0)
{
j=0;
}
while(1){
//通过调用上面自定义的getusername()函数,来得到shell命令中的提示
printf("[%s]",getusername(buffer));
//判断是使用root的提示#,还是其他用户的$
if(j==0)
{
printf("# ");
}
else
{
printf("$ ");
}
//字符串的输入,要用gets函数或者用fgets函数,因为scanf函数把空格看成字符串的结束
gets(str);
//调用字符串的分割函数,获得要截取出来的命令和命令参数,例如:ls -a分别放到argv[0],argv[1]中
status=mystrtok(argv,str);
if(status!=0)
{
printf("fail to getargv!\n");
}
//通过fork()函数来创建一个父进程和一个子进程
pid=fork();
if(-1==pid)
{
printf("your fork failed!\n");
}
else if(pid==0)
{
//子进程用来对shell命令进程进程解释执行的
if(argv[1]!=NULL)
{
execvp(argv[0],argv);
}
else if(argv[1]==NULL)
{
execlp(argv[0],argv[0],NULL,NULL);
}
}
else
{
//父进程用来等待子进程的结束,然后再循环输入shell命令
if(wait(&status)==-1)
{
printf("wait failed!\n");
exit(1);
}
}
}
return 0;
}
实验截图如图5-1和5-2:
图 5-1
图 5-2
·了解Linux进程的创建
·了解子进程创建新任务的方法
·掌握了shell的编写
·对sehll的编写有了初步认识