Understanding the getting of task_struct pointer f

2020-07-16 08:26发布

Right now I'm reading the book "Linux Kernel Development 3d Edition" by Robert Love. There he write about the thread_info struct which contains the pointer to task_struct struct and, as I understood, located at the bottom or at the top of kernel stack of process (depends on architecture). I wasn't familiar with Linux kernel API until recently and I wasn't known of current() method existence. There is an excerpt from the book related to how current() method actually works:

On x86, current is calculated by masking out the 13 least-significant bits of the stack pointer to obtain the thread_info structure.This is done by the current_thread_info() function.The assembly is shown here: movl $-8192, %eax andl %esp, %eax This assumes that the stack size is 8KB.When 4KB stacks are enabled, 4096 is used in lieu of 8192.

My questions are:

  1. As far as I know if we have a decimal value represented as a set of bits, then there is only one least-significant bit in the set, isn't it?
  2. What is the magical number 13?

For thous who will read this topic, the questions I have voiced can lead to conclusion that the author don't understand properly the process of memory allocation and administration. Ok, that's may be right due to the fact that in my mind I can represent the memory allocated for the stack as the ribbon full of bits (or bytes). All of this bytes accessible by a specific memory address represented as some decimal value. The origin of the stack is the lowest memory address and the fin of the stack is the highest value of memory address. But HOW, HOW can we get the the pointer to the thread_info struct located at the, say, end of the stack only by masking out 13 least-significant bits of ARBITRARY located stack pointer (If I understood correctly, we masking out bits of the stack pointer ADDRESS represented as decimal value).

4条回答
啃猪蹄的小仙女
2楼-- · 2020-07-16 08:49

My 2 bits: note that the implementation of 'current' is arch-dependant. The answers so far focus on the x86; various means of getting at the thread_info and thus the task_struct are employed by other arch's on the Linux OS.

For eg., apparently the PPC uses a register (it's RISC, remember, with plenty of GPRs) to store the value of current- in effect rendering it part of hardware context! This is going to be really fast.

The modern x86 port (i looked up the 4.1.0 kernel sources) uses per-cpu data to implement current in a fast and lockless fashion. And so on...

查看更多
smile是对你的礼貌
3楼-- · 2020-07-16 08:50

But HOW, HOW can we get the the pointer to the thread_info struct located at the, say, end of the stack only by masking out 13 least-significant bits of ARBITRARY located stack pointer

Note that both the bottom and limit(top) address (assuming a bottom-up stack with higher address located in the bottom) MUST BE a multiple of stack size. For example, if a stack size is 8192 (=2^13), the 13 least significant bits must all be 0 for both the bottom and limit address. The least-significant 13 bit is NOT ARBITRARY in a sense that it gives the offset between the bottom address and limit address, which both ends with 13 0's. Thus masking out the least-significant 13 bits give you the address of the limit address, which is where the thread_info structure located.

查看更多
兄弟一词,经得起流年.
4楼-- · 2020-07-16 09:06

Each process only gets 8192 bytes of kernel stack, aligned to a 8192-byte boundary, so whenever the stack pointer is altered by a push or a pop, the low 13 bits are the only part that changes. 2**13==8192.

查看更多
欢心
5楼-- · 2020-07-16 09:08

The kernel stack contains a special struct at the top -- thread_info:

 26 struct thread_info {
 27         struct task_struct      *task;          /* main task structure */
 28         struct exec_domain      *exec_domain;   /* execution domain */
 29         __u32                   flags;          /* low level flags */
 30         __u32                   status;         /* thread synchronous flags */
 31         __u32                   cpu;            /* current CPU */
 32         int                     preempt_count;  /* 0 => preemptable,
 33                                                    <0 => BUG */
 34         mm_segment_t            addr_limit;
 35         struct restart_block    restart_block;
 36         void __user             *sysenter_return;
 37 #ifdef CONFIG_X86_32
 38         unsigned long           previous_esp;   /* ESP of the previous stack in
 39                                                    case of nested (IRQ) stacks
 40                                                 */
 41         __u8                    supervisor_stack[0];
 42 #endif
 43         unsigned int            sig_on_uaccess_error:1;
 44         unsigned int            uaccess_err:1;  /* uaccess failed */
 45 };

So, to get the task_struct you'll need to get a thread_info pointer with GET_THREAD_INFO from the ASM-code:

183 /* how to get the thread information struct from ASM */
184 #define GET_THREAD_INFO(reg)     \
185         movl $-THREAD_SIZE, reg; \
186         andl %esp, reg

... or with current_thread_info from the C-code:

174 /* how to get the thread information struct from C */
175 static inline struct thread_info *current_thread_info(void)
176 {
177         return (struct thread_info *)
178                 (current_stack_pointer & ~(THREAD_SIZE - 1));
179 }

Note that THREAD_SIZE defined as (PAGE_SIZE << THREAD_SIZE_ORDER) and THREAD_SIZE_ORDER equals 1 for both x86_32 and x86_64 so THREAD_SIZE results to 8192 (2^13 or 1<<13).

查看更多
登录 后发表回答