四:系统编程函数
1. fork() :创建进程函数
【原型】 pid_t fork(void);
【头文件】 #include <unistd.h>
【功能】 创建一个新的进程
【参数】 无
【返回值】 pid_t(进程PID号的数据类型)
成功:
父进程: 返回子进程的PID号
子进程: 0
失败:
父进程: -1
不会创建出子进程
2. vfork() :创建进程函数
【原型】 pid_t fork(void);
【头文件】 #include <unistd.h>
【功能】 创建一个新的进程
【参数】 无
【返回值】 pid_t(进程PID号的数据类型)
成功:
父进程: 返回子进程的PID号
子进程: 0
失败:
父进程: -1
不会创建出子进程
======================fork()和vfork()的差异对比========================================
fork() 与vfock() 都是创建一个进程,那他们有什么区别呢?总结有以下三点区别:
1. fork():子进程拷贝父进程的数据段,代码段
vfork():子进程与父进程共享数据段
2. fork() 父子进程的执行次序不确定
vfork 保证子进程先运行,在调用exec 或exit 之前与父进程数据是共享的,在它调用exec
或exit 之后父进程才可能被调度运行。
3. vfork()保证子进程先运行,在她调用exec 或exit 之后父进程才可能被调度运行。如果在
调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。
=======================================================================================
代码演示:
————————————————————————————————————————————————————————
#include
#include
int main()
{
printf("下面创建一个进程\n");
pid_t x = fork();
if(x == 0)
{
printf("1.I am children!\n");
}
if(x > 0 )
{
printf("2.I am parent!\n");
}
return 0;
}
函数注意点:
1.fork函数之后在两个不同的进程之中返回的值不一样,父进程返回子进程的PID,子进程返回0.
2.fork之后两个进程里面的数据不会因为互相干扰,即使是全局变量也不会被干扰,毫不相干。
————————————————————————————————————————————————————————
3. getpid()/getppid()查看自身与父进程的PID号
(1)getpid() : 查看自身的PID
(2)getppid():查看父进程的PID
【头文件】
#include
#include
【原型】
pid_t getpid(void);
pid_t getppid(void);
【功能】 返回PID号
注意:这两个函数不会执行失败,只会成功
代码演示:
————————————————————————————————————————————————————————
#include
#include
#include
int main()
{
printf("下面创建一个进程\n");
pid_t x = fork();
if(x == 0)
{
printf("1.I am children!---> My ID --> %d,My parent ID --> %d\n",getpid(),getppid());
}
if(x > 0 )
{
printf("2.I am parent!---->My children PID --> %d\n",x);
}
return 0;
}
————————————————————————————————————————————————————————
4. wait() 回收子进程的资源
【原型】 pid_t wait(int *status); ---> 一直阻塞到子进程退出
【头文件】
#include
#include
【功能】 回收子进程的资源
【参数】 status:监听的子进程退出状态的指针变量
如果status为NULL,代表wait函数不关注子进程的退出状态。
【返回值】
成功: 监听的子进程退出,返回该子进程的PID号
失败: -1
代码演示:
————————————————————————————————————————————————————————
#include
#include
#include
#include
#include
int main()
{
printf("下面创建一个进程\n");
pid_t x = fork();
if(x == 0)
{
printf("1.I am children!\n");
exit(9);
}
if(x > 0 )
{
sleep(1);
int status;
if(wait(&status)!= -1)
printf("My children ID:%d\n",status);
printf("2.I am parent!\n");
}
return 0;
}
函数注意点:
wait里面的参数:status不仅仅是存放退出值的,还有其它信息,
退出值仅仅存放在status这个内存里面最后八位上,status是一个整形指针,共32位,其他24位是存放其他信息的,所以
exit返回的值被status打印出来的东西不一定是其退出值,还包括其他信息。
那么这个其他信息是些什么呢?包括是否是正常退出,异常退出,是否被信号杀死,是哪个信号呀之类的,其他24位就是存放它的。
那么这么多状态信息,24位可能不够存之类的,那么可以通过宏定义来。
比如:要获得最低的一个字节:就是退出值,那么需要一个宏定义
WEXITSTATUS(status)就是获取退出值的。
————————————————————————————————————————————————————————
5.waitpid() 指定等待进程函数
【原型】 waitpid: pid_t waitpid(pid_t pid, int *status, int options);
【头文件】
#include
#include
【功能】 指定某个等待某个进程。
【参数】
(1)pid:
pid < -1 : 等待一个进程组的指定的子进程(以绝对值来计算)
pid == -1 : 等待任何的子进程
pid == 0 : 等待一个进程组中任何的子进程
pid > 0 : 指定等待对应PID号的子进程
(2) options:
options == 0: 选项无效 ---> 阻塞(等价于wait)
options == WNOHANG 监听子进程的退出信息,非阻塞等待
options == WUNTRACED 监听子进程的暂停信号,阻塞等待
options == WCONTINUED 监听子进程的继续信号,阻塞等待
status: 子进程的退出状态
代码演示:
————————————————————————————————————————————————————————
#include
#include
#include
#include
#include
int main()
{
printf("下面创建一个进程\n");
pid_t x = fork();
if(x == 0)
{
printf("1.I am children!\n");
exit(9);
}
if(x > 0 )
{
sleep(1);
int status,options;
waitpid(x,&status,WNOHANG);
printf("等待pid为 %d ,状态为:%d 的非阻塞等待\n",x,status);
printf("2.I am parent!\n");
}
return 0;
}
函数注意点:
等价于
wait(&status) <----------------> waitpid(-1, &status, 0);
————————————————————————————————————————————————————————
6. exit(),_exit() 进程的退出
【函数手册】 man 3 exit(),man 2 _exit()
【exit头文件】 #include <stdlib.h>
【_exit头文件】#include <unistd.h>
【exit函数原型】 void exit(int status); ---> 在退出之前会刷新缓冲区内容
【_exit函数原型】void _exit(int status); ---> 直接退出,不刷新缓冲区
【参数】 status: 0 ---> 正常退出
非0 --> 不正常退出
【返回值】 无
代码演示:
————————————————————————————————————————————————————————
#include
#include
#include
#include
#include
int main()
{
printf("下面创建一个进程\n");
pid_t x = fork();
if(x == 0)
{
printf("1.I am children!\n");
printf("wo shi er zi ");
_exit(0);
}
if(x > 0 )
{
sleep(1);
wait(NULL);
printf("2.I am parent!\n");
}
return 0;
}
函数注意点:
exit函数是会清理IO缓冲,也就是说printf()里面要打印的东西如果不用\n那么这个函数调用后也不会在屏幕上显示,
因为它清理了IO缓冲。但是_exit函数是不会去清理IO缓冲区的,如果用_exit函数,那么就不会打印了。
————————————————————————————————————————————————————————
7.exec函数族
1. 作用
把你给任务覆盖到本进程中,执行了exec函数后,后面的代码都会被屏蔽掉。
./test ./test_sh
父进程去执行某些程序 ,子进程去执行另外程序
【头文件】 #include <unistd.h>
extern char **environ;
【函数原型】
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
【函数参数】
path/file : 要执行的程序的路径 ls ---> /bin/ls
arg:依次写入参数,以NULL作为结束标志 "./jpeg_show","xxx.jpg",NULL
argv:把全部的参数都写入argv数组中 char *A[] = {"ls","-l","NULL"}
envp:指定环境变量的路径
代码演示:
————————————————————————————————————————————————————————
#include
#include
#include
#include
#include
int main()
{
printf("下面创建一个进程\n");
pid_t x = fork();
if(x == 0)
{
printf("1.I am children!\n");
}
if(x > 0 )
{
sleep(1);
wait(NULL);
printf("2.I am parent!\n");
}
return 0;
}
————————————————————————————————————————————————————————
函数注意点:
1. p是代表环境变量。函数名字母p意味着环境变量,就是说用环境变量来查找ls这个程序。
2.如果用execl来呈现相同的效果的话,则需要用绝对路径。
3.这些函数总是配合fork函数一起使用的。
8.pipe() 创建无名管道
【原型】int pipe(int pipefd[2]);
【头文件】 #include <unistd.h>
【参数】 pipefd[0] : 读端 pipefd[1] : 写端
【返回值】 成功:0 失败:-1
代码演示:
————————————————————————————————————————————————————————
#include
#include
#include
#include
#include
int main()
{
int fd[2];
int ret = pipe(fd);
if(ret == -1)
{
printf("create pipe error!\n");
}
pid_t x = fork();
if( x > 0)
{
close(fd[1]);
char buf[50] = {0};
read(fd[0],buf,sizeof(buf));
printf("from child process buf :%s \n",buf);
wait(NULL);
}
if(x == 0)
{
close(fd[0]);
write(fd[1],"what!",5);
exit(0);
}
return 0;
}
————————————————————————————————————————————————————————
函数注意点:
1.pipe创建管道时候不能再fork之后,也就是说管道的创建必须要在其子进程创建之前。
2.无名管道是一对一的关系,只能在亲缘进程之间进行通话。而且是少量 数据传输。
3.pipe里面的读端和写端是要经过内核kernel 进行转接数据的。
4.无名管道不保证写入数据的原子性。
8. mkfifo() :创建有名管道
【函数手册】 man 3 mkfifo
【原型】int mkfifo(const char *pathname, mode_t mode);
【头文件】 #include <sys/types.h>
#include
【参数】 pathname:有名管道的文件名字
mode:管道文件的权限 如:0777
【返回值】 成功: 0 管道文件存在于文件系统中
失败: -1
代码演示:jack进程给 Rose进程发送信息
jack代码
————————————————————————————————————————————————————————
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
if(access("/mnt/myfifo",F_OK))
{
int ret = mkfifo("/mnt/myfifo",0777);
if(ret != 0 )
{
printf("create myfifo file error!\n");
return 0;
}
}
int fd = open("/mnt/myfifo",O_RDWR);
if(fd == -1)
{
printf("open this file error!\n");
return 0;
}
char buf[50];
while(1)
{
bzero(buf,50);
printf("My say:");
fgets(buf,50,stdin);
write(fd,buf,strlen(buf));
if(strncmp(buf,"quit",4) == 0)
{
close(fd);
break;
}
}
return 0;
}
————————————————————————————————————————————————————————
Rose代码
————————————————————————————————————————————————————————
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
if(access("/mnt/myfifo",F_OK))
{
int ret = mkfifo("/mnt/myfifo",0777);
if(ret != 0)
{
printf("create myfifo error!\n");
return 0;
}
}
int fd = open("/mnt/myfifo",O_RDWR);
if(fd == -1)
{
printf("open this file error!\n");
return 0;
}
char buf[50] = {0};
while(1)
{
bzero(buf,50);
read(fd,buf,sizeof(buf));
printf("jack say:%s",buf);
if(strncmp(buf,"quit",4) == 0)
{
close(fd);
break;
}
}
return 0;
}
————————————————————————————————————————————————————————
函数注意点:
1. access() 判断文件是否存在函数
【函数手册】 man 3 access
【头文件】 #include <unistd.h>
【函数原型】 int access(const char *path, int amode);
【参数】 path: 需要检查的文件的路径
amode:检查的选项
R_OK:可读
W_OK:可写
X_OK:可执行
F_OK:是否存在
【返回值】: 成功:0
失败:-1
9. kill()函数发送信号
【函数手册】 man 2 kill
【原型】 int kill(pid_t pid, int sig);
【头文件】 #include <sys/types.h>
#include
【参数】 pid:给哪个进程号发送信号
sig:发送什么信号
【返回值】 成功:0 失败:-1
代码演示:
————————————————————————————————————————————————————————
#include
#include
#include
#include
#include
void fun()
{
int a = 5,b=10;
int c = a + b;
printf("a+b = %d\n",c);
}
int main()
{
pid_t x = fork();
if(x > 0 )
{
sleep(3);
kill(x,SIGUSR1);
wait(NULL);
}
if(x == 0)
{
signal(SIGUSR1,fun);
pause();
exit(0);
}
return 0;
}
————————————————————————————————————————————————————————
10. signal() 函数捕捉信号
【函数手册】man 2 signal
【头文件】 #include <signal.h>
【函数原型】 sighandler_t signal(int signum, sighandler_t handler);
【函数参数】
signum:捕捉的信号名
handler:响应信号的函数
SIG_IGN: 收到捕捉的信号时,忽略该信号
SIG_DFL: 缺省(默认动作)
代码演示:看9
11. pause()把本进程挂起,直到收到一个信号为止
【函数手册】 man 2 pause
【头文件】 #include <unistd.h>
【函数原型】 int pause(void);
代码演示:看9
前言:信号集中包含很多信号,用户可以对信号集进行属性设置,也就是同时对多个信号进行设置。
12. sigemptyset()清空信号集合中的信号
【头文件】 #include <signal.h>
【原型】 int sigemptyset(sigset_t *set);
【功能】 清空信号集合中的信号
【返回值】 成功:0 失败:-1
13. sigfillset()把linux全部信号都加入到set信号集中
【原型】 int sigfillset(sigset_t *set);
【功能】 把linux全部信号都加入到set信号集中
【返回值】 成功:0 失败:-1
14. sigaddset() 添加信号
【原型】 int sigaddset(sigset_t *set, int signum);
【功能】 把signum这个信号添加到set信号集中
【返回值】 成功:0 失败:-1
15. sigismember() 判断信号的存在
【原型】 int sigismember(const sigset_t *set, int signum);
【功能】 判断signum信号是否在set信号集中
【返回值】 在信号集中:1 不在信号集中:0 函数执行失败:-1
【参数】 set: 信号集变量 ---> sigset_t(信号集的数据类型)
signum: 信号
16. sigprocmask() 对信号设置阻塞值
【函数原型】int sigprocmask(int how , const sigset_t *restrict set,
sigset_t *restrict oset);
【函数功能】 对信号集设置阻塞或者解除阻塞
【参数】 how:
SIG_BLOCK :设置set信号集为阻塞属性
SIG_SETMASK:新的信号集替换旧的信号集的阻塞信号
SIG_UNBLOCK:解除set信号集的阻塞属性
set:新的信号集
oset:旧的信号集 ,一般为NULL
12-16:代码演示:
————————————————————————————————————————————————————————
#include
#include
#include
#include
#include
#include
#include
void fun()
{
printf("OK\n");
}
int main()
{
pid_t x = fork();
if(x > 0)
{
int ret,i;
signal(SIGINT,fun);
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGINT);
ret = sigismember(&set,SIGINT);
if(ret == 1)
{
printf("SIGINT is in set!\n");
}
sigprocmask(SIG_BLOCK,&set,NULL);
for(i = 10;i>0;i--)
{
printf("%d s\n",i);
sleep(1);
}
sigprocmask(SIG_UNBLOCK,&set,NULL);
wait(NULL);
}
if(x == 0)
{
kill(getppid(),SIGINT);
printf("I send signal to you!\n");
exit(0);
}
return 0;
}
————————————————————————————————————————————————————————
函数注意点:
结论1: 如果发送过来的信号被设置为阻塞响应,那么这个信号不会被丢弃,直到该信号被解除阻塞为止就响应。
结论2: 进程的挂起队列中,相同的信号会被丢弃。
结论3:进程在响应信号时,信号会相互嵌套。
结论4:挂起队列不会被子进程继承,但是信号阻塞属性会被子进程继承。
17. sigdelset() 删除信号
【原型】 int sigdelset(sigset_t *set, int signum);
【功能】 把signum从信号集中删除
【返回值】 成功:0 失败:-1
18. ftok() 为IPC对象申请key值
【函数手册】 man 3 ftok
【原型】 key_t ftok(const char *pathname, int proj_id);
【头文件】 #include <sys/types.h>
#include
【功能】 为IPC对象申请key值
【参数】 pathname: linux中合法的路径
proj_id: 一个整型数 0 - 256
【返回值】 成功: 如果两个参数都一样,那么申请的key值就会一样
失败: -1
注意: 两个进程的所申请的key值一样才能够正常的通信。
代码演示:
————————————————————————————————————————————————————————
#include
#include
#include
int main()
{
key_t key1,key2,key3;
key1 = ftok(".",20);
key2 = ftok(".",20);
key3 = ftok(".",21);
printf("key1 = %d\n",key1);
printf("key2 = %d\n",key2);
printf("key3 = %d\n",key3);
return 0;
}
函数注意点:
1.函数的两个参数若是一样的话,则生成的key值是一样的。
2. linux系统中,IPC对象分为以下几种: 消息队列、共享内存、信号量,使用这几种通信方式,都要向系统中
申请资源(key值) ---> 用于区别不同的IPC对象。
————————————————————————————————————————————————————————
19. msgget() 为消息队列申请ID号
【函数手册】 man 2 msgget
【头文件】 #include <sys/types.h>
#include
#include
【原型】 int msgget(key_t key, int msgflg);
【功能】 为消息队列申请ID号
【参数】 key: 消息队列的key值
msgflg: 消息队列的标志位
IPC_CREAT | 0777 : 不存在则创建 并将权限设置为0777
IPC_EXCL 存在就报错
【返回值】 成功: ID号
失败: -1
20. msgrcv() 接收消息队列的数据
【函数手册】 man 2 msgrcv
【头文件】 #include <sys/types.h>
#include
#include
【原型】 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz,
long msgtyp,int msgflg);
【功能】 接收消息队列的数据
【参数】 msqid : 消息队列的ID号
msgp : 数据缓冲区
msgsz : 想要读取正文的大小
msgtyp : 消息的类型
msgflg : 标志位,普通属性为0
【返回值】 成功:成功读取到的字节数 失败:-1
21. msgsnd() 发送消息到消息队列上
【头文件】 #include <sys/types.h>
#include
#include
【原型】 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
【参数】 msqid : 消息队列的ID号
msgp : 整个消息的数据缓冲区
msgsz : 写入的正文的大小
msgflg : 标志位,普通属性为0
【功能】 发送消息到消息队列上
【返回值】 成功: 0 失败: -1
22. msgctl() 设置消息队列属性
【函数手册】 man 2 msgctl
【头文件】 #include <sys/types.h>
#include
#include
【原型】 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
【功能】 设置消息队列属性
【参数】 msqid: 消息队列的ID号
cmd: 控制的命令字
(1) IPC_STAT :获取该MSG的信息,存储在结构体msqid_ds中
(2) IPC_SET :设置该MSG的信息,存储在结构体msqid_ds中
(3) IPC_RMID :立即删除该MSG,并且唤醒所有阻塞在该MSG上的进程,同时忽略第3个参数
(4) IPC_INFO :获得关于将当前系统中MSG的限制值信息
(5) MSG_INFO :获得关于当前系统中MSG的相关资源消耗信息
(6) MSG_STAT :同IPC_STAT,但msgid为该消息队列在内核中记录所有消息队列的信息的数组
下标,因此通过迭代所有的下标可以获得系统中所有消息队列的相关信息
buf: 相关信息结构体缓冲区,不需要填该参数时--> NULL
【返回值】 成功: (1) IPC_STAT,IPC_SET,IPC_RMID : 0
(2) IPC_INFO,MSG_INFO :内核中记录所有消息队列信息的数组的下标最大值
(3) MSG_STAT :返回消息队列的ID
失败: -1
【备注】
(1) IPC_STAT获得的属性信息被存放在以下结构体中:
struct msqid_ds
{
struct ipc_perm msg_perm;
time_t msg_stime;
time_t msg_rtime;
time_t msg_ctime;
unsigned long _msg_cbytes
msgqnum_t msg_qnum;
msglen_t msg_qbytes;
pid_t msg_lspid;
pid_t msg_lrpid;
};
其中,权限相关的信息用如下结构体来表示:
struct ipc_perm
{
key_t _key;
uid_t uid;
gid_t gid;
uid_t cuid;
gid_t cgid;
unsigned short mode;
unsigned short _seq;
};
(2) 当使用IPC_INFO时,需要定义一个如下结构体来获取系统关于消息队列的限制值信息,并将这个
结构体指针强制类型转化为第3个参数的类型
struct msginfo
{
int msgpool;
int msgmap;
int msgmax;
int msgmnb;
int msgmni;
int msgssz;
int msgtql;
unsigned short int msgseg;
};
(3) 当使用选项MSG_INFO时,与IPC_INFO一样也是获得了一个msginfo结构体信息
但有如下几点不同:
<1>成员msgpool记录的是系统当前存在的MSG的个数总和
<2>成员msgmap记录的是系统当前所有MSG中的消息个数总和。
<3>成员msgtql记录的是系统当前所有MSG中所有消息的所有字节数总和。
18-22代码演示:
——————————————————————————————————————————————————————
发送端Jack代码:
#include
#include
#include
#include
#include
#include
struct msg_buf
{
long mtype;
char mtext[50];
};
int main()
{
key_t key;
int msgid,ret;
struct msg_buf gec;
key = ftok(".",10);
printf("key = %x\n",key);
msgid = msgget(key,IPC_CREAT | 0777);
printf("msgid = %d\n",msgid);
while(1)
{
bzero(gec.mtext,50);
gec.mtype = 3;
fgets(gec.mtext,50,stdin);
ret = msgsnd(msgid,&gec,strlen(gec.mtext),0);
if(ret == -1)
{
printf("msgsnd error!\n");
exit(1);
}
}
msgctl(msgid,IPC_RMID,NULL);
}
接收端代码Rose:
#include
#include
#include
#include
#include
#include
struct msg_buf
{
long mtype;
char mtext[50];
};
int main()
{
key_t key;
int msgid,ret;
struct msg_buf gec;
key = ftok(".",10);
printf("key = %x\n",key);
msgid = msgget(key,IPC_CREAT | 0777);
printf("msgid = %d\n",msgid);
printf("buf:%s\n",gec.mtext);
while(1)
{
bzero(gec.mtext,50);
ret = msgrcv(msgid,&gec,sizeof(gec.mtext),3,0);
if(ret == -1)
{
printf("msgrcv error!\n");
exit(1);
}
printf("Jact --> Rose:%s\n",gec.mtext);
}
msgctl(msgid,IPC_RMID,NULL);
return 0;
}
——————————————————————————————————————————————————————
23. shmget() 获取共享内存的ID号
【函数手册】 man 2 shmget
【头文件】 #include <sys/ipc.h>
#include
【函数原型】 int shmget(key_t key, size_t size ,
int shmflg);
【函数功能】 获取共享内存的ID号
【函数参数】 key : key值
size : 共享内存尺寸,是4096的整数倍 页的大小:4096
shmflg :
IPC_CREAT :如果key对应的共享内存不存在,则创建
IPC_EXCL :如果该key对应的共享内存已存在,则报错
SHM_HUGETLB:使用"大页面"来分配共享内存
SHM_NORESERVE: 不在交换分区中为这块共享内存保留空间
mode :共享内存的访问权限 (八进制,如0644)
【返回值】 成功: ID号 失败: -1
【备注】 如果key指定为IPC_PRIVATE :则会自动产生一个随机未用的新键值
24. shmat() 映射共享内存的地址
【函数手册】 man 2 shmat
【头文件】 #include <sys/types.h>
#include
【函数原型】 void *shmat(int shmid, const void *shmaddr, int shmflg);
【函数功能】 映射共享内存的地址
【函数参数】 shmid: 共享内存的ID号
shmaddr:
NULL:系统自动分配未使用过的内存空间作为共享内存
不为NULL:手动分配内存地址
shmflg: 标志位,普通属性填0
【返回值】 成功:指向共享内存首地址的指针
失败:(void *)-1
25. shmdt() 解除映射
【函数手册】 man 2 shmdt
【函数原型】 int shmdt(const void *shmaddr);
【函数参数】 shmaddr:需要解除映射的内存的指针
【返回值】 成功:0 失败:-1
26. shmctl()设置共享内存的属性
【函数手册】 man 2 shmctl
【头文件】 #include <sys/ipc.h>
#include
【函数原型】 int shmctl(int shmid, int cmd , struct shmid_ds *buf);
【函数参数】 (1) shmid : 共享内存的ID号
(2) cmd :
IPC_STAT 获取属性信息,放到buf里面
IPC_SET 设置属性信息为buf指向的内容
IPC_RMID 将共享内存标记为 "即将被删除"状态
IPC_INFO 获得关于共享内存的系统限制值信息
SHM_INFO 获得系统为共享内存消耗的资源信息
SHM_STAT 同IPC_STAT,但shmid为该SHM在内核中记录所有SHM信息的数组的下标
因此通过迭代所有的下标可以获得系统中所有SHM的相关消息
SHM_LOCK 禁止系统将该SHM交换至swap分区
SHM_UNLOCK 允许系统将该SHM交换至swap分区
(3) buf 属性信息结构体指针
【返回值】 成功: (1)IPC_INFO,SHM_INFO:内核中记录所有SHM信息的数组的下标最大值
(2)SHM_STAT:下标值为shmid的SHM的ID
失败 -1
【备注】
(1) IPC_STAT获得的属性信息被存放在以下结构体中:
struct shmid_ds
{
struct ipc_perm shm_perm;
size_t shm_segsz;
time_t shm_atime;
time_t shm_dtime;
time_t shm_ctime;
pid_t shm_cpid;
pid_t shm_lpid;
shmatt_t shm_nattch;
};
其权限信息结构体如下:
struct ipc_perm
{
key_t _key;
uid_t uid;
gid_t gid;
uid_t cuid;
gid_t cgid;
unsigned short mode;
unsigned short _seq;
};
(2) 当使用IPC_RMID后,上述结构体 struct ipc_perm 中的成员mode
将可以检测SHM_DEST 但SHM并不会被真正删除,要等到shm_nattch等于0时才会被真正删除。
IPC_RMID 只是为了删除做准备,而不是立即删除。
(3)当使用IPC_INFO时,需要定义一个如下结构体来获取系统关于共享内存的限制值信息,并且
将这个结构体指针强制类型转化为第3个参数的类型
struct shminfo
{
unsigned long shmmax;
unsigned long shmmin;
unsigned long shmmni;
unsigned long shmseg;
unsigned long shmall;
};
(4) 使用选项SHM_INFO时,必须保证宏_GNU_SOURCE有效,获得的相关信息被存放在如下结构体中:
struct shm_info
{
int used_ids;
unsigned long shm_tot;
unsigned long shm_rss;
unsigned long shm_swp;
unsigned long swap_attempts;
unsigned long swap_successes;
};
(5) 注意:选项SHM_LOCK不是锁定读/写权限,而是锁定SHM能否与swap分区发生交换,一个SHM被交换至
swap分区后,如果被设置了SHM_LOCK,那么任何访问这个SHM的进程都将会遇到页错误。
进程可以通过IPC_STAT后得到的mode未检测SHM_LOCKED信息。
23-26代码演示:
#include
#include
#include
#include
#include
#include
#include
int main()
{
key_t key = ftok(".",10);
int shmid = shmget(key,4096,IPC_CREAT| 0777);
char *p = (char *)shmat(shmid,NULL,0);
while(1)
{
fgets(p,4096,stdin);
if(strncmp(p,"quit",4)==0)
break;
}
return 0;
}
——————————————————————————————————————————————————————
#include
#include
#include
#include
#include
#include
#include <
int main()
{
key_t key = ftok(".",10);
int shmid = shmget(key,4096,IPC_CREAT | 0777);
char *p = (char *)shmat(shmid,NULL,0);
while(1)
{
bzero(p);
printf("from shm :%s",p);
usleep(500000);
if(strncmp(p,"quit",4) == 0)
break;
}
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
———————————————————————————————————————————————————————————
27.semget()获取信号量的ID
【函数手册】 man 2 semget
【头文件】 #include <sys/types.h>
#include
#include
【函数原型】 int semget(key_t key, int nsems , int semflg ,IPC_CREAT|0777 ,IPC_EXCL);
【函数功能】 获取信号量的ID
【函数参数】 key: key值
nsems: 信号量元素的个数
semflg: 标志位
IPC_CREAT :如果key对应的信号量不存在,则创建
IPC_EXCL :如果该key对应的信号量已存在,报错
mode :信号量的访问权限(八进制)
【返回值】 成功: 信号量的ID 失败: -1
28.semop() P/V操作
【函数手册】 man 2 semop
【头文件】 #include <sys/types.h>
#include
#include
【函数原型】 int semop( int semid, struct sembuf *sops, unsigned nsops);
【函数参数】 struct sembuf
【参数】 semid 信号量ID
sops 信号量操作结构体数组
nsops 结构体数组元素个数
【返回值】 成功 0 失败 -1
【备注】 使用以上函数接口需要注意以下几点:
(1) 信号量操作结构体的定义如下:
struct sembuf
{
unsigned short sem_num;
short sem_op;
short sem_flg;
};
(2) sem_op根据其数值,信号量操作分成3种情况:
(1) sem_op > 0 :V操作( 信号量元素的值将被加上sem_op的值);
(2) sem_op = 0 : 等零操作(如果信号量元素的值为0,则成功返回);
(3) sem_op < 0 : P操作,信号量元素将被减去sem_op的绝对值
29. semctl() 设置信号量的属性
【函数手册】 man 2 semctl()
【头文件】 #include <sys/types.h>
#include
#include
【函数原型】 int semctl(int semid, int semnum, int cmd,IPC_RMID, SETVAL , ...);
【参数】 (1) semid 信号量ID
(2) semnum 信号量元素序号(数组下标)
(3) cmd
<1> IPC_STAT 获取属性信息
<2> IPC_SET 设置属性信息
<3> IPC_RMID 立即删除该信号量,参数semnum将被忽略
<4> IPC_INFO 获得关于信号量的系统限制值信息
<5> SEM_INFO 获得系统为共享内存消耗的资源信息
<6> SEM_STAT 同IPC_STAT,但shmid为该SEM在内核中记录所有SEM信息的数组
的下标,因此通过迭代所有的下标可以获得系统中所有SEM的相信息。
<7> GETALL 返回所有信号量元素的值,参数semnum将被忽略。
<8> GETNCNT 返回正阻塞在对该信号量元素P操作的进程总数
<9> GETPID 返回最后一个对该信号量元素等零操作的进程总数
<10> GETVAL 返回该信号量元素的值
<11> GETZCNT 返回正阻塞在对该信号量元素等零操作的进程总数
<12> SETALL 设置所有信号量元素的值,参数semnum将被忽略
<13> SETVAL 设置该信号量元素的值
【返回值】 成功 (1) GETNCNT :semncnt
(2) GETPID :sempid
(3) GETVAL :semval
(4) GETZCNT :semzcnt
(5) IPC_INFO 内核中记录所有SEM信息的数组的下标最大值
(6) SEM_INFO 同IPC_INFO
(7) SEM_STAT :内核中记录所有SEM信息的数组,下标为semid的信号量的ID
(8) 其他 : 0
失败 :-1
【备注】
(1) 这是一个变参函数,根据cmd的不同,可能需要第4个参数,第4个参数是一个如下所示的联合体:
用户必须自己定义:
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};
(2) 使用 IPC_STAT 和IPC_SET需要用到以下属性结构体
struct semid_ds
{
struct ipc_perm sem_perm;
time_t sem_otime;
time_t sem_ctime;
unsigned short sem_nsems;
};
<1>权限结构体如下:
struct ipc_perm
{
key_t _key;
uid_t uid;
gid_t gid;
uid_t cuid;
gid_t cgid;
unsigned short mode;
unsigned short _seq;
};
(3) 使用IPC_INFO时,需要提供以下结构体
struct seminfo
{
int semmap;
int semmni;
int semmns;
int semmnu;
int semmsl;
int semopm;
int semume;
int semusz;
int semvmx;
int semaem;
};
(4) 使用SEM_INFO 时,与IPC_INFO一样都是得到一个seminfo结构体,但其中几个成员含义发生了变化:
<1> semusz此时代表系统当前存在的信号量的个数
<2> semaem此时代表系统当前存在的信号量中信号量元素的总数
27-29函数代码实例:
(1)发送方:
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
struct sembuf space;
struct sembuf data;
space.sem_num = 0;
space.sem_op = -1;
space.sem_flg = 0;
data.sem_num = 1;
data.sem_op = 1;
data.sem_flg = 0;
key_t key1 = ftok(".",10);
key_t key2 = ftok(".",15);
int shmid = shmget(key1,4096,IPC_CREAT|0777);
int semid = semget(key2,2,IPC_CREAT|0777);
char *p = (char *)shmat(shmid,NULL,0);
semctl(semid,0,SETVAL,1);
semctl(semid,1,SETVAL,0);
while(1)
{
semop(semid,&space,1);
fgets(p,4096,stdin);
semop(semid,&data,1);
if(strncmp(p,"quit",4) == 0)
break;
}
return 0;
}
———————————————————————————————————————————————————————————
(2)接收方:
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
struct sembuf space;
struct sembuf data;
space.sem_num = 0;
space.sem_op = 1;
space.sem_flg = 0;
data.sem_num = 1;
data.sem_op = -1;
data.sem_flg = 0;
key_t key1 = ftok(".",10);
key_t key2 = ftok(".",15);
int shmid = shmget(key1,4096,IPC_CREAT|0777);
int semid = semget(key2,2,IPC_CREAT|0777);
char *p = (char *)shmat(shmid,NULL,0);
semctl(semid,0,SETVAL,0);
semctl(semid,1,SETVAL,1);
while(1)
{
semop(semid,&data,1);
printf("from shm: %s",p);
semop(semid,&space,1);
if(strncmp(p,"quit",4) == 0)
break;
}
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
semctl(semid,0,IPC_RMID,NULL);
return 0;
}
———————————————————————————————————————————————————————————
30. pthread_create() 创建线程
【函数手册】 man 3 pthread_create
【头文件】 #include <pthread.h>
【函数原型】 int pthread_create(pthread_t *thread , const pthread_attr_t *attr ,
void *(*start_routine) (void *), void *arg );
【函数功能】 创建一条线程
【函数参数】 thread :线程的ID号
attr :线程的属性,如果为NULL,则线程为普通线程
void *(*start_routine) (void *) :线程的执行例程函数
arg :主线程给子线程传递的参数
【返回值】 成功: 0 并且线程创建成功
失败: 错误码 并且线程创建失败
代码演示:
——————————————————————————————————————————————————
#include
#include
#include
void *routine()
{
int i;
for(i=50;i<127;i++)
{
sleep(1);
printf("I'am a child thread===> %c\n",i);
}
}
int main(void)
{
pthread_t tid;
printf("A thread has been created!\n");
pthread_create(&tid,NULL,routine,NULL);
int i;
for(i=50;i<127;i++)
{
sleep(1);
printf("\t\tI'am the parent thread===> %c\n",i);
}
return 0;
}
——————————————————————————————————————————————————
31. pthread_join() 等待子线程的退出,回收子线程的资源
【函数手册】 man 3 pthread_join
【头文件】 #include <pthread.h>
【函数原型】 int pthread_join(pthread_t thread, void **retval);
等待thread号的线程退出
【函数参数】 thread:线程的TID号
*retval:
如果不为NULL,保存子线程的退出状态值的指针
如果为NULL,不关注子线程的退出状态
【返回值】 成功:0 失败:错误码
32. pthread_exit() 线程退出函数
【函数手册】 man 3 pthread_exit
【头文件】 #include <pthread.h>
【函数原型】 void pthread_exit(void *retval);
结束一个线程,返回一个值作为线程退出状态
【函数参数】 retval:退出值数据的地址(不能是局部变量地址)
【返回值】 无
——————————————————————————————————————————————————
31-32:代码演示:
#include
#include
#include
#include
void *routine(void *arg)
{
sleep(1);
char *msg = "abcd";
pthread_exit((void *)msg);
}
int main(void)
{
pthread_t tid;
pthread_create(&tid,NULL,routine,NULL);
void *ret;
pthread_join(tid,&ret);
printf("ret :%s\n",(char *)ret);
pthread_exit(NULL);
}
33.pthread_attr_init()初始化线程属性变量
【函数手册】 man 3 pthread_attr_init
【头文件】 #include <pthread.h>
【函数原型】 int pthread_attr_init(pthread_attr_t *attr);
初始化一个属性变量
【函数参数】 pthread_attr_t: 属性变量的数据类型
【返回值】 成功:0
失败:非0的错误码
【备注】 调用pthread_attr_init之后,pthread_t结构所包含的内容就是操作系统实现支持的线程所有属性的默认值。
如果要去除对pthread_attr_t结构的初始化,可以调用pthread_attr_destroy函数。
如果pthread_attr_init实现时为属性对象分配了动态内存空间
pthread_attr_destroy还会用无效的值初始化属性对象
因此如果经pthread_attr_destroy去除初始化之后的pthread_attr_t结构被pthread_create函数调用
将会导致其返回错误。
线程属性结构如下:
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;
34.pthread_attr_setdetachstate()设置线程的分离属性
【函数手册】 man 3 pthread_attr_setdetachstate
【头文件】 #include <pthread.h>
【函数原型】 int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
把detachstate属性添加到attr变量中
【函数参数】 attr: 线程属性变量
detachstate:分离属性的参数
PTHREAD_CREATE_DETACHED
PTHREAD_CREATE_JOINABLE
【返回值】 成功:0 失败:非0的错误码
34. pthread_attr_setscope() 设置线程 __scope属性
【函数功能】 设置线程 __scope 属性。scope属性表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。
POSIX的标准中定义了两个值: PTHREAD_SCOPE_SYSTEM 和 PTHREAD_SCOPE_PROCESS ,前者表示与系
统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。默认为PTHREAD_SCOPE_PROCESS。
目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。
【头文件 】 <pthread.h>
【函数原型】 int pthread_attr_setscope (pthread_attr_t* attr, int scope);
【函数参数】 (1) attr: 线程属性。
(2) scope:PTHREAD_SCOPE_SYSTEM : 表示与系统中所有线程一起竞争CPU时间,
PTHREAD_SCOPE_PROCESS: 表示仅与同进程中的线程竞争CPU
【返回值】 成功 0, 失败 -1
35. pthread_attr_getdetachstate()获取线程的分离属性
【函数原型】 int pthread_attr_getdetachstate(pthread_attr_t *attr,int *detachstate);
【头文件】 #include <pthread.h>
【功能】 获取线程的分离属性
【参数】 attr 线程属性变量
detachstate:
PTHREAD_CREATE_DETACHED 分离
PTHREAD_CREATE_JOINABLE 接合
【返回值】 成功 0、 失败 errno
备注:线程默认的状态是接合的。
33-34:代码演示:
——————————————————————————————————————————————————
#include
#include
#include
#include
#include
void *routine(void *arg)
{
sleep(1);
char *msg = "abcd";
pthread_exit((void *)msg);
}
int main(void)
{
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
pthread_t tid;
pthread_create(&tid,&attr,routine,NULL);
void *ret;
if((errno = pthread_join(tid,&ret)) == 0)
{
printf("ret :%s\n",(char *)ret);
}
else
{
perror("pthread_join() faild");
}
pthread_exit(NULL);
}
——————————————————————————————————————————————————
36. pthread_attr_setinheritsched()设置线程是否继承创建者的调度策略
【原型】 int pthread_attr_setinheritsched(pthread_attr_t *attr,int inheritsched);
【头文件】 #include <pthread.h>
【功能】 设置线程是否继承创建者的调度策略
【参数】 attr:线程属性变量
inheritsched
PTHREAD_INHERIT_SCHED :继承创建者的调度策略
PTHREAD_EXPLICIT_SCHED :使用属性变量中的调度策略
【返回值】 成功 0
失败 errno
37. pthread_attr_getinheritsched()获取线程是否继承创建者的调度策略
【原型】 int pthread_attr_getinheritsched(pthread_attr_t *attr,int *inheritsched);
【头文件】 #include <pthread.h>
【功能】 获取线程是否继承创建者的调度策略
【参数】 attr:线程属性变量
inheritsched
PTHREAD_INHERIT_SCHED :继承创建者的调度策略
PTHREAD_EXPLICIT_SCHED :使用属性变量中的调度策略
【返回值】 成功 0
失败 errno
38. pthread_attr_setschedpolicy()设置线程的调度策略
【原型】 int pthread_attr_setschedpolicy(pthread_attr_t *attr,int policy);
【头文件】 #include <pthread.h>
【功能】 设置线程的调度策略
【参数】 attr :线程属性变量
policy:
SCHED_FIFO :以先进先出的排队方式调度
SCHED_RR :以轮转的方式调度
SCHED——OTHER :非实时调度的普通线程
返回值: 成功:0 失败:errno
39. pthread_attr_getschedpolicy()获取线程的调度策略
【原型】 int pthread_attr_getschedpolicy(pthread_attr_t *attr,int *policy);
【头文件】 #include <pthread.h>
【功能】 获取线程的调度策略
【参数】 attr :线程属性变量
policy:
SCHED_FIFO :以先进先出的排队方式调度
SCHED_RR :以轮转的方式调度
SCHED——OTHER :非实时调度的普通线程
返回值: 成功:0 失败:errno
40. pthread_attr_setschedparam()设置线程静态优先级
【原型】 int pthread_attr_setschedparam(pthread_attr_t *attr,const struct sched_param *param);
【头文件】 #include<pthread.h>
【功能】 设置线程静态优先级
【参数】 attr :线程属性变量
param :静态优先级 :0-99
【返回值】 成功:0,失败:errno
【备注】 0 为默认的非实时普通进程
1~99 为实时进程,数值越大,优先级越高。
41. nice()获取、设置线程动态优先级
【原型】 int nice(int inc);
【头文件】 #include <unistd.h>
【功能】 获取、设置线程动态优先级
【参数】 inc :动态优先级:-20~19
【返回值】 成功:新的动态优先级
失败 -1
【备注】 (1)动态优先级数值越大,优先级越低
(2)如果编译器gcc的版本低于2.24(不含),该函数成功返回0
42. pthread_attr_setstacksize()设置线程栈大小
【原型】 int pthread_attr_setstacksize(pthread_attr_t *attr,size_t stacksize);
【头文件】 #include <pthread.h>
【功能】 设置线程栈大小
【参数】 attr :线程属性变量
stacksize :线程栈的大小
【返回值】 成功 0 失败:errno
43. pthread_attr_getstacksize()获取线程栈大小
【原型】 int pthread_attr_getstacksize(pthread_attr_t *attr,size_t *stacksize);
【头文件】 #include <pthread.h>
【功能】 获取线程栈大小
【参数】 attr :线程属性变量
stacksize :线程栈的大小
【返回值】 成功 0 失败:errno
44. pthread_attr_setguardsize()设置警戒区大小
【原型】 int pthread_attr_setguardsize(pthread_attr_t *attr,size_t guardsize);
【头文件】 #include <pthread.h>
【功能】 设置警戒区大小
【参数】 attr :线程数形变量
guardsize :警戒区的大小
【返回值】 成功 0 失败:errno
45. pthread_attr_getguardsize()获取警戒区大小
【原型】 int pthread_attr_getguardsize(pthread_attr_t *attr,size_t *guardsize);
【头文件】 #include <pthread.h>
【功能】 获取警戒区大小
【参数】 attr :线程数形变量
guardsize :警戒区的大小
【返回值】 成功 0 失败:errno
46. pthread_join()接合指定线程
【原型】 int pthread_join(pthread_t thread,void **retval);
【头文件】 #include<pthread.h>
【功能】 接合指定线程
【参数】 thread :线程ID号
retval :储存线程退出值的内存指针
【返回值】 成功 0,失败:errno
47. pthread_tryjoin_np()接合指定线程
【原型】 int pthread_tryjoin_np(pthread_t thread,void **retval);
【头文件】 #include<pthread.h>
【功能】 接合指定线程
【参数】 thread :线程ID号
retval :储存线程退出值的内存指针
【返回值】 成功 0,失败:errno
【备注】 pthread_join()是指定的线程如果还在运行,那么它将会阻塞等待
pthread_tryjoin_np()指定的线程如果还在运行,那么它将会立即出错返回
48. pthread_attr_destroy() :销毁属性变量
【函数手册】 man 3 pthread_attr_destroy
【函数原型】 int pthread_attr_destroy(pthread_attr_t *attr);
【函数参数】 attr:已初始化的属性变量
【返回值】 成功:0 失败:非0的错误码
49.pthread_detach()先创建一条普通属性的线程,让线程自身分离出去
【函数手册】 man 3 pthread_detach
【头文件】 #include <pthread.h>
【函数原型】 int pthread_detach(pthread_t thread);
函数可以使得线程被分离出去,线程结束不用被主线程回收,直接返还资源给系统
【函数参数】 thread:线程的TID号
【返回值】 成功:0 失败:错误码
50.pthread_self() 获取自身的TID号
【函数手册】 man 3 pthread_self
【头文件】 #include <pthread.h>
【函数原型】 pthread_t pthread_self(void);
【返回值】 成功: 自身的TID号
失败: 不存在的! f
——————————————————————————————————————————————————
36-37代码演示:
#include
#include
#include
#include
#include
void *routine(void *arg)
{
pthread_detach(pthread_self());
sleep(1);
char *msg = "abcd";
pthread_exit((void *)msg);
}
int main(void)
{
pthread_t tid;
pthread_create(&tid,NULL,routine,NULL);
void *ret;
if((errno = pthread_join(tid,&ret)) == 0)
{
printf("ret :%s\n",(char *)ret);
}
else
{
perror("pthread_join() faild");
}
pthread_exit(NULL);
}
——————————————————————————————————————————————————
注意:
有两种情况:
1.接合成功:子线程的分离在主线程调用pthread_join()接合函数之后。
2.接合失败:子线程的分离在主线程调用pthread_join()接合函数之前。
这恰恰说明:线程是并发执行的,没有先后之分。
51. pthread_cancel() 线程的取消
当线程收到取消请求时,线程会马上消失(注意:不是马上退出)
【函数手册】 man 3 pthread_cancel
【函数头文件】 #include <pthread.h>
【函数原型】 int pthread_cancel(pthread_t thread);
函数可以给线程号为thread的线程发送一个取消请求
【函数参数】 thread: 需要接收取消请求的TID号
【返回值】 成功: 0 失败: 非0的错误码
——————————————————————————————————————————————————
代码演示:
#include
#include
#include
#include
#include
#include
#include
#include
void *routine(void *arg)
{
#ifdef ENABLE
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
#elif DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
#endif
#ifdef DEFERRED
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
#elif ASYNCHRONOUS
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
#endif
unsigned long long i,j;
long double f1,f2;
for(i=0;i<10000;i++)
{
for(j=0;j<100000;j++)
{
f1 = f2;
}
}
while(1)
{
fprintf(stderr,"%c",'X');
}
pthread_exit(NULL);
}
int main(void)
{
pthread_t tid;
pthread_create(&tid,NULL,routine,NULL);
pthread_cancel(tid);
int ret = pthread_join(tid,NULL);
if(ret == 0)
printf("join thread successfully!\n");
else
printf("join thread failed!\n");
pthread_exit(NULL);
}
52. pthread_setcancelstate()设置线程响应取消的使能状态
【函数手册】 man 3 pthread_setcancelstate
【头文件】 #include <pthread.h>
【函数原型】 int pthread_setcancelstate(int state, int *oldstate);
给一条正在运行的线程设置state状态取消请求
【函数参数】 state: 使能状态
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_DISABLE
oldstate:原始类型变量存放的地址,一般NULL
【返回值】 成功: 0 失败: 非0的错误码
53. pthread_setcanceltype()设置线程的取消类型
【函数手册】 man 3 pthread_setcanceltype
【函数原型】 int pthread_setcanceltype(int type, int *oldtype);
【函数参数】 type: 响应取消的类型
PTHREAD_CANCEL_DEFERRED :
延迟取消,必须遇到线程取消点函数才响应取消请求,遇到取消点线程就会消失
PTHREAD_CANCEL_ASYNCHRONOUS : 立即取消
oldtype:旧的类型的状态
【返回值】 成功: 0 失败: 非0的错误码
——————————————————————————————————————————————————
39-40代码演示:
#include
#include
#include
#include
#include
#include
#include
#include
void *routine(void *arg)
{
#ifdef ENABLE
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
#elif DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
#endif
#ifdef DEFERRED
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
#elif ASYNCHRONOUS
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
#endif
unsigned long long i,j;
long double f1,f2;
for(i=0;i<10000;i++)
{
for(j=0;j<100000;j++)
{
f1 = f2;
}
}
while(1)
{
fprintf(stderr,"%c",'X');
}
pthread_exit(NULL);
}
int main(void)
{
pthread_t tid;
pthread_create(&tid,NULL,routine,NULL);
pthread_cancel(tid);
int ret = pthread_join(tid,NULL);
if(ret == 0)
printf("join thread successfully!\n");
else
printf("join thread failed!\n");
pthread_exit(NULL);
}
——————————————————————————————————————————————————
注意:
1.当发送取消线程的函数给某个线程的时候,这个线程并不会马上消失,如果它在执行某些关键代码时候是要执行完毕才能够判断是否能够取消的,如果遇到取消点才能够被取消,线程才会退出。比如遇到一些取消点的函数。
54. pthread_cleanup_push()响应取消请求的例程函数
【原型】 void pthread_cleanup_push(void(*routine)(void *),void *arg);
【头文件】 #include <pthread.h>
【功能】 压栈线程的取消处理例程
【参数】 routine :线程的取消处理例程
arg : 线程取消处理例程的参数
execute : 0 :弹栈线程的取消处理例程,但不执行该例程
非 0 :弹栈线程的取消处理例程,并执行该例程。
【返回值】 不返回
55. pthread_cleanup_pop()弹栈线程的取消处理例程
【原型】 void pthread_cleanup_pop(int execute);
【头文件】 #include <pthread.h>
【功能】 弹栈线程的取消处理例程
【参数】 execute : 0 :弹栈线程的取消处理例程,但不执行该例程
非 0 :弹栈线程的取消处理例程,并执行该例程。
【返回值】 不返回
【备注】 (1)使用pthread_cleanup_push()可以为线程的取消请求压入多个处理例程,
这些例程会以栈的形式保留起来,在线程被取消之后,它们以弹栈的形式后进先出地依次被执行
(2)这两个函数必须配套使用,而且必须出现在同一层代码块中。
——————————————————————————————————————————————————
代码演示:
#include
#include
#include
#include
#include
#include
#include
#include
#include
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
void *count(void *arg)
{
int i = 1;
while(1)
{
sleep(1);
printf("sec :%d\n",i++);
}
}
void handler(void *arg)
{
printf("[%u] is cancelled. \n",(unsigned)pthread_self());
pthread_mutex_t *pm = (pthread_mutex_t *)arg;
pthread_mutex_unlock(pm);
}
void *routine(void *arg)
{
#ifdef CLEANUP
pthread_cleanup_push(handler,(void *)&m);
#endif
pthread_mutex_lock(&m);
printf("[%u] lock the mutex!\n",(unsigned)pthread_self());
sleep(2);
printf("[%u]: job finished!\n",(unsigned)pthread_self());
pthread_mutex_unlock(&m);
printf("[%u] unlock the mutex!\n",(unsigned)pthread_self());
#ifdef CLEANUP
pthread_cleanup_pop(0);
#endif
pthread_exit(NULL);
}
int main(int argc,char **argv)
{
pthread_t t,t1,t2;
pthread_create(&t,NULL,count,NULL);
pthread_create(&t1,NULL,routine,NULL);
pthread_create(&t2,NULL,routine,NULL);
printf("[%u] ==>t1\n",(unsigned)t1);
printf("[%u] ==>t2\n",(unsigned)t2);
printf("[%u] ==>main\n",(unsigned)pthread_self());
sleep(1);
pthread_cancel(t1);
pthread_cancel(t2);
sleep(2);
pthread_mutex_lock(&m);
printf("[%u] locked the mutex!\n",
(unsigned)pthread_self());
pthread_mutex_unlock(&m);
exit(0);
}
56. sem_open() 创建或者打开有名信号量
【函数手册】 man 3 sem_open
【头文件】 #include <fcntl.h>
#include
#include
【函数原型】 sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);
【函数功能】 打开有名信号量
【函数参数】 name:有名信号量的名字,是自定义的,"/名字" ---> 创建有名信号量
oflag:
O_CREAT:如果信号量不存在,则创建信号量
O_EXCL: 存在则报错
mode:八进制权限 0777
value:信号量的初始值
【返回值】 sem_t是有名信号量的数据类型 成功:信号量的地址 失败:NULL
57. sem_close()关闭有名信号量
【函数手册】 man 3 sem_close
【头文件】 #include <semaphore.h>
【函数原型】 int sem_close(sem_t *sem);
【函数参数】 sem:有名信号量的地址
【返回值】 成功:0 失败:-1
58. sem_unlink() 删除信号量
【函数手册】 man 3 sem_unlink
【头文件】 #include <semaphore.h>
【函数原型】 int sem_unlink(const char *name);
【函数参数】 name:有名信号量的名字
【参数】 成功: 0 失败: -1
——————————————————————————————————————————————————
42-44代码演示:
(1)Jack
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
key_t key = ftok(".",10);
int shmid = shmget(key,4096,IPC_CREAT|0777);
char *p = (char *)shmat(shmid,NULL,0);
sem_t* sem;
sem = sem_open("/testaaa",O_CREAT,0777,0);
while(1)
{
fgets(p,4096,stdin);
sem_post(sem);
if(strncmp(p,"quit",4) == 0)
{
break;
}
}
sem_close(sem);
sem_unlink("/testaaa");
return 0;
}
——————————————————————————————————————————————————
(2)Rose
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
key_t key = ftok(".",10);
int shmid = shmget(key,4096,IPC_CREAT|0777);
char *p = (char *)shmat(shmid,NULL,0);
sem_t* sem;
sem = sem_open("/testaaa",O_CREAT,0777,0);
while(1)
{
sem_wait(sem);
printf("%s",p);
if(strncmp(p,"quit",4) == 0)
{
break;
}
}
sem_close(sem);
sem_unlink("/testaaa");
shmdt(p);
return 0;
}
59. sem_wait()实现信号量的P操作
P操作: (资源数减1) ---> sem_wait ---> man 3 sem_wait
【头文件】 #include <semaphore.h>
【函数原型】 int sem_wait(sem_t *sem);
【函数功能】 只有sem的值大于0时,才能进行P操作
如果sem的值=0,一直等待直到能够P操作为止
【函数参数】 sem:有名信号量的地址
【返回值】 成功:0 失败:-1 信号量的值没有改变
60.sem_post()实现信号量的V操作
V操作: (资源数加1) ----> sem_post() ---> man 3 sem_post
【头文件】 #include <semaphore.h>
【函数原型】 int sem_post(sem_t *sem);
【函数功能】 如果信号量的值大于0,那么就会一直等到别的进程
线程调用sem_wait把信号量的值变成0为止
【函数参数】 sem:有名信号量的地址
【返回值】 成功:0 失败:-1 信号量的值没有改变
45-46 函数演示:
#include
#include
#include
#include
#include
sem_t space,data;
void *routine(void *arg)
{
char *buf = (char *)arg;
while(1)
{
sem_wait(&data);
printf("bytes:%d\n",strlen(buf));
sem_post(&space);
}
}
int main(void)
{
sem_init(&space,0,1);
sem_init(&data,0,0);
char buf[32];
pthread_t tid;
pthread_create(&tid,NULL,routine,(void *)buf);
while(1)
{
sem_wait(&space);
bzero(buf,32);
fgets(buf,32,stdin);
sem_post(&data);
}
return 0;
}
61. sem_init() 初始化无名信号量
【函数手册】 man 3 sem_init
【头文件】 #include <semaphore.h>
【函数原型】 int sem_init(sem_t *sem , int pshared , unsigned int value);
【函数参数】 sem : 无名信号量变量的地址
pshared : 0 --> 作用于线程之间 非0 ---> 作用于进程之间
value : 信号量的初始值
【返回值】 成功: 0 失败: -1
62. sem_destroy() 销毁无名信号量
sem_destroy() ---- man 3 sem_destroy
【头文件】 #include <semaphore.h>
【函数原型】 int sem_destroy(sem_t *sem);
【函数参数】 sem:已被初始化的无名信号量变量地址
【返回值】 成功: 0 失败: -1
47-48 代码演示:
#include
#include
#include
#include
sem_t sem;
void *routine(void *arg)
{
sem_wait(&sem);
int i;
char buf[50] = {"helloworld"};
for(i=0;buf[i] != '\0';i++)
{
fprintf(stderr,"%c",buf[i]);
usleep(500000);
}
sem_post(&sem);
pthread_exit(NULL);
}
int main()
{
sem_init(&sem,0,1);
int i;
pthread_t tid[3];
for(i=0;i<3;i++)
{
pthread_create(&tid[i],NULL,routine,NULL);
}
for(i=0;i<3;i++)
{
pthread_join(tid[i],NULL);
}
sem_destroy(&sem);
return 0;
}
63. sigqueue()给进程发送信号同时携带数据
【原型】 int sigqueue(pid_t pid,int sig,const union siguval value);
struct sigaction
{
void (*sa_handler)(int);
void (*sa_sigaction)(int,siginfo_t *,void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
【头文件】 #include <signal.h>
【功能】 给某进程发送一个指定的信号,同时携带一些数据
【参数】 (1) pid : 目标进程PID
(2) sig : 要发送的信号
(3) value :携带的额外数据
【返回值】 成功 0 失败 -1
【注意】 目标进程能获取由sigqueue()发送过去的额外数据value的前提是:
必须设置SA_SIGINFO标识。
64. sigaction() 捕捉指定信号,获取信号携带的额外数据
【原型】 int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact);
【头文件】 #include <signal.h>
【功能】 捕捉一个指定的信号,且可以通过扩展响应函数来获取信号携带的额外数据
【参数】 (1) signum要捕捉的信号
(2) act :
<1> sa_handler :标准信号响应函数指针
<2> sa_sigaction :扩展信号响应函数指针
<3> sa_mask :临时信号阻塞掩码
<4> sa_flags: SA_NOCLDSTOP :子进程暂停时不提醒
SA_NOCLDWAIT :使子进程死亡时跳出僵尸态
SA_NODEFER :不屏蔽来自本信号响应函数内部的信号
SA_ONSTACK :信号响应函数在替补栈中分配内存
SA_RESETHAND :响应函数执行一遍之后重置该信号响应策略
SA_RESTART :自动重启被该信号中断的某些系统调用
SA_SIGINFO :使用扩展信号响应函数而不是标准响应函数
<5> sa_restorer: 废弃的接口
(3) oldact 原有的信号处理函数
【返回值】 成功 0 失败 -1
65. pthread_cond_init()初始化条件变量
【原型】 int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
【功能】 初始化条件变量
【头文件】 #include <semaphore.h>
【参数】 (1) cond 条件变量
(2) attr 条件变量的属性,一般始终为 0
【返回值】 成功 0 失败 -1
66. pthread_cond_destroy()销毁条件变量
【原型】 int pthread_cond_destroy(pthread_cond_t *cond);
【功能】 销毁条件变量
【头文件】 #include <semaphore.h>
【参数】 (1) cond 条件变量
【返回值】 成功 0 失败 -1
67. pthread_cond_timedwait() 条件变量等待获取配套互斥锁
【原型】 int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
【功能】 进入条件变量等待队列同时获取配套的互斥锁,并且提供超时时间限制
【头文件】 #include<semaphore.h>
【参数】 (1) cond 条件变量
(2) mutex 互斥锁
(3) abstime 超时时间限制
【返回值】 成功 0 失败 -1
68. pthread_cond_wait()条件变量等待获取配套互斥锁
【原型】 int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
【功能】 进入条件变量等待队列同时对获取配套的互斥锁
【头文件】 #include<semaphore.h>
【参数】 (1) cond 条件变量
(2) mutex 互斥锁
【返回值】 成功 0 失败 -1
【备注】
首先对互斥锁进行解锁(条件判断不属于函数的功能),解锁之后自身睡眠等待条件达成
注意这时候函数并未返回,因为还缺少一步,待条件完成后重新加锁。
pthread_cond_wait提供的重要功能是保证这两个操作一定是原子操作不可分割。
69. pthread_cond_broadcast()唤醒条件变量等待线程
【原型】 int pthread_cond_broadcast(pthread_cond_t *cond);
【功能】 唤醒全部等待队列中的线程
【头文件】 #incude <semaphore.h>
【参数】 cond 条件变量
【返回值】 成功 0 失败 -1
70. pthread_cond_signal()唤醒条件变量等待线程
【原型】 int pthread_cond_signal(pthread_cond_t *cond);
【功能】 唤醒一个等待中的线程
【头文件】 #incude <semaphore.h>
【参数】 cond 条件变量
【返回值】 成功 0 失败 -1
71. init_pool() 初始化线程池
【原型】 bool init_pool(thread_pool *pool,unsigned int threads_number);
【功能】 创建一个新的线程,包含threads_number个活跃线程
【头文件】 #include<thread_pool.h>
【参数】 (1) pool 线程池指针
(2) thread_number :初始化活跃线程个数(大于或等于1)
【返回值】 成功 true 失败 false
【注意】 线程池最少线程个数为 1
72. add_task()投送任务
【原型】 bool add_task(thread_pool *pool,void *(*do_task)(void *arg),void *arg);
【功能】 往线程池投送任务
【头文件】 #include<thread_pool.h>
【参数】 (1) pool:线程池指针
(2) do_task :投送至线程池的执行例程
(3) arg :执行例程do_task的参数,若该执行例程不需要参数可设置为NULL
【返回值】 成功 true 失败 false
【备注】 任务队列中最大任务个数为MAX_WAITING_TASKS
73. add_thread()增加活跃线程
【原型】 int add_thread(thread_pool *pool,unsigned int additional_threads);
【功能】 增加线程池中活跃线程的个数
【头文件】 #include <thread_pool.h>
【参数】 (1) pool 需要增加线程的线程池指针
(2) additional_threads :新增线程个数
【返回值】 >0 : 实际新增线程个数
-1 : 失败
74. remove_thread()删除活跃线程
【原型】 int remove_thread(thread_pool *pool,unsigned int removing_threads);
【功能】 删除线程池中活跃线程的个数
【头文件】 #include <thread_pool.h>
【参数】 (1) pool :需要删除线程的线程的个数
(2) removing_threads :要删除的线程的个数,该参数设置为0时直接返回当前线程池
线程总数,对线程池不造成任何其他影响
【返回值】 > 0 当前线程池剩余线程个数
-1 失败
【备注】 (1)线程池至少会存在1条活跃线程
(2) 如果被删除的线程正在执行任务,则将等待其完成任务之后删除
77. destroy_pool()销毁线程池
【原型】 bool destroy_pool(thread_pool *pool);
【功能】 阻塞等待所有任务完成,然后立即销毁整个线程池,释放所有资源和内存
【头文件】 #include <thread_pool.h>
【参数】 (1) pool :将要销毁的线程池
【返回值】 成功: true 失败 false
78. pthread_mutex_init() 初始化互斥锁
【原型】 int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
【功能】 初始化一个互斥锁
【头文件】 #include <pthread.h>
【参数】 (1)mutex:互斥锁地址。类型是 pthread_mutex_t 。
(2)attr:设置互斥量的属性,通常可采用默认属性,即可将 attr 设为 NULL。
【返回值】 成功:0,成功申请的锁默认是打开的
失败:非 0 错误码
【备注】 可以使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化互斥锁
比如:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
(1)这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_mutex_init() 来完成动态初始化
(2)不同之处在于 PTHREAD_MUTEX_INITIALIZER 宏不进行错误检查。
79. pthread_mutex_lock() 阻塞等待上锁
【原型】 int pthread_mutex_lock(pthread_mutex_t *mutex);
【功能】 对互斥锁上锁,若互斥锁已经上锁,则调用者一直阻塞,直到互斥锁解锁后再上锁。
【头文件】 #include <pthread.h>
【参数】 mutex 互斥锁地址
【返回值】 成功上锁:返回 0
失败:返回非0 错误码
80. pthread_mutex_trylock()非阻塞等待上锁
【原型】 int pthread_mutex_trylock(pthread_mutex_t *mutex);
【功能】 对互斥锁上锁,若互斥锁已经上锁,不阻塞,直接返回错误
【头文件】 #include <pthread.h>
【参数】 mutex:互斥锁地址。
【返回值】 成功上锁: 返回0
失败 :EBUSY
81. pthread_mutex_unlock()解锁
【原型】 int pthread_mutex_unlock(pthread_mutex_t *mutex);
【功能】 对指定的互斥锁解锁。
【头文件】 #include <pthread.h>
【参数】 mutex:互斥锁地址。
【返回值】 成功:0
失败:非 0 错误码
82. pthread_mutex_destroy()销毁互斥锁
【原型】 int pthread_mutex_destroy(pthread_mutex_t *mutex);
【功能】 销毁指定的一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。
【头文件】 #include <pthread.h>
【参数】 mutex:互斥锁地址。
【返回值】 成功:0
失败:非 0 错误码