进程(二)

本文详细介绍了fork()和wait()系统调用的功能与使用方法,通过示例代码解释如何创建子进程及父进程如何等待子进程结束。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.fork()

fork创建一个新进程。这个系统调用复制当前进程,在进程表中创建一个新的表项,新进程几乎与原进程一模一样,执行的代码也是完全一样,但新进程有自己的数据空间、环境、文件描述符。

#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);
返回值:
  • 如果成功创建一个子进程,对于父进程来说返回子进程ID
  • 如果成功创建一个子进程,对于子进程来说返回值为0
  • 如果为-1表示创建失败

  1. 使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设置、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等。
  2. 子进程与父进程的区别在于:

    • 1、父进程设置的锁,子进程不继承
    • 2、各自的进程ID和父进程ID不同
    • 3、子进程的未决告警被清除;
    • 4、子进程的未决信号集设置为空集。
     3. fork系统调用需要注意的地方

    • fork系统调用之后,父子进程将交替执行。
    • 如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程。(注:任何一个进程都必须有父进程)
    • 如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵进程。
    • 子进程退出会发送SIGCHLD信号给父进程,可以选择忽略或使用信号处理函数接收处理就可以避免僵尸进程。
一个fork的例子
/*fork.c*/
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
    pid_t pid;
    char *message;
    pid=fork();
    if(pid<0)
    {
	perror("fork error!");
	exit(1);
    }
    else if(pid>0)
    {
	message="this is parent!";
	printf("%s ppid=%d\n",message,pid);
    }
    else
    {
	message="this is child!";
	printf("%s ppid=%d\n",message,pid);
    }
    return 0;
}


make fork
./fork1
结果是:
this is parent! ppid=2486
zhou@hyn-virtual-machine:~/os/test/test1/daemon$ this is child! ppid=0
委屈结果是乱的,别急我们来看第二个函数;

2.wait()

在例程fork1.c中由于父进程先于子进程结束;所以输出结果有点乱,我们可以在父进程中调用wait()等待子进程结束;

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int *stat_loc);

wait()会暂时停止进程的执行,直到有信号来到或子进程结束。如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status 返回,而子进程的进程识别码也会一起返回。如果不在意结束状态值,则参数status 可以设成NULL。

结束状态值信息:

  • WIFEXITED(stat_val)如果子进程正常结束,他就取一个非零值
  • WEXITSTATUS(stat_val)如果WIFEXITED非零,他返回子进程的退出码
  • WIFSIGNALED(stat_val)如果子进程因为一个未捕获的信号而终止,他就取一个非零值
  • WTERMSIG(stat_val)如果WIFSIGNALED非零,他返回一个信号代码
  • WIFSTOPPED(stat_val)如果子进程意外终止,它就取一个非零值
  • WSTOPSIG(stat_val)如果WIFSTOPPED非零,他返回一个信号代码

一个wait的例子

/*wait.c*/
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
    pid_t pid;
    char *message;
    pid=fork();
    int exit_code;
    if(pid<0)
    {
		perror("fork error!");
		exit(1);
    }
    else if(pid>0)
    {
		message="this is parent!";
		printf("%s ppid=%d\n",message,pid);
		int stat_val;
		pid_t child_pid;

		child_pid=wait(&stat_val);
		if(WIFEXITED(stat_val))
		{
			printf("child exited with code%d\n",WEXITSTATUS(stat_val));
		}
		else
		{
			printf("child terminated abnormally\n");
		}
    }
    else
    {
		message="this is child!";
		printf("%s ppid=%d\n",message,pid);
		exit_code=37;
    }
    exit(exit_code);
}

make wait
./wait

结果是
this is parent! ppid=2576
this is child! ppid=0
child exited with code37
以下是使用C语言编写的程序,满足题目中的要求: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/wait.h> #define MAX_BUF_SIZE 1024 int main() { int fd1[2], fd2[2]; // 两个管道 pid_t pid1, pid2; // 进程ID if (pipe(fd1) < 0 || pipe(fd2) < 0) { perror("pipe error"); exit(EXIT_FAILURE); } // 创建进程一 pid1 = fork(); if (pid1 < 0) { perror("fork error"); exit(EXIT_FAILURE); } else if (pid1 == 0) { // 子进程一 close(fd1[0]); close(fd2[0]); close(fd2[1]); // 进程一不需要用到第个管道 int fd = open("data.txt", O_RDONLY); char buf[MAX_BUF_SIZE]; int len = read(fd, buf, MAX_BUF_SIZE); write(fd1[1], buf, len); close(fd1[1]); exit(EXIT_SUCCESS); } else { // 父进程 pid2 = fork(); if (pid2 < 0) { perror("fork error"); exit(EXIT_FAILURE); } else if (pid2 == 0) { // 子进程 close(fd1[1]); close(fd2[0]); int len; char buf[MAX_BUF_SIZE]; len = read(fd1[0], buf, MAX_BUF_SIZE); for (int i = 0; i < len; ++i) { if (buf[i] >= '0' && buf[i] <= '9') { buf[i] += 1; } } write(fd2[1], buf, len); close(fd2[1]); exit(EXIT_SUCCESS); } else { // 父进程 close(fd1[0]); close(fd1[1]); close(fd2[1]); int fd = open("result.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); char buf[MAX_BUF_SIZE]; int len = read(fd2[0], buf, MAX_BUF_SIZE); write(fd, buf, len); close(fd2[0]); int status; waitpid(pid1, &status, 0); waitpid(pid2, &status, 0); exit(EXIT_SUCCESS); } } return 0; } ``` 以上代码中,我们首先定义了两个管道,然后创建了两个子进程,分别完成读取文件和写入文件的任务,中间的进程则进行了加1操作。 具体来说,进程一通过管道一将文件读取到缓冲区,然后写入管道一。进程从管道一读取数据,进行加1操作,并将结果写入管道进程三从管道读取数据,并将结果写入文件中。最后,父进程等待两个子进程结束,退出程序。 需要注意的是,在实际的编程中,应该对各种系统调用的返回值进行错误检查。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值