深入理解PHP中“Call to a member function getOriginalExtension() on null“错误

在PHP开发过程中,我们经常会遇到一些错误提示,其中之一就是"Call to a member function getOriginalExtension() on null"。这个错误通常发生在调用一个对象的方法时,而该对象为null。本文将详细介绍这个错误的原因和解决方法,并提供一个完整的实例来帮助读者更好地理解和应用。

在这里插入图片描述

1. 错误原因

"Call to a member function getOriginalExtension() on null"错误表示在调用一个对象的getOriginalExtension()方法时,该对象为null。换句话说,你试图在一个不存在的对象上调用一个方法。

这种错误通常发生在以下情况下:

  • 对象未被正确初始化或赋值。
  • 对象在调用方法之前被销毁或丢失引用。
  • 对象的属性或方法被修改或删除。

2. 解决方法

要解决"Call to a member function getOriginalExtension() on null"错误,你需要确保在调用方法之前,对象已经被正确初始化并赋值。以下是一些常见的解决方法:

2.1 确保对象被正确初始化

在使用对象之前,确保它已经被正确初始化。这可以通过创建一个新的实例来实现。例如,如果你有一个名为$file的变量,你可以使用如下代码来初始化它:

$file = new File(); // 假设File是一个自定义的类,用于处理文件操作

2.2 确保对象没有被销毁或丢失引用

如果对象在调用方法之前被销毁或丢失引用,你需要确保它在方法执行期间仍然有效。这可以通过将对象存储在一个持久的变量中来实现,或者使用静态变量来保持对象的引用。例如:

class MyClass {
    private static $file; // 静态变量用于存储文件对象引用

    public function myMethod() {
        if (self::$file === null) {
            self::$file = new File(); // 如果文件对象为null,则重新初始化它
        }
        // 在这里调用文件对象的方法,如getOriginalExtension()
    }
}

2.3 确保对象的属性或方法没有被修改或删除

如果对象的属性或方法被修改或删除,你需要确保它们仍然存在。检查你的代码,确保没有意外地修改或删除了对象的属性或方法。如果需要修改或删除属性或方法,请谨慎操作,并确保不会对其他代码造成影响。

3. 完整实例

下面是一个包含"Call to a member function getOriginalExtension() on null"错误的完整实例,以及相应的解决方法:

<?php
class File {
    private $name;
    private $extension;

    public function __construct($name) {
        $this->name = $name;
        $this->extension = $this->getOriginalExtension(); // 这里会触发错误,因为$this为null
    }

    private function getOriginalExtension() {
        return pathinfo($this->name, PATHINFO_EXTENSION); // 获取文件扩展名的方法
    }
}
?>

在上面的示例中,我们在构造函数中调用了getOriginalExtension()方法,但此时对象还没有被正确初始化,所以会触发"Call to a member function getOriginalExtension() on null"错误。为了解决这个问题,我们可以在构造函数中先初始化对象的属性,然后再调用getOriginalExtension()方法。修改后的代码如下:

<?php
class File {
    private $name;
    private $extension;

    public function __construct($name) {
        $this->name = $name; // 先初始化属性name,然后再调用getOriginalExtension()方法
        $this->extension = $this->getOriginalExtension(); // 现在可以正常调用getOriginalExtension()方法了,因为$this已经被正确初始化了
    }

    private function getOriginalExtension() {
        return pathinfo($this->name, PATHINFO_EXTENSION); // 获取文件扩展名的方法
    }
}
?>
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
这上传的资源包含一套我工作常用的模板库,及不需要MFC支持的excel操作接口,导出函数调用栈(dump stack)接口,可以直接用VS2008运行TestCodeLib.sln来根据unit test来了解用法。 ⑴ 需求(requirements) 重量级的BOOST非常强大,但有时候项目没有引入它,这时候我们需要自己的模板库。 BOOST is very powerful, but some projects have not include BOOST library. So we need out own template type trait library -- it is the responsibility of this lightweight library. 即使BOOST非常强大,但有些常用的功能其也没有,而经常性的代码又需要这些功能。比如把运行期数据转换为元程序需要的编译期数据。 Even if BOOST is very powerful,it can't still meet all requirements. e.g. convert runtime data into compile period data needed by metaprogramming. /*************************************************************************************************************************************/ ⑵ 益处(advantage) 此泛型库抽象了一些常用的业务需求,可以避免大量的重复工作。 它是完全泛型的并且是类型安全的(没有强制类型转换),如果使用错误将导致编译失败,从而提高了正确率(正确性由编译器保证)。 这个库的很多模板类型推导不需要C++11的支持,这是一个大的优势(VS2010才开始支持C++11)。 this general library draws out some common business and avoid unnecessary repeat work. it is completed general and type-safe(without any type cast), mistake(s) will cause compile failure, so it improves correctness. In this library , type deduce need't C++11's support, it is big advantage. (VS2010 begin to support C++11) /*************************************************************************************************************************************/ ⑶ 用法(usage) 下载这个库后,使用VS打开.\CodeLib\testcase\TestCodeLib\TestCodeLib.sln,直接按F5启动,即可以看到许多单元测试的用法/测试用例的输出。 如果需要使用某功能,可以参考其对应的测试代码的用法。(每个功能文件.\CodeLib\include\MiniMPL\xxx.hpp,都对应一个测试文件.\CodeLib\testcase\MiniMPL\test_xxx.hpp) (这个库的使用及修改是完全自由的,只需要保留文件头的注释即可) usage: download this library, open .\CodeLib\testcase\TestCodeLib\TestCodeLib.sln with VS,you can see many usage/test output of unit test. every feature has according unit test file, it shows its usage. e.g. .\CodeLib\include\MiniMPL\xxx.hpp has according ".\CodeLib\testcase\MiniMPL\test_xxx.hpp" this library is all free, the only requirement is that you need to keep the comments in header file. /*********************************************************************************************************************************************************/ ⑷ 本库提供的主要功能介绍: major feature in this lib: ◆ [typeTraits.hpp] ★ 测试类型的基本属性,比如IsConst/IsVoliate/IsRef/isAtomType/isBuildInType/isEnumType/IsIterator/IsPointer/isString/isInnerFloat/isArray/IsBaseDerive/.... ★ 转换类型的基本属性,比如AddConst/AddVoliate/AddRef/AddPointer,..,RemoveConst/RemoveVoliate/RemoveRef/RemovePointer,... 这类功能是元程序库的基本支持组件,其它库(比如boost)也提供了,但本库时提供的检测属性更多。 ☆ detect type property. e.g.IsConst/IsVoliate/IsRef/isAtomType/isBuildInType/isEnumType/IsIterator/IsPointer/isString/isInnerFloat/isArray/IsBaseDerive/.... ☆ convert type basic qualifier,e.g. AddConst/AddVoliate/AddRef/AddPointer,..,RemoveConst/RemoveVoliate/RemoveRef/RemovePointer,... get type traits.e.g.const/voliate/ref/isAtomType/isBuildInType/isEnumType/isString/isInnerFloat/isArray/IsBaseDerive/.... It is base support component of metaprogramming system,it is similiar with BOOST , but this lib provide more. ◆ [typeConvert.hpp] ★ 实现类型的修饰符转换。比如让输出参数类型的修饰符(const/voliate/ref/*)和输入参数类型的修饰符一样。 SameConst/SameVoliate/SameRef/SamePointer/SameAllQualifier/RefAdapter 应用场景:存取结构体的某类成员,当输入参数有某种const/voliate/ref修饰符时,通常要求返回值也有类似的修饰符。 ★ 当把"智能指针/stl迭代器/C指针/前三者嵌套"都视为指针时,其内的最终值(非指针值)是一致的,在模板函数,某些场景需要取得其最终的非指针值。 应用场景:转发模板函数,如 template<typename T> void transmit(T p) { receive(p); } //void receive(int&); 如果transmit的传入实参p为指针类型(比如smartpointer<vector<int*>::iterator>*或者vector<int*>::iterator), 但是转发的接收函数receive的形参为非指针类型(比如int&),理论上是可以实现转换的。 Get::finalValue接口提供了这种自动的转: template<typename T> void transmit(T p) { receive(Get::finalValue(p)); } ☆ Convert type qualifiers,e.g. addConst/removeConst.. , keep same output qualifier (const/voliate/ref/*) with input type. apply scenario: get member of one structure object. ☆ Think "stlSmartptr<T>/StlContainer<T>::iterator/T*" as pointer, their inner non-pointer value is same. in some scenario, the final non-pointer value is needed. e.g. template<typename T> void transmit(T p) { receive(p); } //void receive(int&); if real paremeter "p" is smartpointer<vector<int*>::iterator>* or vector<int*>::iterator , but needed parameter by "receive" is int&, in theory it is OK. Get::finalValue provide this conversion: template<typename T> void transmit(T p) { receive(Get::finalValue(p)); } ◆ [traverseTypeSet.hpp] ★ C++语法不支持模板函数/模板成员函数作为回调函数。本库采用了封装,可以支持模板函数的回调,并且支持最多7个可变参数(可以简易扩充参数个数)。 可以遍历一个TypeList或者枚举值范围CEnumRange,然后以满足条件的类型回调用户的模板函数。 其广泛的应用场景即是把运行期数据以一种非hard-code的方式转化为编译期数据,从而满足元程序对编译期数据的需求。 ☆ C++ doesn't support template-based callback function. this lib package support template-based callback function(MAX 7 various parameters,easy to expand). It can traverse one TypeList or enum value , then call user's template function by suitable type/enum value. This feature converts runtime data into compile data to meet metaprogramming requirement without hard-code way, it is one big advantage. ◆ [functionTraits.hpp] ★ 获取任意类型函数的各种特征,比如函数的所有参数Params_T,返回值类型Return_T,对象类型Object_T(如果是成员函数),第N个参数的类型GetFunctionParam<F,N>, 这些类型都是包含修饰符(const/voliate/ref)的完整类型。 这些组件对于操作函数非常重要。 ☆ get some traits of any function, include all parameter type "Params_T",return type "Return_T", host type "Object_T"(if member-function) , No.x parameter type "GetFunctionParam<F,x>". this type include all signature qualifiers. This component is very important for metaprogramming based on function. ◆ 有时候STL的算法并不好用,经常是为了第三个参数需要自己写一个专用的琐碎的小函数。 虽然可以用std的bind或者boost的lambda,但是对于某些嵌套情况,用起来非常麻烦,这个库提供了下面的一些解决方式: sometimes STL algorithm is not good and it needs one traival function object(third parameter) , althrough std::bind/boost::lambda is available, but for some nest case, it is very hard to be used.this library provide below features: [function.hpp] ★ 把既有的多元函数转换为一元函数对象UnaryFunction。它通常应用于泛型(比较/排序/遍历)算法的第三个参数。 ☆ convert existing multi-parameters into unary function, it is general used as 3rd parameter in general algorithm. e.g. stl::for_each [functionobject.hpp] ★ 把一些常用目的的函数封装成函数对象,比如"比较器/测试器" ☆ function object with special abstract targart. e.g. "comparer/Tester" ◆ [functionCreater.hpp] ★ 把多元函数封装为一元函数的帮助函数。(一元函数对象的类型通常不易于书写) ☆ helper function to pack multi-parameters into unary function.(it is hard to write unary function object type) ◆ [paramPackage.hpp] ★ 实现了把任意多个(最多7个,可简易扩充),任意类型的参数封装成一个参数以利于数据传递。 ☆ pack any number parameter (max 7,easy expand) into one parameter . it is easy to transfer. ◆ [classregister.hpp] ★ MFC的动态创建不是泛型的,创造出来的对象必须是CObject的派生类,而且支持的创造方式单一,不够灵活有时候甚至不能满足需求。 本库里提供了一个泛型的动态创建方式,可以以多种灵活的方式甚至用户自定义的方式来匿名动态创建对象,创建的对象基类可以由用户指定(必须存在派生关系)。 ☆ like MFC's DYNAMIC_CREATE, but the one of MFC is not general,the instance MUST be drived from class CObject, MFC dynamic creation has only one create way,sometimes it is not enough。 this library provides general dynamic create way, can create object by multiple ways , even customized way.and base class can be specified by user. ◆ [callbackWorker.hpp] ★ 最易于使用的回调函数是无参数的回调函数。 此功能可以把任意多个参数的多元(成员/非成员)函数封装成一个无参数函数,作为简单的回调函数。 ☆ best callback function is non-parameter function. This feature packs multiple-parameter function into one no-parameter function, it is easy to be used as callback function. ◆ [memberPtr.hpp] ★ 以统一的方式实现了任意级数的结构体成员的存和取,比如多级结构体嵌套。例子:a.m_b.m_c.m_d.....m_x,非常易于在模板设计使用。 ☆ access any level member of structure object by one unified way. e.g:a.m_b.m_c.m_d.....m_x,it is easy to be used in template componment. ◆ [anyObject.hpp] ★ 任意对象类(CAnyObject)。提供模板化的指针操作符,如果不支持用户指定指针类型,则转换结果为NULL,从而保证正确性。 ☆ package any object(CAnyObject), it operator function is template-based. if it doesn't support conversion, it return NULL. ◆ [dataset.hpp] ★ 把STL容器和经典数组封装成统一的形式,在使用上不再区别对待。对于C数组,将会自动检测越界情况。 ★ 可以使用初始化列表对数组,STL容器进行(反复)初始化。例如:vector<int> a={1,2,3,45,2}; ☆ pack STL container and class array into unified object with several same interfaces. ☆ can initialize array/stl container with initalization list repeated. e.g. vector<int> a={1,2,3,45,2}; ◆ [macroLoop.hpp] ★ 当多条语句的差别仅仅是一个数字时,可以利用提供的循环宏简化成一条宏语句,从而简化书写。用法可参见对应的单元测试例子。 ☆ if only one number is different in multiple statements, can use one macro loop to simplify them (one macro statement) usage refer to unit test. ◆ [mathOperator.hpp] ★ 泛型的数学操作符。"equal/lesser/NotBinary/NotUnary/notEqual/lesserEqual/greater/greaterEqual及交换函数swap/swapif" ☆ general math operator. "equal/lesser/NotBinary/NotUnary/notEqual/lesserEqual/greater/greaterEqual and swap/swapif" /*************************************************************************************************************************************/ ⑸ 感谢及借鉴: 本库的占位符[placeHolder.hpp]借鉴于boost库,感谢boost库的大师们的灵感。 typelist来自loki库,但是把命名空间Loki改为MiniMPL以避免频繁的命名域切入/切出,感谢Andrei Alexandrescu的精彩演绎与启发. thanks and borrow: Args [placeHolder.h] comes from BOOST::MPL. thanks for BOOST team. typelist comes from loki lib with tiny modification(rename namespace loki to MiniMPL to avoid field switch frequently).thanks for Andrei Alexandrescu
目录树 下面再给个样例 ├─Makefile │ ├─boot │ bootsect.s │ head.s │ setup.s │ ├─fs │ bitmap.c │ block_dev.c │ buffer.c │ char_dev.c │ exec.c │ fcntl.c │ file_dev.c │ file_table.c │ inode.c │ ioctl.c │ Makefile │ namei.c │ open.c │ pipe.c │ read_write.c │ stat.c │ super.c │ truncate.c │ ├─include │ │ a.out.h │ │ const.h │ │ ctype.h │ │ errno.h │ │ fcntl.h │ │ signal.h │ │ stdarg.h │ │ stddef.h │ │ string.h │ │ termios.h │ │ time.h │ │ unistd.h │ │ utime.h │ │ │ ├─asm │ │ io.h │ │ memory.h │ │ segment.h │ │ system.h │ │ │ ├─linux │ │ config.h │ │ fs.h │ │ hdreg.h │ │ head.h │ │ kernel.h │ │ mm.h │ │ sched.h │ │ sys.h │ │ tty.h │ │ │ └─sys │ stat.h │ times.h │ types.h │ utsname.h │ wait.h │ ├─init │ main.c │ ├─kernel │ │ asm.s │ │ exit.c │ │ fork.c │ │ mktime.c │ │ panic.c │ │ printk.c │ │ sched.c │ │ signal.c │ │ sys.c │ │ system_call.s │ │ vsprintf.c │ │ │ ├─blk_drv │ │ blk.h │ │ floppy.c │ │ hd.c │ │ ll_rw_blk.c │ │ Makefile │ │ ramdisk.c │ │ │ ├─chr_drv │ │ console.c │ │ keyboard.S │ │ Makefile │ │ rs_io.s │ │ serial.c │ │ tty_io.c │ │ tty_ioctl.c │ │ │ └─math │ Makefile │ math_emulate. │ ├─lib │ close.c │ ctype.c │ dup.c │ errno.c │ execve.c │ Makefile │ malloc.c │ open.c │ setsid.c │ string.c │ wait.c │ write.c │ _exit.c │ ├─mm │ Makefile │ memory.c │ page.s │ └─tools build.c 样例 main。c 用sourceinsight软件阅读 很方便 /* * linux/init/main.c * * (C) 1991 Linus Torvalds */ #define __LIBRARY__ // 定义该变量是为了包括定义在unistd.h 的内嵌汇编代码等信息。 #include // *.h 头文件所在的默认目录是include/,则在代码就不用明确指明位置。 // 如果不是UNIX 的标准头文件,则需要指明所在的目录,并用双引号括住。 // 标准符号常数与类型文件。定义了各种符号常数和类型,并申明了各种函数。 // 如果定义了__LIBRARY__,则还包括系统调用号和内嵌汇编代码_syscall0()等。 #include // 时间类型头文件。其最主要定义了tm 结构和一些有关时间的函数原形。 /* * we need this inline - forking from kernel space will result * in NO COPY ON WRITE (!!!), until an execve is executed. This * is no problem, but for the stack. This is handled by not letting * main() use the stack at all after fork(). Thus, no function * calls - which means inline code for fork too, as otherwise we * would use the stack upon exit from 'fork()'. * * Actually only pause and fork are needed inline, so that there * won't be any messing with the stack from main(), but we define * some others too. */ /* * 我们需要下面这些内嵌语句 - 从内核空间创建进程(forking)将导致没有写时复制(COPY ON WRITE)!!! * 直到一个执行execve 调用。这对堆栈可能带来问题。处理的方法是在fork()调用之后不让main()使用 * 任何堆栈。因此就不能有函数调用 - 这意味着fork 也要使用内嵌的代码,否则我们在从fork()退出 * 时就要使用堆栈了。 * 实际上只有pause 和fork 需要使用内嵌方式,以保证从main()不会弄乱堆栈,但是我们同时还 * 定义了其它一些函数。 */ static inline _syscall0 (int, fork) // 是unistd.h 的内嵌宏代码。以嵌入汇编的形式调用 // Linux 的系统调用断0x80。该断是所有系统调用的 // 入口。该条语句实际上是int fork()创建进程系统调用。 // syscall0 名称最后的0 表示无参数,1 表示1 个参数。 static inline _syscall0 (int, pause) // int pause()系统调用:暂停进程的执行,直到 // 收到一个信号。 static inline _syscall1 (int, setup, void *, BIOS) // int setup(void * BIOS)系统调用,仅用于 // linux 初始化(仅在这个程序被调用)。 static inline _syscall0 (int, sync) // int sync()系统调用:更新文件系统。 #include // tty 头文件,定义了有关tty_io,串行通信方面的参数、常数。 #include // 调度程序头文件,定义了任务结构task_struct、第1 个初始任务 // 的数据。还有一些以宏的形式定义的有关描述符参数设置和获取的 // 嵌入式汇编函数程序。 #include // head 头文件,定义了段描述符的简单结构,和几个选择符常量。 #include // 系统头文件。以宏的形式定义了许多有关设置或修改 // 描述符/断门等的嵌入式汇编子程序。 #include // io 头文件。以宏的嵌入汇编程序形式定义对io 端口操作的函数。 #include // 标准定义头文件。定义了NULL, offsetof(TYPE, MEMBER)。 #include // 标准参数头文件。以宏的形式定义变量参数列表。主要说明了-个 // 类型(va_list)和三个宏(va_start, va_arg 和va_end),vsprintf、 // vprintf、vfprintf。 #include #include // 文件控制头文件。用于文件及其描述符的操作控制常数符号的定义。 #include // 类型头文件。定义了基本的系统数据类型。 #include // 文件系统头文件。定义文件表结构(file,buffer_head,m_inode 等)。 static char printbuf[1024]; // 静态字符串数组。 extern int vsprintf (); // 送格式化输出到一字符串(在kernel/vsprintf.c,92 行)。 extern void init (void); // 函数原形,初始化(在168 行)。 extern void blk_dev_init (void); // 块设备初始化子程序(kernel/blk_drv/ll_rw_blk.c,157 行) extern void chr_dev_init (void); // 字符设备初始化(kernel/chr_drv/tty_io.c, 347 行) extern void hd_init (void); // 硬盘初始化程序(kernel/blk_drv/hd.c, 343 行) extern void floppy_init (void); // 软驱初始化程序(kernel/blk_drv/floppy.c, 457 行) extern void mem_init (long start, long end); // 内存管理初始化(mm/memory.c, 399 行) extern long rd_init (long mem_start, int length); //虚拟盘初始化(kernel/blk_drv/ramdisk.c,52) extern long kernel_mktime (struct tm *tm); // 建立内核时间(秒)。 extern long startup_time; // 内核启动时间(开机时间)(秒)。 /* * This is set up by the setup-routine at boot-time */ /* * 以下这些数据是由setup.s 程序在引导时间设置的(参见第2 章2.3.1 节的表2.1)。 */ #define EXT_MEM_K (*(unsigned short *)0x90002) // 1M 以后的扩展内存大小(KB)。 #define DRIVE_INFO (*(struct drive_info *)0x90080) // 硬盘参数表基址。 #define ORIG_ROOT_DEV (*(unsigned short *)0x901FC) // 根文件系统所在设备号。 /* * Yeah, yeah, it's ugly, but I cannot find how to do this correctly * and this seems to work. I anybody has more info on the real-time * clock I'd be interested. Most of this was trial and error, and some * bios-listing reading. Urghh. */ /* * 是啊,是啊,下面这段程序很差劲,但我不知道如何正确地实现,而且好象它还能运行。如果有 * 关于实时时钟更多的资料,那我很感兴趣。这些都是试探出来的,以及看了一些bios 程序,呵! */ #define CMOS_READ(addr) ({ \ // 这段宏读取CMOS 实时时钟信息。 outb_p (0x80 | addr, 0x70); \ // 0x70 是写端口号,0x80|addr 是要读取的CMOS 内存地址。 inb_p (0x71); \ // 0x71 是读端口号。 } ) #define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10) // 将BCD 码转换成数字。 static void time_init (void) // 该子程序取CMOS 时钟,并设置开机时间??startup_time(秒)。 { struct tm time; do { time.tm_sec = CMOS_READ (0); // 参见后面CMOS 内存列表。 time.tm_min = CMOS_READ (2); time.tm_hour = CMOS_READ (4); time.tm_mday = CMOS_READ (7); time.tm_mon = CMOS_READ (8); time.tm_year = CMOS_READ (9); } while (time.tm_sec != CMOS_READ (0)); BCD_TO_BIN (time.tm_sec); BCD_TO_BIN (time.tm_min); BCD_TO_BIN (time.tm_hour); BCD_TO_BIN (time.tm_mday); BCD_TO_BIN (time.tm_mon); BCD_TO_BIN (time.tm_year); time.tm_mon--; startup_time = kernel_mktime (&time); } static long memory_end = 0; // 机器具有的内存(字节数)。 static long buffer_memory_end = 0; // 高速缓冲区末端地址。 static long main_memory_start = 0; // 主内存(将用于分页)开始的位置。 struct drive_info { char dummy[32]; } drive_info; // 用于存放硬盘参数表信息。 void main (void) /* This really IS void, no error here. */ { /* The startup routine assumes (well, ...) this */ /* 这里确实是void,并没错。在startup 程序(head.s)就是这样假设的。 */ // 参见head.s 程序第136 行开始的几行代码。 /* * Interrupts are still disabled. Do necessary setups, then * enable them */ /* * 此时断仍被禁止着,做完必要的设置后就将其开启。 */ // 下面这段代码用于保存: // 根设备号 ??ROOT_DEV; 高速缓存末端地址??buffer_memory_end; // 机器内存数??memory_end;主内存开始地址 ??main_memory_start; ROOT_DEV = ORIG_ROOT_DEV; drive_info = DRIVE_INFO; memory_end = (1 << 20) + (EXT_MEM_K < 16 * 1024 * 1024) // 如果内存超过16Mb,则按16Mb 计。 memory_end = 16 * 1024 * 1024; if (memory_end > 12 * 1024 * 1024) // 如果内存>12Mb,则设置缓冲区末端=4Mb buffer_memory_end = 4 * 1024 * 1024; else if (memory_end > 6 * 1024 * 1024) // 否则如果内存>6Mb,则设置缓冲区末端=2Mb buffer_memory_end = 2 * 1024 * 1024; else buffer_memory_end = 1 * 1024 * 1024; // 否则则设置缓冲区末端=1Mb main_memory_start = buffer_memory_end; // 主内存起始位置=缓冲区末端; #ifdef RAMDISK // 如果定义了虚拟盘,则主内存将减少。 main_memory_start += rd_init (main_memory_start, RAMDISK * 1024); #endif // 以下是内核进行所有方面的初始化工作。阅读时最好跟着调用的程序深入进去看,实在看 // 不下去了,就先放一放,看下一个初始化调用 -- 这是经验之谈?。 mem_init (main_memory_start, memory_end); trap_init (); // 陷阱门(硬件断向量)初始化。(kernel/traps.c,181 行) blk_dev_init (); // 块设备初始化。 (kernel/blk_dev/ll_rw_blk.c,157 行) chr_dev_init (); // 字符设备初始化。 (kernel/chr_dev/tty_io.c,347 行) tty_init (); // tty 初始化。 (kernel/chr_dev/tty_io.c,105 行) time_init (); // 设置开机启动时间??startup_time(见76 行)。 sched_init (); // 调度程序初始化(加载了任务0 的tr, ldtr) (kernel/sched.c,385) buffer_init (buffer_memory_end); // 缓冲管理初始化,建内存链表等。(fs/buffer.c,348) hd_init (); // 硬盘初始化。 (kernel/blk_dev/hd.c,343 行) floppy_init (); // 软驱初始化。 (kernel/blk_dev/floppy.c,457 行) sti (); // 所有初始化工作都做完了,开启断。 // 下面过程通过在堆栈设置的参数,利用断返回指令切换到任务0。 move_to_user_mode (); // 移到用户模式。 (include/asm/system.h,第1 行) if (!fork ()) { /* we count on this going ok */ init (); } /* * NOTE!! For any other task 'pause()' would mean we have to get a * signal to awaken, but task0 is the sole exception (see 'schedule()') * as task 0 gets activated at every idle moment (when no other tasks * can run). For task0 'pause()' just means we go check if some other * task can run, and if not we return here. */ /* 注意!! 对于任何其它的任务,'pause()'将意味着我们必须等待收到一个信号才会返 * 回就绪运行态,但任务0(task0)是唯一的意外情况(参见'schedule()'),因为任务0 在 * 任何空闲时间里都会被激活(当没有其它任务在运行时),因此对于任务0'pause()'仅意味着 * 我们返回来查看是否有其它任务可以运行,如果没有的话我们就回到这里,一直循环执行'pause()'。 */ for (;;) pause (); } static int printf (const char *fmt, ...) // 产生格式化信息并输出到标准输出设备stdout(1),这里是指屏幕上显示。参数'*fmt'指定输出将 // 采用的格式,参见各种标准C 语言书籍。该子程序正好是vsprintf 如何使用的一个例子。 // 该程序使用vsprintf()将格式化的字符串放入printbuf 缓冲区,然后用write()将缓冲区的内容 // 输出到标准设备(1--stdout)。 { va_list args; int i; va_start (args, fmt); write (1, printbuf, i = vsprintf (printbuf, fmt, args)); va_end (args); return i; } static char *argv_rc[] = { "/bin/sh", NULL}; // 调用执行程序时参数的字符串数组。 static char *envp_rc[] = { "HOME=/", NULL}; // 调用执行程序时的环境字符串数组。 static char *argv[] = { "-/bin/sh", NULL}; // 同上。 static char *envp[] = { "HOME=/usr/root", NULL}; void init (void) { int pid, i; // 读取硬盘参数包括分区表信息并建立虚拟盘和安装根文件系统设备。 // 该函数是在25 行上的宏定义的,对应函数是sys_setup(),在kernel/blk_drv/hd.c,71 行。 setup ((void *) &drive_info); (void) open ("/dev/tty0", O_RDWR, 0); // 用读写访问方式打开设备“/dev/tty0”, // 这里对应终端控制台。 // 返回的句柄号0 -- stdin 标准输入设备。 (void) dup (0); // 复制句柄,产生句柄1 号 -- stdout 标准输出设备。 (void) dup (0); // 复制句柄,产生句柄2 号 -- stderr 标准出错输出设备。 printf ("%d buffers = %d bytes buffer space\n\r", NR_BUFFERS, NR_BUFFERS * BLOCK_SIZE); // 打印缓冲区块数和总字节数,每块1024 字节。 printf ("Free mem: %d bytes\n\r", memory_end - main_memory_start); //空闲内存字节数。 // 下面fork()用于创建一个子进程(子任务)。对于被创建的子进程,fork()将返回0 值, // 对于原(父进程)将返回子进程的进程号。所以180-184 句是子进程执行的内容。该子进程 // 关闭了句柄0(stdin),以只读方式打开/etc/rc 文件,并执行/bin/sh 程序,所带参数和 // 环境变量分别由argv_rc 和envp_rc 数组给出。参见后面的描述。 if (!(pid = fork ())) { close (0); if (open ("/etc/rc", O_RDONLY, 0)) _exit (1); // 如果打开文件失败,则退出(/lib/_exit.c,10)。 execve ("/bin/sh", argv_rc, envp_rc); // 装入/bin/sh 程序并执行。 _exit (2); // 若execve()执行失败则退出(出错码2,“文件或目录不存在”)。 } // 下面是父进程执行的语句。wait()是等待子进程停止或终止,其返回值应是子进程的进程号(pid)。 // 这三句的作用是父进程等待子进程的结束。&i 是存放返回状态信息的位置。如果wait()返回值不 // 等于子进程号,则继续等待。 if (pid > 0) while (pid != wait (&i)) /* nothing */ ; // 如果执行到这里,说明刚创建的子进程的执行已停止或终止了。下面循环首先再创建一个子进程, // 如果出错,则显示“初始化程序创建子进程失败”的信息并继续执行。对于所创建的子进程关闭所有 // 以前还遗留的句柄(stdin, stdout, stderr),新创建一个会话并设置进程组号,然后重新打开 // /dev/tty0 作为stdin,并复制成stdout 和stderr。再次执行系统解释程序/bin/sh。但这次执行所 // 选用的参数和环境数组另选了一套(见上面165-167 行)。然后父进程再次运行wait()等待。如果 // 子进程又停止了执行,则在标准输出上显示出错信息“子进程pid 停止了运行,返回码是i”,然后 // 继续重试下去…,形成“大”死循环。 while (1) { if ((pid = fork ()) < 0) { printf ("Fork failed in init\r\n"); continue; } if (!pid) { close (0); close (1); close (2); setsid (); (void) open ("/dev/tty0", O_RDWR, 0); (void) dup (0); (void) dup (0); _exit (execve ("/bin/sh", argv, envp)); } while (1) if (pid == wait (&i)) break; printf ("\n\rchild %d died with code %04x\n\r", pid, i); sync (); } _exit (0); /* NOTE! _exit, not exit() */ }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程式员阿波

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

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

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

打赏作者

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

抵扣说明:

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

余额充值