使用进程创建、等待、终止等知识自主实现简单的Linuxshell命令行,Linux环境:CentOS7.3
fork()函数有两个返回值,它会给子进程返回0,给父进程返回子进程的pid,如果创建子进程失败,则会返回-1。
通过fork()创建的子进程与父进程会共享同一份代码,因为代码段的数据是只读的,不会发生写入,而当父进程与子进程的数据段不发生写入时,它们的数据段也是共享的,当子进程或父进程任何一方发生写入时,就会另外开辟自己的数据段,将原来的数据拷贝过去。
#include
#include
pid_t wait(int* status);
返回值:
成功返回则返回被等待进程的ID,失败返回0
参数:
获取子进程的退出状态,不关心则设置为NULL
pid_t waitpid(pid_t pid, int* status, int options);
返回值:
当waitpid收集到已退出的子进程时waitpid返回被等待进程的ID
如果设置了选项WNOHANG,而调用waitpid发现没有已退出的子进程可以被收集时,则返回0
参数说明:
pid:
pid=-1,等待任意一个子进程,与wait效果相同。
pid>0,等待进程ID与pid相等的子进程。
status:
WIFEXITED(status): status若为正常终止子进程返回的状态,则为真。
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程提出码。
options:
WNHANG:若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
#include
#include
#include
#include
#include
#define MAX_ARGV 20
#define MAX_CMD 1024
void cut_str(char* argv[],char cmd[])//将输入的命令分段,保存至argv[]指针数组内
{
char* ptr=cmd;
int i=0;
int flag=0;
while(*ptr!='\0')
{
if(*ptr!=' '&&flag==0)
{
argv[i++]=ptr;
flag=1;
}
else if(*ptr==' '&&flag==1)
{
*ptr='\0';
argv[i++]=ptr+1;
}
ptr+=1;
}
//去掉命令尾部的'\n'
ptr=argv[i-1];
while(*ptr!='\0')
{
ptr+=1;
}
*(ptr-1)='\0';
//以NULL结尾
argv[i]=NULL;
}
void new_pro(char* argv[])
{
pid_t id=fork();
if(-1==id)
{
perror("fork");
exit(-1);
}
else if(0==id)//子进程
{
execvp(argv[0],argv);
}
else//父进程
{
int st;//定义st用来保存子进程的返回信息
while(wait(&st)!=id);//使用while循环为了确保wait()收集到的子进程是上面所创建的子进程
}
}
int main()
{
char cmd[MAX_CMD]={'\0'};//定义数组来保存输入的内容
char* argv[MAX_ARGV];//定义一个数组来存放argv(execvp()函数的第二个参数)
while(1)
{
printf("[ahao@AHAOAHA ~ ]& ");//打印shell提示符
//此处不能使用scanf()函数来接受,因为scanf()在用“%s”格式输入字符时,输入的字符串中含有空白字符(space,tab,newline)。字符串读取结束
fgets(cmd,sizeof(cmd),stdin); //从标准输入来接受将要执行的命令
cut_str(argv,cmd);//将输入的命令切开,并将其的地址存入数组argv[],最终以NULL结尾
new_pro(argv);//新进程,在此函数内会创建一个子进程,在子进程运行的时候,父进程会等待子进程,待到子进程运行完毕,父进程才会继续向下运行
}
return 0;
}
#include
#include
#include
#include
void process_create(pid* pid, void func, void* arg)
{
*pid = fork();
typedef void (PF*)(void* arg);//重命名一个返回值为void,只有一个参数为void*函数指针PF,用来对回调函数强制转换
if(-1 == *pid)//创建子进程失败
{
exit(-1);
}
else if(0 == *pid)//子进程
{
if(NULL == func)//判断是否传递回调函数地址
{
printf("未传递回调函数地址!\n");
exit(0);
}
((PF)func)(arg);//执行func函数,先要进行强制类型转换
exit(0);
}
else//父进程
{
int st;
while(wait(&st) == *pid);
}
}
#include
int system(const char* command);
int system(const char *command)
{
struct sigaction sa_ignore, sa_intr, sa_quit;
sigset_t block_mask, orig_mask;
pid_t pid;
sigemptyset(&block_mask);
sigaddset(&block_mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &block_mask, &orig_mask); //1. block SIGCHLD
sa_ignore.sa_handler = SIG_IGN;
sa_ignore.sa_flags = 0;
sigemptyset(&sa_ignore.sa_mask);
sigaction(SIGINT, &sa_ignore, &sa_intr); //2. ignore SIGINT signal
sigaction(SIGQUIT, &sa_ignore, &sa_quit); //3. ignore SIGQUIT signal
switch((pid = fork()))
{
case -1:
return -1;
case 0:
sigaction(SIGINT, &sa_intr, NULL);
sigaction(SIGQUIT, &sa_quit, NULL);
sigprocmask(SIG_SETMASK, &orig_mask, NULL);
execl("/bin/sh", "sh", "-c", command, (char *) 0);
exit(127);
default:
while(waitpid(pid, NULL, 0) == -1) //4. wait child process exit
{
if(errno != EINTR)
{
break;
}
}
}
}
#include
FILE* popen(const char* command, const char* type);
int pclose(FILE* stream);
popen()也是执行shell命令并且通过管道和shell命令进行通信。
static pid_t *childpid = NULL;
/* ptr to array allocated at run-time */
static int maxfd; /* from our open_max(), {Prog openmax} */
#define SHELL "/bin/sh"
FILE *
popen(const char *cmdstring, const char *type)
{
int i, pfd[2];
pid_t pid;
FILE *fp;
/* only allow "r" or "w" */
if ((type[0] != 'r' && type[0] != 'w') || type[1] != 0) {
errno = EINVAL; /* required by POSIX.2 */
return(NULL);
}
if (childpid == NULL) { /* first time through */
/* allocate zeroed out array for child pids */
maxfd = open_max();
if ( (childpid = calloc(maxfd, sizeof(pid_t))) == NULL)
return(NULL);
}
if (pipe(pfd) < 0)
return(NULL); /* errno set by pipe() */
if ( (pid = fork()) < 0)
return(NULL); /* errno set by fork() */
else if (pid == 0) { /* child */
if (*type == 'r') {
close(pfd[0]);
if (pfd[1] != STDOUT_FILENO) {
dup2(pfd[1], STDOUT_FILENO);
close(pfd[1]);
}
} else {
close(pfd[1]);
if (pfd[0] != STDIN_FILENO) {
dup2(pfd[0], STDIN_FILENO);
close(pfd[0]);
}
}
/* close all descriptors in childpid[] */
for (i = 0; i < maxfd; i++)
if (childpid[ i ] > 0)
close(i);
execl(SHELL, "sh", "-c", cmdstring, (char *) 0);
_exit(127);
}
/* parent */
if (*type == 'r') {
close(pfd[1]);
if ( (fp = fdopen(pfd[0], type)) == NULL)
return(NULL);
} else {
close(pfd[0]);
if ( (fp = fdopen(pfd[1], type)) == NULL)
return(NULL);
}
childpid[fileno(fp)] = pid; /* remember child pid for this fd */
return(fp);
}