Linux—进程控制

来源:互联网 发布:初中毕业可以学编程吗 编辑:程序博客网 时间:2024/06/06 04:01

1、system 函数 :在一个进程中执行另一个程序

//原型:#include<stdio.h>int system(const char* command);//,这里将要执行的命令作为参数传递给system函数,不需要自己来调用forkexec,和waitpid,也不需要自己来处理错误,处理信号,(但是调用system效率低)调用成功,返回相应命令的退出状态码,如果无法启动shall则返回127,发生其他错误时返回-1

system函数使用如下:

#include<stdio.h>#include<stdlib.h>int main(){  printf("Running ps whit system\n");  system("ps -ef");//   调用psm命令查看系统进程  printf("Done.\n");  return 0;}

通过验证得到:bash创建了system进程,system进程会创建出一个shell进程来执行最终的命令
这里写图片描述
2、exec家族(进程程序的替换)
用fork创建子进程后执行的是与父进程相同的程序(可能会执行不同的代码分支),子进程往往需要调用一种exec函数来执行另一个程序,当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的main函数 开始执行,调用exec函数并不创建新进程,所以调用exec前后该进程的id并没有发生改变

#include<unistd.h>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 execvp(const char* file,char* const argv[]);int execve(const char* path, char* const argv[],char* const envp[]);  //真正的系统调用//这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错则返回-1,所以exec函数只有出错的返回值,没有成功的返回值

函数基本用法:
这里写图片描述

实例程序:

#include<unistd.h>#include<stdlib.h>#include<stdio.h>int main(){  printf("Running ps with execlp\n");  execlp("ps","ps","ax","NULL");  //execl("/bin/ls","ls","-a","-l","-n",NULL);//char* _argv[]={"ls","-a","-l","-n",NULL};//execvp("ls",_argv);  printf("Done\n");  return 0;}//也可以用于子进程中

3、进程创建

#include<unistd.h> pid_t fork(void); //函数调用一次,成功时返回两次,子进程的返回值为0,父进程的返回值为子进程的pid,失败返回-1,并设置全局变量error#include <unistd.h>#include<sys/types.h>pid_t vfork(void);//与fork调用和返回值相同,不同在于vfork创建的子进程会堵塞父进程,它也并不是将父进程的地址空间完全复制到子进程中,vfork保证了子进程先运行,在子进程执行了exec函数之后应该调用_exit返回(不能调用return),父进程才会被调用 //在子进程中,如果使用return 返回,就意味着main函数结束了,因为栈是父进程共享的,所以程序的函数栈发生了变化,main函数return之后,通常会调用exit系函数,父进程收到子进程的exit之后,就会开始从vfork返回,但是这时候整个main函数的函数栈都已经不复存在了,所以父进程压根无法返回,

4、wait 与 waitpid 函数

#include<sys/types.h>#include<sys/wait.h>pid_t wait(int* status );//等待任意子进程的结束,status:输出参数,用于获取子进程的退出状态,如果调用者不关心,可设置为null;pid_t waitpid(pid_t pid,int* status,int options);//等待指定子进程的结束  pid :要等待的子进程的ID,值为-1时,称为等待任意进程,options:意为不阻塞调用者进程,常见选项为WNOHANG成功时返回子进程的pid,出错时返回-1,对于waitpid(),若指定了WNOHANG选项,但没有子进程终止,则返回0

调用wait与waitpid可能会发生的情况:
(1)、阻塞,如果其所有子进程都还在运行
(2)、如果一个子进程已经终止,正等待的父进程获取其终止状态,取得之后立即返回
(3)、出错立即返回(如果没有任何子进程)

#include<stdio.h>   //进程的非阻塞式等待#include<unistd.h>#include<stdlib.h>#include<sys/wait.h>int main(){    pid_t pid;    pid = fork();    if (pid < 0)    {        printf("%s  fork  error\n", _FUNCTION_);        return 1;    }    else if (pid == 0)//child    {        printf("chlid is run, pid is:%d\n", getpid());        sleep(5);        exit(1);    }    else    {        int status = 0;        pid_t ret = 0;        do        {         ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待         if(ret == 0)         {         printf("child is running\n");         }         sleep(1);         }while(ret == 0);         if (WIFEXITED(status) && ret == pid)        {            printf("wait child 5s success,child return code is:%d\n", WEXITSTATUS(status));        }        else        {            printf("wait child failed,return.\n");            return 1;        }    }    return 0;}
#include<stdio.h>  //进程阻塞式等待#include<unistd.h>#include<stdlib.h>#include<sys/wait.h>int main(){    pid_t pid;    pid = fork();    if (pid < 0)    {        printf("%s  fork  error\n", _FUNCTION_);        return 1;    }    else if (pid == 0)//child    {        printf("chlid is run, pid is:%d\n", getpid());        sleep(5);        exit(257);    }    else    {        int status = 0;        pid_t ret = waitpid(-1, &status, 0);//阻塞式等待,等待5秒        printf("this is test for wait\n");        if (WIFEXITED(status) && ret == pid)        {            printf("wait child 5s success,child return code is:%d\n", WEXITSTATUS(status));        }        else        {            printf("wait child failed,return.\n");            return 1;        }    }    return 0;}

模拟实现shall 功能

#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<sys/wait.h>#include<sys/types.h>int main(){    while (1)    {        printf("[myshall@name mn]# ");        fflush(stdout);//刷新缓冲区        char buf[1024];        size_t ret = read(0, buf, sizeof (buf)-1);//从标准输入键盘中获取字符串存放在buf中        if (ret > 0)        {            buf[ret - 1] = 0;//将最后一个‘\n’赋值为0;        }        char* _argv[32];//指针数组        char* start = buf;//start的指针指向获取的命令        _argv[0] = start;        int i = 1;        while (*start)        {            //字符串分解为命令存放在——argv[]中            if (*start == ' ')            {                *start = '\0';//一个命令结束                start++;                _argv[i++] = start;            }            else            {                start++;            }        }        _argv[i] = NULL;        pid_t id = fork();//创建子进程并调用execvp()函数        if (id == 0)        {                             //重定向>的实现             int j=0;            for(j=0;j<=i;++j)            {                if(_argv[j] =='>' )  //寻找重定向‘>’                {                char* file=_argv[j+1];  //重定向的文件名                _argv[j]=NULL;  //改变指针数组的有效大小 ,只执行j以前                 close(1);    //关闭标准输出显示器 文件描述符1             while(1)            {                 if(open(file,O_WRONLY|O_CREAT|O_TRUNC,0666) ==1) //打开重定向的文件,成功返回的文件描述符则为1                  break;            else              {                printf("open fail");              }            }            //程序替换             execvp(_argv[0],_argv);               exit(1);        }        else  //father   //父进程只等待子进程结束         {            pid_t ret=waitpid(id,NULL,0);        }    } }    return 0;}