1. 程式人生 > >套介面層之socket系統呼叫實現

套介面層之socket系統呼叫實現

這篇筆記記錄了AF_INET協議族在套介面層對scoket()系統呼叫的實現,注意這裡只介紹了套介面層的實現,相比於完整的socket()系統呼叫實現,這裡缺少兩部分內容:

  1. 檔案系統相關的部分,比如檔案描述符的分配等;
  2. 傳輸層的實現,套接字的建立肯定是要傳輸層參與的,但是不同的傳輸層處理方式又不同,這種協議差異會單獨在相關筆記中介紹。

socket()系統呼叫涉及的核心函式呼叫關係如下圖: 在這裡插入圖片描述

1. 系統呼叫入口

//三個入參就是socket()系統呼叫的三個入參
asmlinkage long sys_socket(int family, int type, int protocol)
{
	int
retval; struct socket *sock; //核心操作,建立一個struct socket,該結構是套接字在套介面層的例項,在建立該 //結構過程中,相關的傳輸層控制塊也會被建立 retval = sock_create(family, type, protocol, &sock); if (retval < 0) goto out; //將struct socket結構與檔案系統描述符繫結,實際上就是為該套接字分配一個檔案 //描述符,使得應用程式可以使用標準的檔案系統呼叫介面操作套接字 retval = sock_map_fd(sock);
if (retval < 0) goto out_release; out: /* It may be already another descriptor 8) Not kernel problem. */ return retval; out_release: //socket與檔案描述符對映失敗時呼叫sock_release()關閉socket sock_release(sock); return retval; }

2. 套介面層通用處理

int sock_create(int family, int type, int protocol, struct socket *
*res) { //新增網路名稱空間引數和使用者空間建立套接字標誌。關於網路名稱空間的概念,先忽略它 return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0); } /* * @kern: 如果是kernel自己建立一個套接字,那麼置標記為1;否則是使用者空間建立套接字,設定為0 */ static int __sock_create(struct net *net, int family, int type, int protocol, struct socket **res, int kern) { int err; struct socket *sock; const struct net_proto_family *pf; //檢查地址族引數和協議型別引數,使得它們不超過當期核心支援的最大值 /* * Check protocol is in range */ if (family < 0 || family >= NPROTO) return -EAFNOSUPPORT; if (type < 0 || type >= SOCK_MAX) return -EINVAL; //如果要基於資料鏈路層收發資料,那麼應該使用socket(PF_PACKET, SOCK_RAW|SOCK_DGRAM, protocol) //來建立套接字。但是老版本核心是使用socket(PF_IENT, SOCK_PACKET, protocol),這裡為了向後相容, //強制修改地址族為PF_PACKET /* Compatibility. This uglymoron is moved from INET layer to here to avoid deadlock in module load. */ if (family == PF_INET && type == SOCK_PACKET) { static int warned; if (!warned) { warned = 1; printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n", current->comm); } family = PF_PACKET; } //SELinux相關的安全檢查 err = security_socket_create(family, type, protocol, kern); if (err) return err; //分配套介面層套接字struct socket.該函式涉及檔案系統INODE相關內容,比較複雜, //但是不影響協議棧分析,這裡忽略不再深入探究 /* * Allocate the socket and allow the family to set things up. if * the protocol is 0, the family is instructed to select an appropriate * default. */ sock = sock_alloc(); if (!sock) { if (net_ratelimit()) printk(KERN_WARNING "socket: no more sockets\n"); return -ENFILE; /* Not exactly a match, but its the closest posix thing */ } //協議型別記錄到struct socket的type欄位中 sock->type = type; //如果協議相關的模組尚未載入進核心,則嘗試載入,涉及核心模組可動態載入特性,目前一般來講, //核心支援的協議族都是直接編譯到核心中的,所以這種情況可以忽略,暫不深究 #if defined(CONFIG_KMOD) /* Attempt to load a protocol module if the find failed. * * 12/09/1996 Marcin: But! this makes REALLY only sense, if the user * requested real, full-featured networking support upon configuration. * Otherwise module support will break! */ if (net_families[family] == NULL) request_module("net-pf-%d", family); #endif //從系統全域性陣列net_families中獲取指定協議族定義的struct net_proto_family結構,每個 //協議族在初始化過程中都會向系統註冊一個這樣的結構,IPv4在inet_init()中完成註冊 rcu_read_lock(); pf = rcu_dereference(net_families[family]); err = -EAFNOSUPPORT; if (!pf) goto out_release; /* * We will call the ->create function, that possibly is in a loadable * module, so we have to bump that loadable module refcnt first. */ if (!try_module_get(pf->owner)) goto out_release; /* Now protected by module ref count */ rcu_read_unlock(); //呼叫協議族提供的create()函式完成協議族相關的套接字建立工作,對於AF_INET,該函式為inet_create() err = pf->create(net, sock, protocol); if (err < 0) goto out_module_put; /* * Now to bump the refcnt of the [loadable] module that owns this * socket at sock_release time we decrement its refcnt. */ if (!try_module_get(sock->ops->owner)) goto out_module_busy; /* * Now that we're done with the ->create function, the [loadable] * module can have its refcnt decremented */ module_put(pf->owner); err = security_socket_post_create(sock, family, type, protocol, kern); if (err) goto out_sock_release; *res = sock; return 0; out_module_busy: err = -EAFNOSUPPORT; out_module_put: sock->ops = NULL; module_put(pf->owner); out_sock_release: sock_release(sock); return err; out_release: rcu_read_unlock(); goto out_sock_release; }

注:上面的流程實際上屬於套介面層對socket()系統呼叫的通用實現,該過程是所有協議族公用的,從程式碼中可以看到,只有pf->create()函式指標是協議族相關的,該函式才是各個協議族實現自己獨有特性的入口。

3. AF_INET協議族套接字建立

AF_INET協議族支援多個傳輸層協議,自然而然的,不同的傳輸層協議,在套接字建立過程中也會有一些自己獨有的流程要執行,為了實現這種能力,協議族在初始化過程中就建立了一個數據結構該支援這種需求,見如下介紹。

3.1 AF_INET協議族支援的套接字定義

/* Upon startup we insert all the elements in inetsw_array[] into
 * the linked list inetsw.
 */
//AF_INET協議族定義了其支援如下型別的套接字
static struct inet_protosw inetsw_array[] =
{
	{
		.type =       SOCK_STREAM,
		.protocol =   IPPROTO_TCP,
        //TCP協議特化的操作
		.prot =       &tcp_prot,
        //AF_INET協議族對於流型別套接字的通用操作
		.ops =        &inet_stream_ops,
		.capability = -1,
		.no_check =   0,
        //ICSK表示是面向連線的;PERMANET表示不可被解除安裝
		.flags =      INET_PROTOSW_PERMANENT |
			      INET_PROTOSW_ICSK,
	},
    {
        .type =       SOCK_DGRAM,
        .protocol =   IPPROTO_UDP,
        //UDP協議特有的操作
        .prot =       &udp_prot,
        //AF_INET協議族對於資料報型別套接字的通用操作
        .ops =        &inet_dgram_ops,
        .capability = -1,
        .no_check =   UDP_CSUM_DEFAULT,
        .flags =      INET_PROTOSW_PERMANENT,
     },
     {
        .type =       SOCK_RAW,
        //RAW套接字比較特殊,這種套接字跨過了傳輸層,直接給予網路層IP程式設計
        .protocol =   IPPROTO_IP,	/* wild card */
        .prot =       &raw_prot,
        .ops =        &inet_sockraw_ops,
        .capability = CAP_NET_RAW,
        .no_check =   UDP_CSUM_DEFAULT,
        .flags =      INET_PROTOSW_REUSE,
     }
};

static int __init inet_init(void)
{
	...
    //協議族初始化過程中將支援的三個套接字新增到AF_INET協議族維護的全域性變數inetsw陣列中
	for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
		inet_register_protosw(q);
	...
}

注:實際上,AF_INET協議族支援的套接字遠不止上面這三個,只是這三個最常用。其它套接字的註冊分散在各個模組的初始化過程中,用到時再研究,這裡不再深入。

3.2 inet_create()

inet_create()是AF_INET協議族的套接字建立函式入口,它由前面的__sock_create()通過pf->create()呼叫。

/*
 *	Create an inet socket.
 */
static int inet_create(struct net *net, struct socket *sock, int protocol)
{
	struct sock *sk;
	struct list_head *p;
	struct inet_protosw *answer;
	struct inet_sock *inet;
	struct proto *answer_prot;
	unsigned char answer_flags;
	char answer_no_check;
	int try_loading_module = 0;
	int err;

	//網路名稱空間校驗
	if (net != &init_net)
		return -EAFNOSUPPORT;

	//對於TCP&UDP,如果inet_ehash_seret變數未初始化,則呼叫build_ehash_secret()進行初始化。目前
    //還沒有看到該變數的作用,但實際上build_ehash_seret()實際上就是生成一個非零隨機數而已
	if (sock->type != SOCK_RAW &&
	    sock->type != SOCK_DGRAM &&
	    !inet_ehash_secret)
		build_ehash_secret();

	//將套接字狀態設定為未連線狀態,注意這裡的狀態僅僅是套介面層記錄的socket狀態,
    //並非傳輸層的狀態,傳輸層狀態由傳輸層控制塊記錄
	sock->state = SS_UNCONNECTED;

	//以引數type(套接字型別)為索引,遍歷陣列inetsw[sock->type],尋找匹配的協議。
    //IPv4協議族將所有支援的協議儲存到陣列inetsw中,相同型別的協議組織成一個雙鏈表
	/* Look for the requested type/protocol pair. */
	answer = NULL;
lookup_protocol:
	err = -ESOCKTNOSUPPORT;
	rcu_read_lock();
	list_for_each_rcu(p, &inetsw[sock->type]) {
		answer = list_entry(p, struct inet_protosw, list);

		//在指定型別的協議中進行精確匹配,所謂精確匹配是指socket()的protocol引數不為0(即IPPROTO_IP)
		/* Check the non-wild match. */
		if (protocol == answer->protocol) {
			if (protocol != IPPROTO_IP)
				break;
		} else {
        	//進行模糊模糊匹配,分為兩種情況:
            //1. 應用程式指定的協議為通配協議即0,這時直接選用指定型別協議連結串列中的第一個協議
			/* Check for the two wild cases. */
			if (IPPROTO_IP == protocol) {
				protocol = answer->protocol;
				break;
			}
            //2.應用程式指定的是具體的協議,但是指定型別協議連結串列中有通配協議,也可以匹配
			if (IPPROTO_IP == answer->protocol)
				break;
		}
		err = -EPROTONOSUPPORT;
		answer = NULL;
	}
	//有些協議是以模組的形式實現的,如果第一遍沒有找到符合要求的協議,則嘗試載入相應的模組後重新進行匹配
	if (unlikely(answer == NULL)) {
		if (try_loading_module < 2) {
			rcu_read_unlock();
			/*
			 * Be more specific, e.g. net-pf-2-proto-132-type-1
			 * (net-pf-PF_INET-proto-IPPROTO_SCTP-type-SOCK_STREAM)
			 */
			if (++try_loading_module == 1)
				request_module("net-pf-%d-proto-%d-type-%d",
					       PF_INET, protocol, sock->type);
			/*
			 * Fall back to generic, e.g. net-pf-2-proto-132
			 * (net-pf-PF_INET-proto-IPPROTO_SCTP)
			 */
			else
				request_module("net-pf-%d-proto-%d",
					       PF_INET, protocol);
			goto lookup_protocol;
		} else
			goto out_rcu_unlock;
	}

	//對於指定了某些特殊許可權才能使用的協議,這裡檢查建立程式是否有相應的許可權
	err = -EPERM;
	if (answer->capability > 0 && !capable(answer->capability))
		goto out_rcu_unlock;

	//將協議族提供給套介面層的操作函式集ops記錄到套接字結構中
	sock->ops = answer->ops;
    //臨時獲取指定協議提供的一些引數
	answer_prot = answer->prot;
	answer_no_check = answer->no_check;
	answer_flags = answer->flags;
	rcu_read_unlock();

	BUG_TRAP(answer_prot->slab != NULL);

	//根據傳輸層協議結構answer_prot建立相應的傳輸層控制塊並進行一定的初始化,見下文分析
	err = -ENOBUFS;
	sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);
	if (sk == NULL)
		goto out;

	//將是否需要校驗標記記錄到傳輸控制塊中
	err = 0;
	sk->sk_no_check = answer_no_check;
    //如果傳輸層協議指定了地址複用標記,則將傳輸控制塊中的sk_reuse欄位置1,表示可以進行地址和埠複用。
    //TCP和UDP預設均未設定該標記
	if (INET_PROTOSW_REUSE & answer_flags)
		sk->sk_reuse = 1;

	//根據是否有INET_PROTOSW_ICSK標記設定傳輸控制塊的is_icsk變數,該標記表示該協議是否是面向連線的協議,
    //顯然,TCP會設定該標記,UDP不會
	inet = inet_sk(sk);
	inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;

	//RAW型別套接字相關處理,暫時忽略
	if (SOCK_RAW == sock->type) {
		inet->num = protocol;
		if (IPPROTO_RAW == protocol)
			inet->hdrincl = 1;
	}
	//根據系統引數no_pmtu_disc設定傳輸控制塊是否支援路徑MTU發現機制
	if (ipv4_config.no_pmtu_disc)
		inet->pmtudisc = IP_PMTUDISC_DONT;
	else
		inet->pmtudisc = IP_PMTUDISC_WANT;

	//進一步初始化通用傳輸控制塊相關成員
	inet->id = 0;
    //初始化通用結構struct sock的一些成員
	sock_init_data(sock, sk);
    //struct sock結構的解構函式
	sk->sk_destruct	   = inet_sock_destruct;
	sk->sk_family	   = PF_INET;
	sk->sk_protocol	   = protocol;
	sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;

	//傳輸控制塊中組播相關成員的初始化
	inet->uc_ttl	= -1;
	inet->mc_loop	= 1;
	inet->mc_ttl	= 1;
	inet->mc_index	= 0;
	inet->mc_list	= NULL;

	sk_refcnt_debug_inc(sk);

	//如果傳輸控制塊中設定了埠號,則呼叫傳輸層的hash()介面將該埠資訊加入到傳輸層的管理結構中。
    //TCP和UDP不會有這種行為,它們的埠繫結都是在socket()呼叫之後顯式或者隱式繫結的。埠的繫結
    //在bind()系統呼叫中再仔細研究
	if (inet->num) {
		/* It assumes that any protocol which allows
		 * the user to assign a number at socket
		 * creation time automatically
		 * shares.
		 */
		inet->sport = htons(inet->num);
		/* Add to protocol hash chains. */
		sk->sk_prot->hash(sk);
	}

	//如果傳輸層協議提供了init()介面,則呼叫它完成傳輸層特有的初始化。對於TCP,
    //該函式為tcp_v4_init_sock(),UDP沒有定義該介面
	if (sk->sk_prot->init) {
		err = sk->sk_prot->init(sk);
		if (err)
			sk_common_release(sk);
	}
out:
	return err;
out_rcu_unlock:
	rcu_read_unlock();
	goto out;
}

3.2.1 傳輸控制塊的分配

/**
 *	sk_alloc - All socket objects are allocated here
 *	@net: the applicable net namespace
 *	@family: protocol family
 *	@priority: for allocation (%GFP_KERNEL, %GFP_ATOMIC, etc)
 *	@prot: struct proto associated with this new sock instance
 *	@zero_it: if we should zero the newly allocated sock
 */
struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
		      struct proto *prot)
{
	struct sock *sk;

	//呼叫sk_prot_alloc()完成傳輸控制塊的分配
	sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family);
	if (sk) {
		sk->sk_family = family;
        //將傳輸層協議介面記錄到傳輸控制塊的sk_prot中
		/*
		 * See comment in struct sock definition to understand
		 * why we need sk_prot_creator -acme
		 */
		sk->sk_prot = sk->sk_prot_creator = prot;
        //傳輸控制塊中的鎖相關初始化
		sock_lock_init(sk);
		sk->sk_net = get_net(net);
	}

	return sk;
}

static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
		int family)
{
	struct sock *sk;
	struct kmem_cache *slab;

	//如果傳輸層協議提供了快取記憶體,則從快取記憶體中分配傳輸控制塊;
    //如果沒有提供,則呼叫普通的記憶體分配函式分配
	slab = prot->slab;
	if (slab != NULL)
		sk = kmem_cache_alloc(slab, priority);
	else
		sk = kmalloc(prot->obj_size, priority);

	if (sk != NULL) {
		if (security_sk_alloc(sk, family, priority))
			goto out_free;

		if (!try_module_get(prot->owner))
			goto out_free_sec;
	}

	return sk;

out_free_sec:
	security_sk_free(sk);
out_free:
	if (slab != NULL)
		kmem_cache_free(slab, sk);
	else
		kfree(sk);
	return NULL;
}

3.2.2 通用傳輸控制塊的初始化

void sock_init_data(struct socket *sock, struct sock *sk)
{
	//初始化傳送佇列
	skb_queue_head_init(&sk->sk_receive_queue);
    //初始化傳送佇列
	skb_queue_head_init(&sk->sk_write_queue);
    //初始化錯誤佇列
	skb_queue_head_init(&sk->sk_error_queue);
#ifdef CONFIG_NET_DMA
	skb_queue_head_init(&sk->sk_async_wait_queue);
#endif
	//傳送指標置空,表示當前沒有任何資料要傳送
	sk->sk_send_head	=	NULL;

	init_timer(&sk->sk_timer);

	sk->sk_allocation	=	GFP_KERNEL;
    //初始化讀寫緩衝區上限為系統預設值(可以通過SO_SNDBUF/SO_RECVBUF修改,如果不修改,三次握手成功之後
    //系統還會執行一次調整)
	sk->sk_rcvbuf		=	sysctl_rmem_default;
	sk->sk_sndbuf		=	sysctl_wmem_default;
	sk->sk_state		=	TCP_CLOSE;
	sk->sk_socket		=	sock;

	sock_set_flag(sk, SOCK_ZAPPED);

	if (sock) {
		sk->sk_type	=	sock->type;
		sk->sk_sleep	=	&sock->wait;
		sock->sk	=	sk;
	} else
		sk->sk_sleep	=	NULL;

	rwlock_init(&sk->sk_dst_lock);
	rwlock_init(&sk->sk_callback_lock);
	lockdep_set_class_and_name(&sk->sk_callback_lock,
			af_callback_keys + sk->sk_family,
			af_family_clock_key_strings[sk->sk_family]);

	sk->sk_state_change	=	sock_def_wakeup;
	sk->sk_data_ready	=	sock_def_readable;
	sk->sk_write_space	=	sock_def_write_space;
	sk->sk_error_report	=	sock_def_error_report;
	sk->sk_destruct		=	sock_def_destruct;

	sk->sk_sndmsg_page	=	NULL;
	sk->sk_sndmsg_off	=	0;

	sk->sk_peercred.pid 	=	0;
	sk->sk_peercred.uid	=	-1;
	sk->sk_peercred.gid	=	-1;
	sk->sk_write_pending	=	0;
	sk->sk_rcvlowat		=	
            
           

相關推薦

介面socket系統呼叫實現

這篇筆記記錄了AF_INET協議族在套介面層對scoket()系統呼叫的實現,注意這裡只介紹了套介面層的實現,相比於完整的socket()系統呼叫實現,這裡缺少兩部分內容: 檔案系統相關的部分,比如檔案描述符的分配等; 傳輸層的實現,套接字的建立肯定是要傳輸層

Linux fsync和fdatasync系統呼叫實現分析(Ext4檔案系統

參考:https://blog.csdn.net/luckyapple1028/article/details/61413724 在Linux系統中,對檔案系統上檔案的讀寫一般是通過頁快取(page cache)進行的(DirectIO除外),這樣設計的可以延時磁碟IO的操作,從而可以減少磁碟讀

裝置介面net_device結構的管理

核心中所有的網絡卡裝置,包括所有的真實網絡卡,以及絕大多數的虛擬網絡卡,要想工作,前提是例項化並向核心註冊一個struct net_device結構,該結構就是軟體層面對網絡卡的抽象。系統中出於不同的目的會有多個網絡卡共存,核心需要能夠合理的管理這些註冊的網絡卡,這篇筆記就來介紹這方面的內容

裝置介面資料包接收

資料包最先當然是由網絡卡收到(不考慮環回介面這樣的虛擬裝置),那麼之後軟體是如何接收該資料,又是如何將資料遞交給協議棧的,這篇筆記就來看看linux核心和驅動程式時如何配合完整這個接收過程的。 1. 資料接收模式 當前核心提供了兩種資料接收模式:非NAPI方式(老方法)和NAPI(新

裝置介面裝置狀態管理

從前面的資料收發過程中也看到了,在收發流程中很多檢查裝置狀態的操作,這篇筆記來完整的看下在設備註冊成功後,到底有哪些狀態,它們是如何控制收發流程的。 1. net_device的state欄位 dev->state欄位描述了裝置和裝置佇列的狀態,當前定義有如下值: /* T

裝置介面資料包傳送

這篇筆記記錄了裝置介面層傳送資料包的過程。這裡不會單獨列舉發送過程中使用到的一些資料結構,而是直接跟蹤程式碼,因為傳送過程中使用到的很多資料結構在接收部分的描述中已經介紹過了,這裡可以對比參考:裝置介面層之資料包接收. 1. 裝置介面層傳送介面 仔細看下dev_queue_xmit(

App介面設計token的php實現

  App介面設計之token的php實現 為了保證移動端和服務端資料傳輸相對安全,需要對介面進行加密傳輸。 一、token的設計目的:  因為APP端沒有和PC端一樣的session機制,所以無法判斷使用者是否登陸,以及無法保持使用者狀態,所以就需要一種機制

Django如何建立一介面級別的許可權系統

從零開始搭建當然是可以的,但是Django的特色就是大而全,所以,我的方案是,利用現有的Django框架,實現功能。 我用的pycharm,操作很方便快捷。 | auth_group | | auth_group_permissi

Linux----網路程式設計(I/O複用select系統呼叫

io_select_ser.c 1. #include <string.h> 2. #include <assert.h> 3. #include <unistd.h> 4. #include <stdio.h> 5. #in

Linux----網路程式設計(IO複用epoll系統呼叫函式)

伺服器端epoll.c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <assert.h> #include <unistd.h&

Linux下系統呼叫實現檔案操作

系統呼叫(系統呼叫是作業系統提供給使用者程式的一組“特殊”函式介面,使用者通過這組介面獲得作業系統提供的服務)中操作I/O的函式,都是針對檔案描述符的。 通過檔案描述符可以直接對相應檔案進行操作,如:open、close、write、read、ioctl #define STDIN_FIL

系統呼叫實現Linux命令 ls -al

二話不說直接上程式碼(這是我之前在網易部落格上寫的搬過來) ls.c 如下: #include "ls.h" /**********************************************************************/ //將路徑定位到

裝置介面net_device註冊與去註冊

網路裝置對應的結構體是struct net_device,而且驅動程式只有將該結構註冊給核心,核心才認為這是一個合法的網路裝置,這篇筆記就記錄了驅動程式如何將網路設備註冊給核心,以及對應反操作,如何去註冊。 1. net_device的建立 首先,驅動程式需要建

計算機網路應用域名系統DNS

一、為什麼存在DNS 就像我們寄信需要一個地址一樣,我們需要向網路上的某臺主機通訊,也要知道我們的主機的地址。我們知道,這個地址就是IP地址,它是一個可以在因特網上唯一標識一臺主機的地址。然而就如我們知道的那樣,IP地址只是4個十進位制數字,並不符合人們的記憶和使用,而人們

linux net子系統-介面

核心資料結構 struct socket struct proto_ops struct sock struct proto 結構體之間的關係 socket建立 註冊struct net_proto_family與st

Linux VFS mount系統呼叫

1. 我們知道,在對檔案的開啟,讀和寫操作之前,必須掛載檔案系統。那麼,核心如何掛載檔案系統,換句話說,在掛載檔案系統時核心都做了哪些事情。這是本文討論的事情。在掛載檔案系統之前,必須格式化檔案系統型別,通過mkfs命令實現。在Linux中,一個檔案系統型別包括多個檔案系統,如/dev/sda, /

Qt QQ系統表情—實現動態顯示效果

簡述 在Qt 之 QQ系統表情(五) 中 我們實現了自定義系統表情視窗,這一篇將簡單介紹如何實現QQ聊天介面中小表情視窗切換至正常表情視窗的動畫效果。 先看看QQ的效果: 當滑鼠懸浮在表情按鈕之上顯示小表情視窗,點選動態顯示正常表情視窗,再點選隱

定製Android系統開發四——系統服務實現的分析

在上一篇博文中,我給出了實現系統服務的步驟,這篇博文則將分析一下,為什麼這樣的實現方式能夠實現客戶端/伺服器模式。 RadioManagerService 首先來看一下RadioManagerService是如何建立的。 如果我沒有記錯的話,Syste

Linux 系統呼叫實現機制

1. 提示:unable to copy the source file ./installer/services.sh to the destination file /etc/init.d/vmware-tools     錯誤原因: 我的解壓包的目錄是 /mnt/cd

brk系統呼叫實現分析

brk(addr)直接修改堆的大小。addr指定current->mm->brk的新值,返回值是線性區新的結束地址,這是一個系統呼叫。當用戶態的程序呼叫brk()系統呼叫時,核心執行sys_brk(addr)函式。下面分析這個函式的執行流程:1:檢測addr引數是