




已阅读5页,还剩61页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
上次内容 虚拟空间=内核空间+进程空间 进程控制块 进程队列组织 后面实验调整为每周五都上 1 进程控制块 2 查2.4内核: struct task_struct struct list_head run_list; struct task_struct *next_task, *prev_task; struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr; struct task_struct *pidhash_next; struct task_struct *pidhash_pprev; 3 当前进程pcb在内核 栈底端 内核定义current宏 可方便找到它: esp 偏移8K= 8192 内核 内核栈 8k task_struct ESP task_struct init_task task_struct task_struct task_struct task_struct task_struct prev next 4 5 查找某个进程 init_task链表 与运行相关current 散列表 u struct task_struct *pidhashPIDHASH_SZ u find_task_by_pid(PID) u 冲突时:*pidhash_next; 6 三、进程创建、终结及相关系统调用 1.进程状态 可运行态 u运行 u就绪 睡眠(等待)态 u浅度:等待资源有效时,被信号或时钟中 断唤醒 u深度:不能由其他进程通过信号和时钟中 断唤醒 暂停态:暂时停止接受某种处理如程序调试(挂 起) 僵死态:执行接受但尚未消亡,尚未释放pcb 7 Linux进程状态及转换 fork() TASK_RUNNING 就绪 TASK_INTERRUPTIBLE 浅度睡眠 TASK_UNINTERRUPTIBLE 深度睡眠 TASK_STOPPED 暂停 TASK_ZOMBIE 僵死 占有CPU 执行 do_exit() schedule() ptrace() schedule() 当前进程 时间片耗尽 等待资源到位 sleep_on() schedule() 等待资源到位 interruptible_sleep_on() schedule() 资源到位 wake_up_interruptible() 或收到信号 wake_up() 资源到位 wake_up() 收到信号 SIGCONT wake_up() 8 2.进程的创建 传统方法单一函数: 创建进程地址空间;读入可执行文件;开始 执行 UNIX分创建和执行两步: fork函数:复制当前进程创建子进程(快速 创建的特点) exec函数:读取可执行文件并载入进程地址 空间执行 9 linux“写时复制 ”技术 fork()与以往不同,不是全部复制进 程空间,实际开销只是复制父进程的 页表以及给子进程创建唯一的PCB。 父进程的资源被设置为只读(父进程 页面被置上写时保护位),当父进程 或子进程试图修改某些内容时,内核 才在修改前将该部分进行拷贝写 时复制。 pcb1 程序1 父进程 fork 页表1 pcb2 页表2 无论谁写时,才 复制要写的页面 10 进程/线程创建函数:fork()/clone() 实际上都是调用内核函数do_fork(),其主要操作为: 调用alloc_task_struct( )函数获得8KB的union task_union内存区,用来存放新进程的PCB和内核栈。 让当前指针指向父进程的PCB,并把父进程PCB的内容 拷贝到刚刚分配的新进程的PCB中。 检查新创建这个子进程后,当前用户所拥有的进程数 目没有超出给他分配的资源的限制。 现在,do_fork( )已经获得它从父进程能利用的几乎 所有的东西;剩下的事情就是集中建立子进程的新资 源,并让内核知道这个新进程已经呱呱落地。 11 接下来,子进程的状态被设置为TASK_UNINTERRUPTIBLE 以保证它不会马上投入运行。 调用get_pid()为新进程获取一个有效的PID。 开始组织关系 更新不能从父进程继承的PCB的其他所有域,例如, 进程间亲属关系的域。 把新PCB插入进程链表,确保进程之间的亲属关系。 把新PCB插入pidhash哈希表。 把子进程PCB的状态域设置成TASK_RUNNING,并调用 wake_up_process()把子进程插入到运行队列链表。 让父进程和子进程平分剩余的时间片。 返回子进程的PID,该PID最终由用户态下的父进程读取 一次调用,两个 PID返回值 12 13 3.进程执行功能 fork调用后,出现两个进程执行,父子进程几乎 完全一样,新的子进程如何执行自己的代码功能? 以简化的shell框架为例: while(true) read_command(command,parameters); if(fork!=0)/*父进程*/ wait(NULL); else/*子进程*/ exec(command,parameters,0); 14 exec函数族包括若干函数,作用是根据 文件名找到相应的可执行文件。 u 子进程的地址空间被填入可执行文件的 内容,其进程功能开始有别于父进程 u exec函数执行成功就会进入新进程执行 不再返回。 u exec调用失败才返回-1,继续在克隆来 的地址空间中从调用点向下执行 15 并发顺序 u fork后,父子进程并发执行,linux会优先调度 执行子进程。因为,子进程创建后就调exec载入可 执行文件内容,而之前如果父进程进行了写时复制 就会成为额外开销。 关于写时复制 u 如果先调父进程 u 因为fork将父进程资源设为只读,只要父进程 进行修改,就要开始“写时复制”,把父进程要改的 页面复制给子进程(写子空间)。 u 继续运行,一旦子进程被调度到,它要用exec 载入另一个可执行文件的内容到自己的空间(又写 子空间),可见上步的写入就多余了。 所以,fork后优先调度子进程是为了从父进程 克隆到子进程后,尽量减少没必要的复制。 16 4.进程的终结 进程结束: u调用exit或在main函数中return(主动 ) u被信号和异常终结(被动) 终结时进程要释放其占用的资源并报告 给父进程。都是通过系统调用靠内核函数 do_exit()完成。 u回收与进程相关的各种内核数据结构, 设置状态为僵死TASK_ZOMBIE u将其所有子进程托付给init进程 u调用schedule函数,选择一个新进程运 行。 17 void exit(int status); 可利用参数status传递进程结束时的状态 调用exit后的进程不是马上消失,而是变为僵 尸状态,放弃了几乎所有内存空间,不再被调 度,但保留有pcb信息供wait收集,包括: u 正常结束还是被退出 u 占用总系统cpu时间和总用户cpu时间 u 缺页中断次数,收到信号数目等 18 分析下面程序中的“僵尸” #include #include main() pid_t pid; pid=fork(); if (pid0) /*父进程 sleep(60); wait(NULL); 子进程一被调度到即 结束成僵死态。谁来 回收其pcb? 父进程被调度执行到最后,也会隐 式结束成僵死态。谁来回收其pcb? 父进程调用wait函数收集僵死 态子进程的pcb信息,将其彻 底销毁后返回 执行: gcc o mywait mywait.c ./mywait pc=fork(); if (pc0) /*父进程 pr=wait(NULL); printf(“catch a child process with pid of %dn”,pr(); exit(0); #include #include #include #include 22 wait起到了同步的作用,父进程只有当子进程 结束后才能继续执行。 子进程退出时的状态会存入wait的整型参数 status中。由于相关信息在整数的不同二进制 位上,wait收集相关信息是是利用定义的一套 专门的宏。 23 进程生命周期中的系统调用 Fork()父亲克隆一个儿子。执行fork()之后,兵分两路 ,两个进程并发执行。 Exec()新进程脱胎换骨,离家独立,开始了独立工作的 职业生涯。 Wait()等待不仅仅是阻塞自己,还准备对僵死的子进程 进行善后处理。 Exit()终止进程,把进程的状态置为“僵死”,并把其 所有的子进程都托付给init进程,最后调用schedule()函 数,选择一个新的进程运行。 24 进程的一生进程的一生 随着一句fork,一个新进程呱呱落地,但这 时它只是老进程的一个克隆。然后,随着exec,新 进程脱胎换骨,离家独立,开始了独立工作的职业 生涯。 人有生老病死,进程也一样,它可以是自然 死亡,即运行到main函数的最后一个“,从容地 离我们而去;也可以是中途退场,退场有2种方式 ,一种是调用exit函数,一种是在main函数内使用 return,无论哪一种方式,它都可以留下留言,放 在返回值里保留下来;甚至它还可能被谋杀,被其 它进程通过另外一些方式结束它的生命。 进程死掉以后,会留下一个空壳,wait站好 最后一班岗,打扫战场,使其最终归于无形。这就 是进程完整的一生。 25 实验二 练习课本中fork、wait、exit中的程序, 记录问题并分析。 实验下页程序,运行若干次,给出打印 结果表现的父子并发顺序的说明;去掉 sleep语句又是什么执行顺序?试分析。 找资料,尝试做个使用fork、exec函数 族函数的小程序。 26 2.给出尽量多的测试结果并进行分析 。 int main(void) pid_t pid; char *message; int n; pid = fork(); if (pid 0; n-) printf(message); sleep(1); return 0; #include #include #include #include 3.exec举例 27 多个子进程 分析试试看 修改不同的位置,根据执行效果试着 分析并发顺序 程序后台执行中用“kill -9 pid号” 结 束子进程试试看结果如何 waitpid参数0换成后面的标志如何 gcc o mywait mywait.c ./mywait p = clone( 0, flags | CLONE_VM ); if ( p ) /* 父*/ return p; else /* 子*/ fn(arg); exit( ); 31 几个特殊身份的几个特殊身份的内核线程内核线程 1.没事闲逛的0号进程(线程) p 从无到有诞生的第一个内核线程,由start_kernel函 数完成内核初始化时创建。 p 执行cpu_idle()函数(省电减热),它的PCB就是 init_task。没有其他进程可调度时,才选择进程0。 2.1号进程init(线程) 初始化阶段如下创建: kernel_thread(init,NULL,CLONE_FS|CLONE_FILES|CLONE_SIGHAND) p 传递给clone的参数表示0号线程和1号线程共享文件 系统、打开文件、信号处理程序。 p 该init内核线程被调度到时,执行内核函数init(), 常规内核任务初始化若干内核线程 32 u用户态第1个进程init的产生 u1号内核线程的init内核函数中调用 execve(),装入用户态下的可执行程序 sbin/init,内核线程变为用户进程。 uinit进程从不终止,负责创建和监视操作 系统外层所有进程的活动。 33 四、进程调度 对系统的总体设计、系统实现 、功能设置以及各个方面的性能都有决定 性影响。 考虑系统实现的复杂程度在功 能和性能方面常要做出必要的权衡和让步 。 34 调度的实质:资源分配 权衡以下因素: u 公平:保证每个进程得到合理的CPU时 间。 u 高效:使CPU保持忙碌状态,即总是有 进程在CPU上运行。 u 响应时间:使交互用户的响应时间尽可 能短 u 周转时间:使批处理用户等待输出的时 间尽可能短。 u 吞吐量:使单位时间内处理的进程数量 尽可能多。 35 调度算法 UNIX采用动态优先数; BSD采用多级反馈队列; windows采用抢占式多任务调度; linux采用基于时间片的动态优先级调度。 分时、优先级抢占是都会采用的策略 36 1.调度的时机 调度程序什么时候被调用?结合状态转换图 看: u 主动调用schedule 进程状态转换的时刻:进程终止exit、进程睡 眠sleep u当前进程的时间片用完时; u设备驱动程序运行时; 驱动程序做长而重复的任务时检查调度 标志,如果必要也会调用schedule放弃cpu u 被动、抢占 u内核态返回用户态; 从系统调用或中断处理返回用户空间时 。不一定立即返回用户态进程继续执行 37 关于抢占“有条件的可剥夺” 对于实时进程,优先级足够高会抢占CPU成为 新的当前进程。 u 若发生抢占时进程在用户空间中运行, 就会直接被剥夺CPU; u 若发生抢占时进程在内核空间中运行, 即使迫切需要其放弃CPU,也仍要等到从 系统空间返回的前夕才被剥夺CPU。 2.4不支持内核抢占,进程一旦进入内 核运就一直执行,直到自己放弃或时间片用尽。 2.6支持一定程度的内核抢占 38 2. 调度策略 u SCHED_OTHER 面向普通分时进程的时间片轮转策略。采用该 策略时,系统为处于TASK_RUNNING状态的每个进程分配一个时 间片。当时间片用完时,再选择下一个优先级相对较高的进程,并 授予CPU使用权。 u SCHED_FIFO 适用于对响应时间需求比较高,运行所需时间 比较短的实时进程。采用该策略时,各实时进程按其进入可运行队 列的顺序依次获得CPU。除了因等待某个事件主动放弃CPU,或出 现优先级更高的进程而剥夺其CPU之外,该进程将一直占用CPU运 行。 u SCHED_RR 适用于对响应时间需求比较高,运行所需时间 比较长的实时进程。采用该策略时,各实时进程按时间片轮流使用 CPU。当一个运行进程的时间片用完后,停止其运行并将其置于可 运行队列的末尾。 2.6分两个类对不同的策略进行了分别处理 不同策略下进程的时间片不同, 优先级通过权重比较;实时进程 优先级相对普通进程要高 39 采用动态优先级和权值调控的方法 u 每个进程有动态变化的counter值,进程运行 时,每一个时钟滴答后其值减1。动态优先级 u 给实时进程更高的权重,确保其总是比普通进 程优先使用CPU。 每次选取下一个运行进程时,给可 运行队列中的每个进程计算权值weight。普通进程 的权值就是其counter和优先级nice的综合(时间 片不同且动态变化),而实时进程的权值是他的 rt_priority的值加1000,确保实时进程的权值总 能大于普通进程。选取权值最大者作为下一个运行 进程 40 3.调度的依据 task_struct中与调度相关的项: uneed_resched 调度标志,决定是否调用 schedule( )函数。 ucounter 进程处于可运行状态时所剩余的 时钟节拍数(即时钟中断的间隔时间,10ms或1ms) ,也叫动态优先级。若counter=2,2个时钟节拍就是 20ms或2ms upriority 进程的基本优先级(静态优先级 ),此项决定counter的初值,一般由系统确定,也可 由用户通过nice()系统调用修改 urt_priority:实时进程的优先级 upolicy: 调度的类型,策略 41 衡量可运行进程运行权重的函数描述: static inline int goodness(struct task_struct * p, struct task_struct *prev) int weight; * 权值,作为衡量进程是否运行 的唯一依据 */ if (p-police!=SCHED_OTHER) /*实时进程*/ weight = 1000 + p-rt_priority; goto out weight = p-counter; /*普通进程*/ if (!weight) goto out;/ *p用完了时间片*/ if (p =prev) weight+= 1; * 细微调整 */ weight+=p-priority/基本优先级+时 间片剩余counter out: return weight p-counter + p-priority 42 时间片轮转调度算法,进程按FIFO排队,时间 片大小相等。 Linux里可运行状态进程怎么组织,怎么选择 ? u 可运行进程双向循环链表,init_task做 开头和结束,新插入进程直接入队尾; u 调度程序通过goodness()函数遍历该链 表中的进程PCB,挑选“运行权重”高的进程 运行。 运行权重与时间片有关,与进程策略 有关 43 3.调度的实现schedule() 运行schedule用到的几个重要变 量: struct task_truct current, *prev, *next; ucurrent:全局变量,表示当前正 在运行的进程。 uprev:局部变量,表示调度发生 之前运行的进程,用它保存current的值 。 unext: 局部变量,表示调度发生 之后要运行的进程。 uc:局部变量,进程值得运行的程 度 schedule( )函数的关键操作是 设置局部变量next,以使next代替prev 而指向被选中进程的PCB。 current Init_task pcb pcb pcb prev next 44 u相关状态和队列的处理 根据调度发生的不同时机,进行各种处理 如果prev是SCHED_RR策略下时间片用完的 实时进程,分配新时间片后入运行队尾 如果prev处于浅睡眠状态且有信号等待处理 ,内核将其状态设为TASK_RUNNING,让其 仍有机会获得CPU; 如果prev仍未处于运行态,则将其从可运行 队列中撤下来; 45 u给prev进程分配新的时间片,并让它到可运行队列末尾 if (prev-policy = SCHED_RR move_last_runqueue(prev); 唤醒prev进程给它一个被选择执行的机会。 if (prev-state = TASK_INTERRUPTIBLE 从运行队列链表中删除不是可运行状态的prev进程: if (prev-state != TASK_RUNNING) del_from_runqueue(prev); 被抢占进程仍以运行态保持在运行队列中原位置等待 schedule重新调度时计算权重即可。 46 u选择下一个时间片内要执行的进 程 next指向待检查的第一个可运行进程 如果prev处于TASK_RUNNING 状态,则next指向prev。若prev自 己调用了yield,置权重变量c=0,否 则计算prev运行权重赋值给c 如果prev不是运行态,则next指 向init_task,权重变量c置一个低值 :1000 找一个比当前权重c更高优先级 的进程做下一个 current Init_task pcb pcb pcb prev next 47 在可运行队列上重复调用goodness ()函数,比较权重 相同权重的进程,选择排在 运行队列前面的。 如果比较后没有权重比next 的c更高的,则不发生进程切换。 若所有运行队列中进程 counter都为0,则需给所有现存 进程分配新时间片。 新时间片 =counter/2+priority 最后得到的next若不等于prev则调 用switch_to函数开始切换 current Init_task pcb 1000+p pcb 1000+p pcb c+p prev next p SCHED_OTHER SCHED_RR 不同策略、不同时间片剩余情况下 ,各进程的权重在动态变化 48 next初始化为要检查的第一个可运行进程。 if (prev-state = TASK_RUNNING) next = prev; if (prev-policy c = 0; /自愿放弃CPU时对下一个要调度进程的权重 起点没要求 else c = goodness(prev, prev); /下一个要调度进程的 权重要大于c else c = -1000; *永不选中,运行队列链表只包含 init_task */ next = 49 扫描可运行进程队列上的最佳候选者: p = init_task.next_run; while (p != if (weight c) /可运行队列排队中相同权重的会取第1个 c = weight; next = p; p = p-next_run; 进程切换: if (prev != next) kstat.context_swtch+; / 统计进程切换的次数 switch_to(prev,next); / 从prev切换到next 50 比较普通分时进程的I/O消耗型进程和cpu消耗 型进程 u 权重为c+p,时间片剩余约多,权重越大 越有机会执行。 设初始权重一样。但前者执行一小 部分时间片就会阻塞去执行I/O操作,显然其 时间片剩余量会多并保持原队内位置;而 cpu消耗型进程可能一下就用完自己的时间 片,排到运行队尾。下次调度时前者权重高 ,会优先被调度。 linux为了保证交互更倾向于调度I/O消耗型进程 51 与调度相关的系统调用与调度相关的系统调用 系统调用描述 nice( ) 改变一个普通进程的优先级 getpriority( ) 取得一组普通进程的最大优先级 setpriority( ) 设置一组普通进程的优先级 sched_getscheduler( )取得一个进程的调度策略 sched_setscheduler( ) 设置一个进程的调度策略和优先级 sched_getparam( ) 取得一个进程的调度优先级 sched_setparam( ) 设置一个进程的优先级 sched_yield( )不阻塞的情况下自愿放弃处理机 sched_get_ priority_min( )取得某种策略的最小优先级 sched_get_ priority_max( )取得某种策略的最大优先级 52 从逻辑上来说,系统调用可被看成是一个内核与 用户空间程序交互的接口它好比一个中间人,把 用户进程的请求传达给内核,待内核把请求处理完毕 后再将处理结果送回给用户空间。用户空间访问内核 的唯一手段。 好处 编程容易,程序员不必关心底层硬件 提高了系统安全性 不同操作系统接口定义一致的条件下,方便程序 移植 四、系统调用内核的出口 53 1、API、系统调用、系统命令、内核函数 内核函数:内核功能单位 系统调用:通过软中断向内核发出的一个明 确的请求,对应1个或若干内核函数。 应用编程接口(API):只是供程序员调用的 一组函数定义。这些函数说明了如何获得一个 给定的服务;并不与系统调用一一对应。 系统命令:管理员使用的,对应在更高的应 用层次上的可执行文件。 C库函数:标准C库函数+系统调用。可看做 API存在的具体形式 编程接口(C库) 系统调用 内核函数 54 1)系统调用与内核函数 内核函数在形式上与普通函数一样,但它是 在内核实现的,需要满足一些内核编程的要 求 系统调用是用户进程进入内核的接口层,它 本身并非内核函数,但它是由内核函数实现 的 进入内核后,不同的系统调用会找到各自对 应的内核函数,这些内核函数被称为系统调 用的“服务例程” 55 2)系统调用与API Linux的应用编程接口(API)遵循 POSIX标 准 API和系统调用关注的都是函数名、参数类型及返回 代码的含义;有可能两者的调用形式一致(长相一 样),但API与系统调用并不一一对应。 系统调用的实现是在内核完成的,而用户态的函数 是在函数库中实现的 API的实现 1个系统调用 n个系统调用 0个系统调用 56 3)系统调用与系统命令 系统命令相对应用编程接口更高一层,每个 系统命令都是一个可执行程序,比如ls、 hostname等, 系统命令的实现最后还是调用了系统调用 通过strace ls或strace hostname 命令可 以查看系统命令所调用的系统调用 57 当用户态的进程调用一个系统调用时,CPU 切换到内核态执行内核函数 如何响应调用:系统调用中利用int$0x80 汇编指令,产生向量为128的异常。内核通过 查中断向量表找到128号异常对应的处理程序 系统调用处理程序 如何找到对应的内核函数:利用系统调用 号查系统调用表sys_call_table,找到对应 的服务 2、系统调用处理程序及服务例程 58 系统调统调 用相关的初始化 内核初始化期间调间调 用trap_init()函数建立 IDT表中128号向量对应对应 的表项项: uset_system_gate(0x80, 该调用把下列值装入该门描述符的相应域 : u 段选择子: u 偏移量:指向system_call( )异常处理程 序 u 类型:置为15,表示该异常是一个陷阱 u DPL(描述符特权级):置为3,这就允 许用户态进程调用这个异常处理程序 59 xyz() system_call: sys_xyz() ret_from_sys _call: iret xyz() int 0x80 sys_xy z() 在应用程序 在libc标准库 系统调用 系统调用 调用中的 中的封装例程 处理程序 服务例程 系统调用 用户态 内核态 调用一个系统调用 60 v参数传递:系统调用的参数先传递给系统调用处 理程序在CPU中的寄存器,然后再复制到内核栈。 v 寄存器的设置由封装例程完成 leax寄存器传递系统调用号 l用寄存器传递传递 参数必须满须满 足两个条件: 每个参数的长度不能超过寄存器的长度 (超过32位的直接传参数的内存地址) 参数的个数不能超过6个(包括eax中传 递的系统调用号),否则,用一个单独的寄存器指向进程 地址空间中这些参数值所在的一个内存区 l在少数情况下,系统调用不使用任何参数 l 系统调用处理程序中使用SAVE_ALL宏将寄存器中的 值保存到内核栈,服务例程执行时在内核栈即可得 到参数 l 最后服务例程的返回值写回eax寄存器中,是在服 务例程执行return n指令时由C编译器自动
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 重庆石柱土家族自治县2025年上半年事业单位公开遴选试题含答案分析
- 浙江省瑞安市2025年上半年事业单位公开遴选试题含答案分析
- 河北省南皮县2025年上半年事业单位公开遴选试题含答案分析
- 河北省满城县2025年上半年公开招聘城市协管员试题含答案分析
- 河北省涞源县2025年上半年公开招聘村务工作者试题含答案分析
- 河北省景县2025年上半年公开招聘城市协管员试题含答案分析
- 2025版区域代理销售合同示范文本
- 2025布料进出口贸易合作协议书
- 2025保温系统施工与质量保证合同范本
- 2025年资产证券化担保合同范本
- 物业管理权交接方案
- GB/T 17622-2008带电作业用绝缘手套
- 绿色施工及环境保护施工方案
- 外请手术医师知情同意书
- 焊接和切割作业的防火、防爆措施
- 公路工程质量监督综合检查内容
- 人事任命书红头文件模板
- 纽扣参考资料专用英语名词08.4
- 《导游英语》全套课件(完整版)
- SAE_USCAR2_Rev3_2001 汽车电子连接器性能标准
- 钢管技术规格书
评论
0/150
提交评论