Linux启动过程简略分析-start_kernel部分代码阅读

本文简要分析Linux启动过程,重点探讨start_kernel和rest_init函数。在start_kernel中,进行内核初始化,包括锁依赖、cgroup、irq、time等;rest_init则创建kthreadd和系统初始化线程。启动过程涉及硬件初始化、调度器设置,最终进入用户态。调试方面,通过设置gdb断点进行分析。文章结尾概述了阅读源代码的体会和下一步的学习方向。

Linux 启动过程分析

源代码和工具

kernel.org上可以下载各个版本的Linux.

概述

简单的说, 电脑加电启动后, 会进行自检(BIOS), 然后根据启动顺序, 依次检查可启动设备, 将找到的第一个可启动设备中特定区域内的内容(MBR), 拷贝到内存指定位置, 然后跳转到这个指定位置运行.到此, 软件系统才开始进行启动.软件系统启动分很多阶段, 但是在这个时候, 可以暂时分为两个阶段:加载阶段, 运行阶段.由于每代计算机都要兼容上一代计算机, 硬件无法把操作系统内核一次性全部加载到内存中, 而且, 内核也需要按照自己的需求, 将一些内容加载到特定内存位置, 所以, 在加载阶段, 计算机硬件加载的只是一个操作系统自己定制的加载器.这个加载器会收集一部分硬件信息, 为内核运行准备好环境, 然后将内核文件加载到指定的内存区域里, 然后在执行加载好的内核.有的操作系统会有两段加载过程, 第一段加载过程完成必要的初始化后加载第二段启动过程, 第二段加载过程就可以提供一个界面, 使用户能设置启动参数, 最后根据向内核传递的设置参数, 加载内核, 影响内核的工作方式.

操作系统有很多依赖体系的部分. 比如体系对内存的管理, 启动流程, 设备通信方式等等.从遥控器到大型机, 有太多的体系结构了.没必要了解每个体系结构.所以, 重要的部分应该是独立于体系结构的部分和操作系统如何处理与体系结构通信的部分, 这样才能更快地理解内核, 移植内核.

在进行一系列初始化(这部分严重依赖体系, 所以在/arch/x86目录下)之后, 进入start_kernel, 可以认为该部分对体系的依赖已经不是很大. 在这里, , 只提供了初始化的通用总体流程, 在流程中执行的各种操作, 会根据体系不同, 进行相应的条件编译. 从start_kernel开始, 就是使用C语言对内核的各个部分进行初始化, 最后进入rest_init, 将自己变为IDLE运行.在这个过程中要经过各种针对体系的初始化, 以支持操作系统的功能.start_kernel包括对硬件的初始化, 获取体系规格, 设置体系工作方式, 还包括对内核本身的初始化, 各种数据结构的初始化, 各种机制的初始化等.由于某些依赖于硬件, 比如任务调度依赖时钟中断, 所以要先初始化一部分硬件. 但是初始化某些硬件的时候, 也需要某些信息才能进行, 比如多核处理器上的调度方法和单核CPU的调度算法有所不同, 所以在调度之前要获取硬件规格.加之系统部件之间的相互依赖, 所以初始化的过程很复杂.每个部分的初始化可能分为很多阶段.

调试

在linux代码目录下执行make menuconfig进行设置, 选择确保kernel hacking -> Compile-time checks and compiler options -> Compile the kernel with debug info选中, 然后在linux目录下执行qemu -kernel ./arch/x86/boot/bzImage -s -S, 在另一个终端中执行gdb, 然后输入file ./linux-3.18.6/vmlinux, target remote :1234, b start_kernel, layout split, c, 之后就会停在start_kernel入口出. 其中file命令是加载要调试的文件, target是连接调试目标, b是设置断点, layout设置gdb的显示布局, c是运行被调试的程序.

调试过程截图

代码分析

start_kernel

在start_kernel之前, 就将task 0设置完毕. 由于对init_task来说, 很多部分是已知的, 所以用静态变量来设置struct task_struct init_task = INIT_TASK(init_task);(/init/init_task.c). 每个task记录了该task可以运行在哪个CPU上, 内存空间(mm_struct), 优先级, 父子任务关系等等很多信

pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, const pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *remote_sdp) { pjsua_call *call = &pjsua_var.calls[call_id]; pjmedia_session_info sess_info; pjmedia_stream_info *si = NULL; pj_status_t status; unsigned i; #if defined(PJSIP_HAS_FIREWALL_FILTER) && PJSIP_HAS_FIREWALL_FILTER!=0 PJ_FIREWALL_RULE* pFwRule = NULL; #endif char localIp[CMSIP_STR_40] = {0}; pj_uint16_t rtpPort = 0; pj_uint16_t rtcpPort = 0; pj_str_t tmpStr; int family = AF_INET; if (!pjsua_var.med_endpt) { /* We're being shutdown */ return PJ_EBUSY; } /*ycw-pjsip*/ /*we must compare the new stream with the old stram to decide to destroy the old stream or not*/ /* Destroy existing media session, if any. */ #if 0 prev_media_st = call->media_st; stop_media_session(call->index); #endif memset(&sess_info, 0, sizeof(sess_info)); /* Create media session info based on SDP parameters. */ status = pjmedia_session_info_from_sdp( call->inv->pool_prov, pjsua_var.med_endpt, PJMEDIA_MAX_SDP_MEDIA, &sess_info, local_sdp, remote_sdp); if (status != PJ_SUCCESS) { return status; } for (i = 0; i < sess_info.stream_cnt; ++i) { if (sess_info.stream_info[i].dir == PJMEDIA_DIR_NONE) { continue; } if (sess_info.stream_info[i].ulptime== 0) { sess_info.stream_info[i].ulptime = 20; } } /* Update audio index from the negotiated SDP */ call->audio_idx = find_audio_index(local_sdp, PJ_TRUE); CMSIP_PRINT("---call audio index(%d)---", call->audio_idx); /* Find which session is audio */ PJ_ASSERT_RETURN(call->audio_idx != -1, PJ_EBUG); PJ_ASSERT_RETURN(call->audio_idx < (int)sess_info.stream_cnt, PJ_EBUG); si = &sess_info.stream_info[call->audio_idx]; /* Reset session info with only one media stream */ sess_info.stream_cnt = 1; CMSIP_PRINT("-----stream cnt(%d)---", sess_info.stream_cnt); /*ycw-pjsip-t38. Ŀǰֻ֧��һ����*/ if (si != &sess_info.stream_info[0]) { pj_memcpy(&sess_info.stream_info[0], si, sizeof(pjmedia_stream_info)); si = &sess_info.stream_info[0]; } pjmedia_transport_udp_local_info(call->med_orig, localIp, &rtpPort, &rtcpPort, &family); CMSIP_PRINT("Get local rtp network address:%s:%d", localIp, rtpPort); CMSIP_PRINT("Get local rtcp network address:%s:%d", localIp, rtcpPort); CMSIP_PRINT("Get local family:%d(AF_INET:%d,AF_INET6:%d)", family, AF_INET, AF_INET6); tmpStr = pj_str(localIp); pj_sockaddr_init(family, &si->local_addr, &tmpStr, rtpPort); pj_sockaddr_init(family, &si->local_rtcp, &tmpStr, rtcpPort); /*ycw-pjsip-t38*/ /*for the new re-INVITE, we accept all. But, we must check the new stream equal the old stream or not. If they are same, we need not re-create the stream*/ if (call->session && call->session->stream_cnt == sess_info.stream_cnt && pjsua_media_stream_info_equal(&call->session->stream_info[0], si) /*now, just support one stream */) { CMSIP_PRINT("----the existed session has the same transport info, do not re-create--"); goto ON_SUCCESS; } CMSIP_PRINT("destroy old stream, create new stream."); /*ycw-pjsip*/ stop_media_session(call->index); /* Check if no media is active */ if (sess_info.stream_cnt == 0 || si->dir == PJMEDIA_DIR_NONE) { /* Call media state */ call->media_st = PJSUA_CALL_MEDIA_NONE; /* Call media direction */ call->media_dir = PJMEDIA_DIR_NONE; /* Don't stop transport because we need to transmit keep-alives, and * also to prevent restarting ICE negotiation. See * http://trac.pjsip.org/repos/ticket/1094 */ #if 0 /* Shutdown transport's session */ pjmedia_transport_media_stop(call->med_tp); call->med_tp_st = PJSUA_MED_TP_IDLE; /* No need because we need keepalive? */ /* Close upper entry of transport stack */ if (call->med_orig && (call->med_tp != call->med_orig)) { pjmedia_transport_close(call->med_tp); call->med_tp = call->med_orig; } #endif } else { pjmedia_transport_info tp_info; /* Start/restart media transport */ CMSIP_PRINT("----start media transport-------------"); status = pjmedia_transport_media_start(call->med_tp, call->inv->pool_prov, local_sdp, remote_sdp, call->audio_idx); if (status != PJ_SUCCESS) return status; CMSIP_PRINT("-----media transport state is running---------"); call->med_tp_st = PJSUA_MED_TP_RUNNING; /* Get remote SRTP usage policy */ pjmedia_transport_info_init(&tp_info); pjmedia_transport_get_info(call->med_tp, &tp_info); if (tp_info.specific_info_cnt > 0) { unsigned i; for (i = 0; i < tp_info.specific_info_cnt; ++i) { if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP) { pjmedia_srtp_info *srtp_info = (pjmedia_srtp_info*) tp_info.spc_info[i].buffer; call->rem_srtp_use = srtp_info->peer_use; break; } } } /*ycw-pjsip*/ #if 0 /* Override ptime, if this option is specified. */ if (pjsua_var.media_cfg.ptime != 0) { si->param->setting.frm_per_pkt = (pj_uint8_t) (pjsua_var.media_cfg.ptime / si->param->info.frm_ptime); if (si->param->setting.frm_per_pkt == 0) si->param->setting.frm_per_pkt = 1; } #endif /* Disable VAD, if this option is specified. */ if (pjsua_var.media_cfg.no_vad) { si->param->setting.vad = 0; } /* Optionally, application may modify other stream settings here * (such as jitter buffer parameters, codec ptime, etc.) */ #if 0 si->jb_init = pjsua_var.media_cfg.jb_init; si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre; si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre; si->jb_max = pjsua_var.media_cfg.jb_max; #endif /* ycw-pjsip: vbd support */ if (call->request_channel_mode == pj_channel_passthrough) { #if 0 si->jb_min_pre = si->jb_max_pre = -2; #endif si->vad = si->cng = 0; } /* Set SSRC */ #if 0 si->ssrc = call->ssrc; #endif /* Set RTP timestamp & sequence, normally these value are intialized * automatically when stream session created, but for some cases (e.g: * call reinvite, call update) timestamp and sequence need to be kept * contigue. */ #if 0 si->rtp_ts = call->rtp_tx_ts; si->rtp_seq = call->rtp_tx_seq; si->rtp_seq_ts_set = call->rtp_tx_seq_ts_set; #endif #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* Enable/disable stream keep-alive and NAT hole punch. */ si->use_ka = (call->acc_id >= 0) ? pjsua_var.acc[call->acc_id].cfg.use_stream_ka : PJ_TRUE; #endif /* Create session based on session info. */ status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info, &call->med_orig, call, call_id, # if (!(defined(PJ_MEDIA_TRANSIT_BY_PJSIP) && 0==PJ_MEDIA_TRANSIT_BY_PJSIP) || \ defined(INCLUDE_PSTN_GATEWAY)) &call->dsp_med_tp, # endif # if defined(PJ_MEDIA_TRANSIT_BY_PJSIP) && 0==PJ_MEDIA_TRANSIT_BY_PJSIP # if defined(INCLUDE_USB_VOICEMAIL) call->isUsbVm, # endif # if defined(INCLUDE_PSTN_GATEWAY) call->isPstn, # endif # endif # if defined(PJSIP_HAS_FIREWALL_FILTER) && PJSIP_HAS_FIREWALL_FILTER!=0 #ifdef INCLUDE_VOIP_SUPPORT_IPV6 pj_firewall_get_fwtype(call->fwRule[0].family), #else pjsua_var.fwType, #endif pjsua_var.tcMark, # endif &call->session ); if (status != PJ_SUCCESS) { return status; } #if defined(PJSIP_HAS_FIREWALL_FILTER) && PJSIP_HAS_FIREWALL_FILTER!=0 for (i = 0; i < PJ_ARRAY_SIZE(call->fwRule); ++i) { pFwRule = &call->fwRule[i]; if (pFwRule->destination.slen > 0 && pFwRule->dport > 0) { status = pj_firewall_set_rule_accept(PJ_FIREWALLCFG_DEL, pFwRule->protocol, &(pFwRule->destination), NULL, pFwRule->dport, NULL, NULL, -1, pj_firewall_get_fwtype(pFwRule->family) #ifdef INCLUDE_VOIP_SUPPORT_IPV6 , pFwRule->family #endif /* INCLUDE_VOIP_SUPPORT_IPV6 */ , pjsua_var.tcMark); if (status != PJ_SUCCESS) { printf("==%s,%d==firewall rule set error!!!!====", __FUNCTION__, __LINE__); } } memset(pFwRule, 0, sizeof(PJ_FIREWALL_RULE)); } #endif CMSIP_PRINT("----session create successfully--------------------------------"); /* If DTMF callback is installed by application, install our * callback to the session. */ if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { #if 0 pjmedia_session_set_dtmf_callback(call->session, 0, &dtmf_callback, (void*)(long)(call->index)); #endif } /* Call media direction */ call->media_dir = si->dir; /* Call media state */ if (call->local_hold) { call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD; } else if (call->media_dir == PJMEDIA_DIR_DECODING) { call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD; } else { call->media_st = PJSUA_CALL_MEDIA_ACTIVE; } } ON_SUCCESS: /* Print info. */ { char info[80]; int info_len = 0; unsigned i; for (i = 0; i < sess_info.stream_cnt; ++i) { int len; const char *dir; pjmedia_stream_info *strm_info = &sess_info.stream_info[i]; switch (strm_info->dir) { case PJMEDIA_DIR_NONE: dir = "inactive"; break; case PJMEDIA_DIR_ENCODING: dir = "sendonly"; break; case PJMEDIA_DIR_DECODING: dir = "recvonly"; break; case PJMEDIA_DIR_ENCODING_DECODING: dir = "sendrecv"; break; default: dir = "unknown"; break; } len = pj_ansi_sprintf( info+info_len, ", stream #%d: %.*s (%s)", i, (int)strm_info->fmt.encoding_name.slen, strm_info->fmt.encoding_name.ptr, dir); if (len > 0) { info_len += len; } } PJ_LOG(4,(THIS_FILE,"Media updates%s", info)); } return PJ_SUCCESS; } 实现通知内核rtp的端口是多少,应该怎么做
最新发布
10-21
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值