第1关:进程创建
任务描述
fork()
函数通过系统调用创建一个与原来进程几乎完全相同的进程。那么,进程中的变量父进程和子进程是否都能使用并修改呢?
围绕问题的提出,我们尝试在父子进程中都修改同一个文件中的内容,最终将文件内容输出,便可知晓答案。
下面我们通过学习相关知识并编写代码来测试你的猜想是否正确。
相关知识
Linux 进程中的几个状态:
-
R 运行状态 (
runing
),并不意味着进程一定在运行中,也可以在运行队列里 -
S 睡眠状态 (
sleeping
),进程在等待事件完成(浅度睡眠,可以被唤醒) -
D 磁盘睡眠状态 (
Disk sleep
),不可中断睡眠(深度睡眠,不可以被唤醒,通常在磁盘写入时发生) -
T 停止状态 (
stopped
),可以通过发送 SIGSTOP 信号给进程来停止进程,可以发送 SIGCONT 信号让进程继续运行 -
Z 僵尸状态 (
zombie
),子进程退出,父进程还在运行,但是父进程没有读到子进程的退出状态,子进程进入僵尸状态
为了完成本关任务,你需要掌握:1. 如何创建进程;2.fork()
函数的执行步骤。
进程的创建
在使用创建进程函数前,我们需要先导入unistd.h
库。
创建进程的函数原型是:pid_t fork(void);
例如:
pid_t pid = fork();
pid_t
是一个整数类型,即fork()
函数会返回新进程的 ID 号(0~32768
的整数)。fork
函数在父进程中返回子进程的pid
,在子进程中返回0
。
注意在子进程中返回的0
,并不是子进程的pid
,子进程的pid
在父进程的返回值中保存。而子进程的返回值是为了标识它是子进程,用来区分父子进程的。
父子进程的注意事项:
-
新进程是当前进程的子进程。
-
父进程和子进程 ①父进程:
fork()
的调用者; ②子进程:新建的进程。 -
子进程是父进程的复制(相同的代码,相同的数据,相同的堆栈),除了 ID 号和时间信息外,两者完全相同。
-
子进程和父进程可以并发运行。
fork()
函数的执行步骤
由于子进程是父进程的复制,所以子进程中也会有创建子进程的语句,如果不加以限制,就会形成递归创建,但实际上并不是这样的。
实际流程是:父进程创建了子进程后,子进程中“创建进程”语句不再执行,并发运行其他语句。
在 Linux 的源码中我们可以找到fork
函数:
...
copy_files(clone_flags,p); //克隆文件
copy_fs(clone_flags,p); //克隆文件系统
copy_mm(clone_flags,p); //克隆内存信息
...
我们可以看到有三条语句,用于拷贝进程的所有信息,这也解释了为什么说子进程是父进程的复制。
编程要求
通过提示,在右侧编辑器中补充代码,完成在指定文件中添加内容,具体要求如下:
- 创建进程;
- 父进程向文件中添加
hello
和world!
; - 子进程向文件中添加
hello
和welcome!
; - 只需修改文件内容即可,平台将为你输出文件内容。
提示:fork()
函数的返回值为0
时则为子进程。
测试说明
平台会对你编写的代码进行测试:
预期输出: hello world!
hello welcome!
开始你的任务吧,祝你成功!
#include <stdio.h>
#include <string.h>
//ÇëÔÚ´Ë´¦Ìí¼Óµ¼ÈëÏàÓ¦¿âº¯Êý´úÂë
int main(){
FILE *fp=fopen("test.txt","w+");
int pid;
//pid±íʾforkº¯ÊýµÄ·µ»ØÖµ
/********** Begin **********/
char *hello="hello ";
char *world="world!\n";
char *welcome="welcome!\n";
fwrite(hello,strlen(hello),1,fp);
if((pid=fork())==0)
{ //子进程
fwrite(welcome,strlen(welcome),1,fp);
}else
{ //父进程
fwrite(world,strlen(world),1,fp);
}
/********** End **********/
fclose(fp);
return 0;
}
第2关:进程加载
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
//ÇëÔÚ´Ë´¦Ìí¼Óµ¼ÈëÏàÓ¦¿âº¯Êý´úÂë
int main(){
/********** Begin **********/
//¸¸½ø³ÌÊä³ö
printf("entering main process---\n");
//×Ó½ø³ÌÖ´ÐÐhello.cÎļþ
if(fork()==0){
int ret=execl("./hello","hello",NULL);
if(ret == -1){
perror("error,failed to execute hello programe\n");
exit(1);
}
}
/********** End **********/
return 0;
}
第3关:进程等待与退出
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
//请在此处添加导入相应库函数代码
int main()
{
int p1, p2; //进程ID
/********** Begin **********/
p1 = fork();
if(p1==0){ //第一个子进程
execl("/bin/echo","echo","I am first process!",NULL);
exit(1);
}else{
wait(NULL); //用于进程同步,wait函数的作用是:父进程在此处暂停运行,等待一个子进程结束后,再从此处继续向下运行
}
p2=fork();
if(p2==0){ //第二个子进程
execl("/bin/echo","echo","I am second process!",NULL);
exit(1);
}else{
wait(NULL);
}
printf("I am father process!\n");
/********** End **********/
return 0;
}