800 likes | 995 Views
第 6 章 Linux 系统调用. 计算机学院 潘 薇 panwei117@qq.com. 本章要点. 6.1 Linux 系统调用概述 6.2 文件系统类系统调用 6.3 进程类系统调用 6.4 内存管理类系统调用 6.5 其他常见系统调用. 6.1 Linux 系统调用概述. 6.1 Linux 系统调用概述. 系统调用是 Linux 操作系统内核提供给用户程序的接口 ; 应用程序通过该接口使用系统资源 ; 如果没有系统调用,应用程序失去内核的支持,也无法利用内核提供的系统资源。. 6.1 Linux 系统调用概述.
E N D
第6章 Linux系统调用 计算机学院 潘 薇 panwei117@qq.com
本章要点 • 6.1 Linux系统调用概述 • 6.2 文件系统类系统调用 • 6.3 进程类系统调用 • 6.4 内存管理类系统调用 • 6.5 其他常见系统调用
6.1 Linux系统调用概述 • 系统调用是Linux操作系统内核提供给用户程序的接口; • 应用程序通过该接口使用系统资源; • 如果没有系统调用,应用程序失去内核的支持,也无法利用内核提供的系统资源。
6.1 Linux系统调用概述 系统调用主要分为如下几类: • 文件系统控制类; • 文件系统操作类; • 进程控制类; • 操作系统控制类; • 内存管理类; • 网络管理类; • Socket控制类; • 用户管理类; • 进程间通信类。
6.1 Linux系统调用概述 • 系统调用是一种函数调用,与一般的函数调用相比,在表现方式上和实现效果上有明显不同: • 表现方式上的不同:一般函数调用时直接由调用过程转向被调用过程;而系统调用不允许由调用过程直接转向被调用过程,要借助于信号(软中断),先进入系统内核,再转向相应的系统调用。 • 实现效果上的不同:一般函数调用一直在用户空间上进行,不能进入操作系统内核空间;而系统调用需要从用户空间切换到内核空间,并在内核空间下进行。由于内核空间处于保护模式下,更加安全。因此,通过系统调用方式提供给应用程序使用内核资源不但更加安全,而且优先级高,响应速度快,效率高。
6.1 Linux系统调用概述 图6.1 linux调用
6.1 Linux系统调用概述 • 在C语言中存在一系列链接库,在链接库中含有一系列函数调用,如链接库stdio.h中含有标准输入、输出操作的fopen、fclose,fread、fwrite、fseek、ftell等,用户应用程序调用这些链接库函数。这些函数位于操作系统之上,需要通过系统调用才能和操作系统内核取得联系。
6.1 Linux系统调用概述 • 链接库函数是高层的,完全运行在用户空间,是为程序员提供调用系统调用的更方便的接口。 • 系统调用在内核态运行并且由内核自己提供。 • 部分标准C函数不使用系统调用,如strlen, strcat, memcpy等等。因为他们不需要使用到系统相关资源。 • 标准C库函数printf()可以被看做是一个通用的输出语句,但它实际做的是将数据转化为符合格式的字符串并且调用系统调用write()输出这些字符串。
6.1 Linux系统调用概述 • #include <stdio.h>int main(void){ printf("hello"); return 0; } • 使用命令gcc -Wall -o hello hello.c编译。 • 用命令 strace ./hello 跟踪该可执行文件。输出的结果每一行都和一个系统调用相对应。 strace是一个非常有用的程序,它可以告诉你程序使用了哪些系统调用和这些系统调用的参数、返回值。 • 其中有这么一行: • write(1, "hello!", 6hello!) = 6
出错的判断 • 系统调用失败时会在一个外部整数errno中留下一个错误号给应用程序判定。 • #include <error.h> • extern int errno; • 函数strerror用于将errno翻译成文字 • #include <string.h> • char *strerror(int errnum);
fcntl:文件控制; flock:文件加/解锁; creat:创建新文件; open:打开文件; close:关闭文件; read:读文件; write:写文件; readv:从文件读入数据到缓冲数组中; writev:将缓冲数组里的数据写入文件; pread:对文件随机读; pwrite:对文件随机写; lseek:移动文件指针; _llseek:在64位地址空间里移动文件指针; dup:复制已打开的文件描述符; dup2:按指定条件复制文件描述符。 poll:I/O多路转换 truncate:截断文件 umask:设置文件权限掩码 fsync:把文件在内存的部分写回到磁盘 6.2.1 文件读写操作
6.2.1 文件读写操作 • 系统调用open可用于将文件打开。 • 系统调用格式为: int open(const char *pathname, int flags); • 该系统调用返回值为打开文件的描述符。如果打开文件成功,则返回文件描述符,否则返回-1。 • flags是对打开文件的访问模式: • O_RDONLY表示可读,O_WRONLY表示可写,O_RDWR表示可读、可写。 • 另外还可以增加O_CREAT, O_EXCL, O_APPEND, ……
6.2.1 文件读写操作 • 当用户在文件使用完之后不再需要时用系统调用close断开用户程序与文件之间的通路,关闭该文件。 • 系统调用格式为: int close(int fd);
6.2.1 文件读写操作 • 当文件打开后,可以用系统调用read对文件进行读操作。 • 系统调用read的格式为: • ssize_t read(int fd, void *buffer, size_t count); • 如果系统调用成功,则返回实际读取的字节数。
6.2.1 文件读写操作 • 写系统调用是在文件被open系统调用打开后,从缓冲区buffer将指定字节的内容写到由open返回的文件描述符所指定的文件中。 • 调用格式为: • ssize_t write(int fd, const void *buf, size_t count); • 其中:fd是open返回的文件描述符;buf是一个缓冲区,用于存放要写的内容;count是要写的字节数;如果系统调用成功,则返回实际写的字节数。
6.2.1 文件读写操作 • 文件系统调用read和write是对文件的顺序访问,每次操作都紧跟在上次操作之后。如果要通过I/O位置指定实现对文件的随机顺序存取,则需要系统调用lseek的配合使用。 • 系统调用lseek的格式为: • off_t lseek(int fildes, off_t offset, int whence);
6.2.1 文件读写操作 • fildes为文件描述符。 • whence是文件读写位置移动的初始值,特殊点为:SEEK_SET 表示文件开始; SEEK_CUR 表示文件当前位置;SEEK_END 表示文件尾。 • offset是文件读写位置相对于whence的偏移量。 • 返回值是文件读写位置移动到的新的绝对值,即随后的读写开始处,如果系统调用出错,则返回-1。
6.2.1 文件读写操作 #include "stdio.h" #include "unistd.h" #include "fcntl.h" #include "string.h" char buf1[20] = "hello,file!"; char buf2[20]; int main() { int fd = -1; fd = open("/home/test", O_CREAT|O_RDWR);
6.2.1 文件读写操作 if (fd == -1) { printf("open error\r\n"); return -1; } int len = strlen(buf1); write(fd, buf1, len); lseek(fd, 0, SEEK_SET); read(fd, buf2,len); close(fd); printf("readfile:%s\r\n",buf2); return 0; }
access:确定文件的存取权限; chdir(fchdir):改变当前工作目录; chroot:改变根目录; chmod(fchmod):改变文件模式; chown(fchown):改变文件的拥有者或拥有者组; stat(lstat,fstat):取文件状态信息; statfs(fstatfs):取文件系统信息; readdir(getdents):读取目录项; mkdir:创建目录; rmdir:删除目录; mknod:创建索引节点; rename:文件重命名; link:创建链接; symlink:创建符号链接; unlink:删除链接; readlink:读符号链接的值; mount:安装文件系统; umount:卸下文件系统; utime(utimes):改变文件的访问修改时间; quotactl:控制磁盘配额。 6.2.2 文件系统操作
6.2.2 文件系统操作 • 改变当前工作目录chdir与fchdir • 系统调用chdir用于改变当前工作目录或指定当前目录。 • 该系统调用格式为: • int chdir(const char *path); • int fchdir(int fd);
6.2.2 文件系统操作 • 改变文件模式chmod与fchmod • 系统调用chmod用于修改文件或目录的访问模式(权限),与命令chmod完全对应。 • 该系统调用的格式为: • int chmod(const char *path, mode_t mode); • int fchmod(int fildes, mode_t mode); • 其中参数mode为访问模式,见第1章chmod命令
6.2.2 文件系统操作 • 系统调用stat、lstat和fstat用于进程查询文件的状态,返回文件类型、文件大小和文件所有者、文件的存取权限、文件的修改时间、链接计数、索引节点号等文件属性。 • 系统调用stat 、lstat和fstat的格式为: • int stat(const char *path, struct stat *buf); • int fstat(int filedes, struct stat *buf); • int lstat(const char *path, struct stat *buf); • 其中buf是数据结构地址,该数据结构用于存放文件信息。
6.2.2 文件系统操作 struct stat { mode_t st_mode; /* 文件模式*/ ino_t st_ino; /* 文件inode号 */ dev_t st_dev; /* 文件驻留的逻辑设备ID*/ dev_t st_rdev; /*设备的ID*/ nlink_t st_nlink; /*文件的链接数*/ uid_t st_uid; /*文件拥有者的用户ID*/ gid_t st_gid; /*文件拥有者组的ID*/ off_t st_size; /*文件逻辑长度*/ time_t st_atime; /*文件最后一次访问时间*/ time_t st_mtime; /*文件最后一次修改时间*/ time_t st_ctime; /*文件状态信息最后修改时间*/ long st_blksize; /*I/O预取块大小*/ long st_blocks; /*块预留数*/ }
6.2.2 文件系统操作 • 系统调用readdir用于读取目录,返回目录中的文件名称。 • 该系统调用的格式为: • int readdir(unsigned int fd, struct dirent *dirp, unsigned int count); • 库函数readdir • 库函数调用的格式为: • struct dirent *readdir(DIR *dir);
6.2.2 文件系统操作 • 系统调用mkdir用于创建目录。 • 该系统调用的格式为: • int mkdir(const char *pathname, mode_t mode); • 系统调用rmdir用于删除一个空目录。 • 该系统调用的格式为: • int rmdir(const char *pathname);
6.2.2 文件系统操作 • 系统调用rename为改变文件的名称或者位置,如果目标已存在,将被自动覆盖。 • 该系统调用的格式为: • int rename(const char *oldpath, const char *newpath); • 其中参数:oldpath为旧文件名;newpath为新文件名或者新位置。
6.2.2 文件系统操作 • 系统调用link是为文件建立一个链接。文件链接是将文件的索引节点指向文件数据。链接的本质是实现多个文件路径名可以指向同一个文件的索引节点。 • 系统调用link的格式为: int link(const char *oldpath, const char *newpath); • 其中:oldpath是指向已存在文件路径名的指针,即源文件名;newpath是指向新文件路径名的指针,即新文件名。
6.2.2 文件系统操作 • 系统调用unlink与系统调用link正好相反,是拆掉文件的一个链接。该系统调用的实质是从文件系统中删除一个文件名称。如果名称是文件的最后一个连接,并且没有其它进程将文件打开,名称对应的文件会实际被删除。 • 系统调用unlink的格式为: int unlink(const char *pathname); • 其中pathname是指向要拆掉的文件路径名。
6.2.2 文件系统操作 • 系统函数symlink()用于建立一个指定的符号连接。 • 该系统函数调用的格式如下: • int symlink(const char *oldpath, const char *newpath); • 其中参数:newpath为建立符号链接指定的名称, oldpath为符号链接指向的已经存在的文件。 • 如果参数newpath指定的名称为一已存在的文件则不会建立链接。
6.2.2 文件系统操作 • 系统函数mount()用于将一个文件系统挂到已经加载的文件系统上。 • 该系统函数调用的格式如下: • int mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data);
6.2.2 文件系统操作 • 其中参数表示如下: • source:将要挂上的文件系统,通常是一个设备名; • target:文件系统所要挂到的目标目录; • filesystemtype:文件系统的类型,可以是“ext2”,“msdos”,“proc”,“nfs,”iso9660“文件类型; • data:文件系统特有的参数,如”codepage=936或iocharset=cp936“; • mountflags:指定文件系统的读写访问标志
6.2.2 文件系统操作 例 加载光盘到目录/cdrom。 #include <sys/mount.h> #include <stdio.h> #include <unistd.h> int main(int argc, char * argv[]) { if(mount("/dev/cdroms/cdrom0", "/cdrom", "iso9660",MS_RDONLY | MS_NOSUID | MS_NODEV,"iocharset=gb2312| iocharset=cp936") < 0) then printf("error\n"); else printf("mount ok"); }
6.2.2 文件系统操作 • 系统函数umount()与mount()正好相反,为文件系统卸载。 • 该系统函数调用的格式如下: • int umount(const char *target); • 其中参数target是挂接点。
6.3 进程类系统函数调用 • 在Linux系统中,用户应用程序通过进程类系统函数调用达到应用进程和控制进程的目的。 • 进程类系统函数调用主要有: • fork:创建一个新进程; • clone:按照指定条件创建子进程; • execve:运行可执行文件; • vfork:创建一个子进程,以供执行新程序; • exit:中止进程; • _exit:立即中止当前进程;
6.3 进程类系统函数调用 • getdtablesize:进程所能打开的最大文件数; • getpid:获取进程标识符; • getppid:获取父进程标识符; • getpgid:获取指定进程组标识符; • getpgrp:获取当前进程组标识符; • setpgid:设置指定进程组标识符; • setpgrp:设置当前进程组标识符; • getpriority:获取调度优先级; • setpriority:设置调度优先级;
nice:改变分时进程的优先级 modify_ldt:读写进程的本地描述表 nanosleep:使进程睡眠指定的时间 pause:挂起进程,等待信号 personality:设置进程运行域 prctl:对进程进行特定操作 ptrace:进程跟踪 sched_get_priority_max:取得静态优先级的上限 sched_get_priority_min:取得静态优先级的下限 sched_getparam:取得进程的调度参数 sched_getscheduler:取得指定进程的调度策略 6.3 进程类系统函数调用
sched_rr_get_interval:取得按RR算法调度的实时进程的时间片长度sched_rr_get_interval:取得按RR算法调度的实时进程的时间片长度 sched_setparam:设置进程的调度参数 sched_setscheduler:设置指定进程的调度策略和参数 sched_yield:进程主动让出处理器,并将自己等候调度队列队尾 wait(wait3):等待子进程终止 waitpid(wait4):等待指定子进程终止 capget:获取进程权限 capset:设置进程权限 getsid:获取会晤标识符 setsid:创建会晤进程并设置会晤标识符setsid 6.3 进程类系统函数调用
fork • 在Linux系统中,系统函数fork()用于创建一个新的进程,创建进程需要系统有足够的资源。在满足系统允许的条件下,用户可以按需要创建多个进程。 • 该系统函数调用的格式如下: • pid = fork(); • 其中pid是新创建进程的标识符。 • 如果该系统函数调用成功,返回值有两个,其中pid=0的返回值用来执行子进程的代码,而大于0的返回值为执行父进程的代码。如果系统函数调用失败,返回值为-1。
fork • 通常情况是: • if (pid = = 0) • call subproc; /*调用子进程处理程序*/ • else if (pid>0) • complete parentproc; /*完成父进程处理程序*/ • 在进程创建上,新进程共享了父进程的代码段,拷贝了父进程的数据段。在新进程创建成功后,父子进程并发执行,各自独立地被调度运行。
fork 例 利用fork创建daemon(守护进程)。 int main() { int pid; pid=fork(); /*创建子进程*/ if (pid>0) /* 正常结束父进程 */ exit(0); else if (pid<0) { perror("fork error"); exit(-1); } for (;;) { printf("hello......\n"); sleep(3); } }
execve • 系统函数execve()可以是一个进程调用或引用一个新程序。 • 引用的新进程的程序副本覆盖了原来进程的存储空间。在execve调用之前存在的用户级上下文的内容,在execve调用之后被引用的程序覆盖而不存在。 • 因此,原进程使用该系统函数调用后不能继续运行,继续进行的是新的程序内容。
execve • 系统函数execve()的调用的格式如下: • int execve(const char *filename, char *const argv[], char *const envp[]); • 其中: • filename是要引用的可执行程序文件名; • argv是一个指向所执行程序的字符串参数数组指针; • envp是指向所执行程序环境的字符串数组指针。
execve • 基于系统函数execve()的函数调用有:execl()、execle()、execlp()、execv()、execve()、execvp()。 • 这些系统函数都是exec系统函数在使用时后面加上了字符l、v、e、p,它们代表的意义如下: • l:表示用长格式,要求参数指针数组的每一个都作为独立的参数传递,最后以空指针0结束。execl(“/usr/bin/ps”, ”ps”, “-ef”, “log”, 0);指定执行”ps –ef log”Shell命令。
execve • v:表示用argv指针数组传递运行参数。filename是文件路径,argv[0]是可执行程序名,之后是运行参数。 • e:表示用enp指针数组传递环境参数,如果要为进程指定新的环境,用execve或execle。 • p:表示执行时在环境变量PATH指定的目录中搜索路径名指定的文件,否则只在当前目录中搜索文件。如果PATH已经指定路径/bin,则为execlp(“ps”, “ps”, “-ef”, “log”, 0)。
vfork • 系统函数调用vfork也是创建一个子进程,但是所创建子进程的目的是用系统函数调用exec执行新程序,所以,vfork常与execve同时使用。 • vfork不需要完全拷贝父进程的数据段,在子进程没有调用exec和exit之前,子进程与父进程共享数据段。 • 在vfork调用中,子进程先运行,父进程挂起,直到子进程调用了exec或exit之后,父子进程的执行次序才不再有限制。 • 系统函数调用vfork与fork完全相同,其格式如下: pid_t vfork(void);
exit • 在Linux系统中提供系统函数调用exit()用于进程的自我结束。每个结束后的进程进入僵死状态,除保留进程表项proc结构外,要释放占有的资源(如关闭已经打开的文件),撤除进程上下文(释放内存)。 • 该系统函数调用的格式如下: void exit(int status); • 其中status是返回给结束进程父进程的参数,为整数。 • 无论在程序中的什么位置,只要执行到exit系统函数调用,进程就会停止剩下的所有操作。