尝试编写一个shell( fork exec wait)

本文介绍了一个简单的Shell命令解释器的实现过程,该解释器基于CentOS5.5平台使用C语言编写,能够接收并执行基本的Linux命令,如ls、who等,通过循环读取用户输入,并利用fork和exec调用执行相应的命令。

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

平台:CentOS 5.5   内核 2.6.13

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

int main() {
	char comm[20];
	pid_t pid;
	int ret;
	while (1) {
		printf("root@bogon:");
		scanf("%s", comm);
		if (strcmp(comm, "exit") == 0)
			break;
		else {
			pid = fork();
			if (pid == -1) {
				perror("fork");
				exit(0);
			} else if (pid == 0) {
				ret = execlp(comm, comm, NULL);
				if (ret == -1) {
					exit(1);
				}
			} else {
				wait(NULL);
			}
		}

	}
	return 0;
}



运行效果:

[root@bogon Debug]# ./text 
root@bogon:ls
makefile  objects.mk  passwd  passwd~  sources.mk  src  text
root@bogon:who
root     :0           2012-08-04 09:22
root     pts/2        2012-08-05 17:45 (:0.0)
root@bogon:pwd
/root/workspace/text/Debug
root@bogon:exit
[root@bogon Debug]# 



1、制作ramdisk,给出主次设备号,并使用df命令显示相关信息。(20分) 要求:(1) ramdisk的大小为学号的后3位*2,单位为M; (2)对文件系统采用ext2进行格式化。 2、编写一个c程序实现下面的功能:监视一个文件,如果文件被其他进程追加了内容,就把追加的内容打印出来。(15分) (1) 假定所编辑的c程序为mytail.c,使用命令行方式将该源程序编译,目标执行程序为mytail。给出具体的编译实现过程。 (2) 假定日志文件为/usr/tmp/pppd.log,给出打印追加内容的方法。 3、设用户mali记录了在Linux系统中的某些用户的月工资清单,记录在文件mylist.txt中,假定文件mylist.txt的内容如下: #================================================== # 登录名 工作证号 姓名 月份 工资 奖金 补助 扣除 总额 #--------------------------------------------------- wang 2076 wangxi 01 1782 1500 300 175 3407 liang 2074 liangyu 02 1560 1400 280 90 3150 zhang 3087 zhangdi 03 1804 1218 206 213 3015 wang 2076 wangxi 03 1832 1550 230 245 3367 wang 2076 wangxi 04 1832 1450 230 245 3267 liang 2074 liangyu 05 1660 1450 230 70 3270 zhang 3087 zhangdi 06 1700 1310 283 270 3023 #================================================== 只允许用户zhang读取行首字符为#的行和与用户zhang有关的行,与其他用户有关的行对用户zhang保密。(15分) (1) 编写相应的查询query.c,给出目标文件为query的实现方法; (2) 给出设置mylist.txt文件权限为仅对文件主具有读写权限的实现命令; (3) 给出设置query文件用户ID权限实现命令; (4) 给出用户zhang执行程序query的结果。 4、使用forkexec以及wait函数构造简单的shell解释程序。(15分) (1) 假定所编辑的c程序为myxsh.c,使用命令行方式将该源程序编译,目标执行程序为myxsh。给出具体的编译实现过程。 (2) 假定当前目录下有文件信息如下: 文件的行大小 文件名 文件的行大小 文件名 920 auther.c 146 licp.txt 127 myxsh.c 124 fus.h 160 chap1.h 152 myxse.c 46 fsme.h 164 fsme 运行myxsh程序出现提示符=>后,分析find . -name *.[ch] -exec wc -l {} ; 执行结果。 5、对/usr目录进行压缩归档,结果文件名位myusr.tar。(10分) 要求:(1)以控制台方式运行; (2)将该任务挂起,再转后台,再转前台,查看进程的状态变迁过程。 6、编写程序实现通过Windows客户端对Linux服务器端进行相关网络信息配置。(25分) 要求:(1)新修改服务器的IP 地址的构成方式:网络号为192.168.2.0,主机号为学号的后3位-150;掩码为255.255.255.0;默认网关为:网络号与服务器相同,主机号为1,DNS202.117.96.10; (2)服务器端程序以deamon程序的方式运行,监听端口为学号的后4位; (3)客服端使用VC++,以GUI 方式完成对服务器端的IP 地址、掩码、默认网关、DNS信息配置输入; (4)服务器端在接收客户端的配置信息后,使用exec 系列函数完成对IP 地址、掩码、默认网关、DNS修改,并写入相应的配置文件。结果验证:使用ifconfig检查IP地址信息配置的正确性;使用nslookup检查DNS信息的正确性。 把实现的信息结果与配置文件比较,检查其一致性。
<think>我们正在讨论C++中的forkwaitexec函数,但需要注意到这些实际上是UNIX/Linux系统调用,通常通过C库提供,在C++中也可以使用(需要包含相应的头文件,如<unistd.h>、<sys/wait.h>)。下面将分别解释这三个函数的功能和使用方法。 ### 1. fork函数 `fork()`函数用于创建一个新的进程(子进程),新进程是调用进程(父进程)的副本。子进程从`fork()`返回的地方开始执行,与父进程共享代码段,但拥有独立的数据空间、堆和栈。 - **返回值**: - 在父进程中,`fork()`返回子进程的进程ID(PID,大于0)。 - 在子进程中,`fork()`返回0。 - 如果创建失败,则返回-1(并设置`errno`)。 示例代码片段: ```c #include <unistd.h> #include <sys/types.h> pid_t pid = fork(); if (pid < 0) { // 处理错误 } else if (pid == 0) { // 子进程代码 } else { // 父进程代码 } ``` ### 2. wait函数 `wait()`函数用于父进程等待子进程的状态变化(如终止),并回收子进程资源,避免僵尸进程。 - **函数原型**: ```c #include <sys/wait.h> pid_t wait(int *status); ``` - 参数`status`:用于存储子进程的退出状态(可以通过宏如`WIFEXITED`、`WEXITSTATUS`等检查)。 - 返回值:成功时返回终止的子进程PID,失败返回-1。 `waitpid()`函数可以等待特定的子进程,并提供了更多选项。 ### 3. exec函数族 `exec`函数族用于将当前进程的映像替换为新的程序。调用`exec`后,原进程的代码段、数据段、堆和栈被新程序替换,但进程ID不变。 - **常见函数**: - `execl`, `execv`, `execle`, `execve`, `execlp`, `execvp`等。 - 以`l`结尾的函数接受参数列表(以NULL结束),以`v`结尾的函数接受字符串数组。 - 以`p`结尾的函数会在`PATH`环境变量中搜索可执行文件。 示例(使用`execl`): ```c execl("/bin/ls", "ls", "-l", NULL); // 如果成功,后续代码不会执行 perror("execl failed"); // 如果执行到这里,说明exec失败 ``` ### 综合示例 以下代码演示了`fork`、`wait`和`exec`的联合使用: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { pid_t pid = fork(); if (pid < 0) { perror("fork"); exit(1); } else if (pid == 0) { // 子进程 printf("Child process (PID: %d)\n", getpid()); execl("/bin/date", "date", "+%s", NULL); // 替换为执行date命令 perror("execl"); // 如果exec失败才会执行 exit(1); } else { // 父进程 printf("Parent process (PID: %d), Child PID: %d\n", getpid(), pid); wait(NULL); // 等待子进程结束 printf("Child process has terminated.\n"); } return 0; } ``` ### 注意事项 1. **错误处理**:每次调用系统调用后都应检查错误。 2. **资源回收**:父进程必须使用`wait`或`waitpid`来回收子进程资源,否则子进程会成为僵尸进程。 3. **并发执行**:`fork`之后,父进程和子进程的执行顺序不确定,取决于系统调度。 4. **文件描述符**:子进程会继承父进程打开的文件描述符(可以使用`fcntl`设置`FD_CLOEXEC`标志来避免)。 ### 引用说明 以上内容参考了提供的引用示例[^1][^2][^3],并进行了扩展解释。 §§相关问题§§ 1. `fork`函数创建子进程时,子进程会继承父进程的哪些资源? 2. 什么是僵尸进程?如何避免僵尸进程? 3. 除了`wait`,还有哪些函数可以用于回收子进程资源? 4. `exec`函数族中,`execve`和`execl`有什么区别? 5. 在C++程序中,使用这些系统调用需要注意哪些问题?</think>### C++中forkwaitexec函数的功能与使用方法分析 #### 1. **fork函数** **功能**:创建子进程(进程复制) - 调用一次返回两次:父进程返回子进程PID,子进程返回0 - 子进程获得父进程代码、数据、堆栈的**完整副本**(写时复制技术优化) - 典型应用:并行任务处理、守护进程创建 **示例代码**: ```c #include <unistd.h> #include <iostream> int main() { pid_t pid = fork(); // 关键调用点 if (pid < 0) { std::cerr << "Fork failed\n"; return 1; } else if (pid == 0) { std::cout << "子进程PID: " << getpid() << std::endl; } else { std::cout << "父进程PID: " << getpid() << ",子进程PID: " << pid << std::endl; } return 0; } ``` #### 2. **wait函数** **功能**:父进程阻塞等待子进程终止 - 回收子进程资源,避免**僵尸进程** - `wait(NULL)`:忽略子进程退出状态 - `wait(&status)`:获取子进程退出状态(需用宏解析:`WIFEXITED(status)`等) **示例代码**: ```c #include <sys/wait.h> #include <unistd.h> #include <iostream> int main() { pid_t pid = fork(); if (pid == 0) { sleep(2); // 子进程模拟工作 std::cout << "子进程退出\n"; } else { std::cout << "父进程等待子进程...\n"; wait(NULL); // 关键阻塞点 std::cout << "子进程已回收\n"; } return 0; } ``` #### 3. **exec函数族** **功能**:替换当前进程映像 - 加载新程序到当前进程空间,**原代码段/数据段被覆盖** - 函数变体:`execl`, `execv`, `execle`, `execvp`等 - 常与`fork`配合:子进程调用`exec`执行新程序 **示例代码**: ```c #include <unistd.h> #include <iostream> int main() { pid_t pid = fork(); if (pid == 0) { execl("/bin/ls", "ls", "-l", NULL); // 替换为ls命令 std::cerr << "exec失败!\n"; // 仅exec失败时执行 return 1; } else { wait(NULL); } return 0; } ``` ### 关键使用场景组合 ```mermaid graph LR A[父进程] --> B[fork创建子进程] B --> C{进程判断} C -->|子进程| D[exec加载新程序] C -->|父进程| E[wait回收资源] ``` ### 注意事项 1. **文件描述符继承**:子进程继承父进程打开的文件描述符,需注意关闭不需要的FD 2. **信号处理**:`exec`保留信号处理设置,`fork`继承信号处理器 3. **原子操作**:`fork`后立即`exec`是避免竞争条件的常见模式 4. **错误处理**:所有系统调用必须检查返回值(如`fork`返回-1表示失败) 5. **替代方案**:C++17起可用`<filesystem>`操作路径,但进程管理仍需系统调用 > 典型应用模式:Web服务器中父进程`fork`子进程处理请求,子进程`exec`CGI程序;Shell实现命令执行[^1][^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

developer_wgl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值