你管这叫操作系统源码(十二)

Rust in Action笔记 第十一、十二章 简易操作系统内核 本章讲了如何用Rust写一个简易的操作系统(FledgeOS),除了Rust的知识外,可以了解操作系统主要的组成部分,加深对操作系统的理解。 阅读详情

打开终端设备文件

上篇说到setup一系列折腾就是为了open函数打开/dev/tty0,还有后面的两个 dup。open 函数会触发 0x80 中断,最终调用到 sys_open 这个系统调用函数,相信你已经很熟悉了。

open.c:

struct file file_table[64] = {0};

int sys_open(const char * filename,int flag,int mode) {
    struct m_inode * inode;
    struct file * f;
    int i,fd;
    mode &= 0777 & ~current->umask;

    for(fd=0 ; fd<20; fd++)
        if (!current->filp[fd])
            break;
    if (fd>=20)
        return -EINVAL;
    current->close_on_exec &= ~(1<<fd);

    f=0+file_table;
    for (i=0 ; i<64 ; i++,f++)
        if (!f->f_count) break;
    if (i>=64)
        return -EINVAL;

    (current->filp[fd]=f)->f_count++;

    i = open_namei(filename,flag,mode,&inode);

    if (S_ISCHR(inode->i_mode))
        if (MAJOR(inode->i_zone[0])==4) {
            if (current->leader && current->tty<0) {
                current->tty = MINOR(inode->i_zone[0]);
                tty_table[current->tty].pgrp = current->pgrp;
            }
        } else if (MAJOR(inode->i_zone[0])==5)
            if (current->tty<0) {
                iput(inode);
                current->filp[fd]=NULL;
                f->f_count=0;
                return -EPERM;
            }
    if (S_ISBLK(inode->i_mode))
        check_disk_change(inode->i_zone[0]);

    f->f_mode = inode->i_mode;
    f->f_flags = flag;
    f->f_count = 1;
    f->f_inode = inode;
    f->f_pos = 0;
    return (fd);
}

先用一张图来描述这一大坨代码的作用:

ch17-5

第一步,在进程文件描述符数组 filp 中找到一个空闲项。还记得进程的 task_struct 结构吧,其中有一个 filp 数组的字段,就是我们常说的文件描述符数组,这里先找到一个空闲项,将空闲地方的索引值即为 fd。

int sys_open(const char * filename,int flag,int mode) {
    ...
    for(int fd=0 ; fd<20; fd++)
        if (!current->filp[fd])
            break;
    if (fd>=20)
        return -EINVAL;
    ...
}

由于此时当前进程,也就是进程 1,还没有打开过任何文件,所以 0 号索引处就是空闲的,fd 自然就等于 0。

第二步,在系统文件表 file_table 中找到一个空闲项。

int sys_open(const char * filename,int flag,int mode) {
    int i;
    ...
    int f=0+file_table;
    for (i=0 ; i<64; i++,f++)
        if (!f->f_count) break;
    if (i>=64)
        return -EINVAL;
    ...
}

注意到,进程的 filp 数组大小是 20,系统的 file_table 大小是 64,可以得出,每个进程最多打开 20 个文件,整个系统最多打开 64 个文件。

第三步,将进程的文件描述符数组项和系统的文件表项,对应起来。代码中就是一个赋值操作。

int sys_open(const char * filename,int flag,int mode) {
    ...
    current->filp[fd] = f;
    ...
}

第四步,根据文件名从文件系统中找到这个文件。其实相当于找到了这个 tty0 文件对应的 inode 信息。

int sys_open(const char * filename,int flag,int mode) {
    ...
    // filename = "/dev/tty0"
    // flag = O_RDWR 读写
    // 不是创建新文件,所以 mode 没用
    // inode 是返回参数
    open_namei(filename,flag,mode,&inode);
    ...
}

接下来判断 tty0 这个 inode 是否是字符设备,如果是字符设备文件,那么如果设备号是 4 的话,则设置当前进程的 tty 号为该 inode 的子设备号。并设置当前进程tty 对应的tty 表项的父进程组号等于进程的父进程组号。这里我们暂不展开讲。

最后第五步,填充 file 数据。其实就是初始化这个 f,包括刚刚找到的 inode 值。最后返回给上层文件描述符 fd 的值,也就是零。

int sys_open(const char * filename,int flag,int mode) {
    ...
    f->f_mode = inode->i_mode;
    f->f_flags = flag;
    f->f_count = 1;
    f->f_inode = inode;
    f->f_pos = 0;
    return (fd);
    ...
}

最后再回过头看这张图,是不是就有感觉了?

ch17-5

其实打开一个文件,即刚刚的 open 函数,就是在上述操作后,返回一个 int 型的数值 fd,称作文件描述符。之后我们就可以对着这个文件描述符进行读写。

之所以可以这么方便,是由于通过这个文件描述符,最终能够找到其对应文件的 inode 信息,有了这个信息,就能够找到它在磁盘文件中的位置(当然文件还分为常规文件、目录文件、字符设备文件、块设备文件、FIFO 特殊文件等,这个之后再说),进行读写。

比如读函数的系统调用入口:

int sys_read (unsigned int fd, char *buf, int count) {
    ...
}

写函数的系统调用入口:

int sys_write (unsigned int fd, char *buf, int count) {
    ...
}

入参都有个 int 型的文件描述符 fd,就是刚刚 open 时返回的,就这么简单。

好,我们回过头看:

void init(void) {
    setup((void *) &drive_info);
    (void) open("/dev/tty0",O_RDWR,0);
    (void) dup(0);
    (void) dup(0);
}

本节利用之前 setup 加载过的根文件系统,通过 open 函数,根据文件名找到并打开了一个文件。返回给上层的是一个文件描述符,然后操作系统底层进行了一系列精巧的构造,使得一个进程可以通过一个文件描述符 fd,找到对应文件的 inode 信息。

好了,我们接着再往下看两行代码。接下来,两个一模一样的 dup 函数,什么意思呢?

其实,刚刚的 open 函数返回的为 0 号 fd,这个作为标准输入设备。

接下来的 dup 为 1 号 fd 赋值,这个作为标准输出设备。

再接下来的 dup 为 2 号 fd 赋值,这个作为标准错误输出设备。

熟不熟悉?这就是我们 Linux 中常说的 stdin、stdout、stderr。

那这个 dup 又是什么原理呢?非常简单,首先仍然是通过系统调用方式,调用到 sys_dup 函数。

int sys_dup(unsigned int fildes) {
    return dupfd(fildes,0);
}

// fd 是要复制的文件描述符
// arg 是指定新文件描述符的最小数值
static int dupfd(unsigned int fd, unsigned int arg) {
    ...
    while (arg < 20)
        if (current->filp[arg])
            arg++;
        else
            break;
    ...
    (current->filp[arg] = current->filp[fd])->f_count++;
    return arg;
}

仍然是把一些错误校验的旁路逻辑去掉了。那这个函数的逻辑非常单纯,就是从进程的 filp 中找到下一个空闲项,然后把要复制的文件描述符 fd 的信息,统统复制到这里。

那根据上下文,这一步其实就是把 0 号文件描述符,复制到 1 号文件描述符,那么 0 号和 1 号文件描述符,就统统可以通过一条路子,找到最终 tty0 这个设备文件的 inode 信息了。

ch17-5a

那下一个 dup 就自然理解了吧,直接再来一张图:

ch17-5b

ok,进程 1 的 init 函数的前四行就讲完了,此时进程 1 已经比进程 0 多了与 外设交互的能力,具体说来是 tty0 这个外设(也是个文件,因为 Linux 下一切皆文件)交互的能力,这句话怎么理解呢?什么叫多了这个能力?

因为进程 fork 出自己子进程的时候,这个 filp 数组也会被复制,那么当进程 1 fork 出进程 2 时,进程 2 也会拥有这样的映射关系,也可以操作 tty0 这个设备,这就是“能力”二字的体现。

而进程 0 是不具备与外设交互的能力的,因为它并没有打开任何的文件,filp 数组也就没有任何作用。进程 1 刚刚创建的时候,是 fork 的进程 0,所以也不具备这样的能力,而通过 setup 加载根文件系统,open 打开 tty0 设备文件等代码,使得进程 1 具备了与外设交互的能力,同时也使得之后从进程 1 fork 出来的进程 2 也天生拥有和进程 1 同样的与外设交互的能力。

再往后看两行找找感觉:

void init(void) {
    setup((void *) &drive_info);
    (void) open("/dev/tty0",O_RDWR,0);
    (void) dup(0);
    (void) dup(0);
    printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS, \
        NR_BUFFERS*BLOCK_SIZE);
    printf("Free mem: %d bytes\n\r",memory_end-main_memory_start);
}

接下来的两行是个打印语句,其实就是基于刚刚打开并创建的 0,1,2 三个文件描述符而做出的操作。

刚刚也说了 1 号文件描述符被当做标准输出,那我们进入 printf 的实现看看有没有用到它:

static int printf(const char *fmt, ...) {
    va_list args;
    int i;
    va_start(args, fmt);
    write(1,printbuf,i=vsprintf(printbuf, fmt, args));
    va_end(args);
    return i;
}

看,中间有个 write 函数,传入了 1 号文件描述符作为第一个参数。

细节我们先不展开,这里知道它肯定是顺着这个描述符寻找到了相应的 tty0 也就是终端控制台设备,并输出在了屏幕上。我们赶紧看看实际上有没有输出。仍然是 bochs 启动 Linux 0.11 看效果:

ch17-6

看到了吧,真的输出了,你偷偷改下这里的源码,再看看这里的输出有没有变化吧!

本节之后,init 函数后面又要 fork 子进程了,也标志着进程 1 的工作基本结束了,准确说是能力建设的工作结束了,接下来就是控制流程和创建新的进程了。

进程2的创建

void init(void) {
    ...
    if (!(pid=fork())) {
        close(0);
        open("/etc/rc",O_RDONLY,0);
        execve("/bin/sh",argv_rc,envp_rc);
        _exit(2);
    }
    ...
}

我们先尝试口述翻译一遍。

  1. fork 一个新的子进程,此时就是进程 2 了。

  2. 在进程 2 里关闭(close) 0 号文件描述符。

  3. 只读形式打开(open) rc 文件。

  4. 然后执行(execve) sh 程序。

听起来还蛮合逻辑的,创建进程(fork)、关闭(close)、打开(open)、执行(execve)四步走,接下来我们一点点拆解。

fork

前面系列之九中fork中进程基本信息的复制讲过了,fork就是将进程的task_struct结构进行复制,比如进程0 fork出进程1的时候:

ch13-2改

之后,新进程再重写一些基本信息,包括元信息和 tss 里的寄存器信息。再之后,用 copy_page_tables 复制了一下页表(这里涉及到写时复制的伏笔),比如进程 0 复制出进程 1 的时候,页表是这样复制的:

ch16-3

而这里的进程 1 fork 出进程 2,也是同样的流程,不同之处在于两点细节:

  1. 进程 1 打开了三个文件描述符并指向了 tty0,那这个也被复制到进程 2 了,具体说来就是进程结构 task_struct 里的 flip[] 数组被复制了一份:

    struct task_struct {
        ...
        struct file *filp[NR_OPEN];
        ...
    };
    

    而进程 0 fork 出进程 1 时是没有复制这部分信息的,因为进程 0 没有打开任何文件。这也是刚刚说的与外设交互能力的体现,即进程 0 没有与外设交互的能力,进程 1 有,哎,其实就是这个 flip 数组里有没有东西而已嘛~

  2. 进程 0 复制进程 1 时页表的复制只有 160 项,也就是映射 640K,而之后进程的复制,统统都是复制 1024 项,也就是映射 4M 空间:

    int copy_page_tables(unsigned long from,unsigned long to,long size) {
        ...
        nr = (from==0)?0xA0:1024;
        ...
    }
    

整体看就是如图所示:

ch13-1

除此之外,就没有别的区别了。

close

fork 完之后,后面 if 里面的代码都是进程 2 在执行了。

close(0) 就是关闭 0 号文件描述符,也就是进程 1 复制过来的打开了 tty0 并作为标准输入的文件描述符,那么此时 0 号文件描述符就空出来了。下面是 close 对应的系统调用函数:

int sys_close(unsigned int fd) {   
    ...
    current->filp[fd] = NULL;
    ...
}

open

open 函数以只读形式打开了一个叫 /etc/rc 的文件,刚好占据了 0 号文件描述符的位置:

void init(void) {
    ...
    if (!(pid=fork())) {
        ...
        open("/etc/rc",O_RDONLY,0);
        ...
    }
    ...
}

这个 rc 文件表示配置文件,具体什么内容,取决于你的硬盘里这个位置放了什么内容,与操作系统内核无关,所以我们暂且不用管。此时,进程 2 与进程 1 几乎完全一样,只不过进程 2 通过 close 和 open 操作,将原来进程 1 的指向标准输入的 0 号文件描述符,重新指向了 /etc/rc 文件。

到目前为止,进程 2 与进程 1 的区别,仅仅是将 0 号文件描述符重新指向了 /etc/rc 文件,其他的没啥区别。而这个 rc 文件是干嘛的,现在还不用管,肯定是后面 sh 程序要用到的,到时候在说。

execve

接下来进程 2 就将变得不一样了,会通过一个经典的,也是最难理解的 execve 函数调用,使自己摇身一变,成为 /bin/sh 程序继续运行,这就是下一章的重点。

void init(void) {
    ...
    if (!(pid=fork())) {
        ...
        execve("/bin/sh",argv_rc,envp_rc);
        ...
    }
    ...
}

这里就包含着操作系统究竟是如何加载并执行一个程序的原理,包括如何从文件系统中找到这个文件,如何解析一个可执行文件(在现代的 Linux 里称作 ELF 可执行文件),如何讲可执行文件中的代码和数据加载到内存并运行。

加载到内存并运行又包含着虚拟内存等相关的知识。所以这里面的水很深,了解了这个函数,再加上 fork 函数,基本就可以把操作系统全部核心逻辑都串起来了。

LLVM系列第一章:编译LLVM源码 详细介绍如何利用CMake和Ninja工具来编译及安装LLVM。 阅读详情

相关推荐

操作系统源代码.doc

#include<stdio.h> #include<time.h> #include<stdlib.h> int memoryStartAddress = -1; int memorySize = -1; struct jobList { int id; /* 作业ID */ int size; /* 作业大小(需要的存储空间大小) */ int status; /* 作业状态 0 : new job ,1 : in the memory , 2 : finished . */ struct jobList *next; /* 作业链表指针 */ }; struct freeList { int startAddress; /* 分区起始地址 */ int size; /* 分区大小 */ struct freeList *next; /* 分区链表指针 */ }; struct usedList { int startAddress; /* 分区起始地址 */ int jobID; /* 分区中存放作业ID */ struct usedList *next; /* 分区链表指针 *

单片机AT32系统卡在_sys_open、freopen,点击几次后,才能进入main函数

【问题描述】单片机AT32系统卡在_sys_open、freopen,点击几次后,才能进入main函数 1 . JLink仿真,每次进入main函数需要点击两次执行 2 . 不仿真,进入不了系统 3 . 工程不进入HardFault_Handler() 思考freopen什么文件 ?? 后发现用了stdio.h没有勾选MicroLIB,勾选后,OK ...

藏镜人 1046

有限时间 vs 固定时间 vs 预定时间滑模:稳定性分析与仿真验证方法对比(中)

有限时间滑模控制(FTSMC)和固定时间滑模控制(FixTSMC)是两种重要的控制策略,旨在实现系统状态的快速收敛。FTSMC通过设计滑模面和控制律,确保系统状态在有限时间内收敛到原点,但其收敛时间与初始状态相关。FixTSMC则进一步优化,使收敛时间与初始状态无关,适用于高动态环境,具有更强的鲁棒性和更平滑的控制信号。两种方法均通过李雅普诺夫函数证明其稳定性,并通过仿真验证其有效性。FixTSMC在工程应用中更为可靠,尤其适合安全关键系统。

Lyajpunov的博客 3224

(附源码)计算机毕业设计SSM-在线作业管理系统

项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:SSM + mybatis + Maven + Vue 等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。2.IDE环境:IDEA,Eclipse,Myeclipse都可以。

bishe507的博客 2595

使用Kettle进行数据同步(增量)

文章目录kettle介绍kettle安装kettle使用使用kettle同步关系型数据库数据(MySQL示例)1. 创建一个转换2. 选择表输入3. 格式转换4. 执行脚本5. 创建job使用kettle同步NoSql数据(MongoDB示例)创建转换时的注意事项MongoDBInput怎么创建job的创建如何在Linux上面运行已经创建好的job?1、把创建好的job传输到Linux机器上面2、编辑kjb文件3、执行任务 kettle介绍 Kettle 是一款国外开源的 ETL 工具,纯 Java 编写

JenKin的博客 7351

基于MATLAB/Simulink的四旋翼无人机PID控制与仿真(3)

《基于MATLAB/Simulink的四旋翼无人机PID控制与仿真》的第三篇,将为大家继续展示并完善控制与仿真模型。本文将在前文的基础上,完成控制环节增补、参数设置、测试等工作,在不考虑性能(低超调、快、准等)的前提下,保证系统对于一般输入(阶跃、斜坡)的响应稳定性。

jlx142857的博客 9321

[附源码]java毕业设计课程作业管理系统

项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:SSM + mybatis + Maven + Vue 等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。2.IDE环境:IDEA,Eclipse,Myeclipse都可以。

卓杰计算机程序设计 1313

claude-hub:GitHub上的AI开发助手,让Claude自动帮你写代码、审代码、合代码

代码不再是人写的,而是人引导 AI 写的。在这个时代,GitHub 不再只是一个代码托管平台,而是 AI 开发代理的工作台。每一个 issue、每一条 PR,都可能是一次与 AI 合作的机会。未来属于那些敢于把开发流程交给 AI 来优化的人——他们不是在取代自己,而是在放大自己的影响力。📌关注本系列文章,我们将持续输出关于 AI 编程、GitHub 自动化、AI Agent 产品化的深度分析。

这里汇聚了前沿的技术分享与实用的开发技巧,带你探索从创意到企业的技术创业之路。 1372

(附源码)计算机毕业设计SSM作业管理系统设计

课程信息管理,在课程信息管理页面中可以查看索引、课程名称、课程图片、班级名称、课程视频、课程课件、教师工号、教师姓名、教师邮箱等信息,并可根据需要进行详情、修改、查看评论或删除等操作,如图4-10所示。教师使用功能:首页、个人中心、课程信息管理、课程作业管理、学生作业管理、作业评分管理、系统管理等。

计算机毕设程序设计 1366

上位机图像处理和嵌入式模块部署(qmacvisual入门)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 虽然我们前面学习了很多的知识点,比如说在windows这边,用qt写界面,用opencv写图像处理代码;在linux这边先用树莓派测试,最后再部署到自己的平台上面。但是,对于一个商业软件是怎么运行的,很多时候我们心理还是没有底,或者说,自己写的软件距离一个商业软件还有多远,大部分学习的同学是不知道的。 就算有的同学说,这个时候可以借鉴一下vision pro,或者是

嵌入式-老费,一个分享专业嵌入式知识的blog 1229

Navicat从入门到精通:数据库管理全攻略

本文系统梳理Navicat数据库管理工具的核心应用体系。从基础的安装与多类型数据库连接入手,详解数据库与数据表的创建、修改、删除等操作逻辑;深入数据增删改查的可视化与 SQL 语句双重操作模式,结合Excel/SQL文件的导入导出场景提供实操指南。同时覆盖数据备份恢复、跨库同步等进阶功能,以及视图、存储过程等高级应用。通过对比同类工具突出Navicat的易用性与功能性优势,并针对连接失败、字符乱码等常见问题提供解决方案,帮助读者从零基础掌握数据库全生命周期管理,提升数据操作效率与安全性。

邓邓子的博客 1896

STM32F407单片机编程入门(十二) FreeRTOS实时操作系统详解及实战含源码

本文介绍FreeRTOS实时操作系统,以及在STM32F407VET6中移植运行FreeRTOS,STM32软件开发中应用FreeRTOS,程序结构清晰,单片机执行效率提升许多

光子物联科技主要介绍STM32单片机,GD32单片机以及各种传感器模块原理以及驱动,适合学生,工程师等学习单片机嵌入式开发、传感器驱动、通信协议等。 4264

二十一、SAP-库存管理

业务逻辑、功能讲解、图文解析、章节总结

m0_72403327的博客 1810

30天自制操作系统笔记(十一十二)——源码

这里我们只给出新增部分和修改部分的源码: 系统运行截图http://blog.youkuaiyun.com/ucan23/article/details/17088983点击打开链接 /* filename: timer.c * description: 定时器函数 * author: Howard * date: 2013-12-02 * version: ...

369540808 228

AI大模型探索之路-应用篇11:AI大模型应用智能评估(Ragas)

随着人工智能技术的飞速发展,AI大模型(LLM)已经成为了推动技术创新和应用的关键因素。这些大模型在语言理解、图像识别、自然语言生成等领域展现出了惊人的能力。然而,随着模型规模的增大,它们对计算资源的消耗、环境适应性、模型稳定性、安全性和可解释性等方面也提出了新的挑战。因此,对AI大模型进行智能评估变得至关重要,以确保它们在实际应用中的稳定性、可靠性和有效性。本文将详细介绍Ragas框架,一个专为AI大模型设计的智能评估工具。

寻道AI,探索AI无限可能! 5542

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

零基础国产GD32单片机编程入门(十二)FreeRTOS实时操作系统移植含源码

FreeRTOS实时操作系统能使在GD32单片机软件开发中,程序结构清晰,单片机执行效率提升许多。本文介绍了GD32F103C8T6移植FreeRTOS,并能实现任务切换,提供代码

光子物联科技主要介绍STM32单片机,GD32单片机以及各种传感器模块原理以及驱动,适合学生,工程师等学习单片机嵌入式开发、传感器驱动、通信协议等。 2934

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Linux 测验

1. 应用场景 学习掌握linux操作系统, 熟练linux下程序开发. 2. 学习/操作 2.1 测试题目 [多项选择题] ... 2.2 备注 简单讲: 把这十二道题目...

"代码"的日常搬运 4276

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

《操作系统真象还原》从零开始自制操作系统 全流程记录

文章目录博客链接 博客链接 《操作系统真象还原》第一章 ---- 安装Vmware Station 安装Ubuntu 装载配置Bochs 安装Vmware tools 开始乘帆历险! 《操作系统真象还原》第二章 ---- 编写MBR主引导记录 初尝编写的快乐 雏形已显! 《操作系统真象还原》第三章 ---- 完善MBR 尝汇编先苦涩后甘甜而再战MBR! 《操作系统真象还原》第四章 ---- 剑指Loader 刃刺GDT 开启新纪元保护模式 解放32位 《操作系统真象还原》第五章 ---- 轻取物理内存容

Love 6's Private Blog 2万+

libevent源码深度剖析十二

libevent源码深度剖析十二——让libevent支持多线程张亮    Libevent本身不是多线程安全的,在多核的时代,如何能充分利用CPU的能力呢,这一节来说说如何在多线程环境中使用libevent,跟源代码并没有太大的关系,纯粹是使用上的技巧。1 错误使用示例     在多核的CPU上只使用一个线程始终是对不起CPU的处理能力啊,那好吧,那就多创建几个线程,比如

sparkliang的专栏 1万+

ssm朝花夕拾二手交易平台(开题+源码)

此外,由于传统二手交易市场的信息不对称和交易不透明,消费者往往难以获得可靠的商品信息和保障交易的安全。在这样的背景下,朝花夕拾二手交易平台的出现,旨在解决这些问题,推动二手交易市场的健康发展。[2]刘震林, 喻春梅. 基于MVC模式的JAVA Web开发与实践应用研究[J]. 网络安全技术与应用, 2021, (01): 57-58.2022年9月至10月:需求分析和规划,进行用户需求调研和分析,确定系统功能和目标。2023年4月至5月:文档编写和培训,编写用户手册和系统文档,并进行相关人员的培训。

杰简程序毕设 915

你管这叫操作系统源码(完结)

你管这叫操作系统源码——以小说的心态去阅读解析linux操作系统源码之完结

wq_0708的博客 1105

自制操作系统日志——第二十二天

今天,我们将继续再完善一下保护操作系统的内容,以及进一步的利用c语言显示字符串!

qq_43696276的博客 644

第五十二篇 操作系统简史——多道技术

目录 第五十二篇 操作系统简史——多道技术 一、操作系统 1.操作系统简介 2.操作系统作用 3.操作系统与应用程序的区别 二、操作系统发展历史 1.第一代计算机(1940~1955):真空管和穿孔卡片 2.第二代计...

weixin_30371469的博客 482

第十二周实验——嵌入式PTOS多任务程序设计

首先:,它与通常的操作系统有所不同,主要是因为它能够在限定的时间内对外部事件做出响应。这种特性使得RTOS非常适合于那些需要实时响应的应用场景,例如嵌入式系统、自动驾驶等。其次: UCOS(Unified Real-Time Operating System)是Micrium公司开发的一款实时操作系统(RTOS)。UCOS有两个主要的版本,分别是UCOSII和UCOSIII。。相比于μC/OS-II删除和更新一些少用的功能,添加更加高效的功能和服务UCOSIII是一个。

whynotsolve的博客 2084

操作系统的应用 - 软考备战(二十二)

操作系统应用层通过UNIX/Linux的稳定服务与Windows的图形交互满足通用计算需求,并通过Android与RTOS分别在移动互联与工业控制领域实现了对硬件资源的高效专用管理。

2401_83227843的博客 539
上一篇: 你管这叫文件系统
下一篇: 认真聊聊中断
wq_0708
博客等级 码龄6年 2万+粉丝 95原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值