POSIX API
POSIX标准总体分析
POSIX,全称为可移植性操作系统接口,是一种关于信息技术的IEEE标准。它包括了系统应用程序接口(API),以及实时扩展(C语言)。
该标准的目的是定义了标准的基于UNIX操作系统的系统接口和环境来支持源代码级的可移植性。现在,标准主要提供了依赖C语言的一系列标准服务,再将来的版本中,标准将致力于提供基于不同语言的规范。
该标准对核心需求部分定义了一系列任何编程语言都通用的服务,这一部分服务主要从其功能需求方面阐述,而非定义依赖于编程语言的接口。语言规范主要有两部分组成。一部分包括了访问核心服务的编程语言的标准接口,这些核心服务为标准中基于编程语言的核心需求部分所定义;另一部分包含了一个特殊语言服务的标准接口。基于任何语言,与该标准一致的执行都必须遵循语言规范的任何章节。
该标准一共被分为四个部分:
1) 陈述的范围和一系列标准参考(第一章);
2) 定义和总概念;(第二章)
3) 各种接口设备;(第三章到第十七章)
4) 头文件;(第十八章)
该标准的主要目的有:
1) 面向应用
2) 定义接口,而不是它的具体实现;
3) 涉及资源和可移植性,而非对象;
4) 基于c语言;
5) 无超级用户,无系统管理;
6) 最小限度的接口,最小限度的定义;
7) 应用领域广泛;
8) 对以前的实现进行最小限度改变;
9) 对原有程序代码做最小的修改;
10) 实时扩展;
二 概述
2.1 范围
定义范围的关键要素有:
(1)定义足够的一套功能适用于实时应用程序领域的重要部分;
(2)定义足够的实现规范和性能相关的函数,以便允许实时应用程序完成系统的确定性的响应;
2.2 一致性
系统须支持标准中定义的接口,系统能够提供标准中没有要求到的函数和工具。在遵循于该标准的实现中,一种一致性文档是需要用到的,它必须具有与该标准相同的结构,包含有全名,数字,和标准所指示的日期,以及头文件<limits.h>和<unistd.h>中的界限值等等。该一致性文档详细说明了标准中定义的执行行为。该标准要求了应用程序的一致性,所有遵循标准的应用程序都使用基于C语言的服务。
2.3 定义
1) 术语
该标准中定义了一系列术语,如一致性文档,被定义的实现,可能性,无用的特性等,还定义了一些通用名词,如绝对路径,存取模式,地址空间,适当权限,定时器,异步I/O操作,后台进程,后台进程组,块文件,阻塞进程等等。
2) 基本概念
扩展安全控制;文件存取允许;文件级别;文件名可移植性;路径名的决定;
3) 错误号
大部分函数都在外部变量errno中提供了错误号,定义如下:
extern int errno;
4) 简单系统的数据类型
这些数据类型在头文件<sys/types.h>中定义,它包含了至少以下类型:
dev_t |
用于设备号 |
gid_t |
用于进程标志符 |
ino_t |
用于文件序列号 |
inode_t |
用于一些文件参数 |
nlink_t |
用于连接内容 |
off_t |
用于文件大小 |
pid_t |
用于进程或进程组标志符 |
size_t |
在c标准(2)中定义 |
ssize_t |
用于返回字节数和错误标志的函数 |
uid_t |
用于用户标志符 |
5) 环境描述
当一个进程开始执行时,将调用一个表示环境的字符串数组,这个数组为外部变量environ所指向,其定义如下:
extern char **environ;
6) 其他
在该章中,标准还总体介绍了c语言定义的一些标志符,数字方面的限制,以及一些符号常量,这些在以后的章节中都会一一出现。
三 进程原语
3.1 进程的创建和执行
1) 进程创建
函数原型:
#include <unistd.h>
pid_t fork(void)
函数功能:
调用时需要引用的头文件是<unistd.h>,fork()创建了一个新的进程。子进程中返回0,父进程中返回子进程ID,出错返回-1。
2) 执行一个文件
函数原型:
#include <unistd.h>
int exec1(const char *path, const char *arg, …);
int execv(const char *path, const *char argv[] );
int execle(const char *path, const char *arg, …);
int execve(const char *path, const *char argv[], char *const envp[]);
int execlp(const char *file, const char *arg. …);
int execvp(const char *file, char *const argv[]);
函数功能:
exec系列的函数用一个新的进程映像替代了当前的进程的正文、数据、堆和栈段,这个新的进程映像创建于一个规则的可执行文件,叫做新进程映像文件。执行成功无返回值因为调用进程映像以为新的进程映像所覆盖。
3) 建立fork处理程序
函数原型:
#include <pthread.h>
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
函数功能:
调用pthread_atfork(),建立fork处理程序,可以清除锁状态。
3.2 进程的终止
进程的终止有两种情况:
(1)从main()函数返回时或者执行exit()或_exit()函数时正常的终止;
(2)被abort()函数请求或者接受到一些信号时不正常的终止;
1) 等待进程终止
函数原型:
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid,int *stat_loc.int options);
函数功能:
wait()和waitpid()允许调用进程获得它的一个子进程的状态信息。wait()函数将挂起调用进程直到获得了它的子进程的状态信息,或者是直到获得一个终止进程信号;如果pid=-1并且options=0,waitpid()函数功能将和waitpid()相同,否则它的功能将依据pid和options的值而改变。
区别:在一个子进程终止前,wait()使其调用者阻塞,而waitpid()有一个选项,可使调用者不阻塞。waitpid()并不等待在起调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程。
2) 终止一个进程
函数原型:
#include <stdlib.h>
void _exit(int status);
函数功能:
_exit()函数将终止一个调用进程,该函数不能返回给其调用者。
说明:exit()和_Exit()是由ISO C说明的,而_exit()是由POSIX.1说明的。
3.3 信号
在头文件<signal.h>中声明了sigset_t类型和sigaction结构。完成所定义的信号分三类:必需的信号、任务控制信号、内存保护信号。分别如下表:
必需信号
符号常量 |
描述 |
SIGABRT |
非正常终止信号 |
SIGALRM |
超时信号 |
SIGFPE |
错误运算操作 |
SIGHUP |
为控制中断所检测到的挂断 |
SIGILL |
无效硬件信号的检测 |
SIGINT |
交互式信号 |
SIGKILL |
终止信号 |
SIGPIPE |
写信号 |
SIGQUIT |
交互式终止信号 |
SIGSEGV |
无效内存引用检测信号 |
SIGTERM |
终止信号 |
SIGUSR1 |
保留信号 |
SIGUSR2 |
保留信号 |
任务控制信号
符号常量 |
描述 |
SIGCHLD |
子进程终止或停止 |
SIGCONT |
停止后继续 |
SIGSTOP |
停止信号 |
SIGTSTP |
交互式的停止信号 |
SIGTTIN |
从控制终端读 |
SIGTTOU |
写到控制终端 |
内存保护信号
符号常量 |
描述 |
SIGBUS |
获取内存中不确定的部分 |
每一个进程有一个进程标记(process mask),它定义了一组产生但被阻塞传递的信号集。sigaction(),sigpromask(),sigsuspend()函数控制这个进程标记的行为。
1) 送一个信号到进程
函数原型:
#include <sys/types.h>
#include<signal.h>
int kill(pid_t pid, int sig)
函数功能:
该函数发送一个信号到一个由pid指明的进程或者进程组,sig标志了信号类型,其值是0或者上表中的值。如果发送成功,返回’0’,否则返回’1’。
2) 操纵信号集
函数原型:
#include<signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set,int signo);
函数功能:
sigemptyset()初始化由set指向的信号集,清除其中所有信号;
sigfillset()初始化由set指向的信号集,使其包括所有信号;
所有应用程序在使用信号集前,要对该信号集调用sigemptyset()和sigfillset()一次;
sigaddset()将一个信号添加到先有集中;
sigdelset()从信号集中删除一个信号;
sigismember()测试一指定位。
3) 检测和更改信号的行为
函数原型:
#include<signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
函数功能:
该函数允许调用函数检查与确定的信号相联系的行为,参数sig确定了信号,sigaction结构在头文件<signal.h>中被定义,描述了所采取的行为。如果参数act不为null,它指向一个结构,它指定了与信号相联系的行为。如果参数oact不为null,先前与信号相联系的行为将被存储到由oact指向的地方。
4) 检查和改变阻塞信号
函数原型:
#include<signal.h>
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
函数功能:
该函数sigprocmask可以检测或更改其信号屏蔽字。首先,若oset是非空指针,那么进程的当前信号屏蔽字通过oset返回。其次,若set是一个非空指针,则参数how指示如何修改当前信号屏蔽字。如果set是空指针,则不改变该进程的信号屏蔽字,how的值也无意义。
5) 检查未定的信号
函数原型:
#include<signal.h>
int sigpending(sigset_t *set);
函数功能:
该函数返回一个信号集,其中的各个信号对于调用进程是阻塞的而不能递送,因而也一定是当前未决的。该信号集通过set参数返回。
6) 等待一个信号
函数原型:
#include<signal.h>
int sigsuspend(const sigset_t *sigmask);
函数功能:
该函数提供了在一个原子操作中先回复信号屏蔽字,然后使进程休眠。
将进程的信号屏蔽字设置为sigmask所指向的值。在捕捉到一个信号或发生了一个会中止该进程的信号之前,该进程被挂起。如果捕捉到了一个信号好而且从该信号处理程序返回,则sigsuspend()返回,并且该进行的信号屏蔽字设置为调用sigsuspend()之前的值。
注意,该函数没有成功返回值。如果它返回到调用者,则总是返回-1,并将errno设置为EINTR(表示一个被中断的系统调用)。
Sigsuspend()的另一种应用时等待一个信号处理程序设置一个全局变量。
7) 同步接受一个信号
函数原型:
#include<signal.h>
int sigwaitinfo(const sigset_t *set, siginfo_t *info);
int sigtimedwait(const sigset_t *set,siginfo_ *info, const struct timespec *timeout);
函数功能:
该函数从参数set所确定的信号集中选择一个未定的信号出来。如果该函数成功,返回一个信号数;否则返回-1。
8) 排队一个信号到进程
函数原型:
#include<signal.h>
int sigqueue(pid_t pid,int signo, const union sigval value);
函数功能:
该函数功能是使由signo确定的信号将参数value所确定的值发送到由pid指明的进程中去。
3.4 定时器操作
1) 调度警报
函数原型:
#include<unistd.h>
unsigned int alarm(unsigned int seconds);
函数功能:
使用该函数可以设置一个计时器,在将来某个指定的时间该计时器会超时。当计时器超时时,产生SIGALRM信号。如果不忽略或者不捕捉该信号,则其默认动作时终止调用该函数的进程。
2) 挂起进程的执行
函数原型:
#include<unistd.h>
int pause(void);
函数功能:
该函数挂起一个调用进程直至捕捉到一个信号,这个信号或者执行信号跟踪功能或者是终止该进程。如果是终止进程,该函数不返回;如果是执行信号跟踪功能,则该函数在信号跟踪函数返回后也要返回。
3) 延迟进程的执行
函数原型:
#include<unistd.h>
unsigned int sleep(unsigned int seconds);
函数功能:
该函数使当前进程从执行状态转化为挂起状态,直到参数seconds所指定的一段实时时间过去后,或者是一个唤醒信号跟踪功能或终止进程功能的信号到来。该挂起时间由于系统的其他调度活动可能会比要求的时间长。
四 进程环境
4.1 进程标志符
1) 获得进程和父进程的ID
函数原型:
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);
函数功能:
getpid()返回调用进程的进程ID,
getppid()返回调用进程的父进程ID.
4.2 用户ID
1) 获得真实用户,有效用户,真是组,有效组的ID
函数原型:
#include <unistd.h>
uid_t getuid(void);
uid_t geteuid(void);
gid_t getgid(void);
gid_t getegid(void);
函数功能:
getuid()返回调用进程的真实用户ID, geteuid()返回调用进程的有效用户ID,getgid()返回调用进程的真实组ID,getegid()返回调用进程的有效组的ID。
2) 设置用户和组的ID
函数原型:
#include <unistd.h>
int setuid(uid_t uid);
int setgid(gid_t gid);
int seteuid(uid_t uid);
int setegid(gid_t gid);
函数功能:
这两个函数分别根据进程的权限设置真实用户ID,有效用户ID,真实组ID,有效组ID。
3) 获得辅助组ID
函数原型:
#include <unistd.h>
int getgroups(int gidsetsize, gid_t grouplist[]);
函数功能:
该函数在队列的组列表中填入调用进程的辅助组ID。参数gidsetsize确定了组列表中的元素个数。
4) 获得用户名
函数原型:
#include <unistd.h>
char *getlogin(void)
函数功能:
该函数返回一个指针,指向与调用进程相关的用户名。
4.3 进程组
1) 获得进程组ID
函数原型:
#include <unistd.h>
pid_t getpgrp(void);
函数功能:
该函数返回调用进程的进程组ID。
2) 创建会话并且设置进程组ID
函数原型:
#include <unistd.h>
pid_t setsid(void)
函数功能:
如果调用进程不是进程组的组长,则该函数将创建一个新会话。结果将发生下面三件事:1、该进程成为一个新进程组的组长进程。此时,该新进程是新会话中唯一的进程。2、该进程成为一个新进程组的组长进程。新进程组ID是该调用进程的进程ID。3、该进程没有控制终端。如果再调用setsid()之前该进程有一个控制终端,那么这种联系也会被中断。
3) 为作业控制设置进程组ID
函数原型:
#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);
函数功能:
如{_POSIX_JOB_CONTROL}被定义,则该函数用来加入已经存在的进程组或者创建一个新的进程组。
4.4 系统标志
1) 获得系统名
函数原型:
#include<sys/utaname.h>
int uname(struct utsname *name);
函数功能:
该函数返回与当前主机与操作系统有关的信息。通过该函数的参数向其传递一个utsname结构的地址,然后改函数填写此结构。
struct utsname{
char sysname[]; /*name of the operation system*/
char nodename[];/*name of this node*/
char release[]; /*current release of operating system*/
char version[]; /*current version of this release*/
char machine[]; /*name of hardware type*/
};
4.5 时间
1) 得到系统时间
函数原型:
#include<time.h>
time_t time(time_t *calptr);
函数功能:
该函数返回自从公元1970年1月1日00:00:00以来的某个时间值,以秒为单位。参数calptr指向一个时间值所存储的地方。
2) 获得进程时间
函数原型:
#include<sys/times.h>
clock_t times(struct time *buffer);
函数功能:
参数buffer指向一个结构,该函数向该结构填写有关时间的信息。clock_t和tms结构在<sys/times.h>中定义。
4.6 环境变量
1) 获取环境
函数原型:
#include<stdlib.h>
char *getenv(const char *name);
函数功能:
该函数在环境列表查找字符串name=value,返回指向value的指针。如果没有找到,则返回null。
2) 设置环境
函数原型:
#include<stdlib.h>
int putenv(char *str);
int setenv(const char *name, const char *value, int rewrite);
int unsetenv(const char *name);
函数功能:
putenv()取形式为name=value的字符串,将其放到环境表中。如果name已经存在,则先删除其原来的定义。
setenv()将name设置为value。如果再环境中name已经存在,那么若rewrite非0,则首先删除其现有的定义,若rewrite为0,则先不删除现有定义。
unsetenv()删除name的定义。
4.7 终端标志
1) 产生终端路径
函数原型:
#include<stdio.h>
char *ctermid(char *s);
函数功能:
该函数产生一个字符串,作为路径名,提交到当前进程的当前控制终端, 用来确定控制终端的名字。此函数的主要作用是帮助提高向其他操作系统的可移植性。
2) 确定终端设备名
函数原型:
#include <unistd.h>
int isatty(int fildes);
char *ttyname(int fildes);
函数功能:
ttyname()返回一个指针指向一个字符串,它包含了该文件描述符上打开的终端设备的路径名;
如果fildes是一个有效的与终端联系的文件描述符,isatty()返回’1’,否则返回’0’。
4.8 可配置的系统变量
1) 获得可配置的系统变量
函数原型:
#include<unistd.h>
long sysconf(int name);
函数功能:
该函数提供了一个应用方法来决定可配置系统变量的当前值。参数name代表了所查询的系统变量。
五 文件和目录
5.1 目录
1) 目录的入口形式
头文件<dirent.h>定义了一个结构和目录程序用到的类型,没有确定的文件内部格式。readdir()返回了一个指针指向一个类型对象struct dirent。
2) 目录操作
函数原型:
#include<sys/types.h>
#include<dirent.h>
dir *opendir(const char *dirname);
struct dirent *readdir(dir *dirp);
void rewinddir(dir *dirp);
int closedir(dir *dirp);
函数功能:
opendir()根据参数dirname打开一个目录流;readdir()返回一个指针,它指向一个目录流中当前位置的目录入口,目录流由参数dirp指向;rewinddir()重置目录流的位置到目录的起点;closedir()关闭目录流,如成功,则返回“0”值。
5.2 工作目录
1) 改变当前的工作目录
函数原型:
#include<unistd.h>
int chdir(const char *path);
函数功能:
path指向当前目录的路径名。该函数使命名的目录成为当前的工作目录。
2) 获得工作目录路径名
函数原型:
#include<unistd.h>
char *getcwd(char *buf,size_t size);
函数功能:
该函数复制当前工作目录的绝对路径名到buf所指向的队列中。
5.3 基本文件的创建
1) 打开一个文件
函数原型:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcnt1.h>
int open(const char *path, int oflag,…);
函数功能:
open()在文件和文件描述符之间建立了一个连接,它创建了一个指向一个文件的打开文件描述,参数path指向文件的路径名。
2) 创建一个新文件或者重写一个已经存在的文件
函数原型:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcnt1.h>
int creat(const char *path, mode_t mode);
函数功能:
该函数创建一个新文件。调用creat(path, mode)相当于open(path, o_wronly|o_creat|o_trunc,mode);
3) 设置文件的创建标记
函数原型:
#include<sys/types.h>
#include<sys/stat.h>
mode_t umask(mode_t cmask);
函数功能:
umask()设置进程的文件模式创建标记到cmask,并且返回原来的标记值。
4) 链接到一个文件
函数原型:
#include<unistd.h>
int link(const char *existingpath ,const char *newpath);
函数功能:
参数existingpath指向路径名来命名存在文件,参数newpath指向一个路径名,它命名了一个创建的新的目录入口。该函数为已存在的文件自动的创建一个新的连接,并且将文件连接数加1。如果newpath已经存在,则返回出错。
5) 创建一个符号链接
函数原型:
#include <unistd.h>
int symlink(const char *path1, const char *path2);
函数功能:
该函数创建了一个指向path1的新目录项path2,在创建此符号链接时,不需要求path1已经存在。
6) 打开符号链接
函数原型:
#include <unistd.h>
int symlink(const char *restrict path, char *restrict buf, size_t bufsize);
函数功能:
打开符号链接,此函数组合了open,read,close的所有操作。如果此函数成功执行,则返回读入buf的字节数。
5.4 文件创建
1) 生成一个目录
函数原型:
#include<sys/types.h>
#include<sys/stat.h>
int mkdir(const char *path,mode_t mode);
函数功能:
该函数依据参数path创建一个新的目录。新目录的允许位根据mode初始化。
2) 创建一个FIFO类型的文件
函数原型:
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);
函数功能:
mkfifo()创建一个新的fifo类型文件,它由pathname指向的路径名命名。
FIFO有下面两种用途:1、FIFO由shell命令使用以便将数据从一条管道线传到另一条,为此无需创建中间临时文件。2、FIFO用于客户进程-服务器进程应用程序中,以在客户进程和服务器进程之间传递数据。
5.5 文件的移动
1) 移动目录入口
函数原型:
#include<unistd.h>
int link(const char *existingpath ,const char *newpath);
函数功能:
该函数删除目录项,并将有pathname所引用文件的链接计数减1。如果还有指向该文件的其他链接,则仍可以通过其他链接访问该文件的数据。如果出错,则不对该文件做任何更改。
2) 移去一个目录
函数原型:
#include<unistd.h>
int rmdir(const char *path)
函数功能:
移除一个路径为path的目录。
3) 重命名一个文件
函数原型:
int rename(const char *old,const char *new);
函数功能:
该函数改变一个文件的名字,参数old指向被重命名文件的路径名,参数new指向文件的新路径名。
5.6 文件特征
1) 获得一个路径下的信息
函数原型:
#include <sys/stat.h>
int stat(const char *restrict path, struct stat *restrict buf);
函数功能:
该函数获得路径下的文件信息,并把信息保存在buf缓冲区内。
2) 获得文件信息
函数原型:
#include <sys/stat.h>
int fstat(int fildes, struct stat *buf);
int lstat(const char *restrict path, struct stat *restrict buf);
函数功能:
fstat()函数获得一个已经打开的文件的信息,并存入缓冲区buf内;lstat()函数获得一个符号链接的文件的信息,并保存在buf缓冲区内。
3) 检查文件的权限
函数原型:
#include <unistd.h>
int access(const char *path, int amode);函数功能:
函数功能:
该函数检查路径下的文件权限,若权限正确返回0,否则返回-1。
5.7 可配置文件变量
1) 改变文件权限变量
函数原型:
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fildes,, mode_t mode);
函数功能:
chmod()和fchmod()函数改变路径下的文件权限变量mode,若修改正确,返回0,否则返回-1。不同的是chmod()是在制定的文件上进行操作,而fchmod()则对已打开的文件进行操作。
2) 更改文件的用户ID和组ID
函数原型:
#include <unistd.h>
int chown(const char *path, uid_t owner, gid_t group);
int fchown(int fildes, uid_t owner, gid_t group);
int lchown(const char *path, uid_t owner, gid_t group);
函数功能:
除了所引用的文件时符号链接外,这三个函数的操作相似。在符号链接的情况下,lchown()更改符号链接本身的所有者,而不是该符号链接所指向的文件。
3) 更改文件访问和修改时间
函数原型:
#include <utime.h>
int utime(const char *path, const struct utimbuf *times);
函数功能:
此函数使用的数据结构是:
struct utimbuf{
time_t actime;
time_t modtime;
}
调用函数后得到文件访问和修改的日历时间。
4) 文件截短
函数原型:
#include <unistd.h>
int truncate(const char *pathname, off_t length);
int ftruncate(int fildes, off_t length);
函数功能:
这两个函数将把现有的文件长度阶段为length字节。如果该文件以前的长度大于length,则超过length以外的数据就不再能访问了。函数ftruncate()是POSIX.1的组成部分,truncate()函数式POSIX.1功能的XSI扩展部分。
5) 文件路径名变量配置
函数原型:
#include <unistd.h>
long fpathconf(int fildes, int name);
long pathconf(const char *path, int name);
函数功能:
配置文件路径名变量。
六 输入与输出源语
6.1 管道
1) 创建内进程通道
函数原型:
#include<unistd.h>
int pipe(int filedw[2]);
函数功能:
该函数创建一个管道并且创建两个文件描述符,一个是fildes[0],一个是fildes[1],它们分别指的是‘读’与‘写’的管道端。
6.2 文件描述符控制
1) 复制一个打开文件描述符
函数原型:
#include<unistd.h>
int dup(int fildes);
int dup2(int fileds,int fileds2);
函数功能:
这两个函数为fcntl()服务提供了两个选择的接口,用到了F_DUPFD命令。
fid = dup(fildes);相当于fid = fcntl(fildes, F_DUPFD, 0);
fid = dup2(fildes, fildes2);相当于close(fildes2);fid = fcntl(fildes, F_DUPFD, fildes2);
6.3 撤除文件描述符
1) 关闭一个文件
函数原型:
#include<unistd.h>
int close(int fildes);
函数功能:
关闭一个文件。关闭一个文件时还会释放该进程加载该文件上的所有记录锁。当一个进程终止时,内核自动关闭它所有打开的文件。
6.4 输入和输出
1) 文件读
函数原型:
#include<unistd.h>
ssize_t read(int fildes,void *buf,size_t nbyte);
函数功能:
从打开文件中读数据。如read成功,则返回读到的字节数。如果已到达文件结尾,则返回0。
2) 文件写
函数原型:
#include<unistd.h>
ssize_t write(int fildes,const void *buf,size_t nbyte);
函数功能:
该函数向打开的文件写数据。
6.5 一个文件的控制操作
1) 文件控制操作的数据定义
头文件<fcnt1.h>为fcntl()和open()定义了一下的要求和参数:
fcntl()的_cmd值
常量 |
描述 |
F_DUPFD |
复制文件描述符fileds |
F_GETFD |
对应于filedes的文件描述符标志作为函数值返回 |
F_GETLK |
判断由flockptr所描述的锁是否会被另外一把锁所排斥 |
F_SETFD |
对于filedes设置文件描述符标志 |
F_GETFL |
对应于filedes的文件状态标志作为函数值返回 |
F_SETFL |
将文件状态标志设置为第三个参数的值(即为整形值) |
F_SETLK |
设置由flockptr所描述的锁 |
F_SETLKW |
这是F_SETLK的阻塞版本(w表示等待(wait)) |
F_GETOWN |
取当前接收SIGIO和SIGURG信号的进程ID或进程组ID |
F_SETOWN |
设置接收SIGIO和SIGURG信号的进程ID或进程组ID |
2) 文件控制
函数原型:
#include <sys/types.h>
#inclued<unistd.h>
#include<fcntl.h>
int fcntl(int fildes,int cmd,…);
函数功能:
fcntl()为打开的文件提供了一系列控制,参数fildes是一个文件描述符。cmd决定了控制的内容。
fcntl()有5种功能:1、复制一个现有的描述符(cmd=F_DUPFD)2、获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD)3、获得/设置文件状态标志(cmd=F_GETFL或F_SETOWN)4、获得/设置一部I/O所有权(cmd=F_GETOWN或F_SETOWN)5、获得/设置记录锁(cmd=F_GETLK、F_SETLK或F_SETLKW)。
3) 读/写文件偏移量的重新定位
函数原型:
#include<sys/types.h>
#include<unistd.h>
off_t lseek(int fildes,off_t offset,int whence);
函数功能:
lseek()为fildes所指定的文件重新设置偏移量。
若whence是SEEK_SET,则将该文件的偏移量设置为距文件开始处offset个字节。
若whence是SEEK_CUR,则将该文件的偏移量设置为其当前值加offset,offset可为正或负。
若whence是SEEK_END,则将该文件的偏移量设置为文件长度加offset,offset可为正或负。
若lseek()成功执行,返回新的文件偏移量。
6.6 文件同步
1) 文件的状态同步
函数原型:
#include<unistd.h>
int fsync(int fildes);
函数功能:
该函数用来暗示所有的文件描述数据被传输到了存储设备上。该函数值对由文件描述符filedes指定的单一文件起作用,并且带写磁盘操作结束,然后返回。
2) 文件数据的同步
函数原型:
#include<unistd.h>
int fdatasync(int fildes);
函数功能:
该函数迫使当前所有排队的I/O操作进入同步 I/O状态。该函数类似于函数fsync(),但它只影响文件的数据部分。
6.7 异步输入与输出
1) 异步输入与输出的数据定义
头文件<aio.h>能使<sys/types.h>,<signal.h>,<time.h>和<fcntl.h>中的符号可见。
异步I/O控制块
异步I/O控制块结构aiocb在许多异步I/O接口中使用,它在<aio.h>中定义。
2) 异步读
函数原型:
#include<aio.h>
int aio_read (struct aiocb *aiocbp);
函数功能:
aiocbp->io_nbytes,表示读的字节数;aiocbp->aio_fildes,表示读的文件;aiocbp->aio_buf,表示读到的缓冲区。
3) 异步写
函数原型:
#include<aio.h>
int aio_write(struct aiocb *aiocbp);
函数功能:
参数表示同上。
4) 列出直接I/O
函数原型:
#include<aio.h>
int lio_listio(int mode,struct aiocb *const list[],int nent,struct sigevent *sig);
函数功能:
该函数允许用一个函数调用初始化一个I/O请求列表。
5) 得到异步I/O操作的错误状态
函数原型:
#include<aio.h>
int aio_error(const struct aiocb *aiocbp);
函数功能:
该函数返回aiocbp指向的结构所表示的错误状态。
6) 得到异步I/O操作的返回状态
函从数原型:
#include<aio.h>
ssize_t aio_return(struct aiocb *aiocbp);
函数功能:
得到异步I/O操作的返回状态。
7) 删除异步I/O请求
函数原型:
#include<aio.h>
int aio_cancel (int fildes,struct aiocb *aiocbp);
函数功能:
参数fildes是文件描述符,参数aiocbp指向异步I/O控制块上的请求删除部分。
8) 等待异步I/O请求
函数原型:
#include<aio.h>
int aio_suspend(const struct aiocb *const list[],int nent,const struct timespec *timeout);
函数功能:
标准定义该函数挂起调用进程直到至少一个list指向的异步I/O操作完成,或者一个信号中断了一个函数,或者超时了(timeout指定)。
9) 异步文件同步化
函数原型:
#include<aio.h>
int aio_fsync(int op,struct aiocb *aiocbp);
函数功能:
该函数迫使所有与(参数aiocbp指向的)结构aiocb中aio_fildes所指定的文件相关异步I/O操作进入同步状态。
七 设备和面向类的函数
7.1 基本的终端接口
1) 接口特性
●当一个终端文件被打开,通常它将引起进程等待直到连接被建立。
●进程组一个终端可以具有与它相关的前台进程组,它发挥特定的角色,后面会讲到。
●控制终端
●终端存取控制
●输入操作和读进程
●规范的输入操作
●非规范模式的输入操作
●写数据和输出处理
●特殊的符号(INTR,QUIT,ERASE,KILL…)
●modem断掉连接
●关闭终端设备文件
2) 可设置的参数
●termios机构
该结构在<termios.h>中定义,在控制特定的终端I/O特性中要用到。
●输入模式
termios c_iflag
标记名 |
描述 |
BRKINT |
信号中断 |
ICRNL |
输入时将CR映射到NL |
IGNBRK |
忽略中断状态 |
IGNCR |
忽略CR |
IGNPAR |
忽略奇偶错误 |
INLCR |
输入时将NL映射到CR |
INPCK |
输入奇偶校验使能 |
ISTRIP |
Strip字符 |
IXOFF |
开始/停止输入控制使能 |
IXON |
开始/停止输出控制使能 |
PARMRK |
产生奇偶错误 |
●输出模式
termios c_oflag
标记名 |
描述 |
BSDLY |
退格延迟屏蔽 |
CMSPAR |
标记或空奇偶性 |
CRDLY |
CR延迟屏蔽 |
FFDLY |
换页延迟屏蔽 |
NLDLY |
NL延迟屏蔽 |
OCRNL |
将输出的CR转换为NL |
OFILL |
对于延迟使用填充符 |
OLCUC |
将输出的小写字符转换为大写字符 |
ONLCR |
将NL转化为CR-NL |
ONLRET |
NL执行CR功能 |
ONOCR |
在0列不输出CR |
ONOEOT |
在输出中删除EOT(^D)字符 |
OPOST |
执行输出处理 |
OXTABS |
将制表符扩充为空格 |
TABDLY |
水平制表符延迟屏蔽 |
VTDLY |
垂直制表符延迟屏蔽 |
●控制模式
termios c_cflag
标记名 |
描述 |
CBAUDEXT |
扩充的波特率 |
CREAD |
启用接收装置 |
CSIZE |
字符大小屏蔽 |
CS5 |
5位 |
CS6 |
6位 |
CS7 |
7位 |
CS8 |
8位 |
CSTOPB |
送两个停止位,否则为1位 |
HUPCL |
在最后的关闭中挂起 |
PARENB |
奇校验使能 |
PARODD |
奇校验或偶校验 |
●本地模式
termios c_lflag 值
标记名 |
描述 |
ECHO |
进行回送 |
ECHOE |
可见擦除符 |
ECHOK |
回送kill符 |
ECHONL |
响应’/n’ |
ICANON |
规范输入 |
IEXTEN |
启用扩充的输入字符处理 |
ISIG |
启用中断产生的信号 |
NOFLSH |
中断,停止或挂起后关掉flush |
TOSTOP |
为后台输出发送SIGTTOU |
●特殊的控制字符
这些特殊的控制字符值在队列c_cc中定义,分为规范和非规范两种模式。
●波特率值
3) 波特率函数
函数原型:
#include<termios.h>
speed_t cfgetospeed(const struct termios *termios_p);
int cfsetospeed(struct termios *termios_p,speed_t speed);
speed_t cfgetispeed(const struct termios *termios_p);
int cfsetispeed(struct termios *termios_p,speed_t speed);
函数功能:
以上这些接口被用来在termios结构获得和设定输入与输出的波特率值。
7.2 基本的终端接口控制函数
1) 获得并设定状态
函数原型:
#include<termios.h>
int tcgetattr(int fildes,struct termios *termios_p);
int tcsetattr(int fildes,int optional_actions,const struct termios * termios_p);
函数功能:
tcgetattr()获得fildes所确定的文件的参数并将其存储在t_erops_p所指向的结构中;tcsetattr()将设置参数。
2) 行控制函数
函数原型:
#include<termios.h>
int tcsendbreak(int fildes,int duration);
int tcdrain(int fildes);
int tcflush(int fildes,int queue_selector);
int tcflow(int fildes,int action)’
函数功能:
如果终端使用异步连续数据传输,tcsendbreak()引起在一段时间内连续的‘0’位传输;tcdrain()等待直到输出传输完毕;tcflush()函数刷清输入缓冲区或输出缓冲区;tcflow()用于对输入和输出流控制进行控制。
3) 获得和设置终端属性
函数原型:
#include<termios.h>
int tcgetattr(int filedes, stuct termios *termptr);
int tcsetattr(int filedes, int opt, const struct termios *termptr);
函数功能:
这两个函数都有一个指向termios结构的指针作为其参数,它们返回当前终端的属性,或者设置该终端的属性。
八 基于C语言的服务
8.1 C语言输入/输出函数
1) 设置程序环境
函数原型:
#include <locale.h>
char *setlocale(int category, const char *locale);
函数功能:
该函数设置程序运行环境。
2) 映射一个流指针到一个文件描述符
函数原型:
#include<stdio.h>
int fileno(FILE *stream);
函数功能:
每个标准I/O流都有一个与其相关联的文件描述符,可以对一个流调用fileno函数以获得其描述符。该函数是POSIX.1支持的扩展。
3) 根据一个文件描述符打开一个流
函数原型:
#include<stdio.h>
FILE *fdopen(int fildes,const char *type);
函数功能:
该函数获取一个现有的文件描述符(我们可能从open,dup,dup2,fcntl,pipe,socket,socketpair或accept函数得到此文件描述符),并使一个标准的I/O流与该描述符相结合。该函数常用于由创建管道和网络通信通道函数返回的描述符。
4) 其他文件类型函数之间的相互作用
一个单一的文件描述说明可以通过流和文件描述符访问,流或者是文件描述符被称作位打开文件的句柄,一个打开文件说明可以有多个句柄。句柄可以在不影响重要的打开文件说明的情况下被创建和删除,创建如fcntl(),dup(),fdopen(),fileno(),fork();删除如fclose(),close()。
a) fopen()随着open()的调用分配描述符,基础函数为open()。
b) fclose():该函数完成在与FILE流相关的,对文件描述符的close()功能。
c) freopen():具有fclose()和fopen()的特点。
d) fflush():如果流可写或者缓存数据还没有写入文件时,该函数标记下基础文件st_ctime和st_mtime的值。
e) fgetc(),fgets(),fread(),getc(),getchar(),gets(),scanf(),fscanf():这些函数标记更新的st_atime值。 基础函数是read()和lseek()。
f) fputc(),fputs(),fwrite(),putc(),putchar(),puts(),printf(),fprintf():从以上任一个函数的成功执行到下一个调用(在同一个流中的fflush()或fclose()或exit()或abort()),记下更新的st_ctime和st_mtime值。基础函数是write()和 lseek()。
g) fseek(),rewind():如果流可写或者缓存数据还没有写入文件时,该函数标记下文件更新的st_ctime和st_mtime值。基础函数是lseek()和write()。
h) perror():记下与标准错误流相关的文件。
i) tmpfile():fopen()执行后为文件分配文件描述符。
j) ftell(): 基础文件是lseek()。执行fflush()后执行该函数的结果与执行fflush前执行该函数的结果相同。
k) 报错
l) exit(),abort():exit()终止进程的时候要关闭流, abort()只终止进程对流没有影响。
5) 文件操作remove()函数
该函数和unlink()函数的功能一样。
8.2 其他的C函数
1) 非局部跳转
函数原型:
#include<setjmp.h>
int sigsetjmp(sigjmp_buf env,int savemask);
void siglongjmp(sigjmp_buf env,int val);
函数功能:
sigsetjmp()宏要与标准中setjmp()宏的定义一致,如果参数savemask不为“0”,sigsetjmp()就要保存当前的信号标记作为调用环境的一部分。siglongjmp()同理。
2) 设置时间域
函数原型:
#include<time.h>
void tzset(void);
函数功能:
该函数用环境变量TZ的值来设定时间变化信息。
九 系统数据库
9.1 系统数据库
本章描述了两个数据库:组数据库和用户数据库。
组数据库包括的信息有:组名,组的数字ID,组中的用户列表;
用户数据库包含的信息有:用户名,用户的数字ID,组的数字ID,初始化的工作目录和初始化的用户程序。
9.2 数据库的访问
1) 组数据库的访问
函数原型:
#include<sys/type.h>
#include<grp.h>
struct group *getgrgid(gid_t gid);
struct group *getgrnam(const char *name);
函数功能:
getgrid()和getgrnam()返回指针,它指向一个struct group类型的对象,包含了组数据库的入口。
2) 用户数据库的访问
函数原型:
#include<sys/types.h>
#include <pwd.h>
struct paswd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
函数功能:
getpwuid()和getpwnam()返回一个指针,指向struct passwd类型的一个对象,它包含了用户数据库的入口。
十 信号量
10.1信号量特征
头文件<semaphore.h>定义了sem_t类型,它用于信号量操作中。sem_t代表了信号量,用文件描述符能够实现这些信号量,应用程序能够打开至少{OPEN_MAX}这么多的文件和信号量。标准中,头文件<semaphore.h>能使头文件<sys/types.h>和<fcntl.h>中的符号可见。
为了获得共享资源,进程需要执行下列操作:
(1) 测试控制该资源的信号量
(2) 若此信号量的值为正,则进程可以使用该资源。进程可以使用该资源。进程将信号量只减1,表示它使用了一个资源单位。
(3) 若此信号量的值为0,则进程进入休眠状态,直至信号量值大于0.进程被唤醒后,它返回至第(1)步。
10.2信号量函数
1) 初始化一个未命名的信号量
函数原型:
#inxlude<semaphore.h>
int sem_init(sem_t *sem,int pshared,unsigned int value);
函数功能:
该函数被用来初始化sem引用的未命名信号量。在成功调用该函数以后,在接下来的sem_wait(),sem_trywait(),sem_post(),sem_destroy()的调用中,该信号量将被用到。如果参数pshared不为零,信号量将在进程中被共享,任何可以访问信号量sem的进程都可以使用sem。只有sem能被用来进行同步。如果参数pshared为零,则结构不确定。如果标准中未定义,可以由执行者来支持该函数。
2) 删除一个未命名信号量
函数原型:
#include <semaphore.h>
int sem_destroy(sem_t *sem);
函数功能:
该函数用来删除sem引用的未命名信号量,只有在sem_init()中创建的信号量才能被该函数删除。
3) 初始化/打开一个命名信号量
函数原型:
#include<semaphore.h>
sem_t *sem_open(const char *name,int oflag,…);
函数功能:
该函数在进程和命名信号量之间创建一个链接。接着调用带有信号量名name的该函数,进程引用与name相关的信号量。该信号量在一些函数的调用中用到,如sem_wait(),sem_trywait(),sem_post,和sem_close()。信号量一直可用知道调用函数sem_close(),_exit,exec()关闭它。参数oflag控制是否信号量由sem_open()创建或者仅被它访问。
4) 关闭一个命名信号量
函数原型:
#include<semphore.h>
int sem_close(sem_t *sem);
函数功能:
该函数用来提示调用进程已经完成使用sem所指明的信号量。该函数释放系统资源,这些资源被拥有该信号量的进程占有。
5) 移走一个命名信号量
函数原型:
#include<semaphore.h>
int sem_unlink(const char *name);
函数功能:
该函数将移走被字符串name命名的信号量。如果该信号量当前被其他进程引用,则该函数对信号量状态没有影响。如果一个和更多的进程打开了该信号量,则销毁该信号量被延迟知道所有的信号量被sem_close(),_exit(),exec关闭。
6) 锁定一个信号量
函数原型:
#include<semaphore.h>
int sem_wait (sem_t*sem);
int sem_trywait(sem_t *sem);
函数功能:
sem_wait()锁定sem引用的一个信号量,对该信号进行锁定操作。如果信号量为’0’,调用进程将不会返回直到锁定了这个信号量或者被一个信号中断。Sem_trywait()只能在信号量当前没被锁定的情况下锁定它,否则将不会锁定信号量。成功返回,信号量的状态将被锁定直到sem_post()被调用并且成功返回。
7) 解锁一个信号量
函数原型:
#include<semaphore.h>
int sem_post(sem_t *sem);
函数功能:
该函数通过对一个信号量的解锁操作解锁一个被sem引用的信号量。如果该操作的结果,信号量值为正数,则没有进程被锁定来等待一个信号量解锁,信号量值是单一的增加。如果信号量值为’0’,则进程被锁定来等待一个信号量被允许从sem_wait()成功返回。
8) 取得一个信号量值
函数原型:
#include<semaphore.h>
int sem_getvalue(sem_t *sem,int *sval);
函数功能:
该函数更新参数sval所引用的位置,在不改变信号量状态的情况下得到信号量值。更新的值代表了一个确切的值,它产生在调用中的一个不定时刻,但它返回给调用进程是不一定需要信号量的确定值。如果sem被锁定,则sem_getvalue()返回的值为0,或者负数,它代表了等待信号量的进程数。
十一 内存管理
该章节描述了进程内存锁定,内存映象文件和共享内存工具。
内存锁定和内存映象文件以页的方式定义。执行者可以据据页大小的规范限制和排列锁定和映象范围。页的大小,以字节为单位,是一个可配置系统变量。默认为1B。
内存锁定保证了一部分地址空间的驻留。
11.1内存锁定函数
1) 锁定/解锁进程地址空间
函数原型:
#include<sys/mman.h>
int mlockall(int flags);
int munlockall(void);
函数功能:
mlockall()使进程地址空间所映射的所有页面成为内存驻留区,直到解锁或者进程退出或者execs另一个进程映象。参数flags决定了是否被锁定的页面是由当前,(将来,或者两者都是)的进程地址空间映射的。Munlockall()解锁当前所有的进程地址空间映射页面。所有映射到进程地址空间的页面,调用了该函数,将不会被锁定,除非有中断调用mlockall()确定MCL_CURRENT,或者并发调用mlockall()确定MCL_CURRENT。
2) 锁定/解锁一连续的地址空间
函数原型:
#include<sys/mman.h>
int mlock(const void *addr,size_t len);
int munlock(const void *addr,size_t len);
函数功能:
mlock()使一个范围的进程地址空间成为内存驻留区(addr决定了空间的起始地址,len决定了空间长度)直到解锁或者进程退出或者execs另一个进程映象;munlock()解锁一个范围的进程地址空间(addr决定了空间的起始地址,len决定了空间长度);
11.2内存映象函数
1) 映象进程地址到一个内存目标
函数原型:
#include<sys/mman.h>
void *mmap(void *addr,size_t len,int prot,int flags,int fildes,off_t off);
函数功能:
该函数在一个进程的地址空间和内存对象间创建一个映射,调用格式如:pa=mmap(addr,len,prot,flags,fildes,off);pa代表进程进程地址空间的地址,由执行者定义(addr和flags),len表示空间长度,fildes代表内存对象,off表示偏移量。函数成功调用返回pa,空间地址范开始与pa,长度为连续的len个字节;内存对象开始与off,长度为len字节。参数prot决定了读,写,执行,或一些映射数据的访问活动的集合。
2) 取消以前映射地址的映射关系
函数原型:
#include<sys/mman.h>
int munmap(void *addr,size_t len);
函数功能:
该函数移去任何包含有进程地址空间的页面的映射关系,该进程地址空间起始于addr,长度为len字节。
3) 改变内存保护
函数原型:
#include <sys/mman.h>
int mprotet(const void *addr,size_t len,int prot);
函数功能:
该函数更改访问的保护活动,由参数prot确定。访问对象是一部分进程地址空间,起始于addr,长度为len字节。
4) 内存对象同步
函数原型:
#inxlude<sys/mman.h>
int msync(void *addr,size_t len,int flags);
函数功能:
该函数将所有更改了的数据写到拥有存储的地方,它包含了进程地址空间,起始于addr,长度为len字节。如果没有这样的存储区域存在,则给函数没有作用。该函数须保证写操作的完成符合所定义的同步I/O数据的一致完成性。参数flags确定了写的同步/异步性。
11.3共享内存函数
1) 打开一个共享内存对象
函数原型:
include<sys/mman.h>
int shm_open(const *name,int oflag,mode_t mode);
函数功能:
该函数在共享内存对象和文件描述符之间创建了一个连接。它创建了一个打开文件描述涉及到共享内存对象,并且创建了一个文件描述符指向这个打开文件描述。这个文件描述符可悲其他函数用来指向共享内存对象,参数name指向共享内存对象名。如函数调用成功,则返回一个共享内存的文件。文件状态和文件访问模式根据oflag的值来设定。
2) 移去一个共享内存对象
函数原型:
#include <sys/mman.h>
int shm_unlink(const char *name);
函数功能:
该函数移去由name命名的共享内存对象。如果一个或者多个对共内存的引用在对象断开连接的同时退出的话,这个名字应在shim_unlink()返回之前移走,但是对内存对象的移走须延迟到所有对共享内存对打开和映射引用被移去后。
十二 执行调度
12.1调度参数
一个调度参数结构sched_param包括了调度策略所支持的执行者所需要的调度参数,它在头文件<sched.h>中定义。执行者可根据规对该结构进行扩展。
12.2调度策略
该章所阐述的调度术语是从概念模型上定义的,它包含了一组进程列表。这个模型只讨论了可运行进程的处理器调度,但是它注重了在其他资源考虑到处理器调度策略的情况下,增强了实时操作的可预见性。在这里,概念上讲是一个进程列表一个策略。调度策略的目的就是对这一组列表定义允许的操作(例如,在进程列表之中和之间移动进程)。每一个进程应由相关的调度策略和优先级,于每一个策略相关的是一个优先级范围。
1) SCHED_FIFO
该策略是一种先进先出的调度策略。如果正在执行的进程是被抢占的进程,则他应该在进程列表头;如果一个锁定的进程变成可执行进程,它就进入进程列表尾;当执行进程调用sched_setscheduler(),则函数中确定的进程被改为指定的策略;当执行进程调用sced_setparam(),进程的优先级根据参数param被修改,如果改进程是正在执行和可执行,则它将进入进程列表尾;当正在执行的进程调用seced_yield(),进程进入列表尾。
2) SCHED_RR
该策略与上面的策略不同的是,如果执行中检测到进程的执行时间已经到达或超过了sched_rr_get_interval()所返回的时间片,进程就进入列表尾并且进程列表头移动一个进程进入执行状态。
3) SCHED_OTHER
12.3进程调度函数
1) 设置调度参数
函数原型:
#include<sched.h>
int sched_setparam(pid_t pid,const struct sched_param *param);
函数功能:
pid指明了进程,param指向了sched_param结构,该结构设定了进程的调度参数。如果pid=0,调度参数为调用进程设定。如果pid指定的进程的优先级高于正在指向的进程并且该进程可执行,则抢占现在正在运行的进程。如果当前的策略不是前面将的三种方式,则由执行者定义。
2) 取得调度参数
函数原型:
#include<sched.h>
int sched_getparam(pid_t,struct sched_param *param);
函数功能:
该函数返回调度参数,如果一个pid进程退出并且调用进程允许,则ID等于pid的进程返回其调用参数。如果pid=0,则返回调用进程的调度参数。
3) 设置调度策略和调度参数
函数原型:
#include<sched.h>
int sched_setscheduler(pid_t pid,int policy,const struct sched_param *param);
函数功能:
该函数设置进程的调度策略和调度参数,pid表示进程,policy指明策略,参数param指向的sched_param结构指明了调度参数。执行者可要求请求进程能允许设定自己或其他进程的调度参数。
4) 取得调度策略
函数原型:
#include<sched.h>
int sched_getscheduler(pid_t pid);
函数功能:
该函数返回pid进程的调度策略,其返回值在头文件<sched.h>中定义。
5) 放弃处理器
函数原型:
#include<sched.h>
int sched_yield(void);
函数功能:
该函数迫使正在执行进程放弃处理器直到重新进入进程列表头。
6) 获得调度参数范围
函数原型:
#incude<sched.h>
int sched_get_priority_max(int policy);
int sched get_priority min(int policy);
int sched_rr_get_interval(pid_t pid, struct timespec *interval);
函数功能:
sched_get_priority_max()和sched_get_priority_min()返回policy调度策略相应的最大最小值。Sched_rr_get_interval()更新interval参数引用的timespec结构,包含了当前进程执行的时间限制。
十三 时钟和定时器
13.1时钟和定时器的数据定义
头文件<time.h>定义了时间设备用到的类型和主要常量。
1) 时间值的规格结构
一个时间值结构timespec确定了单一时间值并且包括了以下值:
成员类型 |
成员名 |
描述 |
time_t |
tv_sec |
seconds |
long |
tv_nsec |
nanosenconds |
执行者可对他做扩展,tv_nsed成员要大于等于零时才可用。
时间值结构itimerspec确定了一个初始化的定时器和各进程定时器函数用到的重复间隔。结构包括:
成员类型 |
成员名 |
描述 |
Struct timespec |
It_interval |
Timer period |
Struct timespec |
It_value |
Timer expiration |
执行者也可以对其做扩展。
2) 定时器活动唤醒控制块
为了使完成支持实时信号扩展功能,各进程定时器被创建通过排列实时扩展信号来通知定时器超时的进程。Sigevent结构在头文件<signal.h>中定义,用来创建一个这样的定时器。
3) 类型定义
在<sys/types.h>中定义。
4) 主要常量
在<time.h>中定义:
CLOCK_REALTIME 系统范围的实时时钟标志
TIMER_ABSTIME flag指定的时间对于时钟是“完全”的
13.2时钟和定时器函数
1) 时钟
函数原型:
#include<time.h>
int clock_settime(clockid_t clock_id,const struct timespec *tp);
int coock_gettime(clockid_t clock_id,struct timespec*tp);
int clock_getres(clockid_t clock_id,struct timespec *res);
函数功能:
clock_settime()设置特定时钟,clock_id,到tp指定的值。
Clock_gettime()返回当前的tp值,即时钟,clock_id;
clock_getres()可以得到时钟的确定值,该值由执行者定义而不由进程设定。如果res不为空,则该值被存储在res指向的地方,如果res为空,则时钟决定值不被返回。
2) 创建一个总进程定时器
函数原型:
#include<signal>
#include<time.h>
int timer_create(clockid_t clock_id,struct sigevent *evp,timer_t *timerid);
函数功能:
timer_create()创建一个总进程定时器,用来指明时钟,clock_id,作为计时基础。该函数在timerid指向的地方返回一个timer_t类型的定时器ID,该ID在调用进程中必须是唯一的直到定时器被删除。参数evp如果不为空,则指向一个sigevent结构,定义了定时器超时时出现的异步通知。结构中sigev_notify为SIGEV_SIGNAL时,结构就包括了超时时送往进程的信号数和应用数据;如果sigev_notify为SIGEV_NONE,则不发送任何通知;sigev_notify其他的值由完成者自行定义。总进程的定时器不被其子进程通过fork()继承,它应该被exec撤销。
3) 删除一个总进程定时器
函数原型:
#include<time.h>
int timer_delete(time_t timerid);
函数功能:
该函数删除一个指定的定时器,timerid,该定时器是在以前的timer_create()中创建的。
4) 总进程定时器
函数原型:
#include<time.h>
int timer_settime(timer_t timerid,int flags,const struct itimerspec *value,struct itimerspec *ovalue);
int timer_gettime(timer_t timerid,struct itimerspec *value);
int timer_getoverrun(timer_t timerid);
函数功能:
timer_settime()设置时间直到下一个timerid指定的定时器终止。如果该函数调用时定时器已经被装备,该调用则重置时间直到value确定的下一次终止。如果value的it_value成员为零,定时器被解除。如果flag没有设定为TIMER_ABSTIME,则定时器从调用开始在it_value十亿分之一秒内超时;如果设定为TIMER_ABSTIME,该函数表现为时间直到下一次超时被设定为it_value指定的绝对时间和与timerid相联的时钟值的差值。定时器的再装由value的it_interval成员值来设定。如果参数ovalue不为空,该函数被存储在ovalue引用的地方。该函数要存储时间累计值直到timerid指定的定时器终止或重装定时器值到value参数。在任一时刻,仅有一个单一信号被派了在指定的定时器的进程中。如果支持实时信号扩展,timer_getoverrun()返回定时器终止的溢出值。
5) 高度睡眠
函数原型:
#include<time.h>
int nanosleep(const struct timespec *rqtp,struct timespec *rmtp);
函数功能:
该函数使当前执行的进程挂起直到参数rptp指定的时间间隔到达或者信号被送到调用进程并且其行为就是唤醒信号跟踪功能或者使终止进程。挂起时间也许要比要求时间长是因为参数值向上舍入成一个整数或者是因为系统对其他活动的调度。但是除非被信号打断,挂起时间不会少于tqtp值,这是系统时钟,CLOCK_REALTIME测量的。
十四 消息传递
14.1消息队列的数据定义
1) 数据结构
头文件< mqueue.h>定义了以下执行者定义的类型:
mqd_t :用于消息队列描述符
头文件< mqueue.h>定义了以下执行者定义的结构:
mq_attr用来设定消息队列参数。
struct sigevent结构包含了至少以下的成员:
类型 |
成员名 |
描述 |
long |
mq_flags |
消息队列标志 |
long |
mq_maxmsg |
最大消息数 |
long |
mq_msgsize |
消息的最大长度 |
long |
mq_curmsgs |
当前排列的消息数 |
14.2消息传递函数
1) 打开消息队列
函数原型:
#include<mqueue.h>
mqd_t mq_open(const char *name,int oflag,…);
函数功能:
该函数在进程和消息队列之间建立连接。它创建了一个消息队列描述符指向消息队列。参数oflag请求对消息队列发送或接收所需信息。如果调用进程承认对相应保护文件的读写请求,则对接收或发送消息的请求允许被通过。
2) 关闭一个消息队列
函数原型:
#include<mqueue.h>
int mq_close(mqd_t mqdes);
函数功能:
该函数撤销消息队列描述符(mqdes)和消息队列之间的关系。如果进程成功的配属了mqdes表示的消息队列通知请求,则这个配属关系被撤销,该消息队列可用于其他进程来配属通知。
3) 移去一个消息队列
函数原型:
#include<mqueue.h>
int mq_unlink(const char *name);
函数功能:
该函数移去路径名name指向的消息队列。如果该调用成功,并且flag没有设为O_CREATE,则mq_open()对于同一个name将会失败。所有对该队列的引用撤销后,该队列才能被关闭。该函数调用不会被阻塞指定所有的引用被关闭。
4) 发送一个消息到消息队列
函数原型:
#include<mqueue.h>
int mq_send(mqd_t mqdes,const char *msg_ptr,size_t mag_len,unsigned int msg_prio);
函数功能:
该函数添加参数msg_ptr指向的消息到mqdes指定的消息队列中去。参数msg_len表示消息长度,以字节为单位,该参数应小于等于消息队列的mq_msgsize参数,否则调用失败。如果消息队列没有满,则该函数的功能就是插入消息到消息队列的指定位置,这个位置邮msg_prio参数指定。msg_prio大者先插入队列,msg_prio的值应小于等于{MQ_PRIO_MAX}。如果消息已满并且O_NONBLOCK没有设定,该函数阻塞一直到空间可用或者mq_send()被信号中断。如果空间可用时,多于一个进程在等待发送则按优先级,等待最久的进程先发送它的信息。如果O_NONBLOCK被设定,并且队列已满,则函数返回error。
5) 从消息队列接受一条消息
函数原型:
#include<mqueue.h>
ssize_t mq_receive(mqd_t mqdes,char *msg_ptr,size_t msg_len,unsigned int *msg_prio);
函数功能:
该函数接受mqdes确定的消息队列中最久优先级最高的消息,对参数值的限制同上。所选消息从队列移出,复制到msa_ptr指向的缓冲区。如果参数msg_prio不为空,则指定消息被存储在msa_prio所引用的地方。如果消息队列为空,并且O_NONBLODK没有设定,该函数阻塞直到消息排列入队列中或者该函数被信号中断。当有多个进程在消息到达队列后请求接受,则优先级原则与上相同。
6) 通知一个进程队列中的一条信息可用
函数原型:
#include<mqueue.h>
int mq_notify(mqd_t mqdes,const struct sigevent *notification);
函数功能:
如果参数notification不为空,函数记录下调用进程被通知空的消息队列(由消息队列描述符mqdes相联)中有一条消息到来。当消息队列从空到非空时,一条通知消息将发送到进程中,在任一时刻,只有一个通知消息被一个消息队列记录。如果notifiction为空并且进程当前被指定的队列记录,则已存在的记录被移去。
7) 设置消息队列参数
函数原型:
#include <mqueue.h>
int mq_setattr(mqd_t mqdes,const struct mq_attr *mqstat,struct mq_attr *omqstat);
函数功能:
该函数用来设置与mqdes指定的消息队列相关的参数。mq_attr结构中的mq_maxmsg,mq_magsize,mq_curmsgs成员被mq_setattr()忽略。如果omqstat为非空,则该函数被存储,由omqstat指明存储位置。
8) 得到消息队列参数
函数原型:
#include<mqueue.h>
int mq_getattr(mqd_t mqdes,struct mq_attr *mqstat);
函数功能:
该函数用来取得状态信息和与mqdes消息队列相关的参数。
十五 互斥量
15.1互斥量属性
1) 初始化互斥量属性
函数原型:
#include <pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
函数功能:
pthread_mutexattr_init()函数将用默认值初始化互斥属性对象attr来定义的所有属性,可以用宏PTHREAD_MUTEX_INITIALIZER来静态的初始化锁。如果初始化成功,返回0,否则返回error。
2) 删除互斥量属性
函数原型:
#include <pthread.h>
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
函数功能:
pthread_mutexattr_ destroy()函数删除互斥属性对象attr定义的所有属性。
3) 获得和设置互斥量属性
函数原型:
int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *restrict attr, int *restrict protocol);
int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol);
函数功能:
pthread_mutexattr_getprotocol()函数获得互斥属性对象的协议属性; pthread_mutexattr_setprotocol()函数设置互斥属性对象的协议属性,设置是否支持优先级翻转。有以下三种协议属性:
PTHREAD_PRIO_NONE:线程的优先级和调度不会受到互斥锁拥有权的影响。
PTHREAD_PRIO_INHERIT:使用该属性值可以避免优先级倒置。
PTHREAD_PRIO_PROTECT:当线程拥有一个或多个使用初始化的互斥锁属性时,此协议值会影响其他线程的优先级和调度。
4) 互斥量属性优先级上限属性
函数原型:
#include <pthread.h>
int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *restrict attr, int *restrict prioceiling);
int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int prioceiling);
函数功能:
pthread_mutexattr_setprioceiling()可用来设置互斥锁属性对象的优先级上限属性。prioceiling 指定已初始化互斥锁的优先级上限。优先级上限定义执行互斥锁保护的临界段时的最低优先级。prioceiling 位于 SCHED_FIFO 所定义的优先级的最大范围内。要避免优先级倒置,请将 prioceiling 设置为高于或等于可能会锁定特定互斥锁的所有线程的最高优先级。oldceiling 包含以前的优先级上限值。
如果成功完成,pthread_mutexattr_setprioceiling() 会返回 0。其他任何返回值都表示出现了错误。
如果出现以下任一情况,pthread_mutexattr_setprioceiling() 将失败并返回对应的值。
ENOSYS:选项_POSIX_THREAD_PRIO_PROTECT未定义并该实现不支持此函数。
EINVAL:attr 或 prioceiling 指定的值无效。
EPERM:调用方无权执行该操作。
pthread_mutexattr_getprioceiling()函数可用来获取互斥锁属性对象的优先级上限属性,返回 prioceiling 中已初始化互斥锁的优先级上限。
5) 共享互斥量属性
函数原型:
int pthread_mutexattr_getpshared(const pthread_mutexattr_t * restrict attr, int *restrict pshared);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
函数功能:
pthread_mutexattr_getpshared()可为属性对象 mattr 获取 pshared 的当前值,该值为PTHREAD_PROCESS_SHARED或 PTHREAD_PROCESS_PRIVATE。该函数成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。EINVAL:由 mattr指定的值无效。
要在多个进程中的线程之间共享互斥量,可以利用pthread_mutexattr_setpshared()函数,在共享内存中创建互斥量,并将 pshared 属性设置为 PTHREAD_PROCESS_SHARED。
15.2互斥量
1) 初始化和删除互斥量
函数原型:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
函数功能:
如果针对以前初始化的但尚未销毁的互斥锁调用 pthread_mutex_init(),则该互斥锁不会重新初始化。如果属主无法使状态保持一致,请勿调用 pthread_mutex_init(),而是解除锁定该互斥锁。在这种情况下,所有等待的线程都将被唤醒。以后对 pthread_mutex_lock() 的所有调用将无法获取互斥锁,并将返回错误代码 ENOTRECOVERABLE。现在,通过调用 pthread_mutex_destroy() 来取消初始化该互斥锁,即可使其状态保持一致。调用 pthread_mutex_init() 可重新初始化互斥锁。
2) 互斥量锁操作
函数原型:
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
函数功能:
锁操作主要包括加锁pthread_mutex_lock()、解锁 pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁和适应锁类型, 解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。 pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。
3) 互斥量高级锁操作
函数原型:
#include <pthread.h>
#include <time.h>
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);
函数功能:
当线程试图获取一个已加锁的互斥变量时,pthread_mutex_timedlock互斥量原语允许绑定线程阻塞的时间。pthread_mutex_timedlock函数与pthread_mutex_lock是基本等价的,但是在达到超时时间值时,pthread_mutex_timedlock不会对互斥量进行加锁,而是返回错误码ETIMEDOUT。
4) 互斥锁的优先级上限
函数原型:
#include <pthread.h>
int pthread_mutex_getprioceiling(const pthread_mutex_t *restrict mutex,int *restrict prioceiling);
int pthread_mutex_setprioceiling(pthread_mutex_t *restrict mutex,int prioceiling, int *restrict old_ceiling);
函数功能:
pthread_mutex_getprioceiling()会返回 mutex 的优先级上限 prioceiling。pthread_mutex_setprioceiling()可更改互斥锁 mutex 的优先级上限 prioceiling。可锁定互斥锁(如果未锁定的话),或者一直处于阻塞状态,直到 pthread_mutex_setprioceiling()成功锁定该互斥锁,更改该互斥锁的优先级上限并将该互斥锁释放为止。锁定互斥锁的过程无需遵循优先级保护协议。
十六 线程管理
16.1线程属性
1) 线程属性的初始化与删除
函数原型:
#include <pthread.h>
int pthread_attr_destroy(pthread_attr_t *attr);
int pthread_attr_init(pthread_attr_t *attr);
函数功能:
线程具有属性,用pthread_attr_t()表示,在对该结构进行处理之前必须进行初始化,在使用后需要对其去除初始化。我们用pthread_attr_init()函数对其初始化,用pthread_attr_destroy()对其去除初始化。调用pthread_attr_init()之后,pthread_t结构所包含的内容就是操作系统实现支持的线程所有属性的默认值。
线程属性结构如下:
typedef struct
{
int detachstate; /*线程的分离状态*/
int schedpolicy; /*线程调度策略*/
structsched_param schedparam; /*线程的调度参数*/
int inheritsched; /*线程的继承性*/
int scope; /*线程的作用域*/
size_t guardsize; /*线程栈末尾的警戒缓冲区大小*/
int stackaddr_set;
void* stackaddr; /*线程栈的位置*/
size_t stacksize; /*线程栈的大小*/
}pthread_attr_t;
2) 线程属性的分离状态
函数原型:
#include <pthread.h>
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
函数功能:
线程的分离状态决定一个线程以什么样的方式来终止自己。在默认情况下线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()返回时,创建的线程才算终止,才能释放自己占用的系统资源。
而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。所以如果我们在创建线程时就知道不需要了解线程的终止状态,则可以pthread_attr_t结构中的detachstate线程属性,让线程以分离状态启动。
可以使用pthread_attr_setdetachstate()把线程属性detachstate设置为下面的两个合法值之一:设置为PTHREAD_CREATE_DETACHED,以分离状态启动线程;或者设置为PTHREAD_CREATE_JOINABLE,正常启动线程。可以使用pthread_attr_getdetachstate()获取当前的datachstate线程属性。
3) 线程属性的继承性
函数原型:
#include <pthread.h>
int pthread_attr_getinheritsched(const pthread_attr_t *restrict attr,int *restrict inheritsched);
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
函数功能:
这两个函数分别用来设置和得到线程的继承性。
这两个函数具有两个参数,第1个是指向属性对象的指针,第2个是继承性或指向继承性的指针。继承性决定调度的参数是从创建的进程中继承还是使用在schedpolicy和schedparam属性中显式设置的调度信息。Pthreads不为inheritsched指定默认值,因此如果你关心线程的调度策略和参数,必须先设置该属性。
继承性的可能值是PTHREAD_INHERIT_SCHED(表示新现成将继承创建线程的调度策略和参数)和PTHREAD_EXPLICIT_SCHED(表示使用在schedpolicy和schedparam属性中显式设置的调度策略和参数)。
如果你需要显式的设置一个线程的调度策略或参数,那么你必须在设置之前将inheritsched属性设置为PTHREAD_EXPLICIT_SCHED.线程属性结构如下:
4) 线程属性的调度策略
函数原型:
#include <pthread.h>
int pthread_attr_getschedpolicy(const pthread_attr_t *restrict attr,int *restrict policy);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
函数功能:
这两个函数分别用来设置和得到线程的调度策略。
这两个函数具有两个参数,第1个参数是指向属性对象的指针,第2个参数是调度策略或指向调度策略的指针。调度策略可能的值是先进先出(SCHED_FIFO)、轮转法(SCHED_RR),或其它(SCHED_OTHER)。
SCHED_FIFO策略允许一个线程运行直到有更高优先级的线程准备好,或者直到它自愿阻塞自己。在SCHED_FIFO调度策略下,当有一个线程准备好时,除非有平等或更高优先级的线程已经在运行,否则它会很快开始执行。
SCHED_RR(轮循)策略是基本相同的,不同之处在于:如果有一个SCHED_RR
策略的线程执行了超过一个固定的时期(时间片间隔)没有阻塞,而另外的SCHED_RR或SCHBD_FIPO策略的相同优先级的线程准备好时,运行的线程将被抢占以便准备好的线程可以执行。
当有SCHED_FIFO或SCHED_RR策赂的线程在一个条件变量上等持或等持加锁同一个互斥量时,它们将以优先级顺序被唤醒。即,如果一个低优先级的SCHED_FIFO线程和一个高优先织的SCHED_FIFO线程都在等待锁相同的互斥且,则当互斥量被解锁时,高优先级线程将总是被首先解除阻塞。
5) 线程属性的调度参数
函数原型:
#include <pthread.h>
int pthread_attr_getschedparam(const pthread_attr_t *restrict attr,struct sched_param *restrict param);
int pthread_attr_setschedparam(pthread_attr_t *restrict attr,const struct sched_param *restrict param);
函数功能:
这两个函数分别用来设置和得到线程的调度参数。
这两个函数具有两个参数,第1个参数是指向属性对象的指针,第2个参数是sched_param结构或指向该结构的指针。
结构sched_param的子成员sched_priority控制一个优先权值,大的优先权值对应高的优先权。系统支持的最大和最小优先权值可以用sched_get_priority_max函数和sched_get_priority_min函数分别得到。
注意:如果不是编写实时程序,不建议修改线程的优先级。因为,调度策略是一件非常复杂的事情,如果不正确使用会导致程序错误,从而导致死锁等问题。如:在多线程应用程序中为线程设置不同的优先级别,有可能因为共享资源而导致优先级倒置。
6) 线程属性的堆栈大小
函数原型:
#include <pthread.h>
int pthread_attr_getstacksize(const pthread_attr_t *restrict attr,size_t *restrict stacksize);
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
函数功能:
这两个函数用于获取和设置堆栈大小属性。stacksize属性定义系统分配的栈大小(以字节为单位),size不应小于系统定义的最小栈大小。
7) 线程属性的堆栈地址
函数原型:
#include <pthread.h>
int pthread_attr_getscope(const pthread_attr_t * attr, int * scope );
int pthread_attr_setscope(pthread_attr_t*, int scope);
函数功能:
这两个函数分别用来设置和得到线程的作用域。attr是线程属性变量,scope是线程的作用域。若成功返回0,若失败返回-1。作用域控制线程是否在进程内或在系统级上竞争资源,可能的值是:PTHREAD_SCOPE_PROCESS(进程内竞争资源)、PTHREAD_SCOPE_SYSTEM(系统级竞争资源)。
16.2线程
1) 线程的创建
函数原型:
#include <pthread.h>
int pthread_create(pthread_t *restrict thread,const pthread_attr_t *restrict attr,void *(*start_routine)(void*), void *restrict arg);
函数功能:
pthread_create是创建线程函数。若成功则返回0,否则返回出错编号。thread为指向线程标识符的指针。attr用来设置线程属性。第三个参数是线程运行函数的起始地址。arg是运行函数的参数。
2) 线程的终结
函数原型:
#include <pthread.h>
void pthread_exit(void *value_ptr);
函数功能:
使用函数pthread_exit()退出线程,这是线程的主动行为。
3) 线程的分离
函数原型:
#include <pthread.h>
int pthread_detach(pthread_t thread);
函数功能:
该函数将子线程的状态设置为分离的,则该线程运行结束后会自动释放所有资源。
4) 等待线程结束
函数原型:
#include <pthread.h>
int pthread_join(pthread_t thread, void **value_ptr);
函数功能:
pthread_join()使一个线程等待另一个线程结束。代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。
5) 获得线程自身ID
函数原型:
#include <pthread.h>
pthread_t pthread_self(void);
函数功能:
该函数获得线程自身的ID。pthread_t的类型为unsigned long int,所以在打印的时候要使用%lu方式,否则将产生奇怪的结果。
6) 比较线程ID
函数原型:
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
函数功能:
比较两个线程的ID是否一样,如果一样,返回非0,否则返回0。
7) 动态包比较
函数原型:
#include <pthread.h>
int pthread_once(pthread_once_t *once_control,void (*init_routine)(void));
pthread_once_t once_control = PTHREAD_ONCE_INIT;
函数功能:
该函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。
8) 获取/设置动态线程调度参数
函数原型:
#include <pthread.h>
int pthread_getschedparam(pthread_t thread, int *restrict policy,struct sched_param *restrict param);
int pthread_setschedparam(pthread_t thread, int policy,const struct sched_param *param);
函数功能:
前面的那些函数pthread_attr_setschedparam()与pthread_attr_getschedparam()只能通过线程的属性对象pthread_attr_t来设置线程的调度策略和优先级,不能够直接设置正在运行的线程的调度策略和优先级,而这两个函数可以直接设置。
注意,当pthread_setschedparam()函数的参数 policy == SCHED_RR 或者 SCHED_FIFO 时,程序必须要在超级用户下运行。
16.3进程原语与线程原语的比较
进程原语 |
线程原语 |
描述 |
fork |
pthread_create |
创建新的控制流 |
exit |
pthread_exit |
从现有的控制流中退出 |
waitpid |
pthread_join |
从控制流中得到退出状态 |
atexit |
pthread_cleanup_push |
注册在退出控制流时调用的函数 |
getpid |
pthread_self |
获取控制流的ID |
abort |
pthread_cancel |
请求控制流的非正常退出 |
在默认情况下,线程的终止状态会保存到对该线程调用pthread_join(),如果线程已经处于分离状态,线程的底层存储资源可以再线程终止时立即被收回。当线程被分离时,并不能用pthread_join()函数等待它的终止状态。对分离状态的线程进行pthread_join()的调用可以产生失败,返回EINVAL。pthread_detach()调用可以用于是线程进入分离状态。[apue]
十七 条件变量
17.1条件变量
条件变量时线程可用的另一种同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。
17.2条件变量属性
1) 属性初始化与删除
函数原型:
#include <pthread.h>
int pthread_condattr_destroy(pthread_condattr_t *attr);
int pthread_condattr_init(pthread_condattr_t *attr);
函数功能:
这两个函数用于创建和销毁条件变量属性对象。若成功返回0,若失败返回错误编号。一旦某个条件变量对象被初始化了,我们就可以利用下面函数来查看或修改特定属性了。
2) 进程共享条件变量属性
函数原型:
#include <pthread.h>
int pthread_condattr_getpshared(const pthread_condattr_t *restrict attr,int *restrict pshared);
int pthread_condattr_setpshared(pthread_condattr_t *attr,int pshared);
函数功能:
这两个函数用来获得或者设置进程共享条件变量属性。
17.3条件变量
1) 初始化与删除
函数原型:
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
函数功能:
条件变量使用之前必须首先进行初始化,pthread_cond_t数据类型代表的条件变量可以用两种方式进行初始化,可以把常量PTHREAD_COND_INITIALIZER赋给静态变量分配的条件变量,但是如果条件变量时动态分配的,可以使用pthread_cond_init函数进行初始化。在释放底层的内存空间之前,可以使用pthread_cond_destroy()函数对条件变量进行去除初始化。
2) 信号量与广播
函数原型:
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
函数功能:
pthread_cond_signal()作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。应该在互斥量被锁定后调用。若不止一个线程阻塞在条件变量上,则应用pthread_cond_broadcast()向其它所以线程发生信号
3) 等待(阻塞)
函数原型:
#include <pthread.h>
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
函数功能:
使用pthread_cond_wait()等待条件变为真,如果再给定的时间内条件不能满足,那么会生成一个代表出错码的返回变量。传递给pthread_cond_wait()的互斥量对条件进行保护,调用者把锁住的互斥量传给函数,函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作室原子操作。这样就关闭了条件检查的线程和线程进入休眠状态等待条件改变着两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wait()返回时,互斥量再次被锁住。
pthread_cond_timedwait()函数的工作方式与pthread_cond_wait()相似,只是多了一个timeout。timeout值指定了等待等待的时间。
十八 附录
18.1UNIX系统所需要的头文件
Posix标准C头文件(24项)
<assert.h> ---------------------- 验证程序断言
<complex.h> ---------------------- 支持复数算术运算
<ctype.h> ---------------------- 字符类型
<errno.h> ---------------------- 出错码
<fenv.h> ---------------------- 浮点环境
<float.h> ---------------------- 浮点常量
<inttypes.h> ---------------------- 整型格式转换
<iso646.h> ---------------------- 替代关系操作符宏
<limits.h> ---------------------- 实现常量
<locale.h> ---------------------- 局部类别
<math.h> ---------------------- 数学常量
<setjmp.h> ---------------------- 非局部goto
<signal.h> ---------------------- 信号
<stdarg.h> ---------------------- 可变参数表
<stdbool.h> ---------------------- 布尔类型和值
<stddef.h> ---------------------- 标准定义
<stdint.h> ---------------------- 整型
<stdio.h> ---------------------- 标准I/O库
<stdlib.h> ---------------------- 实用程序库函数
<string.h> ---------------------- 字符串操作
<tgmath.h> ---------------------- 通用类型数学宏
<time.h> ---------------------- 时间和日期
<wchar.h> ---------------------- 扩展的多字节和宽字符支持
<wctype.h> ---------------------- 宽字符分类和映射支持
POSIX标准定义的基本头文件(26项)
<dirent.h> ---------------------- 目录项
<fcntl.h> ---------------------- 文件控制
<fnmatch.h> ---------------------- 文件名匹配类型
<glob.h> ---------------------- 路径名模式匹配类型
<grp.h> ---------------------- 组文件
<netdb.h> ---------------------- 网络数据库操作
<pwd.h> ---------------------- 口令文件
<regex.h> ---------------------- 正则表达式
<tar.h> ---------------------- tar归档值
<termios.h> ---------------------- 终端I/O
<unistd.h> ---------------------- 符号常量
<utime.h> ---------------------- 文件时间
<wordexp.h> ---------------------- 字扩展类型
<arpa/inet.h> ---------------------- Internet定义
<net/if.h> ---------------------- 套接字本地接口
<netinet/in.h> ---------------------- Internet地址族
<netinet/tcp.h> ---------------------- 传输控制协议定义
<sys/mman.h> ---------------------- 内存管理声明
<sys/select.h> ---------------------- select函数
<sys/socket.h> ---------------------- 套接字接口
<sys/stat.h> ---------------------- 文件状态
<sys/times.h> ---------------------- 进程时间
<sys/types.h> ---------------------- 基本系统数据类型
<sys/un.h> ---------------------- UNIX域套接字定义
<sys/utsname.h> ---------------------- 系统名
<sys/wait.h> ---------------------- 进程控制
POSIX标准定义的XSI扩展头文件(26项)
<cpio.h> ---------------------- cpio归档值
<dlfcn.h> ---------------------- 动态链接
<fmtmsg.h> ---------------------- 消息显示结构
<ftw.h> ---------------------- 文件树漫游
<iconv.h> ---------------------- 代码集转换实用程序
<langinfo.h> ---------------------- 语言信息常量
<libgen.h> ---------------------- 模式匹配函数定义
<monetary.h> ---------------------- 货币类型
<ndbm.h> ---------------------- 数据库操作
<nl_types.h> ---------------------- 消息类别
<poll.h> ---------------------- 轮询函数
<search.h> ---------------------- 搜索表
<strings.h> ---------------------- 字符串操作
<syslog.h> ---------------------- 系统出错日志记录
<ucontext.h> ---------------------- 用户上下文
<ulimit.h> ---------------------- 用户限制
<utmpx.h> ---------------------- 用户帐户数据库
<sys/ipc.h> ---------------------- IPC
<sys/msg.h> ---------------------- 消息队列
<sys/resource.h> ------------------- 资源操作
<sys/sem.h> ---------------------- 信号量
<sys/shm.h> ---------------------- 共享存储
<sys/statvfs.h> ---------------------- 文件系统信息
<sys/time.h> ---------------------- 时间类型
<sys/timeb.h> ---------------------- 附加的日期和时间定义
<sys/uio.h> ---------------------- 矢量I/O操作
POSIX标准定义的可选头文件(8项)
<aio.h> ---------------------- 异步I/O
<mqueue.h> ---------------------- 消息队列
<pthread.h> ---------------------- 线程
<sched.h> ---------------------- 执行调度
<semaphore.h> ---------------------- 信号量
<spawn.h> ---------------------- 实时spawn接口
<stropts.h> ---------------------- XSI STREAMS接口
<trace.h> ---------------------- 时间跟踪
---------------------------------------------------------------------------------------------------
参考文献
[1] W.Richard Stevens, Stephen A.Rago. UNIX环境高级编程(第2版).
[2] David R.Butenhof. POSIX多线程程序设计
[3] the User’s Guide for the POSIX API support provided in RTEMS
[4] Standard for Information Technology–Portable Operating System Interface(POSIX®)
[5] http://www.cnblogs.com/mywolrd/archive/2009/02/05/1930707.html#phtread_ref
[6] http://www.cnblogs.com/jishume/articles/2216054.html
[7] http://www.ibm.com/developerworks/cn/linux/thread/posix_thread1/index.html#icomments