linux线程同步机制有触发机制吗

Linux自主访问控制机制模块之详细描述
对于vfs_setxattr()函数,其定义在fs/xattr.c中,函数头如下所示:
图2-3 vfs_setxattr()函数调用流程图
对于vfs_setxattr()函数调用的两个函数xattr_permission()和__vfs_setxattr_noperm(),下面分别对其进行分析:
1. xattr_permission()
xattr_permission()函数用于判断用户是否可以对指定文件执行指定的操作,若可以,返回0。由于该函数只是根据扩展属性所属的命名空间进行不同的处理,其处理过程比较简单,所以这里直接对其处理过程说明如下:
①对参数进行判断,如果要对不可修改为文件或只能追加的文件进行写操作,则函数直接返回-EPERM。
②判断扩展属性是否位于security或system命名空间,若是,则VFS不对其进行限制,而是将扩展属性的判断留给底层文件系统或安全模块,这里直接返回0。
③判断扩展属性是否位于trust命名空间,若是,则只有特权用户才能进行访问。
④判断扩展属性是否位于user命名空间,若是,则按下述步骤进行处理:
a. 判断待访问的文件是否是普通文件或目录,因为只有普通文件和目录才具有扩展属性,若不是,则返回相应的错误码。
b. 判断待访问的文件是否是目录、“粘着位”是否置位、当前进程是否是目录的所有者或是由特权用户触发的,并根据判断结果进行相应处理。因为对于“粘着位”置位的目录来说,只有所有者和特权用户才能写入属性。
⑤调用inode_permission()函数来判断当前进程对待处理的文件是否具有mask指定的权限,并返回该函数的执行结果。对于该函数的详细分析,详见2.3.4.1小节。
2. __vfs_setxattr_noperm()
__vfs_setxattr_noperm()函数用于在不进行权限检查的情况下执行setxattr操作,函数头如下所示:
int (struct *, const char *,const void *,,int )
该函数包含5个参数,各参数含义和setxattr()的参数含义基本相同,这里不再赘述。
图2-4 __vfs_setxattr_noperm()函数调用流程图
如图2-4所示,下面结合源码对该函数的执行步骤进行说明:
① 判断要设置的扩展属性是否位于security命名空间,若是,则将issec设为0;反之设为1。
② 判断索引节点所属的文件系统是否实现了setxattr()方法。如果实现,则调用该方法,并对函数执行结果进行判断,如果函数成功执行,则采用inotify机制通知父目录该文件的扩展属性发生变化。
③ 如果索引节点所属的文件系统没有实现setxattr()函数,则接着判断issec变量。如果该变量为真,即指定的扩展属性在security命名空间中,则调用security_inode_setsecurity()函数来进行扩展属性的设置,该函数实际上是由LSM机制提供的,视具体的安全策略而设zhi置。如果其成功执行,则调用inotify机制通知父目录。
④ 结束并返回。
责任编辑:
声明:本文由入驻搜狐号的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。
今日搜狐热点什么是Linux系统调用,包括哪些内容_百度知道
什么是Linux系统调用,包括哪些内容
我有更好的答案
了解系统调用相关的知识,需要以下几点: 1.了解系统调用的实现原理,参数如何传入到内核,返回值如何返回到用户空间; 2.目前的系统调用有哪些,可以搜索系统调用api表; 3.可以实践自己编写系统调用。
采纳率:65%
来自团队:
为您推荐:
其他类似问题
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。在Linux中,信号是进程间通讯的一种方式,它采用的是异步机制。当信号发送到某个进程中时,操作系统会中断该进程的正常流程,并进入相应的信号处理函数执行操作,完成后再回到中断的地方继续执行。
需要说明的是,信号只是用于通知进程发生了某个事件,除了信号本身的信息之外,并不具备传递用户数据的功能。
1 信号的响应动作
每个信号都有自己的响应动作,当接收到信号时,进程会根据信号的响应动作执行相应的操作,信号的响应动作有以下几种:
中止进程(Term)
忽略信号(Ign)
中止进程并保存内存信息(Core)
停止进程(Stop)
继续运行进程(Cont)
用户可以通过signal或sigaction函数修改信号的响应动作(也就是常说的&注册信号&,在文章的后面会举例说明)。另外,在多线程中,各线程的信号响应动作都是相同的,不能对某个线程设置独立的响应动作。
2 信号类型
Linux支持的信号类型可以参考下面给出的列表。
2.1 在POSIX.1-1990标准中的信号列表
信号值动作说明
终端控制进程结束(终端连接断开)
用户发送INTR字符(Ctrl+C)触发
用户发送QUIT字符(Ctrl+/)触发
非法指令(程序错误、试图执行数据段、栈溢出等)
调用abort函数触发
算术运行错误(浮点运算错误、除数为零等)
无条件结束程序(不能被捕获、阻塞或忽略)
无效内存引用(试图访问不属于自己的内存空间、对只读内存空间进行写操作)
消息管道损坏(FIFO/Socket通信时,管道未打开而进行写操作)
时钟定时信号
结束程序(可以被捕获、阻塞或忽略)
子进程结束(由父进程接收)
继续执行已经停止的进程(不能被阻塞)
停止进程(不能被捕获、阻塞或忽略)
停止进程(可以被捕获、阻塞或忽略)
后台程序从终端中读取数据时触发
后台程序向终端中写数据时触发
注:其中SIGKILL和SIGSTOP信号不能被捕获、阻塞或忽略。
2.2 在SUSv2和POSIX.1-2001标准中的信号列表
信号值动作说明
Trap指令触发(如断点,在调试器中使用)
非法地址(内存地址对齐错误)
Pollable event (Sys V). Synonym for SIGIO
性能时钟信号(包含系统调用时间和进程占用CPU的时间)
无效的系统调用(SVr4)
有紧急数据到达Socket(4.2BSD)
虚拟时钟信号(进程占用CPU的时间)(4.2BSD)
超过CPU时间资源限制(4.2BSD)
超过文件大小资源限制(4.2BSD)
注:在Linux 2.2版本之前,SIGSYS、SIGXCPU、SIGXFSZ以及SIGBUS的默认响应动作为Term,Linux 2.4版本之后这三个信号的默认响应动作改为Core。
2.3 其它信号
信号值动作说明
IOT捕获信号(同SIGABRT信号)
实时硬件发生错误
协同处理器栈错误(未使用)
文件描述符准备就绪(可以开始进行输入/输出操作)(4.2BSD)
子进程结束(由父进程接收)(同SIGCHLD信号)
电源错误(System V)
电源错误(同SIGPWR信号)
文件锁丢失(未使用)
窗口大小改变时触发(4.3BSD, Sun)
无效的系统调用(同SIGSYS信号)
注意:列表中有的信号有三个值,这是因为部分信号的值和CPU架构有关,这些信号的值在不同架构的CPU中是不同的,三个值的排列顺序为:1,Alpha/Sparc;2,x86/ARM/Others;3,MIPS。
例如SIGSTOP这个信号,它有三种可能的值,分别是17、19、23,其中第一个值(17)是用在Alpha和Sparc架构中,第二个值(19)用在x86、ARM等其它架构中,第三个值(23)则是用在MIPS架构中的。
3 信号机制
文章的前面提到过,信号是异步的,这就涉及信号何时接收、何时处理的问题。
我们知道,函数运行在用户态,当遇到系统调用、中断或是异常的情况时,程序会进入内核态。信号涉及到了这两种状态之间的转换,过程可以先看一下下面的示意图:
接下来围绕示意图,将信号分成接收、检测和处理三个部分,逐一讲解每一步的处理流程。
3.1 信号的接收
接收信号的任务是由内核代理的,当内核接收到信号后,会将其放到对应进程的信号队列中,同时向进程发送一个中断,使其陷入内核态。
注意,此时信号还只是在队列中,对进程来说暂时是不知道有信号到来的。
3.2 信号的检测
进程陷入内核态后,有两种场景会对信号进行检测:
进程从内核态返回到用户态前进行信号检测
进程在内核态中,从睡眠状态被唤醒的时候进行信号检测
当发现有新信号时,便会进入下一步,信号的处理。
3.3 信号的处理
信号处理函数是运行在用户态的,调用处理函数前,内核会将当前内核栈的内容备份拷贝到用户栈上,并且修改指令寄存器(eip)将其指向信号处理函数。
接下来进程返回到用户态中,执行相应的信号处理函数。
信号处理函数执行完成后,还需要返回内核态,检查是否还有其它信号未处理。如果所有信号都处理完成,就会将内核栈恢复(从用户栈的备份拷贝回来),同时恢复指令寄存器(eip)将其指向中断前的运行位置,最后回到用户态继续执行进程。
至此,一个完整的信号处理流程便结束了,如果同时有多个信号到达,上面的处理流程会在第2步和第3步骤间重复进行。
重点理解上图三个部分:信号检测/信号调用/信号处理程序返回。明白怎么从正常进程到信号处理程序再到正常进程的过程。
4 信号的使用
4.1 发送信号
用于发送信号的函数有raise、kill、killpg、pthread_kill、tgkill、sigqueue,这几个函数的含义和用法都大同小异,这里主要介绍一下常用的raise和kill函数。
raise函数:向进程本身发送信号
函数声明如下:
#include &signal.h&
int raise(int sig);
函数功能是向当前程序(自身)发送信号,其中参数sig为信号值。
kill函数:向指定进程发送信号
函数声明如下:
#include &sys/types.h&
#include &signal.h&
int kill(pid_t pid, int sig);
函数功能是向特定的进程发送信号,其中参数pid为进程号,sig为信号值。
在这里的参数pid,根据取值范围不同,含义也不同,具体说明如下:
pid & 0 :向进程号为pid的进程发送信号
pid = 0 :向当前进程所在的进程组发送信号
pid = -1 :向所有进程(除PID=1外)发送信号(权限范围内)
pid & -1 :向进程组号为-pid的所有进程发送信号
另外,当sig值为零时,实际不发送任何信号,但函数返回值依然有效,可以用于检查进程是否存在。
4.2 等待信号被捕获
等待信号的过程,其实就是将当前进程(线程)暂停,直到有信号发到当前进程(线程)上并被捕获,函数有pause和sigsuspend。
pause函数:将进程(或线程)转入睡眠状态,直到接收到信号
函数声明如下:
#include &unistd.h&
int pause(void);
该函数调用后,调用者(进程或线程)会进入睡眠(Sleep)状态,直到捕获到(任意)信号为止。该函数的返回值始终为-1,并且调用结束后,错误代码(errno)会被置为EINTR。
sigsuspend函数:将进程(或线程)转入睡眠状态,直到接收到特定信号
函数声明如下:
#include &signal.h&
int sigsuspend(const sigset_t *mask);
该函数调用后,会将进程的信号掩码临时修改(参数mask),然后暂停进程,直到收到符合条件的信号为止,函数返回前会将调用前的信号掩码恢复。该函数的返回值始终为-1,并且调用结束后,错误代码(errno)会被置为EINTR。
4.3 修改信号的响应动作
用户可以自己重新定义某个信号的处理方式,即前面提到的修改信号的默认响应动作,也可以理解为对信号的注册,可以通过signal或sigaction函数进行,这里以signal函数举例说明。
首先看一下函数声明:
#include &signal.h&
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
第一个参数signum是信号值,可以从前面的信号列表中查到,第二个参数handler为处理函数,通过回调方式在信号触发时调用。
下面为示例代码:
#include &stdio.h&
#include &signal.h&
#include &unistd.h&
/* 信号处理函数 */
void sig_callback(int signum) {
switch (signum) {
case SIGINT:
/* SIGINT: Ctrl+C 按下时触发 */
printf("Get signal SIGINT. \r\n");
/* 多个信号可以放到同一个函数中进行 通过信号值来区分 */
/* 其它信号 */
printf("Unknown signal %d. \r\n", signum);
/* 主函数 */
int main(int argc, char *argv[]) {
printf("Register SIGINT(%u) Signal Action. \r\n", SIGINT);
/* 注册SIGINT信号的处理函数 */
signal(SIGINT, sig_callback);
printf("Waitting for Signal ... \r\n");
/* 等待信号触发 */
printf("Process Continue. \r\n");
源文件下载:
例子中,将SIGINT信号(Ctrl+C触发)的动作接管(打印提示信息),程序运行后,按下Ctrl+C,命令行输出如下:
./linux_signal_example
Register SIGINT(2) Signal Action.
Waitting for Signal ...
^CGet signal SIGINT.
Process Continue.
进程收到SIGINT信号后,触发响应动作,将提示信息打印出来,然后从暂停的地方继续运行。这里需要注意的是,因为我们修改了SIGINT信号的响应动作(只打印信息,不做进程退出处理),所以我们按下Ctrl+C后,程序并没有直接退出,而是继续运行并将"Process Continue."打印出来,直至程序正常结束。
转载:http://hutaow.com/blog//linux-signal/
阅读(...) 评论()1474人阅读
操作系统(4)
服务器端编程(17)
epoll是linux中的一个系统调用。是linux中的一种I/O复用方式,最早是出现在linux内核2.5.44中。早期的I/O复用主要是基于select和poll实现,但是select方式在在检测具体发生I/O/事件的描述符时,需要对所有的描述符进行循环检测,所以检测时间是描述符总数量的常数倍。epoll实现机制是,当某个描述符发生I/O事件时,直接将描述符加入到相应的链表中,因此应用程序在常数时间内就可以获得发生I/O事件的文件描述符。
epoll事件触发机制主要是分为两种:边缘出发(edge-triggered)和水平出发(level triggered)。对于一个文件描述符,把他加入到epoll监听队列中,当该文件描述符发生可读写事件时,epoll_wait()会返回。对于边缘出发,epoll_wait()只返回一次,即只在该读写事件发生时返回,也就是说如果事件处理函数只读取了该文件描述缓冲区的部分内容时返回,再次调用epoll_wait(),虽然此时该描述符对应缓冲区中还有数据,但epoll_wait()函数不会返回。相反,对于水平出发,它不管是否有事件反生,只要文件描述符对应的缓冲区中有数据可读写,epoll_wait()就会返回。

我要回帖

更多关于 linux 消息机制 的文章

 

随机推荐