'Developer's Infos'에 해당되는 글 11건

  1. 2011.10.10 __attribute__
  2. 2011.09.30 [Linux] asmlinkage
  3. 2011.09.15 uint32_t을 IP로 표현하기
  4. 2011.09.06 [Shared Memory] shmget() 공유 메모리 생성
  5. 2011.09.05 PCI Driver Flow

__attribute__는 GCC 컴파일러에 있는 메커니즘으로 컴파일러에게 추가적인 에러 체킹 같은 것을 하도록 지시하는 것이다.

형식 : __attribute__(x)
괄호 안의 x에는 가능한 몇 가지들이 있는데 특히, packed는 채우라는 의미이다:
typedef struct
{
    char a;
    int b;
} myST;

위 구조체의 sizeof(myST)는 8이다.
상식적으로 char =1byte, int = 4byte이므로 5가 나와야 하는데.. 
이런 문제 때문에 구조체를 가지고 작업할 경우 예상치 못한 버그로 고생을 가끔한다.
이런 현상이 생기는 이유는 현재 사용하고 있는 컴퓨터가 32비트이기 때문이다. 즉, CPU가 메모리 어드레스를 지정할 때 4바이트(32비트/8) 단위가 내부적으로 가장 최적화되어 빠른 데이터 접근을 가능하게 한다. 4바이트 배수 단위가 아닌 경우 당연히 접근은 가능하지만 속도 차이가 있어 디폴트로 저런 접근을 하도록 해 둔 것이다.

그럼, 메모리에 저 구조체가 잡힐 경우 어떻게 존재하느냐는 4바이트 단위로 채워지기 때문에 char a;는 1바이트이지만 4바이트를 할당해서 거기다 char a;를 담아둔다. 다음 int b;는 4바이트므로 그냥 4바이트를 할당한다. 이렇게 해서 8바이트의 크기가 필요한 것이다.

첫 char a;에서 1바이트를 제외한 나머지 3바이트는 어떤 걸로 채워질까? 의미없는 값이 채워진다. 그냥 공간만 예약되는 것이다. 그래서 이런 현상을 방지할 수 있도록 컴파일러는 __attribute__ (packed) 메커니즘을 두어서 우리가 예상한 (1바이트+4바이트) 크기가 되도록 빌드한다:
typedef struct
{
    char a;
    int b;
__attribute__((packed)) myST;

결국, 위 처럼 사용하게되면 컴파일러는 구조체 멤버의 실제 크기 만큼 할당한다. 그래서 정확한 5바이트가 나오게 된다. 물론, 속도는 약간 더 늦어진다.

여기에서 __attribute__라는 의미는 컴파일러에게 특성을 주겠다는 것을 의미하며 괄호 안은 여러 가지가 가능한데 여기서는 그 중 한가지인 구조체 정렬을 사용하겠다는 의미인 packed를 사용했다.

packed의 의미는 꽉 채우다라는 것인데 이 기능을 사용하지 않았을 경우 char a;는 실제 1바이트 외에 나머지 3바이트가 자리만 차지한 채 남아있다. 이런 비어있는 것들을 제거해서 구조체 멤버들이 빈 자리없이 꽉 채우라는 단어 의미이다. 이런 것을 구조체 정렬(alignment)이라고 한다.

위는 unix/linux의 GCC에서 사용하는 것이고 윈도우 상의 비주얼 씨의 경우는
#pragma pack(1)

처럼 사용한다. 괄호 안의 의미는 1바이트 단위까지 체크해서 빈 자리가 없도록 차곡차곡 채우라는 것이다. 4가 올 수도 있겠지만 그렇게 되면 가장 작은 단위가 4가되어 char 형 같은 경우 역시 4로 채워지게 되어 낭패이다. 따라서 확실한 경우 아니면 위 처럼 사용하면 된다.

출처: 지식IN(edson)

'Developer's Infos > Linux Programming' 카테고리의 다른 글

pci-driver  (0) 2011.10.10
add_wait_queue()/remove_wait_queue()/schedule() 설명 및 사용법  (0) 2011.10.10
[Linux] asmlinkage  (0) 2011.09.30
[Shared Memory] shmget() 공유 메모리 생성  (0) 2011.09.06
PCI Driver Flow  (0) 2011.09.05
Posted by 삼성동고양이
커널 소스를 보다보면 asmlinkage로 선언된 함수들을 볼 수 있다.
asmlinkage는 어셈블리 코드에서 직접 호출(링크)할 수 있다는 의미이며
커널 소스의 <include/linux/linkage.h>에 다음과 같이 정의되어 있다.

#include <linux/config.h>
#include <asm/linkage.h>

#ifdef __cplusplus
#define CPP_ASMLINKAGE extern "C"
#else
#define CPP_ASMLINKAGE
#endif

#ifndef asmlinkage
#define asmlinkage CPP_ASMLINKAGE
#endif

...

그렇다면 어셈블리 코드에서 직접 호출할 수 있다는 것은 무엇을 의미할까?
일반적으로 C 함수는 어셈블리 코드에서 별 어려움없이 호출할 수 있지만
함수의 인자를 넘기거나 리턴값을 받는 부분 등의 호출 규약이 필요하다.

레지스터에 여유가 있는 ARM이나 MIPS 등의 아키텍처에서는
함수의 인자를 넘길 때 특정 레지스터를 사용하도록 호출 규약이 정의되어 있지만,
그렇지 못한 x86 아키텍처에서는 때에 따라 레지스터, 스택 혹은 별도의 메모리 영역에
함수의 인자를 저장하여 넘길 수 있도록 지원한다.

당연히 인자를 레지스터에 저장하여 넘기는 방식이 빠르기 때문에 (fastcall)
최적화 옵션을 켜고 컴파일하는 경우 인자를 레지스터를 통해 전달하도록
함수의 호출부와 구현부를 변경해 버릴 수 있다. (일반적인 최적화 방법)
이 경우 GCC를 통해 자동 생성되는 코드는 적절히 변환되므로 문제가 없을테지만
직접 작성한 어셈블리 코드에서 함수를 호출하는 경우 문제가 발생하게 된다.

이 경우를 방지하기 위해 어셈블리 코드와 링크되는 함수는
인자를 (레지스터를 이용하지 않고) 스택을 이용해서 전달하도록
선언하는 데, 이 때 asmlinkage가 사용된다.

위의 코드에서 <asm/linkage.h> 파일을 #include 하고 있는 것에 주의하자.
대부분의 아키텍처에서 이 파일은 asmlinkage를 정의하지 않지만
i386에서는 다음과 같이 정의한다.

#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
#define FASTCALL(x) x __attribute__((regparm(3)))
#define fastcall __attribute__((regparm(3)))

regparm 속성은 레지스터를 이용하여 전달한 인자의 수를 지정한다.
asmlinkage로 선언된 경우 모두 스택을 이용하고 (레지스터로 0개의 인자 전달)
fastcall로 선언된 경우 최대 3개의 인자를 레지스터로 전달한다.
(이 경우 EAX, EDX, ECX 레지스터를 이용하며, 인자는 정수형이어야 한다.)


출처 :  
http://studyfoss.egloos.com
Posted by 삼성동고양이


  •  
  • c Syntax 


    10 1 10 127

    00001010 00000001 00001010 01111111


     
    then the 32 bit binary number as one single integer has the decimal value.

    1010000000010000101001111111(bin) = 167840383(dec)

    you can programmatically convert between these number systems in either direction. consider (and understand) this snippet:
     

    unsigned int  ipAddress = 167840383;

    unsigned char octet[4]  = {0,0,0,0}; 

    for (i=0; i<4; i++){

       octet[i] = ( ipAddress >> (i*8) ) & 0xFF;

    }

    unsigned int  ipAddress = 167840383;

    unsigned char octet[4]  = {0,0,0,0};


    for (i=0; i<4; i++)

    {

        octet[i] = ( ipAddress >> (i*8) ) & 0xFF;

    }



    now you have the four octets as unsigned chars in the "octet[]" array (char is just an 8-bit integer), and you can print them in the decimal dot format: 

    c Syntax (Toggle Plain Text)
     

    printf("%d.%d.%d.%d\n",octet[3],octet[2],octet[1],octet[0]);printf("%d.%d.%d.%d\n",octet[3],octet[2],octet[1],octet[0]);






    Posted by 삼성동고양이

    간략하게 정리가 잘 되어 있어서 퍼옴

    설명

    shmget() 함수는 공유 메모리를 생성합니다.

    공유 메모리는 단어 뜻에서 알 수 있듯이 하나의 프로세스에서가 아니라 여러 프로세스가 함께 사용하는 메모리를 말합니다. 이 공유 메모리를 이용하면 프로세스끼리 통신을 할 수 있으며, 같은 데이터를 공유할 수 있습니다.

    이렇게 같은 메모리 영역을 공유하기 위해서는 공유 메모리를 생성한 후에 프로세스의 자신의 영역에 첨부를 한 후에 마치 자신의 메모리를 사용하듯 사용합니다.

    즉, 공유 메모리를 사용하기 위해서는 공유 메모리를 생성한 후에, 이 메모리가 필요한 프로세스는 필요할 때 마다 자신의 프로세스에 첨부를 한 후에 다른 메모리를 사용하듯 사용하면 되겠습니다.

    헤더

    #include <sys/ipc.h>
    #include <sys/shm.h>

    형태 int shmget(key_t key, int size, int shmflg);
    인수
    key_t key 공유 메모리를 구별하는 식별 번호
    int size 공유 메모리 크기
    int shmflg 동작 옵션
    shmflg 옵션 내용
    IPC_CREATE key에 해당하는 공유 메모리가 없다면 새로 생성한다. 만약있다면 무시하며 생성을 위해 접근 권한을 지정해 주어야 한다.
    IPC_EXCL 공유 메모리가 이미 있다면 실패로 반환하며 공유 메모리에 접근하지 못한다. 이 옵션이 없어야 기존 공유 메모리에 접근할 수 있다.
    반환
    -1 실패
    -1 이외 공유 메모리 생성 성공, 공유 메모리 식별자

    예제

    예제를 위해 두 개의 프로세스를 만들겠습니다. counter.c 는 공유 메모리에 1초 마다  0부터 계속 증가하는 카운터 문자열을 공유 메모리에 넣으면 show_counter.c에서 공유 메모리를 화면에 출력하도록 하겠습니다.

    // counter.c   공유 메모리를 생성하고 공유 메모리에
    // 카운터 문자열을 계속 갱신하여 넣습니다.
    
    #include <stdio.h>      // printf()
    #include <unistd.h>     // sleep()
    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    #define  KEY_NUM     9527
    #define  MEM_SIZE    1024
    
    int main( void)
    {
       int   shm_id;
       void *shm_addr;
       int   count;
    
       if ( -1 == ( shm_id = shmget( (key_t)KEY_NUM, MEM_SIZE, IPC_CREAT¦0666)))
       {
          printf( "공유 메모리 생성 실패\n");
          return -1;
       }
    
       if ( ( void *)-1 == ( shm_addr = shmat( shm_id, ( void *)0, 0)))
       {
          printf( "공유 메모리 첨부 실패\n");
          return -1;
       }
    
       count = 0;
       while( 1 )
       {
          sprintf( (char *)shm_addr, "%d", count++);       // 공유 메모리에 카운터 출력
          sleep( 1);
       }
       return 0;
    }
    
    // show_counter.c   counter.c가 공유 메모리에 넣는
    // 카운터 문자열을 화면에 계속 출력합니다.
    
    #include <stdio.h>      // printf()
    #include <unistd.h>     // sleep()
    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    #define  KEY_NUM     9527
    #define  MEM_SIZE    1024
    
    int main( void)
    {
       int   shm_id;
       void *shm_addr;
    
       if ( -1 == ( shm_id = shmget( (key_t)KEY_NUM, MEM_SIZE, IPC_CREAT¦0666)))
       {
          printf( "공유 메모리 생성 실패\n");
          return -1;
       }
    
       if ( ( void *)-1 == ( shm_addr = shmat( shm_id, ( void *)0, 0)))
       {
          printf( "공유 메모리 첨부 실패\n");
          return -1;
       }
    
       while( 1 )
       {
          printf( "%s\n", (char *)shm_addr);    // 공유 메모리를 화면에 출력
          sleep( 1);
       }
       return 0;
    }
    
    ]$ gcc counter.c -o counter          
    ]$ gcc show_counter.c -o show_counter
    ]$ ./counter &
    [1] 8077
    ]$ ./show_counter
    2
    3
    4
    5
    6
    7
    8 :



    'Developer's Infos > Linux Programming' 카테고리의 다른 글

    add_wait_queue()/remove_wait_queue()/schedule() 설명 및 사용법  (0) 2011.10.10
    __attribute__  (0) 2011.10.10
    [Linux] asmlinkage  (0) 2011.09.30
    PCI Driver Flow  (0) 2011.09.05
    [IP] IPv6 헤더, IPv4 vs IPv6  (0) 2011.09.01
    Posted by 삼성동고양이
    1. Fill up the pci_device_id table as follows.

    static const struct pci_device_id pci_ids[] = {
    {
    .vendor = VENDOR_ID,
    .device = DEVICE_ID,
    .subvendor = PCI_ANY_ID,
    .subdevice = PCI_ANY_ID,
    .driver_data = (unsigned long) ~HOST_FORCE_PCI,
    },

    {   /* all zero 's */ },
    };

    2. This pci_device_id structure needs to be exported to user space to allow the hotplug
    and module loading systems know what module works with what hardware devices.
    The macro MODULE_DEVICE_TABLE accomplishes this.

    An example is:
    MODULE_DEVICE_TABLE(pci, pci_ids);

    3. Fill the standard PCI_Driver structure with the PCI_id table,probe and remove functions.

    say for example:

    static struct pci_driver mypci_driver = {
    .name       =  DRIVER_NAME,
    .id_table    = pci_ids,
    .probe       =  mypci_probe,
    .remove     = __devexit_p(mypci_remove),
    .suspend    = NULL,
    .resume     = NULL,
    };

     The main structure that all PCI drivers must create in order to be registered with the kernel properly is the struct pci_driver structure. This structure consists of a number of function callbacks and variables that describe the PCI driver to the PCI core.

    4. Registering a PCI Driver:

    **To register the struct pci_driver with the PCI core, a call to pci_register_driver (for network register_netdev,for char misc_register,for block drivers register_blkdev) is made with a pointer to the struct pci_driver.

    **This is traditionally done in the module initialization code for the PCI driver:

    static int pci_init(void)
    {
    PRINT(" \n\nPCI DRIVER IS LOADED IN KERNEL SPACE \n\n");
    return pci_register_driver(&mypci_driver);
    }

    **Note that the pci_register_driver function either returns a negative error number
    or 0 if everything was registered successfully.

    5.Accessing PCI configuration space:

    The configuration space can be accessed through 8-bit, 16-bit, or 32-bit data transfers at any time.
    The relevant functions are prototyped in <linux/pci.h>:

    int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val);
    int pci_read_config_word(struct pci_dev *dev, int where, u16 *val);
    int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);
    int pci_write_config_byte(struct pci_dev *dev, int where, u8 val);
    int pci_write_config_word(struct pci_dev *dev, int where, u16 val);
    int pci_write_config_dword(struct pci_dev *dev, int where, u32 val);

    6. Enabling the PCI Device:

    **In the probe function for the PCI driver, before the driver can access any device resource
    (I/O region or interrupt) of the PCI device, the driver must call the pci_enable_device
    function:

    **int pci_enable_device(struct pci_dev *dev);

    **This function actually enables the device. It wakes up the device and in some cases also assigns its interrupt line and I/O regions.

    struct pci_dev {
            struct list_head global_list;   /* node in list of all PCI devices */
            struct list_head bus_list;      /* node in per-bus list */
            struct pci_bus  *bus;           /* bus this device is on */
            struct pci_bus  *subordinate;   /* bus this device bridges to */

            void            *sysdata;       /* hook for sys-specific extension */
            struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */

            unsigned int    devfn;          /* encoded device & function index */
            unsigned short  vendor;
            unsigned short  device;
            unsigned short  subsystem_vendor;
            unsigned short  subsystem_device;
            unsigned int    class;          /* 3 bytes: (base,sub,prog-if) */
            u8              hdr_type;       /* PCI header type (`multi' flag masked out) */
            u8              rom_base_reg;   /* which config register controls the ROM */
            u8              pin;            /* which interrupt pin this device uses */

            struct pci_driver *driver;      /* which driver has allocated this device */
            u64             dma_mask;       /* Mask of the bits of bus address this
                                               device implements.  Normally this is
                                               0xffffffff.  You only need to change
                                               this if your device has broken DMA
                                               or supports 64-bit transfers.  */

            pci_power_t     current_state;  /* Current operating state. In ACPI-speak,
                                               this is D0-D3, D0 being fully functional,
                                               and D3 being off. */

            pci_channel_state_t error_state;        /* current connectivity state */
            struct  device  dev;            /* Generic device interface */

            /* device is compatible with these IDs */
            unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];
            unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];

            int             cfg_size;       /* Size of configuration space */

            /*
             * Instead of touching interrupt line and base address registers
             * directly, use the values stored here. They might be different!
             */
            unsigned int    irq;
            struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */

            /* These fields are used by common fixups */
            unsigned int    transparent:1;  /* Transparent PCI bridge */
            unsigned int    multifunction:1;/* Part of multi-function device */
            /* keep track of device state */
            unsigned int    is_enabled:1;   /* pci_enable_device has been called */
            unsigned int    is_busmaster:1; /* device is busmaster */
            unsigned int    no_msi:1;       /* device may not use msi */
            unsigned int    block_ucfg_access:1;    /* userspace config space access is blocked */

            u32             saved_config_space[16]; /* config space saved at suspend time */
            struct hlist_head saved_cap_space;
            struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
            int rom_attr_enabled;           /* has display of the rom attribute been enabled? */
            struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */
    };


    7.Enabling the PCI-Bus mastering for the device by calling pci_set_master Kernel API.
      This API enables bus-mastering on the device and calls pcibios_set_master  to do the needed arch specific settings
      It is done in the driver,if it is needed.

    **BusMastering:
    bus mastering is the capability of devices on the PCI bus (other than the system chipset, of course) to
      take control of the bus and perform transfers directly.PCI's design allows bus mastering of multiple devices on the bus simultaneously,
      with the arbitration circuitry working to ensure that no device on the bus (including the processor!) locks out any other device.
      At the same time though, it allows any given device to use the full bus throughput if no other device needs to transfer anything.

    **PCI transactions work in a master-slave relationship. A master is an agent that initiates a transaction (can be a read or a write).
    While the host CPU is often the bus master, all PCI boards can potentially claim the bus and become a bus master.

    **When a PCI device is enabled, it's bus mastering is also enabled.  This occurs before any driver code is
    executed.

    8. Accessing Memory Regions

    **unsigned long pci_resource_start(struct pci_dev *dev, int bar);
    The function returns the first address (memory address or I/O port number)
    associated with one of the six PCI I/O regions. The region is selected by the integer
    bar (the base address register), ranging from 0–5 (inclusive).

    **unsigned long pci_resource_end(struct pci_dev *dev, int bar);
    The function returns the last address that is part of the I/O region number bar.
    Note that this is the last usable address, not the first address after the region.

    **unsigned long pci_resource_flags(struct pci_dev *dev, int bar);
    This function returns the flags associated with this resource.
    All resource flags are defined in <linux/ioport.h>; the most important are:
    IORESOURCE_IO
    IORESOURCE_MEM ( if the resource type is memory resource, then we can access the register by means of readl/b/w/writel/b/w functions)

    #define pci_resource_start(dev,bar)   ((dev)->resource[(bar)].start)
    #define pci_resource_end(dev,bar)     ((dev)->resource[(bar)].end)
    #define pci_resource_flags(dev,bar)   ((dev)->resource[(bar)].flags)
    #define pci_resource_len(dev,bar) \
            ((pci_resource_start((dev),(bar)) == 0 &&       \
              pci_resource_end((dev),(bar)) ==              \
              pci_resource_start((dev),(bar))) ? 0 :        \
                                                            \
             (pci_resource_end((dev),(bar)) -               \
              pci_resource_start((dev),(bar)) + 1))

    9.Reserving the PCI I/O and memory regions.

    int pci_request_region (struct pci_dev * pdev, int bar, char * res_name);

    Arguments:
    pdev
        PCI device whose resources are to be reserved
    bar
        BAR to be reserved
    res_name
        Name to be associated with resource.

    Description
    Mark the PCI region associated with PCI device pdev BR bar as being reserved by owner res_name.
    Do not access any address inside the PCI regions unless this call returns successfully.

    10. Converting physical address to virtual address.

    pci.pVirtualBaseAddr=(unsigned int *)ioremap_nocache(pci.PciBaseStart,pci.length);

    11.

    else
        KERNELDIR ?= /lib/modules/$(shell uname -r)/build
        PWD  := $(shell pwd)
    default:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    Endif


    static struct miscdevice miscdev= {
            /*
             * We don't care what minor number we end up with, so tell the
             * kernel to just pick one.
             */
            MISC_DYNAMIC_MINOR,
            /*
             * Name ourselves /dev/UniPro.
             */
            "miscdev",
            /*
             * What functions to call when a program performs file
             * operations on the device.
             */
            &fops
    };

    misc_register(&miscdev);

    출처 : http://inbasudhakar.blogspot.com/2011/08/pci-driver-flow.html 
    Posted by 삼성동고양이
    이전버튼 1 2 3 이전버튼