導航:首頁 > 編程系統 > 0x80000000linux

0x80000000linux

發布時間:2023-09-08 01:18:23

linux內存管理--linux內核高端內存

linux內核地址映射模型
x86
CPU採用了段頁式地址映射模型。進程代碼中的地址為邏輯地址,經過段頁式地址映射後,才真正訪問物理內存。
段頁式機制如下圖。
linux內核地址空間劃分
通常32位linux內核地址空間劃分0~3G為用戶空間,3~4G為內核空間。注意這里是32位內核地址空間劃分,64位內核地址空間劃分是不同的。
linux內核高端內存的由來
當內核模塊代碼或線程訪問內存時,代碼中的內存地址都為邏輯地址,而對應到真正的物理內存地址,需要地址一對一的映射,如邏輯地址0xc0000003對應的物理地址為0×3,0xc0000004對應的物理地址為0×4,…
…,邏輯地址與物理地址對應的關系為
物理地址
=
邏輯地址
0xC0000000
邏輯地址物理內存地址0xc00000000×00xc00000010×10xc00000020×20xc00000030×3…

0xe00000000×20000000……0xffffffff0×40000000
??
顯然不能將內核地址空間0xc0000000
~
0xfffffff全部用來簡單的地址映射。因此x86架構中將內核地址空間劃分三部分:ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM。ZONE_HIGHMEM即為高端內存,這就是內存高端內存概念的由來。
在x86結構中,三種類型的區域如下:
ZONE_DMA
內存開始的16MB
ZONE_NORMAL
16MB~896MB
ZONE_HIGHMEM
896MB
~
結束
linux內核高端內存的理解
前面我們解釋了高端內存的由來。
linux將內核地址空間劃分為三部分ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM,高端內存HIGH_MEM地址空間范圍為0xF8000000
~
0xFFFFFFFF(896MB~1024MB)。那麼如內核是如何藉助128MB高端內存地址空間是如何實現訪問可以所有物理內存?
當內核想訪問高於896MB物理地址內存時,從0xF8000000
~
0xFFFFFFFF地址空間范圍內找一段相應大小空閑的邏輯地址空間,借用一會。借用這段邏輯地址空間,建立映射到想訪問的那段物理內存(即填充內核PTE頁面表),臨時用一會,用完後歸還。這樣別人也可以借用這段地址空間訪問其他物理內存,實現了使用有限的地址空間,訪問所有所有物理內存。如下圖。
例如內核想訪問2G開始的一段大小為1MB的物理內存,即物理地址范圍為0×80000000
~
0x800FFFFF。訪問之前先找到一段1MB大小的空閑地址空間,假設找到的空閑地址空間為0xF8700000
~
0xF87FFFFF,用這1MB的邏輯地址空間映射到物理地址空間0×80000000
~
0x800FFFFF的內存。映射關系如下:
邏輯地址物理內存地址0xF87000000×800000000xF87000010×800000010xF87000020×80000002…
…0xF87FFFFF0x800FFFFF
當內核訪問完0×80000000
~
0x800FFFFF物理內存後,就將0xF8700000
~
0xF87FFFFF內核線性空間釋放。這樣其他進程或代碼也可以使用0xF8700000
~
0xF87FFFFF這段地址訪問其他物理內存。
從上面的描述,我們可以知道高端內存的最基本思想:借一段地址空間,建立臨時地址映射,用完後釋放,達到這段地址空間可以循環使用,訪問所有物理內存。
看到這里,不禁有人會問:萬一有內核進程或模塊一直佔用某段邏輯地址空間不釋放,怎麼辦?若真的出現的這種情況,則內核的高端內存地址空間越來越緊張,若都被佔用不釋放,則沒有建立映射到物理內存都無法訪問了。

⑵ 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進程調度的技術。

⑶ 如何讓linux重新枚舉pci設備

在Linux下,lspci可以枚舉所有PCI設備。它是通過讀取PCI配置空間(PCI Configuration Space)信息來實現PCI設備的枚舉的。這里,我通過兩種方式來簡單的模擬一下lspci的功能。一種是通過PCI匯流排的CF8和CFC埠來枚舉(參考PCI匯流排規范);另一種是利用proc filesystem。

方法一:這種方法需要對埠進行操作,在Linux下,普通應用程序沒有許可權讀寫I/O 埠,需要通過iopl或ioperm來提升許可權,我的代碼裡面使用iopl。


[cpp] view plainprint?
/*
* Enum all pci device via the PCI config register(CF8 and CFC).
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/io.h>

#define PCI_MAX_BUS 255 /* 8 bits (0 ~ 255) */
#define PCI_MAX_DEV 31 /* 5 bits (0 ~ 31) */
#define PCI_MAX_FUN 7 /* 3 bits (0 ~ 7) */

#define CONFIG_ADDRESS 0xCF8
#define CONFIG_DATA 0xCFC

#define PCICFG_REG_VID 0x00 /* Vendor id, 2 bytes */
#define PCICFG_REG_DID 0x02 /* Device id, 2 bytes */
#define PCICFG_REG_CMD 0x04 /* Command register, 2 bytes */
#define PCICFG_REG_STAT 0x06 /* Status register, 2 bytes */
#define PCICFG_REG_RID 0x08 /* Revision id, 1 byte */


void list_pci_devices()
{
unsigned int bus, dev, fun;
unsigned int addr, data;

//printf("BB:DD:FF VID:DID\n");


for (bus = 0; bus <= PCI_MAX_BUS; bus++) {
for (dev = 0; dev <= PCI_MAX_DEV; dev++) {
for (fun = 0; fun <= PCI_MAX_FUN; fun++) {
addr = 0x80000000L | (bus<<16) | (dev<<11) | (fun<<8);
outl(addr, CONFIG_ADDRESS);
data = inl(CONFIG_DATA);

/* Identify vendor ID */
if ((data != 0xFFFFFFFF) && (data != 0)) {
printf("%02X:%02X:%02X ", bus, dev, fun);
printf("%04X:%04X", data&0xFFFF, data>>16);
addr = 0x80000000L | (bus<<16) | (dev<<11) | (fun<<8) | PCICFG_REG_RID;
outl(addr, CONFIG_ADDRESS);
data = inl(CONFIG_DATA);
if (data&0xFF) {
printf(" (rev %02X)\n", data&0xFF);
} else {
printf("\n");
}
}
} end func
} // end device
} // end bus
}


int main()
{
int ret;

/* Enable r/w permission of all 65536 ports */
ret = iopl(3);
if (ret < 0) {
perror("iopl set error");
return 1;
}

list_pci_devices();

/* Disable r/w permission of all 65536 ports */
ret = iopl(0);
if (ret < 0) {
perror("iopl set error");
return 1;
}

return 0;
}

方法二:這種方法需不需要對埠進行操作,而是利用Linux procfs來實現對PCI 配置空間的訪問。

[cpp] view plainprint?
/*
* Enum all pci device via /proc/bus/pci/.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

#define PCI_MAX_BUS 255 /* 8 bits (0 ~ 255) */
#define PCI_MAX_DEV 31 /* 5 bits (0 ~ 31) */
#define PCI_MAX_FUN 7 /* 3 bits (0 ~ 7) */

/*
* PCI Configuration Header offsets
*/
#define PCICFG_REG_VID 0x00 /* Vendor id, 2 bytes */
#define PCICFG_REG_DID 0x02 /* Device id, 2 bytes */
#define PCICFG_REG_CMD 0x04 /* Command register, 2 bytes */
#define PCICFG_REG_STAT 0x06 /* Status register, 2 bytes */
#define PCICFG_REG_RID 0x08 /* Revision id, 1 byte */
#define PCICFG_REG_PROG_INTF 0x09 /* Programming interface code, 1 byte */
#define PCICFG_REG_SUBCLASS 0x0A /* Sub-class code, 1 byte */
#define PCICFG_REG_BASCLASS 0x0B /* Base class code, 1 byte */
#define PCICFG_REG_CACHE_LINESZ 0x0C /* Cache line size, 1 byte */
#define PCICFG_REG_LATENCY_TIMER 0x0D /* Latency timer, 1 byte */
#define PCICFG_REG_HEADER_TYPE 0x0E /* Header type, 1 byte */
#define PCICFG_REG_BIST 0x0F /* Builtin self test, 1 byte */
#define PCICFG_REG_BAR0 0x10 /* Base addr register 0, 4 bytes */
#define PCICFG_REG_BAR1 0x14 /* Base addr register 1, 4 bytes */
#define PCICFG_REG_BAR2 0x18 /* Base addr register 2, 4 bytes */
#define PCICFG_REG_BAR3 0x1C /* Base addr register 3, 4 bytes */
#define PCICFG_REG_BAR4 0x20 /* Base addr register 4, 4 bytes */
#define PCICFG_REG_BAR5 0x24 /* Base addr register 5, 4 bytes */
#define PCICFG_REG_CIS 0x28 /* Cardbus CIS Pointer */
#define PCICFG_REG_SVID 0x2C /* Subsystem Vendor ID, 2 bytes */
#define PCICFG_REG_SDID 0x2E /* Subsystem ID, 2 bytes */
#define PCICFG_REG_ROMBAR 0x30 /* ROM base register, 4 bytes */
#define PCICFG_REG_CAPPTR 0x34 /* Capabilities pointer, 1 byte */
#define PCICFG_REG_INT_LINE 0x3C /* Interrupt line, 1 byte */
#define PCICFG_REG_INT_PIN 0x3D /* Interrupt pin, 1 byte */
#define PCICFG_REG_MIN_GNT 0x3E /* Minimum grant, 1 byte */
#define PCICFG_REG_MAX_LAT 0x3F /* Maximum lat, 1 byte */


void list_pci_devices()
{
unsigned int bus, dev, fun;

//printf("BB:DD:FF VID:DID(RID)\n");


for (bus = 0; bus <= PCI_MAX_BUS; bus++) {
for (dev = 0; dev <= PCI_MAX_DEV; dev++) {
for (fun = 0; fun <= PCI_MAX_FUN; fun++) {
char proc_name[64];
int cfg_handle;
uint32_t data;
uint16_t vid, did;
uint8_t rid;

snprintf(proc_name, sizeof(proc_name),
"/proc/bus/pci/%02x/%02x.%x", bus, dev, fun);
cfg_handle = open(proc_name, O_RDWR);
if (cfg_handle <= 0)
continue;

lseek(cfg_handle, PCICFG_REG_VID, SEEK_SET);
read(cfg_handle, &data, sizeof(data));

/* Identify vendor ID */
if ((data != 0xFFFFFFFF) && (data != 0)) {
lseek(cfg_handle, PCICFG_REG_RID, SEEK_SET);
read(cfg_handle, &rid, sizeof(rid));
vid = data&0xFFFF;
did = data>>16;

printf("%02X:%02X:%02X", bus, dev, fun);
if (rid > 0) {
printf(" %04X:%04X (rev %02X)\n", vid, did, rid);
} else {
printf(" %04X:%04X\n", vid, did);
}
}
} // end func
} // end device
} // end bus
}

int main(int argc, char **argv)
{
list_pci_devices();

return 0;
}


這兩種方法各有優缺點,第一種方法方便移植到其他OS,第二種就只適用於Linux。但是,第一種方法需要對I/O port進行直接操作。第二種就不需要。

注意:執行這兩段代碼時,需要超級用戶(root) 許可權。

補充:今天在枚舉 Westmere-EP Processor(Intel Xeon Processor 5500 Series(Nehalem-EP))的 IMC(Integrated Memory Controller)時發現一個問題。lspci無法枚舉到IMC設備。Westmere-EP 是 Intel 新的處理器架構。和以往的CPU不一樣,它把Memory Controller集成到了CPU裡面。IMC控制器被映射到了PCI匯流排上,Bus Number 是0xFE~0xFF,procfs(/proc/bus/pci/)下沒有這幾個設備。但是,通過 CF8/CFC 埠可以枚舉到這些設備。
3. 這段代碼是在驅動中可以用來查找特定的pci device,並且返回一個pci_dev的結構體變數。通過這樣一個struct變數,內核提供的介面函數可以直接套用,如pci_read_config_word(),pci_write_config_word()等。
[cpp] view plainprint?
void list_pci_device()
{
struct pci_dev *dev;
struct pci_bus *bus,*childbus;

list_for_each_entry(bus, &pci_root_buses, node) { //globle pci_root_buses in pci.h
list_for_each_entry(dev, &bus->devices, bus_list) { // for bus 0
printk("%02X:%02X:%02X %04X:%04X\n",dev->bus->number,dev->devfn >> 3, dev->devfn & 0x07,dev->vendor,dev->device);
}
list_for_each_entry(childbus, &bus->children,node) { // for bus 1,2,3,...
list_for_each_entry(dev, &childbus->devices, bus_list) {
printk("%02X:%02X:%02X %04X:%04X\n",dev->bus->number,dev->devfn >> 3, dev->devfn & 0x07,dev->vendor,dev->device);
}
}
}

⑷ 對linux上的匯編的一些疑問

這是at&t格式的匯編
===================================
局部標號可以用數字,而且可以重復。在以這些標號為目的的轉移指令上,標號要帶上後綴,b表示向前,f表示向後。
例:
orw %bx,%bx
jz 1f
1:
movl $0x101000,%eax
movl %eax,%cr3 /* set the page table pointer.. */
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 /* ..and set paging (PG) bit */
jmp 1f /* flush the prefetch-queue */
1:
movl $1f,%eax
jmp *%eax /* make sure eip is relocated */
1:
絕對跳轉/調用指令中的內存操作數必須以』*』為前綴,否則gas總是認為是相對跳轉/調用指令,而且gas匯編程序自動對跳轉指令進行優化,總是使用盡可能小的跳轉偏移量。如果8比特的偏移量無法滿足要求的話,as會使用一個32位的偏移量,as匯編程序暫時還不支持16位的跳轉偏移量,所以對跳轉指令使用』addr16』前綴是無效的。還有一些跳轉指令只支持8位的跳轉偏移量,這些指令是:
』jcxz』,』jecxz』,』loop』,』loopz』,』loope』,』loopnz』』loopne』
如果你在匯編中使用了這些指令,用gas的匯編可能會出錯,因為gcc在編譯過程中不產生這些指令,所以在c語言中不必擔心這些問題。
Array、 實模式下的語法與Intel指令語法基本相同;可以用上述格式的匯編單獨寫程序(有許多宏定義和它特有的文件格式),而後用gcc/gas將其匯編成目標代碼。在linux中,這種形式的代碼主要集中在啟動部分。

閱讀全文

與0x80000000linux相關的資料

熱點內容
ps格式文件打開軟體 瀏覽:371
下載網路資源遇到哪些問題 瀏覽:887
抖音交友app廣告怎麼投 瀏覽:870
esxiha配置文件 瀏覽:573
pubg交易平台有哪些APP 瀏覽:144
excel插入文件夾地址 瀏覽:948
學編程累如何調節 瀏覽:382
圖片傳入iphone 瀏覽:730
rs485雙機通信程序 瀏覽:967
怎麼托日本的朋友買蘋果手機 瀏覽:145
築業電力軟體怎麼倒出文件 瀏覽:572
全民飛機大戰軍銜怎麼升級 瀏覽:431
公司自動化編程屬於什麼 瀏覽:925
位位用車app在哪裡下載 瀏覽:427
格式工廠iphone鈴聲 瀏覽:171
linux設置字元編碼 瀏覽:15
帝王世紀升級哪個兵種 瀏覽:409
c編程開發軟體是什麼 瀏覽:334
二的大寫怎麼寫app 瀏覽:612
樂視數據刪除了怎麼找回來 瀏覽:651

友情鏈接