系统调用
来源:互联网 发布:激战2母夏尔捏脸数据 编辑:程序博客网 时间:2024/06/06 04:55
系统调用:
1.0x80中断--->系统调用引发的中断
2.系统调用表:函数指针的数组
3.系统调用的函数名转换:每个系统调用都有相应的系统调用号作为唯一的标识,内核维护一张系统调用表,表中的元素是系统调用函数的起始地址,而系统调用号就是系统调用在调用表的偏移量。在进行系统调用是只要指定对应的系统调用号,就可以明确的要调用哪个系统调用,这就完成了系统调用的函数名称的转换。
4.系统调用的参数传递:通过寄存器实现的。
5.具体操作:在用户态的时候将系统调用号存到寄存器中,通过中断,切换到内核态的时候,操作系统先将系统调用号从寄存器eax中读进来(读到内核中),然后通过其去系统调用表(sys_call_table)中去找相应的函数实现,然后将结果返回,将其写入寄存器eax中,然后返回。
6.以fork为例:(不带参数的)
源码中系统调用表sys_call_table是在entry.c文件中。
先根据函数参数个数选择其中的某一个syscall。
其中_syscall0()是unistd.h中的内嵌宏代码,它以嵌入汇编的形式调用Linux的系统调用中断int 0x80。
eg:
// 以下定义系统调用嵌入式汇编宏函数。
// 不带参数的系统调用宏函数。type name(void)。
// %0 - eax(__res),%1 - eax(__NR_##name)。其中name 是系统调用的名称,与 __NR_ 组合形成上面的系统调用符号常数,从而用来对系统调用表中函数指针寻址。
// 返回:如果返回值大于等于0,则返回该值,否则置出错号errno,并返回-1。
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \ //调用系统中断
: "=a" (__res) \ //返回值---->eax(__res)。
: "0" (__NR_##name)); \ //输入为系统调用号
__syscall_return(type,__res); \
}
//如果返回值大于0,则直接返回该值,否则置出错号,并返回-1
#define __syscall_return(type, res) \
do { \
if ((unsigned long)(res) >= (unsigned long)(-(128 + 1))) { \
errno = -(res); \
res = -1; \
} \
return (type) (res); \
} while (0)
问题:(__NR_##name)如果我调用 _syscall0(int,fork)替代进去不是变成__NR_##fork了吗?但系统调用常数符号的定义是#define __NR_fork 2啊,其中##是干嘛的?
答:##的意思就是宏中的字符直接替换,
如果name = fork,那么在宏中__NR_##name就替换成了__NR_fork了。
__NR_##name是系统调用号,##指的是两次宏展开。即用实际的系统调用名字代替"name",然后再把__NR_...展开.如name == fork,则为__NR_fork。
具体实现:
fork(),vfork(),和_clone()库函数都根据各自需要的参数标志去调用do_fork()。
do_fork()完成了创建中的大部分工作,它定义在kernel/fork.c中。该函数调用copy_process(),接下来copy_process()实现的工作如下 :
1.调用dup_task_struct()为新进程创建一个内核栈,thread_info结构和task_struct结构中,这些值与当前进程的值相同。此时,子进程和父进程的描述符是完全相同的。
dup_task_struct(struct task_struct *orig)函数:
(1)orig是旧的结构体,也就是关于父进程的信息在里面。
(2)tsk是子进程的结构体,开始几行还未给其赋值。
(3)同理ti是新的thread_info,现在也没有给他赋值。
(4)tsk = alloc_task_struct(); 开辟新的内核栈,并且创建新的子进程结构体
(5)ti = alloc_thread_info(tsk); 同理,创建新的thread_info。
(6)arch_dup_task_struct(tsk, orig);简化版本就是:*tsk=*orig,为整个task_struct结构复制给tsk。(thread_info里面有一个指向task_struct的指针 ,子进程指向子进程的,父进程指向父进程的,而现在,这两个thread_info中的某个指针,都指向了父进程的task_struct,所以还要使得子进程thread_info的指针指向tsk的task_struct,而不是父进程的task_struct:task_thread_info(p)->task = p;)
2.检查新创建的这个子进程后,当前用户所拥有的进程数目没有超出给他分配的资源的限制。
3.现在,子进程着手使自己与父进程区别拷来。进程描述符内的许多成员都要被清0或设为初始值。进程描述符中大多数的数据都是共享的。
4.接下来,子进程的状态被设置为TASK_UNINTERRUPTIBLE(不可中断)以保证它不会投入运行。
5.copy_process()调用copy_flags()以更新task_struct的flags成员。
(1)更新从父进程复制到tsk_flags字段中的一些标志。
(2)首先清除PF_SUPERPRIV。该标志表示进程是否使用了某种超级用户权限
(3)然后设置PF_FORKNOEXEC标志。它表示子进程还没有发出execve系统调用。
6.调用get_pid()为新进程获取一个有效的PID
7.根据传递给clone()的参数标志,copy_process()拷贝或共享打开的文件,文件系统信息,信号处理函数,进程地址空间和命名空间等。在一般情况下,这些资源会被给定进程的所有线程共享;否则,这些资源对每个进程是不同的,因此被拷贝到这里。
8.让父进程和子进程平分剩余的时间片。(若子进程在其第一个时间片前终止或执行新的程序,则剩余的时间片会归还给父进程)
9.最后copy_process()做扫尾工作并返回一个指向子进程的指针
再回到do_fork()函数,如果copy_process函数成功返回,新创建的子进程被唤醒并让其投入运行。内核有意选择子进程首先执行。因为一般子进程都会马上调用exec()函数,这样可以避免写时拷贝的额外开销,如果父进程首先执行的话,有可能会开始向地址空间写入。
(1)如果父子进程运行在同一个CPU上,并且不能共享同一组页表(CLONE_VM标志被清0).那么,就把子进程插入父进程运行队列.
(2)并且子进程插在父进程之前.这样做的目的是:如果子进程在创建之后执行新程序,就可以避免写时复制机制执行不必要时页面复制.
(3)否则,如果运行在不同的CPU上,或者父子进程共享同一组页表.就把子进程插入父进程运行队列的队尾.
系统调用
描述
fork
fork创造的子进程是父进程的完整副本,复制了父亲进程的资源,包括内存的内容task_struct内容
vfork
vfork创建的子进程与父进程共享数据段,而且由vfork()创建的子进程将先于父进程运行
clone
Linux上创建线程一般使用的是pthread库 实际上linux也给我们提供了创建线程的系统调用,就是clone
先介绍下进程必须的4要点:
a.要有一段程序供该进程运行,就像一场戏剧要有一个剧本一样。该程序是可以被多个进程共享的,多场戏剧用一个剧本一样。
b.有起码的私有财产,就是进程专用的系统堆栈空间。
c.有“户口”,既操作系统所说的进程控制块,在linux中具体实现是task_struct
d.有独立的存储空间。
当一个进程缺少d条件时候,我们称其为线程。
1.fork 创造的子进程复制了父亲进程的资源,包括内存的内容task_struct内容(2个进程的pid不同)。这里是资源的复制不是指针的复制。
2.vfork创建出来的不是真正意义上的进程,而是一个线程,因为它缺少了我们上面提到的进程的四要素的第4项,独立的内存资源。
3.clone函数功能强大,带了众多参数,因此由他创建的进程要比前面2种方法要复杂。clone可以让你有选择性的继承父进程的资源,你可以选择想vfork一样和父进程共享一个虚存空间,从而使创造的是线程,你也可以不和父进程共享,你甚至可以选择创造出来的进程和父进程不再是父子关系,而是兄弟关系。
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- line-height剖析
- sdfds
- 大数据分析平台搭建教程:基于Apache Zeppelin Notebook和R的交互式数据科学
- 无序数组找出其中的第K大的数
- 从数据库中查询邮箱,然后发送多人邮件
- 系统调用
- 邻接矩阵和弗洛伊德
- Android 不同版本7.0以下,7.0,7.1以上 popwindow展示位置控制分享
- openSMILE安装教程
- iOS和android的屏幕适配
- DEv Grid打印
- 如何在github上搭建个人博客
- 浏览器内核引擎分类归纳
- 错误: 找不到或无法加载主类 Welcome