Linux下进程控制

Linux下进程控制函数

重要的进程函数

获取进程ID

#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void) //返回调用进程的ID
pid_t getppid(void) //返回父进程的ID

创建与终止进程

创建进程
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void)

fork出来的进程会获得与父进程用户级虚拟地址空间相同(但是独立)的一份副本,但是PID不同。
fork是一个有趣但令人迷惑的函数,它只被调用一次,但是会返回两次,一次在父进程中,一次在子进程中。在父进程中会返回子进程的pid(非零),在子进程中会返回0,因此可以以此来区分是程序是在子进程中执行还是在父进程中执行。

int main(){
	pid_t pid;
	int x = 1;
	pid = Fork();//fork的包装函数
	if(pid == 0){ /*Child*/
		printf("child : x=%d \n",++x);
		exit(0);
	}
	
	/*Parent*/
	printf("parent:x=%d\n",--x);
	exit(0);
}

获得输出:

parent:x=0 
child:x=2 

从结果看出父子进程并不共享数据x,但是共享文件,因为都把输出打到了标志输出stdout上。

终止进程
#include <stdlib.h>
void exit(int status);

回收子进程

僵死进程(zombie process)
终止但未被回收的进程被称为僵尸进程。当一个进程由于某中原因被终止时,内核并不会立刻把它从系统中清除,它会被保持在一种已终止的状态,直到被它的父进程回收。
如果一个僵死进程在被回收之前,父进程就已经结束,那么内核会安排init进程来回收,init进程的pid为1,是所有进程的祖先。
一个进程可以通过调用waitpid函数来等待它的子进程终止:

#include <sys/type.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid,int *statusp,int options)
options参数

waitpid中options参数对行为的影响:

options行为
0(默认)挂起调用线程,若此时pid>0,则等待对应pid的子进程结束,若此时pid=-1,则等待任一子进程结束
WNOHANG不等待子进程结束,立即返回,若此时返回值为0,表示没有子进程结束
WUNTRACED挂起调用线程,直到任一子线程终止或者被停止,注意与默认值0的区别为,默认值只返回正常终止的子进程
WCONTINUED挂起调用线程,直到任一子线程终止,或者任一一个被停止的子线程收到SIGCONT信号重新开始执行

注意options的参数可以进行组合,比如WNOHANG | WUNTRACED。

status参数
status值含义
WIFEXITED(status)如果子进程通过exit或者return返回,则为真
WEXITSTATUS(status)返回一个正常终止的子进程的退出状态,只有在WIFEXITED(status)为真时,才会定义这个状态
WIFSIGNALED(status)如果子进程是因为一个未被捕获的信号终止的,那么就返回真
WTERMSIG(status)返回导致子进程终止的信号编号,只有当WIFSIGNALED(status)为真时,才定义这个信号
WIFSTOPPED(status)如果引起返回的子进程当前是停止的,那么久返回真
WSTOPSIG(status)返回引起子进程停止的信号的编号。只有在WIFSTOPPED()返回为真时,才定义这个状态
WIFCONTINUED(status)如果子进程收到SIGCONT信号重新启动,则返回真
错误条件

如果调用进程没有子进程,waitpid返回-1,并且设置errorno为ECHILD,
如果waitpid函数被一个信号中断,返回-1,设置errorno为EINTR。

代码演示
#include <stdlib.h>
#include <wait.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>

int main(){
    int status,i;
    const int N = 10;
    pid_t pid;
    for(i = 0; i < N;++i){
        if(fork() == 0){
            printf("It's child \n");
            exit(100+i);
        }
    }

    while((pid = waitpid(-1,&status,0)) > 0){
        if(WIFEXITED(status))
            printf("child %d terminate normally with exit status = %d\n",
                    pid,WEXITSTATUS(status));
        else
            printf("child %d termintate abnormally \n");
    }

    exit(0);
}

注意子进程退出的顺序不是一定的,如果要让父进程按照子进程创建的顺序来回收子进程,可以做出如下修改:

#include <stdlib.h>
#include <wait.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>

int main(){
    int status,i;
    const int N = 10;
    pid_t pid[N],retpid;
    for(i = 0; i < N;++i){
        if(fork() == 0){
            exit(100+i);
        }
    }
	i = 0;
    while((retpid= waitpid(pid[i++],&status,0)) > 0){
        if(WIFEXITED(status))
            printf("child %d terminate normally with exit status = %d\n",
                    pid,WEXITSTATUS(status));
        else
            printf("child %d termintate abnormally \n");
    }

    exit(0);
}

wait函数

wait函数时waitpid的简单版本:

#include <sys/type.h>
#include <sys/wait.h>
pid_t wait(int *startup)

调用wait函数等价于调用waitpid(-1,&status,0)。

让进程休眠

sleep函数

sleep函数会让进程挂起一段时间:

#include <unistd.h>
unsigned int sleep(unsigned int secs);

如果挂起时间到,函数返回0,如果被中断而导致返回,将返回剩余的挂起时间。

pause函数

pause函数会将进程挂起,直到进程收到一个信号

#include <unistd.h>
int pause(void)

加载并运行新的程序

execve函数可以在当前进程的上下文中加载并允许一个新的程序

#include <unistd.h>
int execve(const char* filename,const char* argv[],
const char* envp[]);

其中argv与envp均指向以Null为结尾的指针数组;
在使用中,可以用environ(系统全局变量)变量传入envp[]数组中。

环境变量相关函数

获取环境变量

#include <stdlib.h>
char* getenv(const char* name);

设置与取消环境变量

#include <stdlib.h>
int setenv(const char* name,const char* newvalue,int overwrite);
void unsetenv(const char* name);

一般来说,环境变量已"name=value"这样键值对的方式存在。

利用进程控制接口,编写简单的shell程序

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <wait.h>

#define MAX_COMMAND_LINE 256
#define MAX_COMMAND_COUNT 256

void eval(char* command);
int parseline(char* command,char* argv[]);
int builtin_command(char* argv[]);

int main(){

    char commandline[MAX_COMMAND_LINE];
    while (1){
        printf("> ");
        fgets(commandline,MAX_COMMAND_LINE,stdin);
        if(feof(stdin)){
            exit(0);
        }

        eval(commandline);
    }
    
    exit(0);
}

void eval(char* command){
    int argc = 0;
    char* argv[MAX_COMMAND_COUNT];
    pid_t pid;
    int status;
    int bg = parseline(command,argv);
    if(argv[0] == NULL){
        return ;
    }

    if(builtin_command(argv)){
        return ;
    }


    if((pid = fork()) == 0){
        printf("execute child \n");
        if(execve(argv[0],argv,__environ)<0){
            printf("Can't find command %s \n",argv[0]);
            exit(0);
        }
    }


    if(!bg){
        if(waitpid(pid,&status,0) < 0){
            printf("Fail to excute pid %d command %s \n",pid,argv[0]);
        }
        if(WIFSTOPPED(status)){
            printf("the stoped signal is %d \n",WSTOPSIG(status));
        }
    } else{
        // waitpid(-1,&status,WNOHANG);
        printf("pid:%d command:%s run in background \n",pid,argv[0]);
    }
}

int parseline(char* command,char* argv[]){
    int argc = 0;
    char* delim;
    command[strlen(command)-1]=' ';
    while(*command && command[0] == ' '){
        ++command;
    }
    while((delim = strchr(command,' '))){
        *delim='\0';
        argv[argc++] = command;
        command = (++delim);
        while (*command &&command[0] == ' '){
            ++command;
        }
    }
    argv[argc] = NULL;
    if(argc == 0){
        return 0;
    }
    if(*argv[argc-1] == '&'){
        argv[--argc] = NULL;
        return 1;
    } else{
        return 0;
    }
}

int builtin_command(char* argv[]){
    if(!strcmp(argv[0],"quit")){
        exit(0);
    } else if(!strcmp(argv[0],"&")){
        return 1;
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值