當前位置:首頁 » 操作系統 » linux源碼解析

linux源碼解析

發布時間: 2024-04-01 23:26:58

1. 怎麼使用,手上有一本linux內核2.4版源代碼分析大全

一般在Linux系統中的/usr/src/linux*.*.*(*.*.*代表的是內核版本,如2.4.23)目錄下就是內核源代碼(如果沒有類似目錄,是因為還沒安裝內核代碼)。另外還可從互連網上下載。注意,不要總到去下載,最好使用它的鏡像站點下載。請在mirrors/里找一個合適的下載點,再到pub/linux/kernel/v2.6/目錄下去下載2.4.23內核。
代碼目錄結構
在閱讀源碼之前,還應知道Linux內核源碼的整體分布情況。現代的操作系統一般由進程管理、內存管理、文件系統、驅動程序和網路等組成。Linux內核源碼的各個目錄大致與此相對應,其組成如下(假設相對於Linux-2.4.23目錄):
1.arch目錄包括了所有和體系結構相關的核心代碼。它下面的每一個子目錄都代表一種Linux支持的體系結構,例如i386就是Intel CPU及與之相兼容體系結構的子目錄。PC機一般都基於此目錄。
2.include目錄包括編譯核心所需要的大部分頭文件,例如與平台無關的頭文件在include/linux子目錄下。
3.init目錄包含核心的初始化代碼(不是系統的引導代碼),有main.c和Version.c兩個文件。這是研究核心如何工作的好起點。
4.mm目錄包含了所有的內存管理代碼。與具體硬體體系結構相關的內存管理代碼位於arch/*/mm目錄下。
5.drivers目錄中是系統中所有的設備驅動程序。它又進一步劃分成幾類設備驅動,每一種有對應的子目錄,如音效卡的驅動對應於drivers/sound。
6.ipc目錄包含了核心進程間的通信代碼。
7.moles目錄存放了已建好的、可動態載入的模塊。
8.fs目錄存放Linux支持的文件系統代碼。不同的文件系統有不同的子目錄對應,如ext3文件系統對應的就是ext3子目錄。
Kernel內核管理的核心代碼放在這里。同時與處理器結構相關代碼都放在arch/*/kernel目錄下。
9.net目錄里是核心的網路部分代碼,其每個子目錄對應於網路的一個方面。
10.lib目錄包含了核心的庫代碼,不過與處理器結構相關的庫代碼被放在arch/*/lib/目錄下。
11.scripts目錄包含用於配置核心的腳本文件。
12.documentation目錄下是一些文檔,是對每個目錄作用的具體說明。
一般在每個目錄下都有一個.depend文件和一個Makefile文件。這兩個文件都是編譯時使用的輔助文件。仔細閱讀這兩個文件對弄清各個文件之間的聯系和依託關系很有幫助。另外有的目錄下還有Readme文件,它是對該目錄下文件的一些說明,同樣有利於對內核源碼的理解。
在閱讀方法或順序上,有縱向與橫向之分。所謂縱向就是順著程序的執行順序逐步進行;所謂橫向,就是按模塊進行。它們經常結合在一起進行。對於Linux啟動的代碼可順著Linux的啟動順序一步步來閱讀;對於像內存管理部分,可以單獨拿出來進行閱讀分析。實際上這是一個反復的過程,不可能讀一遍就理解。

2. Linux 之mutex 源碼分析

 mutex相關的函數並不是linux kernel實現的,而是glibc實現的,源碼位於nptl目錄下。

http://ftp.gnu.org/pub/gnu/glibc/glibc-2.3.5.tar.gz

首先說數據結構:

typedef union

{

  struct

  {

    int __lock;

    unsigned int __count;

    int __owner;

    unsigned int __nusers;

    /* KIND must stay at this position in the structure to maintain

       binary compatibility.  */

    int __kind;

    int __spins;

  } __data;

  char __size[__SIZEOF_PTHREAD_MUTEX_T];

  long int __align;

} pthread_mutex_t;

 int __lock;  資源競爭引用計數

 int __kind; 鎖類型,init 函數中mutexattr 參數傳遞,該參數可以為NULL,一般為 PTHREAD_MUTEX_NORMAL

結構體其他元素暫時不了解,以後更新。

/*nptl/pthread_mutex_init.c*/

int

__pthread_mutex_init (mutex, mutexattr)

     pthread_mutex_t *mutex;

     const pthread_mutexattr_t *mutexattr;

{

  const struct pthread_mutexattr *imutexattr;

  assert (sizeof (pthread_mutex_t) <= __SIZEOF_PTHREAD_MUTEX_T);

  imutexattr = (const struct pthread_mutexattr *) mutexattr ?: &default_attr;

  /* Clear the whole variable.  */

  memset (mutex, '\0', __SIZEOF_PTHREAD_MUTEX_T);

  /* Copy the values from the attribute.  */

  mutex->__data.__kind = imutexattr->mutexkind & ~0x80000000;

  /* Default values: mutex not used yet.  */

  // mutex->__count = 0;        already done by memset

  // mutex->__owner = 0;        already done by memset

  // mutex->__nusers = 0;        already done by memset

  // mutex->__spins = 0;        already done by memset

  return 0;

}

init函數就比較簡單了,將mutex結構體清零,設置結構體中__kind屬性。

/*nptl/pthread_mutex_lock.c*/

int

__pthread_mutex_lock (mutex)

     pthread_mutex_t *mutex;

{

  assert (sizeof (mutex->__size) >= sizeof (mutex->__data));

  pid_t id = THREAD_GETMEM (THREAD_SELF, tid);

  switch (__builtin_expect (mutex->__data.__kind, PTHREAD_MUTEX_TIMED_NP))

    {

     …

    default:

      /* Correct code cannot set any other type.  */

    case PTHREAD_MUTEX_TIMED_NP:

    simple:

      /* Normal mutex.  */

      LLL_MUTEX_LOCK (mutex->__data.__lock);

      break;

  …

  }

  /* Record the ownership.  */

  assert (mutex->__data.__owner == 0);

  mutex->__data.__owner = id;

#ifndef NO_INCR

  ++mutex->__data.__nusers;

#endif

  return 0;

}

該函數主要是調用LLL_MUTEX_LOCK, 省略部分為根據mutex結構體__kind屬性不同值做些處理。

宏定義函數LLL_MUTEX_LOCK最終調用,將結構體mutex的__lock屬性作為參數傳遞進來

#define __lll_mutex_lock(futex)                                                \

  ((void) ({                                                                \

    int *__futex = (futex);                                                \

    if (atomic_compare_and_exchange_bool_acq (__futex, 1, 0) != 0)        \

      __lll_lock_wait (__futex);                                        \

  }))

atomic_compare_and_exchange_bool_acq (__futex, 1, 0)宏定義為:

#define atomic_compare_and_exchange_bool_acq(mem, newval, oldval) \

  ({ __typeof (mem) __gmemp = (mem);                                      \

     __typeof (*mem) __gnewval = (newval);                              \

      \

     *__gmemp == (oldval) ? (*__gmemp = __gnewval, 0) : 1; })

這個宏實現的功能是:

如果mem的值等於oldval,則把newval賦值給mem,放回0,否則不做任何處理,返回1.

由此可以看出,當mutex鎖限制的資源沒有競爭時,__lock 屬性被置為1,並返回0,不會調用__lll_lock_wait (__futex); 當存在競爭時,再次調用lock函數,該宏不做任何處理,返回1,調用__lll_lock_wait (__futex);

void

__lll_lock_wait (int *futex)

{

  do

    {

      int oldval = atomic_compare_and_exchange_val_acq (futex, 2, 1);

      if (oldval != 0)

lll_futex_wait (futex, 2);

    }

  while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0);

}

atomic_compare_and_exchange_val_acq (futex, 2, 1); 宏定義:

/* The only basic operation needed is compare and exchange.  */

#define atomic_compare_and_exchange_val_acq(mem, newval, oldval) \

  ({ __typeof (mem) __gmemp = (mem);                                      \

     __typeof (*mem) __gret = *__gmemp;                                      \

     __typeof (*mem) __gnewval = (newval);                              \

      \

     if (__gret == (oldval))                                              \

       *__gmemp = __gnewval;                                              \

     __gret; })

這個宏實現的功能是,當mem等於oldval時,將mem置為newval,始終返回mem原始值。

此時,futex等於1,futex將被置為2,並且返回1. 進而調用

lll_futex_wait (futex, 2);

#define lll_futex_timed_wait(ftx, val, timespec)                        \

({                                                                        \

   DO_INLINE_SYSCALL(futex, 4, (long) (ftx), FUTEX_WAIT, (int) (val),        \

     (long) (timespec));                                \

   _r10 == -1 ? -_retval : _retval;                                        \

})

該宏對於不同的平台架構會用不同的實現,採用匯編語言實現系統調用。不過確定的是調用了Linux kernel的futex系統調用。

futex在linux kernel的實現位於:kernel/futex.c

SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,

struct timespec __user *, utime, u32 __user *, uaddr2,

u32, val3)

{

struct timespec ts;

ktime_t t, *tp = NULL;

u32 val2 = 0;

int cmd = op & FUTEX_CMD_MASK;

if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||

      cmd == FUTEX_WAIT_BITSET ||

      cmd == FUTEX_WAIT_REQUEUE_PI)) {

if (_from_user(&ts, utime, sizeof(ts)) != 0)

return -EFAULT;

if (!timespec_valid(&ts))

return -EINVAL;

t = timespec_to_ktime(ts);

if (cmd == FUTEX_WAIT)

t = ktime_add_safe(ktime_get(), t);

tp = &t;

}

/*

 * requeue parameter in 'utime' if cmd == FUTEX_*_REQUEUE_*.

 * number of waiters to wake in 'utime' if cmd == FUTEX_WAKE_OP.

 */

if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||

    cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)

val2 = (u32) (unsigned long) utime;

return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);

}

futex具有六個形參,pthread_mutex_lock最終只關注了前四個。futex函數對參數進行判斷和轉化之後,直接調用do_futex。

long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,

u32 __user *uaddr2, u32 val2, u32 val3)

{

int clockrt, ret = -ENOSYS;

int cmd = op & FUTEX_CMD_MASK;

int fshared = 0;

if (!(op & FUTEX_PRIVATE_FLAG))

fshared = 1;

clockrt = op & FUTEX_CLOCK_REALTIME;

if (clockrt && cmd != FUTEX_WAIT_BITSET && cmd != FUTEX_WAIT_REQUEUE_PI)

return -ENOSYS;

switch (cmd) {

case FUTEX_WAIT:

val3 = FUTEX_BITSET_MATCH_ANY;

case FUTEX_WAIT_BITSET:

ret = futex_wait(uaddr, fshared, val, timeout, val3, clockrt);

break;

         …

default:

ret = -ENOSYS;

}

return ret;

}

省略部分為對其他cmd的處理,pthread_mutex_lock函數最終傳入的cmd參數為FUTEX_WAIT,所以在此只關注此分之,分析futex_wait函數的實現。

static int futex_wait(u32 __user *uaddr, int fshared,

      u32 val, ktime_t *abs_time, u32 bitset, int clockrt)

{

struct hrtimer_sleeper timeout, *to = NULL;

struct restart_block *restart;

struct futex_hash_bucket *hb;

struct futex_q q;

int ret;

           … … //delete parameters check and convertion

retry:

/* Prepare to wait on uaddr. */

ret = futex_wait_setup(uaddr, val, fshared, &q, &hb);

if (ret)

goto out;

/* queue_me and wait for wakeup, timeout, or a signal. */

futex_wait_queue_me(hb, &q, to);

… … //other handlers

return ret;

}

futex_wait_setup 將線程放進休眠隊列中,

futex_wait_queue_me(hb, &q, to);將本線程休眠,等待喚醒。

喚醒後,__lll_lock_wait函數中的while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0); 語句將被執行,由於此時futex在pthread_mutex_unlock中置為0,所以atomic_compare_and_exchange_bool_acq (futex, 2, 0)語句將futex置為2,返回0. 退出循環,訪問用戶控制項的臨界資源。

/*nptl/pthread_mutex_unlock.c*/

int

internal_function attribute_hidden

__pthread_mutex_unlock_usercnt (mutex, decr)

     pthread_mutex_t *mutex;

     int decr;

{

  switch (__builtin_expect (mutex->__data.__kind, PTHREAD_MUTEX_TIMED_NP))

    {

   … …

    default:

      /* Correct code cannot set any other type.  */

    case PTHREAD_MUTEX_TIMED_NP:

    case PTHREAD_MUTEX_ADAPTIVE_NP:

      /* Normal mutex.  Nothing special to do.  */

      break;

    }

  /* Always reset the owner field.  */

  mutex->__data.__owner = 0;

  if (decr)

    /* One less user.  */

    --mutex->__data.__nusers;

  /* Unlock.  */

  lll_mutex_unlock (mutex->__data.__lock);

  return 0;

}

省略部分是針對不同的__kind屬性值做的一些處理,最終調用 lll_mutex_unlock。

該宏函數最終的定義為:

#define __lll_mutex_unlock(futex)                        \

  ((void) ({                                                \

    int *__futex = (futex);                                \

    int __val = atomic_exchange_rel (__futex, 0);        \

\

    if (__builtin_expect (__val > 1, 0))                \

      lll_futex_wake (__futex, 1);                        \

  }))

atomic_exchange_rel (__futex, 0);宏為:

#define atomic_exchange_rel(mem, value) \

  (__sync_synchronize (), __sync_lock_test_and_set (mem, value))

實現功能為:將mem設置為value,返回原始mem值。

__builtin_expect (__val > 1, 0) 是編譯器優化語句,告訴編譯器期望值,也就是大多數情況下__val > 1 ?是0,其邏輯判斷依然為if(__val > 1)為真的話執行 lll_futex_wake。

現在分析,在資源沒有被競爭的情況下,__futex 為1,那麼返回值__val則為1,那麼 lll_futex_wake (__futex, 1);        不會被執行,不產生系統調用。 當資源產生競爭的情況時,根據對pthread_mutex_lock 函數的分析,__futex為2, __val則為2,執行 lll_futex_wake (__futex, 1); 從而喚醒等在臨界資源的線程。

lll_futex_wake (__futex, 1); 最終會調動同一個系統調用,即futex, 只是傳遞的cmd參數為FUTEX_WAKE。

在linux kernel的futex實現中,調用

static int futex_wake(u32 __user *uaddr, int fshared, int nr_wake, u32 bitset)

{

struct futex_hash_bucket *hb;

struct futex_q *this, *next;

struct plist_head *head;

union futex_key key = FUTEX_KEY_INIT;

int ret;

if (!bitset)

return -EINVAL;

ret = get_futex_key(uaddr, fshared, &key);

if (unlikely(ret != 0))

goto out;

hb = hash_futex(&key);

spin_lock(&hb->lock);

head = &hb->chain;

plist_for_each_entry_safe(this, next, head, list) {

if (match_futex (&this->key, &key)) {

if (this->pi_state || this->rt_waiter) {

ret = -EINVAL;

break;

}

/* Check if one of the bits is set in both bitsets */

if (!(this->bitset & bitset))

continue;

wake_futex(this);

if (++ret >= nr_wake)

break;

}

}

spin_unlock(&hb->lock);

put_futex_key(fshared, &key);

out:

return ret;

}

該函數遍歷在該mutex上休眠的所有線程,調用wake_futex進行喚醒,

static void wake_futex(struct futex_q *q)

{

struct task_struct *p = q->task;

/*

 * We set q->lock_ptr = NULL _before_ we wake up the task. If

 * a non futex wake up happens on another CPU then the task

 * might exit and p would dereference a non existing task

 * struct. Prevent this by holding a reference on p across the

 * wake up.

 */

get_task_struct(p);

plist_del(&q->list, &q->list.plist);

/*

 * The waiting task can free the futex_q as soon as

 * q->lock_ptr = NULL is written, without taking any locks. A

 * memory barrier is required here to prevent the following

 * store to lock_ptr from getting ahead of the plist_del.

 */

smp_wmb();

q->lock_ptr = NULL;

wake_up_state(p, TASK_NORMAL);

put_task_struct(p);

}

wake_up_state(p, TASK_NORMAL);  的實現位於kernel/sched.c中,屬於linux進程調度的技術。

3. Linux內核源碼解析-list.h

開頭就說明了這里的 list.h 文件來自 Linux Kernel ( */include/linux/list.h ),只是去除了列表項的硬體預載入部分。

進行宏替換後就是

Note: 沒搞懂這里為什麼加個 osn 前綴,原本是 list_add ,現在是 osn_list_add 。

可以看到就是個簡單的鏈表節點刪除過程,同時把刪除節點的前後指針設為無法訪問。

刪除節點後初始化,前後指針都指向自己

從A鏈表刪除後頭插法插入B鏈表

從A鏈表刪除後尾插法插入B鏈表

先對 list 判空,非空就把 list 鏈表除頭節點外裁剪到 head 頭節點在的鏈表中。函數不安全, list 節點可以繼續訪問其他節點。

多了一步 list 重新初始化的過程。

(unsigned long)(&((type *)0)->member))) 將0x0地址強制轉換為 type * 類型,然後取 type 中的成員 member 地址,因為起始地址為0,得到的 member 的地址就直接是該成員相對於 type 對象的偏移地址了。
所以該語句的功能是:得到 type 類型對象中 member 成員的地址偏移量。
先將 ptr 強制轉換為 char * 類型(因為 char * 類型進行加減的話,加減量為 sizeof(char)*offset , char 佔一個位元組空間,這樣指針加減的步長就是1個位元組,實現加一減一。)
整句話的意思就是:得到指向 type 的指針,已知成員的地址,然後減去這個成員相對於整個結構對象的地址偏移量,得到這個數據對象的地址。

就是從前往後,從後往前的區別

Note: 從head節點開始(不包括head節點!)遍歷它的每一個節點!它用n先將下一個要遍歷的節點保存起來,防止刪除本節點後,無法找到下一個節點,而出現錯誤!

已知指向某個結構體的指針pos,以及指向它中member成員的指針head,從下一個結構體開始向後遍歷這個結構體鏈

Note: 同理,先保存下一個要遍歷的節點!從head下一個節點向後遍歷鏈表。

list.h使用說明
linux內核list.h分析(一)
linux內核list.h分析(二)
【Linux內核數據結構】最為經典的鏈表list

4. 求一段Linux操作系統源代碼分析

這個文件非常小 你們老師分析模板給了很好分析了
一,源代碼文件名稱
Linux/lib/setsid.c
二,源代碼功能描述
整體功能:如果調用的進程不是一個組的組長時,setsid創建一個新會話。調用進程將成為該新會話的組長,新進程組的組長,並且沒有控制終端。調用進程的組id和會話id被設置成進程的PID,調用進程將成為新進程組合和新會話中的唯一進程。
函數輸入:pid-t,setsid
函數輸出:調用進程的會話標識符
函數功能:(就一個函數,所以就跟整體功能相同就行了)
三,程序流程圖 (可以省略了)

5. linux內核源碼詳解

Linux的內核源代碼可以從很多途徑得到。一般來講,在安裝的linux系統下,/usr/src/linux目錄下的東西就是內核源代碼。
對於源代碼的閱讀,要想比較順利,事先最好對源代碼的知識背景有一定的了解。對於linux內核源代碼來講,我認為,基本要求是:1、操作系統的基本知識; 2、對C語言比較熟悉,最好要有匯編語言的知識和GNU C對標准C的擴展的知識的了解。
另外在閱讀之前,還應該知道Linux內核源代碼的整體分布情況。我們知道現代的操作系統一般由進程管理、內存管理、文件系統、驅動程序、網路等組成。看一下Linux內核源代碼就可看出,各個目錄大致對應了這些方面。Linux內核源代碼的組成如下(假設相對於linux目錄):
arch 這個子目錄包含了此核心源代碼所支持的硬體體系結構相關的核心代碼。如對於X86平台就是i386。
include 這個目錄包括了核心的大多數include文件。另外對於每種支持的體系結構分別有一個子目錄。
init 此目錄包含核心啟動代碼。
mm 此目錄包含了所有的內存管理代碼。與具體硬體體系結構相關的內存管理代碼位於arch/-/mm目錄下,如對應於X86的就是arch/i386/mm/fault.c 。
drivers 系統中所有的設備驅動都位於此目錄中。它又進一步劃分成幾類設備驅動,每一種也有對應的子目錄,如音效卡的驅動對應於drivers/sound。
ipc 此目錄包含了核心的進程間通訊代碼。
moles 此目錄包含已建好可動態載入的模塊。
fs Linux支持的文件系統代碼。不同的文件系統有不同的子目錄對應,如ext2文件系統對應的就是ext2子目錄。
kernel 主要核心代碼。同時與處理器結構相關代碼都放在arch/-/kernel目錄下。
net 核心的網路部分代碼。裡面的每個子目錄對應於網路的一個方面。
lib 此目錄包含了核心的庫代碼。與處理器結構相關庫代碼被放在arch/-/lib/目錄下。
scripts 此目錄包含用於配置核心的腳本文件。
Documentation 此目錄是一些文檔,起參考作用。

6. linux源碼分析

linux的tcp-ip棧代碼的詳細分析

1.數據結構(msghdr,sk_buff,socket,sock,proto_ops,proto)

bsd套接字層,操作的對象是socket,數據存放在msghdr這樣的數據結構:

創建socket需要傳遞family,type,protocol三個參數,創建socket其實就是創建一個socket實例,然後創建一個文件描述符結構,並且互相建立一些關聯,即建立互相連接的指針,並且初始化這些對文件的寫讀操作映射到socket的read,write函數上來。

同時初始化socket的操作函數(proto_ops結構),如果傳入的type參數是STREAM類型,那麼就初始化為SOCKET->ops為inet_stream_ops,如果是DGRAM類型,則SOCKET-ops為inet_dgram_ops。對於inet_stream_ops其實是一個結構體,包含了stream類型的socket操作的一些入口函數,在這些函數里主要做的是對socket進行相關的操作,同時通過調用下面提到的sock中的相關操作完成socket到sock層的傳遞。比如在inet_stream_ops里有個inet_release的操作,這個操作除了釋放socket的類型空間操作外,還通過調用socket連接的sock的close操作,對於stream類型來說,即tcp_close來關閉sock

釋放sock。

創建socket同時還創建sock數據空間,初始化sock,初始化過程主要做的事情是初始化三個隊列,receive_queue(接收到的數據包sk_buff鏈表隊列),send_queue(需要發送數據包的sk_buff鏈表隊列),backlog_queue(主要用於tcp中三次握手成功的那些數據包,自己猜的),根據family、type參數,初始化sock的操作,比如對於family為inet類型的,type為stream類型的,sock->proto初始化為tcp_prot.其中包括stream類型的協議sock操作對應的入口函數。

在一端對socket進行write的過程中,首先會把要write的字元串緩沖區整理成msghdr的數據結構形式(參見linux內核2.4版源代碼分析大全),然後調用sock_sendmsg把msghdr的數據傳送至inet層,對於msghdr結構中數據區中的每個數據包,創建sk_buff結構,填充數據,掛至發送隊列。一層層往下層協議傳遞。一下每層協議不再對數據進行拷貝。而是對sk_buff結構進行操作。

熱點內容
電腦主機做伺服器下載快不 發布:2024-11-28 00:32:40 瀏覽:386
冷凍存儲盒 發布:2024-11-28 00:21:04 瀏覽:127
達內幼兒編程 發布:2024-11-28 00:21:02 瀏覽:320
我的世界下100層是什麼伺服器 發布:2024-11-28 00:16:50 瀏覽:548
怎麼改配置密碼 發布:2024-11-28 00:16:44 瀏覽:113
伺服器晶元v幾是什麼 發布:2024-11-28 00:15:37 瀏覽:599
家庭麥克需要什麼配置才能用 發布:2024-11-28 00:05:28 瀏覽:384
c語言then是什麼意思 發布:2024-11-27 23:54:07 瀏覽:195
提升訪問 發布:2024-11-27 23:41:39 瀏覽:821
為什麼學習編程 發布:2024-11-27 23:41:37 瀏覽:942