1、fork与vfork

fork

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
 
int main()
{
    pid_t pid;
    pid = fork();
    if(pid<0)
        printf("error in fork!\n");
    else if(pid == 0)
        printf("I am the child process,ID is %d\n",getpid())

    else 
        printf("I am the parent process,ID is %d\n",getpid()
;
    return 0;
 
}

image-20221118162211635.png

fork创建出来的进程,父子进程没有必须的执行顺序,顺序也不是确定的,这取决于linux内核的调度算法。

fork在使用时会拷贝父进程的数据段和代码段,这就避免了在操作子进程数据时导致父进程数据发生改变。

vfork

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
 
int main()
{
    pid_t pid;
    pid = vfork();
    if(pid<0)
        printf("error in fork!\n");
    else if(pid == 0)
        {
		printf("I am the child process,ID is %d\n",getp
d());
		exit(0);
	}
    else 
        printf("I am the parent process,ID is %d\n",getpid()
;
    return 0;
 
}

image-20221118164224159.png

vfork则是先执行子进程,且在子进程执行结束收回后,才能执行父进程的内容。

由于vfork是共享父进程的数据段,所以使用vfork时需格外注意子进程的回收情况,如果没有回收就会出现段错误。

区别:

  • 执行顺序
  • 是否共用数据段和代码段

对比分析

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

int main(void)
{ 
	int data = 0 ; 
	pid_t pid ; 
	int choose = 0 ; 
	while((choose = getchar( ))!='q')
	{ 
 		switch(choose)
		{ 
			case '1': 
			pid = fork( ); 
  			if(pid < 0 ) 
			{ 
				printf("Error !\n"); 
			} 
			if(pid == 0 )
			{
				data++; 
				exit(0); 
			} 
			 wait(pid);	//wait用于回收子进程		
			if(pid > 0 )
			{ 
				printf("data is %d\n",data); 
		 	}
			break; 
			case '2' :
			pid = vfork( ); 
			if(pid < 0 )
			{ 
				perror("Error !\n"); 
			} 
			if(pid == 0 )
			{ 
				data++;
				exit(0); 
			} 
			wait(pid); 
			if(pid > 0 )
			{ 
				printf("data is %d\n",data); 
			} 
			break; 
			default : break; 
		} 
	} 
}

image-20221124154121476.png

代码分析:

  1. 每当输入1的时候就会用fork创建子进程;
  2. 每当输入2的时候就会用vfork创建子进程;
  3. 子进程执行data++命令,父进程打印data值。

结果:

  1. 每次输入1的时候,并没有经过data++指令就将data的值打印了出来;每次输入2的时候,会先进行data++指定再打印data的值。
  2. 当多次重复输入1后,会发现data的值依旧是初始值;在多次重复输入2后,data的值都会加一。

结果分析:

  • 结果一:

    1. 输入1,使用fork创建子进程。由于 fork创建的子进程,其子进程和父进程的执行顺序是不确定的,每次都是先执行了父进程,所以打印结果就没有经过data++这一步。

      如果凑巧先执行了子进程,那么打印的结果就是data++。这个可以用sleep或wait函数去控制先执行子进程然后观察结果。

    2. 输入2,使用vfork创建子进程。vfork创建出的子进程,父进程会等子进程执行完且回收了子进程资源后再开始执行,所以每次都打印出的是data++。

  • 结果二:

    1. 使用fork创建子进程时,fork会拷贝父进程的数据段和代码段。子进程在执行指令的时候也是在拷贝下来的数据段和代码段上执行,并不会影响到父进程的数据段内容,所以每一次data++都是在取父进程中的data值,且data值改变后不会影响到父进程中的data值。
    2. 使用vfork创建子进程时,vfork是共享父进程的数据段。子进程执行data++也就改变了父进程中的data值。

2、完善程序

要求:

实现一个程序启动另一个程序后自身仍然在运行,即在子进程中加载执行其他程序而父进程等待子进程结束后才结束。

参考

#include <error.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <sys/types.h> 
int main()
 { 
/*接受键盘输入命令字符*/
/*创建子进程*/
if(创建失败) 
/*打印“创建子进程失败”*/
else if (子进程){
/*用exec()加载程序执行输入的命令*/
}
else {
/*等待子进程信息*/
/*继续父进程的执行*/
}
}
#include <error.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <wait.h>

int main()
 { 
	char a[20]={0};
	pid_t pid;
/*接受键盘输入命令字符*/
	printf("请输入程序名(命令路径):");
	scanf("%s",a);
/*创建子进程*/
	pid=fork();
	if(pid<0) 
/*打印“创建子进程失败”*/
	{
		printf("Error:fork fail!\n");
	}
	else if(pid==0)
		{
/*用exec()加载程序执行输入的命令*/
			execv(a,0);
			_exit(0);
		}
		else
		{
/*等待子进程信息*/
			wait(NULL);
/*继续父进程的执行*/
			printf("This is parent process\n");
}
}

image-20221123152803497.png

Hello是在同目录下编辑出的一个Hello word程序。

3、父子进程通过无名管道传递三条消息

程序思路:

父进程发送信息,三条信息分三次发送;子进程也是一条信息一条信息的获取。父进程向管道写入一条信息后sleep1秒,然后等子进程读取出来,读完之后也sleep1秒如此往复。完成三条信息的传输。

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

int main()
{
	pid_t pid;
	char buf[3][35]={0};
	int fd[2]={0};
	int i;
	char* context[]={"管道文件的测试程序开始","管道文件测试正在进行","管道通信测试结束"};

	switch(pipe(fd))
	{
		case -1:
			printf("ERROR:Creat pipe fail!\n");
			exit(1);
		break;
		case 0:
			switch(pid=fork())
			{
				case -1:
					printf("ERROR:Creat pipe fail!\n");
					exit(1);
				case 0:
					close(fd[1]);
					for(i=0;i<3;i++)
					{
						read(fd[0],buf[i],35);
						printf("%s\n",buf[i]);
						sleep(1);
					}
				break;
				default :
					close(fd[0]);
					for(i=0;i<3;i++)
					{
				write(fd[1],context[i],strlen(context[i]));
						sleep(1);
					}
					wait(NULL);
				break;
			}
		break;
	}
}

image-20221124154310892.png

4、 利用Linux/UNIX的软中断信号,编写一段C语言程序完成:显示数字1到100,在程序运行中如果捕获到一个SIGINT信号,则转去执行一段显示当前系统时间的程序。在编程中要考虑到信号被复位的情况,使程序能够实现多次被打断却多次的恢复执行。

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

void main()
{
    void catchint(int signo);
    int i;
    signal(SIGINT,catchint);

    for(i=1;i<101;i++)
    {
        printf("%d\n",i);
        sleep(1);
    }

    printf("Exiting.\n");
    exit(0);
}

void catchint(int signo)
{
    struct tm *prt;
    time_t it;

    it=time(NULL);
    prt=localtime(&it);
    printf("%4d年%2d月%2d日 %2d:%2d:%d\n",prt->tm_year+1900,prt->tm_mon+1,prt->tm_mday,prt->tm_hour,prt->tm_min,prt->tm_sec);
}

image-20221124154546633.png