嵌入式-进程、线程

一、fork

实例1

使用fork()系统调用来创建一个新的进程(子进程)

#include <stdio.h>  
#include <sys/types.h>  
#include <unistd.h>  
#include <stdlib.h> // 添加了stdlib.h头文件,用于exit()函数  
  
int main(int argc, const char *argv[])  
{  
    // 定义局部变量  
    int a = 0;  
  
    // 使用fork来创建一个子进程  
    pid_t pid = fork();  
    if(pid < 0)  
    {  
        perror("fork child process error"); // 当fork失败时,打印错误信息  
        return -1; // 退出程序,返回-1  
    }  
    else if(0 == pid)  
    {  
        // 子进程在执行代码  
        a++; // 子进程中a的值自增  
        printf("子进程:a = %d\n", a); // 输出子进程中a的值  
        printf("I am child process!\n"); // 输出子进程的信息  
  
        // 子进程退出  
        exit(0); // 使用exit函数退出子进程  
    }  
    else  
    {  
        // 父进程在执行代码  
        a++; // 父进程中a的值自增,但注意这个自增对子进程没有影响  
        printf("父进程:a = %d\n", a); // 输出父进程中a的值  
        printf("I am parent process!\n"); // 输出父进程的信息  
  
        // 父进程后续代码  
        // 注意:以下代码在父进程中执行,子进程已经通过exit(0)退出了  
    }  
  
    // 接下来的代码只会在父进程中执行,因为子进程已经通过exit(0)退出了  
    printf("test!!!\n"); // 这行代码只会在父进程中输出  
  
    while(1); // 这行代码是死循环,会导致父进程无限循环下去
 
  
    return 0; // 程序正常结束,只有父进程会执行到这里  
}

代码演示了使用fork()系统调用在Linux环境中创建子进程的过程,并在父进程和子进程中分别执行了不同的代码块。

原代码中存在一个潜在的问题:在父进程的代码块之后有一个死循环while(1);在实际应用中,通常不会让父进程进入死循环,而是根据需求进行后续处理或等待子进程结束。

实例2

getpid()返回当前进程的PID,getppid()返回当前进程的父进程的PID

#include <stdio.h>           // 引入标准输入输出库,用于printf等函数  
#include <sys/types.h>       // 引入系统类型定义,如pid_t  
#include <unistd.h>          // 引入Unix标准库,提供fork()等系统调用  
  
int main(int argc, const char *argv[]) // 程序主入口  
{  
    printf("hello,24031!\n");        // 打印一条消息  
  
    int a = 90;                      // 定义一个整型变量a并初始化为90  
  
    pid_t pid = fork();              // 调用fork()创建子进程  
    // fork()调用后,如果成功,在父进程中返回新创建的子进程的PID,在子进程中返回0,如果出错则返回-1  
  
    printf("test1!\n");              // 在fork()之后,无论是父进程还是子进程都会执行到这一行  
    printf("qianrushi !\n");         // 同样,这一行也会被父进程和子进程都执行  
  
    if(pid < 0)                      // 检查fork()的返回值,如果小于0,表示fork失败  
    {  
        perror("fork error");        // 打印fork失败的原因  
        return -1;                   // 退出程序,返回-1  
    }  
    else if(0 == pid)                // 如果fork()返回0,表示当前代码在子进程中执行  
    {  
        // 子进程部分  
        printf("a = %d\n",++a);      // 子进程中a的值自增后打印,但这里的a是子进程的局部变量,与父进程的a无关  
        printf("child process! PID = %d, PPID = %d\n", // 打印子进程的PID和父进程的PID  
        getpid(), getppid()); // getpid()返回当前进程的PID,getppid()返回当前进程的父进程的PID  
    }  
    else  
    {  
        // 父进程部分  
        printf("a = %d\n",++a);      // 父进程中a的值自增后打印  
        printf("parent process PID = %d, childID = %d\n", // 打印父进程的PID和子进程的PID  
        getpid(), pid);       // 这里pid是fork()在父进程中返回的子进程的PID  
    }  
  
    // 注意:无论是父进程还是子进程,都会执行到下面的printf语句  
    printf("Over!\n");               // 打印"Over!"  
  
    return 0;                        // 主函数返回0,表示程序正常结束  
}

注意:

  1. fork()调用后,父进程和子进程都会继续执行fork()之后的代码。
  2. 父进程和子进程有各自的地址空间,包括各自的变量副本(如这里的a)。因此,子进程中a的修改不会影响到父进程中a的值。
  3. 父进程和子进程唯一的联系是它们的PID(进程ID)和PPID(父进程ID)。你可以使用getpid()getppid()来获取这些值。

实例3

使用wait阻塞回收子进程的退出资源

#include <stdio.h>  
#include <sys/types.h>  
#include <unistd.h>  
#include <sys/wait.h>  
  
int main(int argc, const char *argv[])  
{  
    // 定义局部变量a,初始化为0  
    int a = 0;  
  
    // 使用fork来创建一个子进程  
    pid_t pid = fork(); // fork返回两次,一次在父进程中,一次在子进程中  
    if(pid < 0)  
    {  
        // 如果fork返回小于0,表示创建子进程失败  
        perror("fork child process error");  
        return -1;  
    }  
    else if(0 == pid)  
    {  
        // 如果fork返回0,表示这是子进程的执行代码  
        // 注意:a在这里对子进程是独立的,与父进程中的a不同  
        while(1)  
        {  
            a++;  
            printf("子进程:a = %d\n",a);  
            printf("I am child process!\n");  
            sleep(1); // 子进程休眠1秒  
        }  
  
    }  
    else  
    {  
        // 如果fork返回的值大于0,表示这是父进程的执行代码,并且返回的值是子进程的PID  
        // 使用wait阻塞回收子进程的退出资源  
        printf("等待回收子进程中......\n");  
        pid_t exitPID = wait(NULL); // 阻塞等待子进程结束,返回子进程的PID  
        printf("exitPID = %d的子进程已被回收!\n", exitPID);  
  
        // 父进程在执行代码  
        // 注意:因为下面的while(1)循环,父进程也会进入无限循环,所以"test!!!"同样不会被打印  
        while(1)  
        {  
            a++;  
            printf("父进程:a = %d\n",a);  
            printf("I am parent process!\n");  
            sleep(1); // 父进程休眠1秒  
        }  
  
        
    }  
  
    // 注意:此处的printf("test!!!\n"); 永远不会被执行,因为父进程进入了无限循环  
    printf("test!!!\n");  
    return 0;  
}

实例4

使用waitpid回收子进程的退出资源  

#include <stdio.h>  
#include <sys/types.h>  
#include <unistd.h>  
#include <sys/wait.h>  
  
int main(int argc, const char *argv[])  
{  
    // 定义局部变量a,初始化为0  
    int a = 0;  
  
    // 使用fork来创建一个子进程  
    pid_t pid = fork();  
  
    if(pid < 0)  
    {  
        // 如果fork返回小于0,表示创建子进程失败  
        perror("fork child process error");  
        return -1;  
    }  
    else if(0 == pid)  
    {  
        // 这是子进程的执行代码  
        // 子进程中的变量a与父进程中的a是独立的  
        while(1)  
        {  
            a++;  
            printf("子进程:a = %d\n", a);  
            printf("I am child process!\n");  
            sleep(1); // 子进程休眠1秒  
        }  
        
    }  
    else  
    {  
        // 父进程在执行代码  
  
        // 使用waitpid回收子进程的退出资源  
        // 注意:waitpid(-1, NULL, 0)会阻塞直到有子进程结束,但waitpid(-1, NULL, WNOHANG)是非阻塞的  
        while(1)  
        {  
             
            printf("等待回收资源中...\n");  // 输出提示信息 
  
            // 尝试非阻塞地回收子进程  
            pid_t exitPID = waitpid(-1, NULL, WNOHANG);  
            if(exitPID < 0)  
            {  
                // waitpid调用失败  
                perror("waitpid error");  
            }  
            else if(0 == exitPID)  
            {  
                // 没有检测到有子进程结束  
                printf("未检测到有子进程结束...\n");  
            }  
            else  
            {  
                // 成功回收了一个子进程  
                printf("已成功回收exitPID = %d的子进程资源!\n", exitPID);    
            }  
  
            // 父进程的其他工作  
            a++;  
            printf("父进程:a = %d\n", a);  
            printf("I am parent process!\n");  
            sleep(1);  
        }    
    }  
  
     
    // printf("test!!!\n");  // 因为父进程和子进程都有无限循环,所以这个printf永远不会执行 
  
    // return 0;  // 程序永远不会到达这里,所以return 0;也是多余的  
}

实例5

测试正常退出还是非正常退出

#include <stdio.h>  
#include <sys/types.h>  
#include <unistd.h>  
#include <sys/wait.h>  
#include <stdlib.h>  
  
int main(int argc, const char *argv[])  
{  
    // 定义局部变量a,初始化为0  
	int a = 0;  
  
	// 使用fork来创建一个子进程  
	pid_t pid = fork();  
	if(pid < 0)  
	{  
		// fork调用失败,输出错误信息并返回-1  
		perror("fork child process error");  
		return -1;  
	}  
	else if(0 == pid)  
	{  
		// 这是子进程执行的代码  
		while(1)  
		{  
			a++;  
			printf("子进程:a = %d\n",a);  
			printf("I am child process!\n");  
			sleep(1);  
  
			// 当a的值大于等于5时,子进程主动退出  
			if(a >= 5)  
			{  
				exit(0); // 子进程正常退出,返回0给父进程  
			}  
		}  
	}  
	else  
	{  
		// 这是父进程执行的代码  
		while(1)  
		{  
			printf("等待回收资源中...\n");  
  
			// 使用waitpid非阻塞地等待子进程退出,回收资源  
			// status变量用于保存子进程的退出状态  
			int status;  
			pid_t exitPID = waitpid(-1, &status, WNOHANG);  
			if(exitPID < 0)  
			{  
				// waitpid调用失败,输出错误信息  
				perror("waitpid error");  
			}  
			else if(0 == exitPID)  
			{  
				// 尚未检测到有子进程结束  
				printf("未检测到有子进程结束...\n");  
			}  
			else  
			{  
				// 检测到子进程已经退出,分析子进程的退出状态  
				if(WIFEXITED(status))  
				{  
					// 子进程正常退出  
					printf("正常退出!\n");  
				}  
				else  
				{  
					// 子进程异常退出  
					printf("非正常退出!\n");  
				}  
				printf("已成功回收exitPID = %d的子进程资源!\n", exitPID);  
  
				// 获取子进程的退出状态数值  
				printf("退出状态数值:%d\n",WEXITSTATUS(status));  
  
				// 注意:通常当父进程知道子进程已结束时,可以选择退出循环  
				// 但为了演示,这里保持循环  
			}  
			a++;  
			printf("父进程:a = %d\n",a);  
			printf("I am parent process!\n");  
			sleep(1);  
		}  
	}  
  
	// 注意:由于父进程和子进程都有无限循环,以下代码永远不会执行  
	// printf("test!!!\n");  
	// return 0;  
}

二、vfork()

#include <stdio.h>  
#include <sys/types.h>  
#include <unistd.h>  
#include <stdlib.h>  
  
int main(int argc, const char *argv[])  
{  
	int a = 0;  
  
	// 注意:通常建议使用fork()而不是vfork(),因为vfork()在现代系统中已被认为是不安全的  
	// 使用vfork来创建一个子进程(不推荐使用,为了示例)  
	pid_t pid = vfork();  
  
	if(pid < 0)  
	{  
		// vfork调用失败,输出错误信息并返回-1  
		perror("vfork child process error");  
		return -1;  
	}  
	else if(0 == pid)  
	{  
		// 子进程在执行代码  
		// 注意:在vfork()中,子进程共享父进程的地址空间,直到子进程调用exec函数族或_exit/_Exit/exit  
		// 因此,在vfork()后的子进程中,应该避免调用malloc/free等库函数,以及避免修改父进程的数据  
  
		// 此处直接执行子进程的逻辑,因为vfork后子进程会立即执行  
		a++; // 注意:这个a是父进程的变量,但因为在vfork之后直接执行,所以修改是可见的  
		printf("子进程:a = %d\n",a);  
		printf("I am child process!\n");  
		sleep(1); // 在vfork后调用sleep是危险的,但这里为了演示目的使用  
  
		// 子进程退出  
		// 注意:在vfork子进程中,应该使用_exit而不是exit,因为exit会调用库函数,可能会修改父进程的数据  
		_exit(0); // 子进程正常退出  
	}  
	else  
	{  
		// 父进程在执行代码  
		// 在父进程中,由于vfork的特殊性,不建议在子进程调用_exit之前进行任何操作  
		// 但为了演示目的,我们仍然在这里放一个循环  
  
		while(1)  
		{  
			a++; // 这个a是父进程的变量,但子进程对它的修改在vfork后是不可见的  
			printf("父进程:a = %d\n",a);  
			printf("I am parent process!\n");  
			sleep(1);  
  
			// 注意:在vfork中,父进程通常会在子进程调用_exit后继续执行  
			// 但这里为了演示目的,我们让父进程持续运行  
		}  
	}  
  
	// 注意:由于父进程和子进程都有无限循环,以下代码(return 0;)永远不会执行  
	// 在实际情况中,父进程应该通过某种方式(如等待子进程结束)来结束循环  
  
	// return 0; // 这行代码不会执行,因为上面有无限循环  
}

在上面的代码中,我添加了关于vfork()的一些注意事项和警告。vfork()在现代系统中已经被视为不安全,因为它让子进程和父进程共享地址空间,直到子进程调用exec函数族或_exit。在子进程中,应该避免调用可能会修改父进程数据的库函数。

此外,我指出了在vfork()后调用sleep()是危险的,尽管在这个示例中为了演示目的我保留了它。同样,在子进程中应该使用_exit()而不是exit(),因为exit()会调用库函数,可能会修改父进程的数据。

最后,我强调了在vfork()的父进程中,应该避免在子进程调用_exit()之前进行任何操作,尽管在这个示例中我添加了一个无限循环来演示。在实际情况中,父进程通常会等待子进程的结束。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值