进程和线程

1. fork函数

进程和线程_第1张图片

进程和线程_第2张图片

这个函数接口本身非常简单,简单到连参数都没有,但是这个函数有个非常与众不同的 地方:他会使得进程一分为二!就像细胞分裂一样:

进程和线程_第3张图片

父子进程是相互独立的:由于子进程完整地复制了父进程的内存空间,因此从内存 空间的角度看他们是相互独立、互不影响的

fork.c

#include
#include
#include 

//使用fork函数
int b = 40;//数据段的全局变量

int main()
{

    int a = 10;//栈空间申请的局部变量
    int* p = (int*)malloc(sizeof(int));//堆空间
    *p = 80;

    pid_t pid = fork();//创建一个子进程,此行代码后面所有的代码,子进程都会原封不动的复制一份执行

    if(pid==0){//子进程
        a = 20;
        b = 50;
        *p = 100;
        printf("子进程:%d\n",a);//20
        printf("子进程:%d\n",b);//50
        printf("子进程:%d\n",*p);//100
    }
    if(pid>0){
        sleep(1);
        printf("父进程:%d\n",a);//10
        printf("父进程:%d\n",b);//40
        printf("父进程:%d\n",*p);//80
    }

    return 0;
}

2 _exit、exit和wait、waitpid

2.1 _exit、exit、atexit

那么怎么让子进程先运行并退出之后,父进程再继续呢?子进程的退出状态 又怎么传递给父进程呢?答案是:可以使用 exit( )/_exit( )来退出并传递退出值,使用 wait( )/waitpid( )来使父进程阻塞 (sè) 等待子进程,顺便还可以帮子进程收尸

进程和线程_第4张图片

所谓的“退出处理函数”指的是进程使用 exit( )退出时被自 动执行的函数,需要使用 atexit( )来注册

注意printf() 加了换行一定会打印,但是不加换行不一定打印

2.1.1 代码

exit.c
#include 
#include 
#include 
#include 
#include 

void f1(void)
{
	printf("f1 is calling\n");//正在调用
	//关闭文件描述符
	//删除路径
	//.....
}

void f2(void)
{
	printf("f2 is calling\n");//正在调用
}


int main()
{
	//退出处理函数
	atexit(&f1);//像入栈(先声明的后执行),收尾工作
	atexit(&f2);
	
	//注意printf() 加了换行一定会打印,但是不加换行不一定打印
	printf("abcd  ");//printf行输出,   标准缓冲区不满一行,printf不输出
	
	//直接退出,什么都不管,慎用
	// _exit(0);
	
	//在退出之前,检查有没有注册退出处理函数,如果有,会调用处理函数,并且检查IO缓冲区有没有数据,如果有,会直接打印
	exit(0);	
}

使用exit(0); 由结果看出,先打印缓冲区的东西,然后再执行退出处理函数

使用_exit(0); 什么也不会打印直接退出

2.2 wait和waitpid

父进程如果需要,可以使用 wait( )/waitpid( )来获得子进程正常退出的退出值, 当然,这两个函数还可以使得父进程阻塞等待子进程的退出

进程和线程_第5张图片

注意,所谓的退出状态不是退出值,退出状态包括了退出值。如果使用以上两个函数成 功获取了子进程的退出状态,则可以使用以下宏来进一步解析

进程和线程_第6张图片

2.2.1 代码

execl.c

#include 
#include 
#include 

int main()
{
    printf("hello\n");
    pid_t pid = fork();//创建一个子进程
    if(pid == 0){
        //子进程
        execl("./son","./son","1.txt","2.txt","3.txt",NULL);//子进程那边设置了至少传4个参数,这个正确
        // execl("./son","./son","1.txt","2.txt",NULL);//这个调用子进程会参数异常退出

        //如果上面使用了execl函数那么这句话将不会输出
        printf("子进程的ID[%d],父进程的ID[%d]\n",getpid(),getppid());//1744孤儿回收进程
    }

    if(pid>0){//父进程
        int status;//定义一个整形的退出状态值,4个字节
        wait(&status);//用wait把退出值状态值传递进去,用来接收子进程的退出状态值
        
        //WEXITSTATUS(status)可以输出子进程的退出值
        printf("[退出值]%d\n",WEXITSTATUS(status));
		printf("父进程的ID[%d],爷爷ID[%d]\n",getpid(),getppid());
    }

    return 0;
}
son.c
#include
#include

int main(int argc,char** argv)
{
    if(argc<4){
        printf("子进程温馨提示:参数不正确\n");
        exit(2);//子进程参数不正确异常退出
    }
    for(int i=0;i

进程和线程_第7张图片

3. exec函数组

进程和线程_第8张图片

3.1 execl函数

代码和2.2.1一样

execl.c

#include 
#include 
#include 

int main()
{
    printf("hello\n");
    pid_t pid = fork();//创建一个子进程
    if(pid == 0){
        //子进程
        execl("./son","./son","1.txt","2.txt","3.txt",NULL);//子进程那边设置了至少传4个参数,这个正确
        // execl("./son","./son","1.txt","2.txt",NULL);//这个调用子进程会参数异常退出

        //如果上面使用了execl函数那么这句话将不会输出
        printf("子进程的ID[%d],父进程的ID[%d]\n",getpid(),getppid());//1744孤儿回收进程
    }

    if(pid>0){//父进程
        int status;//定义一个整形的退出状态值,4个字节
        wait(&status);//用wait把退出值状态值传递进去,用来接收子进程的退出状态值
        
        //WEXITSTATUS(status)可以输出子进程的退出值
        printf("[退出值]%d\n",WEXITSTATUS(status));
		printf("父进程的ID[%d],爷爷ID[%d]\n",getpid(),getppid());
    }

    return 0;
}

3.2 execv函数

execv.c

#include 
#include 
#include 

int main()
{
    printf("hello\n");
    pid_t pid = fork();//创建一个子进程
    if(pid == 0){
        //子进程
        char* arg[] = {"./son","1.txt","2.txt","3.txt",NULL};
        execv("./son",arg);//子进程那边设置了至少传4个参数,这个正确

        //如果上面使用了execl函数那么这句话将不会输出
        printf("子进程的ID[%d],父进程的ID[%d]\n",getpid(),getppid());//1744孤儿回收进程
    }

    if(pid>0){//父进程
        int status;//定义一个整形的退出状态值,4个字节
        wait(&status);//用wait把退出值状态值传递进去,用来接收子进程的退出状态值
        
        //WEXITSTATUS(status)可以输出子进程的退出值
        printf("[退出值]%d\n",WEXITSTATUS(status));
		printf("父进程的ID[%d],爷爷ID[%d]\n",getpid(),getppid());
    }

    return 0;
}

进程和线程_第9张图片

3.3 execlp函数

execlp.c

#include 
#include 
#include 

int main()
{
    printf("hello\n");
    pid_t pid = fork();//创建一个子进程
    if(pid == 0){
        //子进程


        //execl也可以直接使用shell命令替换子进程的代码
        // execl("/bin/ls","ls","-l",NULL);

        //利用环境变量 PATH 来找寻指定的执行文件
        execlp("ls","ls",NULL);


        //如果上面使用了execl函数那么这句话将不会输出
        printf("子进程的ID[%d],父进程的ID[%d]\n",getpid(),getppid());//1744孤儿回收进程
    }

    if(pid>0){//父进程
        int status;//定义一个整形的退出状态值,4个字节
        wait(&status);//用wait把退出值状态值传递进去,用来接收子进程的退出状态值
        
        //WEXITSTATUS(status)可以输出子进程的退出值
        printf("[退出值]%d\n",WEXITSTATUS(status));
		printf("父进程的ID[%d],爷爷ID[%d]\n",getpid(),getppid());
    }

    return 0;
}

进程和线程_第10张图片

你可能感兴趣的:(进程线程,linux,c语言)