system的另一种封装

本文详细解析了如何使用vfork实现system函数的封装,讨论了vfork相对于fork的效率优势。同时,介绍了execl函数在进程替换中的作用,以及waitpid在等待子进程结束时的角色。文章还提到了SIGCHLD信号对system调用的影响,并阐述了进程退出状态的检查方法,如WIFEXITED、WEXITSTATUS等宏的使用。

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

system的封装代码

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
char *pmalloc = NULL;

int system_vfork(const char * cmdstring) 
{ 
	pid_t pid; 
	int status; 
	
	
	if(cmdstring == NULL) 
	{ 
		return (1); //如果cmdstring为空,返回非零值,一般为1 
	} 
	if((pid = vfork())<0) 
	{ 
	 	status = -1; //fork失败,返回-1 
	} 
	else if(pid == 0) 
	{ 
		execl("/bin/sh", "sh", "-c", cmdstring, (char *)0); 
		_exit(127); // exec执行失败返回127,注意exec只在失败时才返回现在的进程,成功的话现在的进程就不存在啦~~ 
	} 
	else //父进程 
	{ 
		while(waitpid(pid, &status, 0) < 0) 
		{ 
			if(errno != EINTR) 
			{ 
				status = -1; //如果waitpid被信号中断,则返回-1   EINTR Interrupted system call
				break; 
			} 
		} 
	} 
	return status; //如果waitpid成功,则返回子进程的返回状态 
}

/* 封装system系统函数,打印记录错误 */
int INF_System(const char *cmd)
{
    int status = -1;
    if (NULL == cmd)
    {
        printf("INF_System error, cmd is NULL");
        return -1;
    }

    status = system_vfork(cmd);

    if (-1 == status)
    {
        printf("INF_System error, status = %d, cmd[%s], errno%d : %s", \
             status, cmd, errno, strerror(errno));
        return status;
    }

    if (WIFEXITED(status))
    {
        if (127 == WEXITSTATUS(status))
        {
            printf("INF_System error, status = %d, cmd[%s], errno%d : %s", \
            WEXITSTATUS(status), cmd, errno, strerror(errno));
            return status;
        }
    }
    else if (WIFSIGNALED(status))
    {
        printf("INF_System error, status = %d, sig = [%d], cmd[%s], errno%d : %s", \
            WEXITSTATUS(status), WTERMSIG(status), cmd, errno, strerror(errno));
    }
    else if (WIFSTOPPED(status))
    {
        printf("INF_System error, status = %d, sig = [%d], cmd[%s], errno%d : %s", \
            WEXITSTATUS(status), WSTOPSIG(status), cmd, errno, strerror(errno));
    }

    return status;
}

int main()
{
	INF_System("ls -l");
	return 0;
}

system的封装代码其实就是fork改成了vfork来实现,虽然fork实现了写时拷贝,效率得到很大改善,但如果只是执行shell命令这一块,vfork的效率还是比fork高的。
如果system之前,把SIGCHLD信号行为被设置为SIG_IGN,或者其他行为,而不是SIG_DFL 行为,执行system就会报错,因为waitpid()函数有可能因为找不到子进程而报ECHILD错误。(SIGCHLD 忽略信号 当子进程停止或退出时通知父进程)

**int execl(const char path, const char arg, …)
execl函数特点:
  当进程调用一种exec函数时,该进程完全由新程序代换,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段。exec函数·一旦成功,后面的代码是无法执行的。
  
wait/waitpid(等待子进程中断或结束)
**作用:**使父进程阻塞等待子进程的退出,将子进程的状态切换为死亡态以便于系统释放子进程的资源。
定义函数 pid_t waitpid(pid_t pid,int * status,int options);
函数调用 waitpid(pid, NULL, 0);
函数说明 waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用waitpid()时子进程已经结束,则waitpid()会立即返回子进程结束状态值。子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则参数status可以设成NULL。参数pid为欲等待的子进程识别码,其他数值意义如下:
pid<-1 等待进程组识别码为pid绝对值的任何子进程。
pid=-1 等待任何子进程,相当于wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为pid的子进程。

返回值 如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno。

waitpid把子进程的结束状态返回后存于status,有几个宏可判别结束情况:
WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的(通过exit()或者_exit()),如果是,它会返回一个非零值。

WEXITSTATUS(status) 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status)就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出的,也就是说,WIFEXITED返回0,这个值就毫无意义。
WIFEXITED(status) 如果子进程正常退出,则该宏伟真

WEXITSTATUS(status) 如果子进程正常退出,则宏将获取子进程的退出值

WIFSIGNALED(status)如果子进程是因为信号而结束,则此宏值为真

WIFSTOPPED(status)如果子进程的被信号暂停,且optionWUNTRACED已经被设置时,则此宏值为真

WTERMSIG(status) 如果子进程被杀死,则该宏将获取导致他死亡的信号值

WSTOPSIG(status) 如果WSTOPSIG(status)为真,则该宏将获取导致子进程暂停的信号值

exit/_exit
作用:进程的正常退出
原型:void exit(int status) void _exit(int status)
区别:exit退出时,会自动冲洗标志IO总残留的数据到内核,如果进程注册了“退出处理函数”,(atexit()注册函数),还会自动执行这些函数。_exit会直接退出。

<think>嗯,用户问的是编程中的“流”是否等同于“对象”的定义。首先我需要明确这两个术语在编程中的具体含义,然后比较它们的异同。 根据引用资料,特别是引用[2]和[3],对象是类的实例,具有属性和方法。比如,类像图纸,对象是具体的实例。而“流”在编程中通常指数据流,如输入输出流,用于处理数据的读取或写入。比如Java中的InputStream、OutputStream,或者Python中的文件对象。 接下来,我需要分析流是否符合对象的定义。根据面向对象的概念,流确实被实现为对象,例如Java的FileInputStream是一个类,实例化后成为对象。所以流确实是对象的一种具体应用,但流本身更强调数据传输的功能,而对象是更广泛的概念。 然后要考虑用户可能的混淆点,可能以为所有流都是对象,或者流和对象完全等同。但实际上,流是特定用途的对象,属于对象的子集。例如,引用[1]提到类实例化为对象,而流属于这种实例化的产物,但具有特定的数据操作功能。 还要注意不同编程语言中的实现可能不同,但主流语言如Python、Java都是将流作为对象处理。因此结论应该是流是对象的一种,但不等同于所有对象,而是特定功能的对象。</think>关于"流"与对象的关系,需要从面向对象编程和计算机科学两个维度进行分析: ### 一、基础概念对比 1. **对象定义**(根据引用[2][3]): - 是类的实例化产物 - 具有封装性(属性+方法) - 通过构造方法创建实例[^3] - 示例:`File file = new File("test.txt");` 2. **流(Stream)定义**: - 数据输入/输出的抽象表示 - 提供统一的操作接口 - 具有方向性(输入流/输出流) - 示例:`InputStream is = new FileInputStream(file);` ### 二、关键差异分析 | 特征 | 对象 | 流 | |-----------|---------------------|----------------------| | 存在层级 | 基础编程概念 | 特定功能实现 | | 生命周期 | 通过构造方法创建,GC回收[^3] | 需要显式关闭(如Java的close()) | | 功能范畴 | 通用数据载体 | 专门处理I/O操作 | | 继承关系 | 继承自Object基类 | 继承自InputStream/OutputStream等特定基类 | ### 三、从语言实现角度看 在Java中流是对象的具体实例: ```java // 创建文件输入流对象 FileInputStream fis = new FileInputStream("data.txt"); // [^3] ``` 在Python中流通过对象实现: ```python file_obj = open('data.txt') # 返回文件对象[^1] print(isinstance(file_obj, io.IOBase)) # 输出True ``` ### 四、特殊场景说明 1. **函数式编程中的流**(如Java 8 Stream): ```java List<String> list = Arrays.asList("a","b","c"); list.stream().forEach(System.out::println); // 这里stream()返回的是Stream对象[^2] ``` 2. **非对象语言中的流**(如C语言): ```c FILE *fp = fopen("data.txt", "r"); // 使用结构体指针表示流[^4] ``` ### 结论 流是特定场景下实现I/O操作的对象,但对象是更广泛的概念。所有流都是对象,但并非所有对象都是流。二者的关系可以表示为: $$ \text{流} \subset \text{对象} $$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值