最后的话
最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!
资料预览
给大家整理的视频资料:
给大家整理的电子书资料:
如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
文章目录
前言
一、struct task_struct
列出struct task_struct结构体中与pid有关的成员:
struct task\_struct {
......
pid\_t pid;
pid\_t tgid;
/\* PID/PID hash table linkage. \*/
struct pid\_link pids[PIDTYPE_MAX];
struct list\_head thread_group;
/\* namespaces \*/
struct nsproxy \*nsproxy;
}
/\*
\* A structure to contain pointers to all per-process
\* namespaces - fs (mount), uts, network, sysvipc, etc.
\*
\* The pid namespace is an exception -- it's accessed using
\* task\_active\_pid\_ns. The pid namespace here is the
\* namespace that children will use.
\*
\* 'count' is the number of tasks holding a reference.
\* The count for each namespace, then, will be the number
\* of nsproxies pointing to it, not the number of tasks.
\*
\* The nsproxy is shared by tasks which share all namespaces.
\* As soon as a single namespace is cloned or unshared, the
\* nsproxy is copied.
\*/
struct nsproxy {
atomic\_t count;
struct uts\_namespace \*uts_ns;
struct ipc\_namespace \*ipc_ns;
struct mnt\_namespace \*mnt_ns;
struct pid\_namespace \*pid_ns_for_children;
struct net \*net_ns;
struct cgroup\_namespace \*cgroup_ns;
};
从上面我们可以看到 struct pid_namespace 的命名和其他的有点不一样。注释说明:pid namespace是个例外——使用task_active_pid_ns来访问pid namespace。这里的 pid namespace是children将使用的namespace。
task_active_pid_ns通过struct task_struct来获取struct pid_namespace:
struct pid\_namespace \*task\_active\_pid\_ns(struct task\_struct \*tsk)
{
return ns\_of\_pid(task\_pid(tsk));
}
EXPORT\_SYMBOL\_GPL(task_active_pid_ns);
通过struct task_struct获取struct pid:
static inline struct pid \*task\_pid(struct task\_struct \*task)
{
return task->pids[PIDTYPE_PID].pid;
}
struct pid\_link
{
struct hlist\_node node;
struct pid \*pid;
};
struct task\_struct {
......
/\* PID/PID hash table linkage. \*/
struct pid\_link pids[PIDTYPE_MAX];
......
}
通过struct pid获取struct pid_namespace,ns_of_pid()返回分配了指定pid的pid namespace。
/\*
\* ns\_of\_pid() returns the pid namespace in which the specified pid was
\* allocated.
\*
\* NOTE:
\* ns\_of\_pid() is expected to be called for a process (task) that has
\* an attached 'struct pid' (see attach\_pid(), detach\_pid()) i.e @pid
\* is expected to be non-NULL. If @pid is NULL, caller should handle
\* the resulting NULL pid-ns.
\*/
static inline struct pid\_namespace \*ns\_of\_pid(struct pid \*pid)
{
struct pid\_namespace \*ns = NULL;
if (pid)
ns = pid->numbers[pid->level].ns;
return ns;
}
struct pid\_namespace {
struct kref kref;
struct pidmap pidmap[PIDMAP_ENTRIES];
struct rcu\_head rcu;
int last_pid;
unsigned int nr_hashed;
struct task\_struct \*child_reaper;
struct kmem\_cache \*pid_cachep;
unsigned int level;
struct pid\_namespace \*parent;
#ifdef CONFIG\_PROC\_FS
struct vfsmount \*proc_mnt;
struct dentry \*proc_self;
struct dentry \*proc_thread_self;
#endif
#ifdef CONFIG\_BSD\_PROCESS\_ACCT
struct fs\_pin \*bacct;
#endif
struct user\_namespace \*user_ns;
struct ucounts \*ucounts;
struct work\_struct proc_work;
kgid\_t pid_gid;
int hide_pid;
int reboot; /\* group exit code if this pidns was rebooted \*/
struct ns\_common ns;
};
kref:是一个引用计数器,代表此命名空间在多少进程中被使用。
该结构体只有一个成员,为什么只有一个成员也要封装成结构呢?
这是为了防止原子变量被不小心直接赋值,这样封装后就不能直接操作该值,让开发者通过对应的函数接口来操作该原子变量。
struct kref {
atomic\_t refcount;
};
比如,必须通过调用kref_init函数来初始化该原子变量:
/\*\*
\* kref\_init - initialize object.
\* @kref: object in question.
\*/
static inline void kref\_init(struct kref \*kref)
{
atomic\_set(&kref->refcount, 1);
}
pidmap[]:记录当前系统的PID使用情况。
last_pid:记录上一次分配给进程的PID值。
child_reaper:每个PID命名空间都有一个类似于全局的init进程,其作用和init进程一样,一个局部的init进程。比如:init进程对孤儿进程调用wait,命名空间的局部init进程也要完成该工作。child_reaper保存了指向该局部init进程的task_struct的指针。
pid_cachep:指向struct pid高速缓存的指针,sla(u)b分配器指针,slab对象为 struct pid 。
struct pid\_namespace \*ns;
struct pid \*pid = kmem\_cache\_alloc(ns->pid_cachep, GFP_KERNEL);
parent:是指向父命名空间的指针。
level:表示当前命名空间在命名空间层次结构中的深度,初始命名空间的level为0,该命名空间的子空间level为1,下一层的子空间level为2,以此类推。level的计算比较重要,因为level较高的命名空间中的ID,对level较低的命名空间来说是可见的,通过给定的level设置,内核即可推断进程会关联到多少个ID。level = 0,代表全局命名空间。
struct pid_namespace命名空间结构体的分配采用的是sla(u)b分配器进行分配,pid_ns_cachep为指向struct pid_namespace的高速缓存的指针,slab对象为struct pid_namespace。
static struct kmem\_cache \*pid_ns_cachep;
static __init int pid\_namespaces\_init(void)
{
pid_ns_cachep = KMEM\_CACHE(pid_namespace, SLAB_PANIC);
......
return 0;
}
等价于:
pid_ns_cachep = kmem\_cache\_create("pid\_namespace", sizeof(struct pid\_namespace), \_\_alignof\_\_(struct pid\_namespace), (0x00040000UL), NULL)
二、struct pid简介
2.1 struct pid
// linux-4.10.1/include/linux\pid.h
struct pid
{
atomic\_t count;
unsigned int level;
/\* lists of tasks that use this pid \*/
struct hlist\_head tasks[PIDTYPE_MAX];
struct rcu\_head rcu;
struct upid numbers[1];
};
struct pid是内核对PID的内部表示:
count:代表当前使用此struct pid的任务数量。
level:表示可以看到该进程的命名空间数目,也就是进程所属的进程号命名空间的层次。level = 0,代表全局命名空间。
tasks:是当前使用此struct pid的任务列表。tasks是一个数组,每个数组项都是一个哈希表头,对应于一个进程ID的类型。因为每一个进程ID可能属于几个进程,所有共享同一给定进程ID的task_struct实例都通过该哈希表连接起来。
numbers是一个struct upid实例的数组,每个数组项对应一个命名空间。这里这个数组项只有1个,只有1个时代表这个进程只属于全局的命名空间。但是这个数组位于结构体末端,这样就可以分配更多的内存空间,即可以向这个numbers数组增加更多成员,这样这个进程就可以属于多个局部命名空间了。
其中PIDTYPE_MAX表示PID类型:
enum pid\_type
{
PIDTYPE_PID, //进程的进程号
PIDTYPE_PGID, //进程组领头进程的进程号
PIDTYPE_SID, //会话领头进程的进程号
PIDTYPE_MAX //表示进程号类型的数目 == 3
};
一个进程可能在多个命名空间中可见。而在各个命名空间的局部ID也都不相同
备注:可以使用 int nr(局部进程PID的数值) 和 struct pid_namespace *ns 通过 find_pid_ns() 找到 struct pid。
2.2 struct upid
struct upid {
/\* Try to keep pid\_chain in the same cacheline as nr for find\_vpid \*/
int nr;
struct pid\_namespace \*ns;
struct hlist\_node pid_chain;
};
struct upid则表示特定的命名空间的可见信息,用于获取struct pid的数值id,在特定的名称空间中被看到。
nr:进程ID的数值,保存进程的局部PID值。
ns:指向该进程ID所属的命名空间的指针。
pid_chain:哈希表节点,哈希表溢出链表。
所有struct upid实例都保存在一个哈希表中
注意:struct pid的tasks是struct hlist_head是哈希表表头,struct upid中的struct hlist_node是哈希表节点。
2.3 pid_t pid/tgid
struct pid是进程ID的内核表现形式,pid_t pid是用户空间可见的数值PID。内核提供了两者相互转换的辅助函数。
(1)
pid_t pid 是全局的进程号ID,全局ID是在内核本身和初始化命名空间中的唯一ID号,在系统启动期间开始的init进程即属于初始命名空间。对于每一个ID类型,都有一个给定的全局ID,保证在整个系统中是唯一的。
struct task\_struct {
......
pid\_t pid;
pid\_t tgid;
......
}
task_struct结构体中的pid和tgid都是全局ID。
(2)
应用层调用getpid函数(返回当前进程的PID),实际上获取的是struct task_struct 结构体的pid_t tgid。
如果一个进程没有使用线程(即该进程只有一个主线程),则该进程的pid和tgid一样。如果一个进程有多个线程,这多个线程的pid是不一样的,但tgid是一样的。
内核中每个任务的 pid_t pid 都是不一样。
/\*\*
\* sys\_getpid - return the thread group id of the current process
\*
\* Note, despite the name, this returns the tgid not the pid. The tgid and
\* the pid are identical unless CLONE\_THREAD was specified on clone() in
\* which case the tgid is the same in all threads of the same group.
\*
\* This is SMP safe as current->tgid does not change.
\*/
SYSCALL\_DEFINE0(getpid)
{
return task\_tgid\_vnr(current);
}
static inline pid\_t task\_tgid\_vnr(struct task\_struct \*tsk)
{
return pid\_vnr(task\_tgid(tsk));
}
static inline struct pid \*task\_tgid(struct task\_struct \*task)
{
return task->group_leader->pids[PIDTYPE_PID].pid;
}
从源码中可以看到是获取线程组组长的pid,即tgid。
(3)
应用层调用gettid函数,获取线程标识,也就是获取线程的thread ID (TID),实际上是获取struct task_struct 结构体的pid_t pid。
/\* Thread ID - the internal kernel "pid" \*/
SYSCALL\_DEFINE0(gettid)
{
return task\_pid\_vnr(current);
}
static inline pid\_t task\_pid\_vnr(struct task\_struct \*tsk)
{
return \_\_task\_pid\_nr\_ns(tsk, PIDTYPE_PID, NULL);
}
pid\_t \_\_task\_pid\_nr\_ns(struct task\_struct \*task, enum pid\_type type,
struct pid\_namespace \*ns)
{
pid\_t nr = 0;
rcu\_read\_lock();
if (!ns)
ns = task\_active\_pid\_ns(current);
if (likely(pid\_alive(task))) {
if (type != PIDTYPE_PID)
task = task->group_leader;
nr = pid\_nr\_ns(rcu\_dereference(task->pids[type].pid), ns);
}
rcu\_read\_unlock();
return nr;
}
EXPORT\_SYMBOL(__task_pid_nr_ns);
pid\_t pid\_nr\_ns(struct pid \*pid, struct pid\_namespace \*ns)
{
struct upid \*upid;
pid\_t nr = 0;
if (pid && ns->level <= pid->level) {
upid = &pid->numbers[ns->level];
为了做好运维面试路上的助攻手,特整理了上百道 **【运维技术栈面试题集锦】** ,让你面试不慌心不跳,高薪offer怀里抱!
这次整理的面试题,**小到shell、MySQL,大到K8s等云原生技术栈,不仅适合运维新人入行面试需要,还适用于想提升进阶跳槽加薪的运维朋友。**
![](https://img-blog.csdnimg.cn/img_convert/e4105c6067af165e0ab8a3a0af1db4c2.png)
本份面试集锦涵盖了
* **174 道运维工程师面试题**
* **128道k8s面试题**
* **108道shell脚本面试题**
* **200道Linux面试题**
* **51道docker面试题**
* **35道Jenkis面试题**
* **78道MongoDB面试题**
* **17道ansible面试题**
* **60道dubbo面试题**
* **53道kafka面试**
* **18道mysql面试题**
* **40道nginx面试题**
* **77道redis面试题**
* **28道zookeeper**
**总计 1000+ 道面试题, 内容 又全含金量又高**
* **174道运维工程师面试题**
> 1、什么是运维?
> 2、在工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?
> 3、现在给你三百台服务器,你怎么对他们进行管理?
> 4、简述raid0 raid1raid5二种工作模式的工作原理及特点
> 5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?
> 6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?
> 7、Tomcat和Resin有什么区别,工作中你怎么选择?
> 8、什么是中间件?什么是jdk?
> 9、讲述一下Tomcat8005、8009、8080三个端口的含义?
> 10、什么叫CDN?
> 11、什么叫网站灰度发布?
> 12、简述DNS进行域名解析的过程?
> 13、RabbitMQ是什么东西?
> 14、讲一下Keepalived的工作原理?
> 15、讲述一下LVS三种模式的工作过程?
> 16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?
> 17、如何重置mysql root密码?
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618542503)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
么区别,工作中你怎么选择?
> 7、Tomcat和Resin有什么区别,工作中你怎么选择?
> 8、什么是中间件?什么是jdk?
> 9、讲述一下Tomcat8005、8009、8080三个端口的含义?
> 10、什么叫CDN?
> 11、什么叫网站灰度发布?
> 12、简述DNS进行域名解析的过程?
> 13、RabbitMQ是什么东西?
> 14、讲一下Keepalived的工作原理?
> 15、讲述一下LVS三种模式的工作过程?
> 16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?
> 17、如何重置mysql root密码?
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618542503)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**