扫描关注一起学嵌入式,一起学习,一起成长
大家好,今天分享Linux系统调用相关的知识,由于文章篇幅较长,《详解 Linux 的系统调用》分为上下两篇,本文为下篇。
上篇链接:详解 Linux 的系统调用 (上)
linux系统调用分为3个部分:调用请求 ,响应请求 ,功能实现。
linux系统调用流程图如下:
系统调用提供给应用程序的调用请求接口,调用请求中执行了软中断的指令,应用程序使用调用请求后,处理器会产生一个中断,中断服务得到执行,中断服务根据调用号执行特定的功能实现函数。
linux系统调用的第一部分是调用请求,调用请求作为系统调用提供给应用程序的接口,在linux系统调用的3部分中,应用程序只有使用调用请求的权限(其它两部分应用程序无法接触到),调用请求需要完成以下两个基本功能:
linux系统调用有四种调用请求方式:
1、使用 glibc 库函数。
2、使用 syscall函数。
3、通过linux系统调用宏。
4、使用软中断陷入。
以上这4种方法,每种方法的底层都会用到触发软中断指令。
glibc是GNU 发布的开源的标准 C 库,glibc 为程序员提供丰富的 API(Application Programming Interface),除了字符串处理、数学运算等用户态服务之外,重要的是封装了操作系统提供的系统服务,即系统调用的封装(本质上还是执行的系统调用)。
系统调用和glibc提供的API的对应关系如下:
情形一:每一个系统调用对应了一个 glibc 库函数,如系统提供的打开文件系统调用 sys_open 对应的是 glibc 中的 open 函数。
情形二:单独的一个glibc 库函数 可能调用多个系统调用,如 glibc 提供的 printf 函数就会调用如 sys_open、sys_mmap、sys_write、sys_close 等等系统调用。
情形三:多个glibc 库函数 也可能只对应同一个系统调用,如glibc 下实现的 malloc、calloc、free 等函数用来分配和释放内存,都利用了内核的 sys_brk 的系统调用。
例如应用程序通过glibc 提供的chmod函数来改变文件的属性,示例代码如下:
#include
int main()
{
int rc=chmod("./weiwei",0666);
if(rc==-1)
perror("chmod fail\n");
else
printf("chmod succeed\n");
return 0;
}
chmod函数用于改变文件weiwei的属性,编译程序得到chmod.o执行文件,运行chmod.o后观察weiwei文件的属性,结果如下:
使用glibc的方法有很多好处,glibc封装了操作系统提供的系统服务,程序员无需知道细节,如 chmod 系统调用号,程序员只需知道 glibc 提供的 API 的原型。其次使用glibc的方法具有更好的移植性,可以很轻松地植到其他平台。
任何事物都有两面性,如果 glibc 没有封装某个内核提供的系统调用时,我们就没办法通过使用glibc的方法来调用该系统调用。假设我们自己通过编译内核增加了一个系统调用,这时 glibc 不可能有我们自己新增系统调用的封装 API。
在这种情况下,可以利用 glibc 提供的syscall 库函数直接调用。syscall是一个通过特定子功能号和特定参数调用汇编语言接口的库函数。该函数定义在 unistd.h 头文件中,函数原型如下:
long int syscall (long int sysno, ...)
sysno :为系统调用号,每个系统调用都有唯一的系统调用号来标识。
… :可变长的参数,为系统调用所带的参数,不同的系统调用可带0~5个不等的参数。
返回值:该函数返回值为特定系统调用的返回值,如果系统调用失败则返回 -1。
例如应用程序通过 syscall函数来改变文件的属性,示例代码如下:
#include
#include
//syscall接口函数声明在头文件unistd.h中
#include
//SYS_chmod在头文件syscall.h中
int main()
{
int rc=syscall(SYS_chmod,"./weiwei",0777);
if(rc==-1)
perror("SYS_chmod chmod fail\n");
else
printf("SYS_chmod chmod succeed\n");
return 0;
}
syscall(SYS_chmod,“./weiwei”,0777)函数用于改变文件weiwei的属性,编译得到syschmod.o执行文件,运行syschmod.o后观察weiwei文件的属性,结果如下:
Linux内核提供了一组宏,用于实现系统调用接口函数,实现系统调用后应用程序就可以使用该系统调用。这组宏是_syscalln(),其中n的范围从0到6,这组宏会设置好寄存器并调用软中断指令,_syscalln()宏如下:
_syscall0(),_syscall1(),_syscall2(),_syscall3(),_syscall4(),_syscall5(),_syscall6()。
宏名称字符串“syscall0”中的0表示无参数,“syscall1”中的1表示带1个参数,“syscall2”中的2表示带2个参数。如果系统调用带有1个参数,那么就应该使用宏_syscall1()。
以_syscall0()为例,_syscall0()为不带参数的系统调用宏函数,它以嵌入汇编的形式调用软中断指令int 0x80。
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \ // 调用系统中断0x80。
: "=a" (__res) \ // 返回值eax(__res)。
: "0" (__NR_##name)); \ // 输入为系统中断调用号__NR_name。
if (__res >= 0) \ // 如果返回值>=0,则直接返回该值。
return (type) __res; \
errno = -__res; \ // 否则置出错号,并返回-1。
return -1; \
}
假设我们要新增一个名为test系统调用,我们可以使用_syscall系统调用宏实现,假设test系统调用参数个数为0个,我们通过_syscall0(int, test)申请了一个新的系统调用。
我们通过_syscall0(int, test)声明一个系统调用,根据宏定义我们把_syscall0(int, test)这个宏展开并替代得到如下语句:
static inline int test(void)
{
long __res;
__asm__ volatile ("int $0x80" : "=a" (__res) : "0" (__NR_test));
if (__res >= 0)
return (int) __res;
errno = -__res;
return -1;
}
_syscall0(int, test)实际上是声明了一个名为test的函数,声明函数后,应用程序就可以使用test这个系统调用了(当然还有内核相关代码需要实现),test系统调用示例代码如下:
#include
#include
//syscall接口函数声明在头文件unistd.h中
#include
//SYS_chmod在头文件syscall.h中
_syscall0(int, test);
int main()
{
int rc=test();
if(rc==-1)
perror("test fail\n");
else
printf("test succeed\n");
return 0;
}
用户态程序通过软中断指令int 0x80 来陷入内核态,通过寄存器传递参数,eax 传递的是系统调用号,ebx、ecx、edx、esi和edi 来依次传递参数,当系统调用返回值存放在 eax 中。
以调用chmod修改文件属性为例,系统调用通过内联汇编实现,示例代码如下:
#include
#include
#include
//SYS_chmod在头文件syscall.h中
#include
int main()
{
long rc;
unsigned short mode = 0777;
char *file_name = "./weiwei";
/*内联汇编 软中断指令 */
asm(
"int $0x80"
: "=a" (rc)
: "0" (SYS_chmod), "b" ((long)file_name), "c" ((long)mode)
);
if (rc == -1)
perror("SYS_chmod chmod fail\n");
else
printf("SYS_chmod chmod succeed\n");
return 0;
}
以上这4种方法,每种方法的底层都会用到触发软中断指令。系统调用的参数 由各通用寄存器传递,然后执行软中断指令(INT 0x80) ,处理器响应中断并开始执行中断服务程序,此时处理器模式变为特权模式。我们以glibc-2.23中的X86体系为例,下面分别讲述4中方法如何调用软中断指令。
glibc-2.23\sysdeps\unix\sysv\linux\generic中有chmod函数的定义,代码如下:
#include
#include
#include
#include
#include
/* Change the protections of FILE to MODE. */
int
__chmod (const char *file, mode_t mode)
{
return INLINE_SYSCALL (fchmodat, 3, AT_FDCWD, file, mode);
}
weak_alias (__chmod, chmod)
INLINE_SYSCALL 宏在glibc-2.23\glibc-2.23\sysdeps\i386\sysdep.h中定义,代码如下:
# define INLINE_SYSCALL(name, nr, args...) \
({ \
unsigned int resultvar = INTERNAL_SYSCALL (name, , nr, args); \
__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (resultvar, )) \
? __syscall_error (-INTERNAL_SYSCALL_ERRNO (resultvar, )) \
: (int) resultvar; })
INTERNAL_SYSCALL 宏在glibc-2.23\glibc-2.23\sysdeps\i386\sysdep.h中定义,代码如下:
#define INTERNAL_SYSCALL(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
INTERNAL_SYSCALL_MAIN_##nr (name, err, args); \
(int) resultvar; })
INTERNAL_SYSCALL_MAIN_##nr宏的几种形式在glibc-2.23\glibc-2.23\sysdeps\i386\sysdep.h中定义如下:
#define INTERNAL_SYSCALL_MAIN_0(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 0, args)
#define INTERNAL_SYSCALL_MAIN_1(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 1, args)
#define INTERNAL_SYSCALL_MAIN_2(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 2, args)
#define INTERNAL_SYSCALL_MAIN_3(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 3, args)
#define INTERNAL_SYSCALL_MAIN_4(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 4, args)
#define INTERNAL_SYSCALL_MAIN_5(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 5, args)
INTERNAL_SYSCALL_MAIN_INLINE宏在glibc-2.23\glibc-2.23\sysdeps\i386\sysdep.h中定义,代码如下:
# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
LOADREGS_##nr(args) \
asm volatile ( \
"int $0x80" \
: "=a" (resultvar) \
: "a" (__NR_##name) ASMARGS_##nr(args) : "memory", "cc")
chmod库函数的定义最终通过int $0x80 触发软中断。
glibc-2.23\glibc-2.23\sysdeps\unix\sysv\linux\hppa\syscall.c中的syscall函数定义如下:
long int
syscall (long int __sysno, ...)
{
/* FIXME: Keep this matching INLINE_SYSCALL for hppa */
va_list args;
long int arg0, arg1, arg2, arg3, arg4, arg5;
long int __sys_res;
/* Load varargs */
va_start (args, __sysno);
arg0 = va_arg (args, long int);
arg1 = va_arg (args, long int);
arg2 = va_arg (args, long int);
arg3 = va_arg (args, long int);
arg4 = va_arg (args, long int);
arg5 = va_arg (args, long int);
va_end (args);
{
LOAD_ARGS_6 (arg0, arg1, arg2, arg3, arg4, arg5)
register unsigned long int __res asm("r28");
PIC_REG_DEF
LOAD_REGS_6
asm volatile (SAVE_ASM_PIC
" ble 0x100(%%sr2, %%r0) \n"
" copy %1, %%r20 \n"
LOAD_ASM_PIC
: "=r" (__res)
: "r" (__sysno) PIC_REG_USE ASM_ARGS_6
: "memory", CALL_CLOB_REGS CLOB_ARGS_6);
__sys_res = __res;
}
if ((unsigned long int) __sys_res >= (unsigned long int) -4095)
{
__set_errno (-__sys_res);
__sys_res = -1;
}
return __sys_res;
}
在syscall 函数中使用嵌入汇编的形式调用软中断指令。
以_syscall0()为例,它以嵌入汇编的形式调用软中断指令int 0x80。
使用软中断陷入是直接使用汇编指令int $0x80 触发软中断。
软中断是通过一条具体指令SWI,当CPU执行到SWI指令时会触发中断,进入中断程序。以X86 体系架构为例,软件中断指令( int 0x80)用于产生软中断,同时处理器从用户模式变换到特权模式。
应用程序调用系统调用接口函数后,底层会执行软中断指令( int 0x80 ),处理器就从用户态切换到内核态,并跳转到了 0x80 中断向量号 ,对应的中断向量服务程序 ,并根据传入的调用号执行相应的系统服务函数,中断触发后的响应流程如下图:
\linux-2.6.28.6\arch\x86\include\asm\irq_vectors.h中SYSCALL_VECTOR宏如下:
# define SYSCALL_VECTOR 0x80
linux-2.6.28.6\arch\x86\kernel\traps.c 中断配置如下:
void __init trap_init(void)
{
.....省略代码.....
set_intr_gate(0, ÷_error);
set_intr_gate_ist(1, &debug, DEBUG_STACK);
set_intr_gate_ist(2, &nmi, NMI_STACK);
/* int3 can be called from all */
set_system_intr_gate_ist(3, &int3, DEBUG_STACK);
/* int4 can be called from all */
set_system_intr_gate(4, &overflow);
set_intr_gate(5, &bounds);
set_intr_gate(6, &invalid_op);
set_intr_gate(7, &device_not_available);
.....省略代码.....
set_system_trap_gate(SYSCALL_VECTOR, &system_call);
.....省略代码.....
}
0x80 中断向量服务入口为system_call,system_call函数定义在linux-2.6.28.6/arch/x86/kernel/ entry_32.S中ENTRY(system_call),system_call汇编代码涉较多,下面的代码保留了最核心部分代码:
ENTRY(system_call)
RING0_INT_FRAME # can't unwind into user space anyway
pushl %eax # save orig_eax
CFI_ADJUST_CFA_OFFSET 4
SAVE_ALL
GET_THREAD_INFO(%ebp)
# system call tracing in operation / emulation
/* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */
testw $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
jnz syscall_trace_entry
cmpl $(nr_syscalls), %eax
jae syscall_badsys
syscall_call:
call *sys_call_table(,%eax,4)
movl %eax,PT_EAX(%esp) # store the return value
.....省略代码.....
ENDPROC(system_call)
我们将以上简化的代码根据syscall_call标号分为两部分:syscall_call准备,syscall_call调用
syscall_call准备
SAVE_ALL 的主要工作就是将 gs、fs、es、ds、eax、ebp 等寄存器中的值在调用系统调用服务程序之前将其压入到内核栈中保存。
cmpl $(nr_syscalls), %eax 指令语句则是用来判断从用户态传入的系统调用号是否大于系统中所实现的最大的系统调用编号,判断传入的系统调用编号是否合法,如果不合法程序就跳转到 syscall_badsys 处执行相应的出错处理程序。
syscall_call调用
syscall_call中的工作就是就是去调用与传入系统调用号对应的系统调用服务程序,而这一步中就只有一条非常简单的 call 指令语句,如下所示:
call *sys_call_table(,%eax,4)
call *sys_call_table(,%eax,4) 根据 eax 中传入的系统调用号来调用对应的系统调用服务程序, sys_call_table是一个函数指针数组(跳转表)。
sys_call_table跳转表
sys_call_table定义如下:
/* 代码文件路径:/linux-2.6.28.6/arch/x86/kernel/syscall_32.c */
#undef __SYSCALL
#define __SYSCALL(nr, sym) [nr] = sym,
#undef _ASM_X86_UNISTD_64_H
typedef void (*sys_call_ptr_t)(void);
extern void sys_ni_syscall(void);
const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
/*
*Smells like a like a compiler bug -- it doesn't work
*when the & below is removed.
*/
[0 ... __NR_syscall_max] = &sys_ni_syscall,
#include
};
sys_call_table 是函数指针类型的的数组,数组长度就是系统中所包含的全部系统调用的数量__NR_syscall_max + 1,[0 … __NR_syscall_max] = &sys_ni_syscall代码完成的就是对数组元素初始化的工作,
#include
asm/unistd_64.h文件内容如下:
#ifndef _ASM_X86_UNISTD_64_H
#define _ASM_X86_UNISTD_64_H
#ifndef __SYSCALL
#define __SYSCALL(a, b)
#endif
/* at least 8 syscall per cacheline */
#define __NR_read 0
__SYSCALL(__NR_read, sys_read)
#define __NR_write 1
__SYSCALL(__NR_write, sys_write)
#define __NR_open 2
__SYSCALL(__NR_open, sys_open)
#define __NR_close 3
__SYSCALL(__NR_close, sys_close)
#define __NR_stat 4
__SYSCALL(__NR_stat, sys_newstat)
#define __NR_fstat 5
__SYSCALL(__NR_fstat, sys_newfstat)
#define __NR_lstat 6
__SYSCALL(__NR_lstat, sys_newlstat)
#define __NR_poll 7
__SYSCALL(__NR_poll, sys_poll)
.....省略代码.....
sys_call_table扩展开后为:
__visible const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
[0 ] = sys_read,
[1] = sys_write
[2] = sys_open,
[3] = sys_close,
.....省略代码.....
};
sys_call_table[0] 就是执行sys_read函数,使用跳转表后程序的执行状态如下图:
系统调用号和系统调用服务程序是一一对应关系,一个系统调用对应一个系统调用服务程序。90号系统调用对应的系统调用服务程序就是 sys_chmod,如下所示:
#define __NR_chmod 90
__SYSCALL(__NR_chmod, sys_chmod)
sys_chmod函数定义如下:
linux-2.6.28.6\linux-2.6.28.6\fs\open.c
SYSCALL_DEFINE2(chmod, const char __user *, filename, mode_t, mode)
{
return sys_fchmodat(AT_FDCWD, filename, mode);
}
为什么不是 sys_chmod() 作为函数名呢?Linux 系统中的系统调用服务函数都是使用 SYSCALL_DEFINEx (0,1,2,3,4,5,6) 宏来实现的,这样使得用户态和内核态的两个函数“看起来一样”。
SYSCALL_DEFINEx 的定义如下所示,
/* 代码文件路径:/linux-3.18.6/include/linux/syscalls.h */
#define SYSCALL_METADATA(sname, nb, ...)
#define SYSCALL_DEFINE0(sname) \
SYSCALL_METADATA(_##sname, 0); \
asmlinkage long sys_##sname(void)
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
#define SYSCALL_DEFINEx(x, sname, ...) \
SYSCALL_METADATA(sname, x, __VA_ARGS__) \
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__)
#define __SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \
__attribute__((alias(__stringify(SyS##name)))); \
所以根据上面的定义,宏 SYSCALL_DEFINE2(chmod, const char __user *, filename, mode_t, mode)展开后得到的结果就是:
asmlinkage long sys_chmod(const char __user *filename, mode_t mode)
{
return sys_fchmodat(AT_FDCWD, filename, mode);
}
通常情况下已有的系统调用可以满足我们绝大多少需求,但是在极少数情况下,我们需要额外增加一个自定义的系统调用。接下来我们来学习一下如何增加一个自定义系统调用。例如我们自定义一个名为weiweicall的系统调用(该系统调用的参数为0)。
一旦系统调用的名字确定下来了,那么在系统调用中的几个相关名字也就确定下来了。
调用接口函数:weiweicall
系统调用的编号名字:__NR_weiweicall
内核中系统调用的实现程序的名字:sys_weiweicall
在前面我们描述了4种系统调用的接口,我们可以使用linux系统调用宏_syscall0来定义一个可以产生软中断的函数:
#include
_syscall0(int,weiweicall) /* 注意这里没有分号*/
int main()
{
weiweicall();
}
定义了weiweicall用户系统调用函数,接下来我们定义系统调用号。
我们要做的就是在文件asm/unistd_64.h文中添加自定义的系统调用号__NR_weiweicall,代码如下:
linux-2.6.28.6\arch\x86\include\asm\unistd_64.h
#define __NR_eventfd2 290
__SYSCALL(__NR_eventfd2, sys_eventfd2)
#define __NR_epoll_create1 291
__SYSCALL(__NR_epoll_create1, sys_epoll_create1)
#define __NR_dup3 292
__SYSCALL(__NR_dup3, sys_dup3)
#define __NR_pipe2 293
__SYSCALL(__NR_pipe2, sys_pipe2)
#define __NR_inotify_init1 294
__SYSCALL(__NR_inotify_init1, sys_inotify_init1)
//增加自定义系统调用
#define __NR_weiweicall 295
__SYSCALL(__NR_weiweicall, sys_weiweicall)
添加系统调用号之后,系统才能根据这个号作为索引,去找syscall_table中的相应表项。目前系统已经能够正确地找到并且调用sys_mysyscall。剩下事情的就只剩下sys_mysyscall内核函数的实现。
我们自定义的程序添加在内核代码中,如kernel/sys.c 里面,我们没有在kernel 目录下另外 添加自己的一个文件,这样做的不用修改Makefile,可以直接编译。我们可以用宏实现功能函数,也可以直接定义实现功能函数:
//直接定义sys_xxx函数
asmlinkage int sys_weiweicall(void)
{
printk( "weiwei system call!");
return 1;
}
//使用SYSCALL_DEFINE定义函数
SYSCALL_DEFINE0(weiweicall)
{
printk( "weiwei system call!");
return 1;
}
编译内核后,应用程序就可以使用weiweicall系统调用。
#include
_syscall0(int,weiweicall) /* 注意这里没有分号*/
int main()
{
weiweicall();
}
系统调⽤按照功能逻辑⼤致可分为“进程控制”、“⽂件系统控制”、“系统控制”、“存管管理”、“⽹络管理”、“socket控制”、“⽤户管理”、“进程间通信”。
进程控制类系统调用
fork,clone,execve,exit,_exit,getdtablesize,getpgid,setpgid,getpgrp,setpgrp,getpid,getppid,getpriority,setpriority,modify_ldt,nanosleep,nice,pause,personality,prctl,ptrace,sched_get_priority_max,sched_get_priority_min,sched_getparam,sched_getscheduler,sched_rr_get_interval,sched_setparam,sched_setscheduler,sched_yield,vfork,wait,wait3,waitpid,wait4,capget,capset,getsid,setsid
文件操作类系统调用
fcntl,open,creat,close,read,write,readv,writev,pread,pwrite,lseek,_llseek,dup,dup2,flock,polltruncate,ftruncate,umask,fsync
文件系统操作类系统调用
access,chdir,fchdir,chmod,fchmod,chown,fchown,lchown,chroot,stat,lstat,fstat,statfs,fstatfs,readdir,getdents,mkdir,mknod,rmdir,rename,link,symlink,unlink,readlink,mount,umount,ustat,utime,utimes,quotactl
系统控制类系统调用
ioctl,_sysctl,acct,getrlimit,setrlimit,getrusage,uselib,ioperm,iopl,outb,reboot,swapon,swapoff,bdflush,sysfs,sysinfo,adjtimex,alarm,getitimer,setitimer,gettimeofday,settimeofday,stime,time,times,uname,vhangup,nfsservctl,vm86,create_module,delete_module,init_modulequery_module,get_kernel_syms
内存管理类系统调用
brk,sbrk,mlock,munlock,mlockall,munlockall,mmap,munmap,mremap,msync,mprotect,getpagesize,sync,cacheflush
网络管理类系统调用
getdomainname,setdomainname,gethostid,sethostid,gethostname,sethostname,socketcall,socket,bind,connect,accept,send,sendto,sendmsg,recv,recvfrom,recvmsg,listen,select,shutdown,getsockname,getpeername,getsockopt,setsockopt,sendfile,socketpair
用户管理类系统调用
getuid,setuid,getgid,setgid,getegid,setegid,geteuid,seteuid,setregid,setreuid,getresgid,setresgid,getresuid,setresuid,setfsgid,setfsuid,getgroups,setgroups
进程间通信类系统调用
sigaction,sigprocmask,sigpending,sigsuspend,signal,kill,sigblock,siggetmask,sigsetmask,sigmask,sigpause,sigvec,ssetmask,msgctl,msgget,msgsnd,msgrcv,pipe ,semctl,semget,semop,shmctl,shmget,shmat,shmdt
总结:系统调用使用接口函数产生软中断,处理器进入中断状态执行相应的功能程序。
原文:https://blog.csdn.net/li_man_man_man/article/details/124599825
文章来源于网络,版权归原作者所有,如有侵权,请联系删除。
关注【一起学嵌入式】,回复“加群”进技术交流群。
觉得文章不错,点击“分享”、“赞”、“在看” 呗!