Linux系统中消息队列,共享内存、信号和线程高级操作

  • 第十一章 消息队列
    • 10.1消息队列定义
    • 10.2 消息队列特点
    • 10.3 key值
    • 10.4 创建消息队列
      • 10.4.1 发送消息
      • 10.4.2 接收消息
      • 10.4.3 消息队列的控制
  • 第十二章 共享内存
    • 12.1 共享内存概述
    • 12.2获得一个共享存储标识符
    • 12.3共享内存映射(attach)
    • 12.4解除共享内存映射(detach)
    • 12.5共享内存控制
  • 第十三章 信号
    • 13.1信号的概述
    • 13.2 信号的基本操作
  • 第十四章 线程概述
    • 14.1 线程概述
    • 14.2线程和进程的比较
    • 14.3调度
    • 14.4拥有资源:
      • 14.4.1并发性
      • 14.4.2系统开销
    • 14.5使用多线程的目的主要有以下几点:
      • 14.5.1多任务程序的设计
      • 14.5.2并发程序设计
      • 14.5.3网络程序设计
      • 14.5.4 数据共享
    • 14.6 线程的基本操作
      • 1 pthread_create
      • 2 pthread_join
      • 3 pthread_detach
      • 4 exit
      • 5 ptread_cancel
      • 6 pthhread_setcancelstate
      • 7 pthread_testcancel
      • 8 pthread_cleanup_push
    • 14.7 同步与互斥
      • 14.7.1线程同步与互斥 线程同步与互斥

第十一章 消息队列

10.1消息队列定义

消息队列是消息的链表,存放在内存中,由内核维护

10.2 消息队列特点

消息队列的特点:
消息队列允许一个或多 个进程向它写入或者读取消息,并且每条消息都有类型。
消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。
与无名管道、有名管道一样,从消息队列中读出消息,消息队列中数据会被删除。同样消息队列中的消息是有格式的。只有内核重启或人工删除时,该消息才会被删除,若不人工删除消息队列,消息队列会一直存在于内存中
消息队列标识符,来标识消息队列。消息队列在整个系统中是唯一的。

Linux操作系统中消息队列限制值如下:
消息队列个数最多为16个
消息队列总容量最多为16384字节
每个消息内容最多为8192字节

System V提供的IPC通信机制需要一 个key值, 通过key值就可在系统内获得一 个唯一的消息队列ID。

10.3 key值

key值可以是人为指定的,也可以通过ftok函数获得。

#include <sys/types.h>
#include <sys/ipc.h>
key_ _t ftok(const char *pathname, int proj. _id);
功能:获得项目相关的唯一-的IPC键值。
◆参数:
➢pathname: 路径名
➢proj. _id: 项目ID,非0整数(只有低8位有效)

返回值:
➢成功返回key值
➢失败返回-1

10.4 创建消息队列

#include <sys/msg.h>
int msgget(key. _t key, int msgflg);
功能:创建一个新的或打开一个已经存在的消息队列。不同的进程调用此函数, 只要用相
同的key值就能得到同一个消息队列的ID。
参数:
➢key: IPC键值
➢msgflg: 标识函数的行为: IPC_ CREAT(创建)或IPC_ EXCL(如果已经存在则返回失败)。

返回值:
➢成功:消息队列的标识符
➢失败:返回-1

使用shell命令操作消息队列:

查看消息队列
ipcs-q
删除消息队列
ipcrm -q msqid

◆消息队列的消息的格式。

typedef struct . msg { long mtype;
/*消息类型*/
char mtext[100];
/*消息正文*/ ../*消息的正文可以有多个成员*/ }MSG;

◆消息类型必须是长整型的,而且必须是结构体类型的第一个成员, 类型下面是消息正文,正文可以有多个成员(正文成员可以是任意数据类型的。

10.4.1 发送消息

发送消息:

#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size. _t msgsz, int msgflg);
功能:将新消息添加到消息队列。
参数:
➢msqid: 消息队列的队列D。
➢msgp:待发送消息结构体的地址。
➢msgsz: 消息正文的字节数。
msgflg: 函数的控制属性
➢: msgsnd调用阻塞直到条件满足为止。
➢IPC_ NOWAIT:若消息没有立即发送则调用该函数的进程会立即返回。

返回值:
➢成功:0
➢失败:返回-1

10.4.2 接收消息

接收消息:

#include <sys/msg.h>
ssize_ t msgrcv(int msqid, void *msgp, size_ t msgsz, long msgtyp, int msgflg);
功能:从ID为msqid的消息队列中接收一 个消息。- - -旦接收消息成功,则消息在消息队列
中被删除。
参数:
➢msqid: 消息队列的ID,代表要从哪个消息列中获取消息。
➢msgp:存放消息结构体的地址。
➢msgsz: 消息正文的字节数。

msgtyp: 消息的类型、 可以有以下几种类型
msgtyp = 0:返回队列中的第一个消息
msgtyp > 0:返回队列中消息类型为msgtyp的消息
msgtyp < 0:返回队列中消息类型值小于或等于msgtyp绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。

注意:若消息队列中有多种类型的消息, msgrcv获取消息的时候按消息类型获取,不是先
进先出的。在获取某类型消息的时, 若队列中有多条此类型的消息,则获取最先添加的消息,即先进先出原则。

msgflg:函数的控制属性
: msgrcv调用阻塞直到接收消息成功为止。
MSG NOERROR:若返回的消息字节数比nbytes字节数多,则消息就会截短到nbytes字节,且不通知消息发送进程。
IPC_ NOWAIT:调用进程会立即返回。若没有收到消息则立即返回-1。
返回值:
➢成功返回读取消息的长度
➢失败返回-1

10.4.3 消息队列的控制

#include <sys/msg.h>
int msgct(int msqid, int cmd, struct msqid. _ds *buf);
功能:对消息队列进行各种控制,如修改消息队列的属性,或删除消息消息队列。
参数:
msqid:消息队列的ID
cmd:函数功能的控制
buf: msqid_ ds数据类型的地址,用来存放或更改消息队列的属性
cmd:函数功能的控制
IPC_ RMID:删除由msqid指示的消息队列,将它从系统中删除并破坏相关数据结构。
IPC STAT:将msqid相关的数据结构中各个元素的当前值存入到由buf指向的结构中。
IPC SET:将msqid相关的数据结构中的元素设置为由buf指向的结构中的对应值。

返回值:
➢成功:返回0
➢失败:返回-1
例: 01_ message_ queue. write.c 01 message_ queue_ read.c

第十二章 共享内存

12.1 共享内存概述

共享内存允许两个或者多个进程共享给定的存储区域。
共享内存是进程间共享数据的一种最快的方法,-个进程向共享的内存区域写入了数据,
共享这个内存区域的所有进程就可以立刻看到其中的内容。
使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。若-一个进程正在向
共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。

在Linux操作系统中共享内存限制值如下
共享存储段的最大字节数: 33554432
共享存储段的最小字节数: 1
系统中共享存储段的最大段数: 4096
每个进程共享存储段的最大段数: 4096

12.2获得一个共享存储标识符

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key. _t key, size. _t size,intshmflg);
功能:创建或打开-块共享内存区
参数:
➢key: IPC键值
➢size: 该共享存储段的长度(字节)
➢shmflg: 用来标识函数的行为
参数:
shmflg:用来标识函数的行为
➢IPC. _CREAT: 如果不存在就创建
➢IPC_ EXCL:如果已经存在则返回失败
➢IPC_ NOWAIT:调用进程会立即返回。若发生错误则返回-1
SHM_ R:可读
SHM_ _W:可写

◆返回值:
➢成功:返回共享内存标识符
➢失败:返回-1

使用shell命令操作共享内存:
查看共享内存
ipcs -m
删除共享内存
ipcrm -m shmid

12.3共享内存映射(attach)

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
◆功能:将-个共享内存段映射到调用进程的数据段中。
◆参数:
shmid:共享内存标识符
shmaddr: 共享内存映射地址(若为NUL则由系统自动指定),推荐使用NULL
参数:
shmflg:共享内存段的访问权限和映射条件:共享内存具有可读可写权限
SHM_ RDONLY:只读
SHM_ _RND: (shmaddr非空时才有效) 没有指定SHM_ RND则此段连接到shmaddr所指定的
地址.上(shmaddr必需页对齐)。指定了SHM_ RND则此段连接到shmaddrshmaddr%SHMLAB所
表示的地址上

返回值:
➢成功:返回共享内存段首地址
➢失败:返回-1
注: shmat函数使用的时候第二 个和第三个参数一般设为NULL和0, 即系统自动指定共享内存地址,并且共享内存可读可写。

12.4解除共享内存映射(detach)

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:将共享内存和当前进程分离(仅仅是断开联系并不删除共享内存)。
参数:
➢shmaddr: 共享内存映射地址。

返回值:
➢成功返回0
➢失败返回-1

12.5共享内存控制

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid, _ds *buf);
功能:共享内存空间的控制。
参数:
➢shmid: 共享内存标识符
➢cmd:函数功能的控制
➢buf: shmid_ ds数据类型的地址,用来存放或更改消息队列的属性
> 参数:
➢cmd:函数功能的控制
➢IPC_ RMID:删除
➢IPC_ _SET: 设置shmid_ ds参数
➢IPC. _STAT:保存shmid_ _ds参数。
➢SHM_ _LOCK:锁定共享内存段(超级用户)
➢SHM_ _UNLOCK:解锁共享内存段

返回值:
➢成功返回0
➢失败返回-1

◆注意:
SHM_ LOCK用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问。
其真正的意义是:被锁定的内存不允许被交换到虚拟内存中。这样做的优势在于让共享内存一直处于内存中,从而提高程序性能。

第十三章 信号

13.1信号的概述

  1. 1.信号是软件中断,它是在软件层次上对中断机制的一种模拟。
  2. 信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。
  3. 信号是一种异步通信方式。
  4. 进程不必等待信号的到达,进程也不知道信号什么时候到达。
  5. 信号可以直接进行用户空间进程和内核空间进程的交互,内核进程可以利用它来通知用户空间进程发生了哪些系统事件。
  6. 每个信号的名字都以字符SIG开头。
  7. 每个信号和一个数字编码相对应,在头文件signum.h中,这些信号都被定义为正整数。
    参考路径:
    /usr/ include/ i386-1 inux-gnu/bits/si gnum. h
    在Linux下,要想查看这些信号和编码的对应关系,可
    使用命令: kill -1

以下条件可以产生一个信号
➢1、当用户按某些终端键时,将产生信号。
例如:
终端上按“Ctr1+c” 组合键通常产生中断信号SIGINT、按"Ctr1+"键通常产生中断信号SIGQUIT、终端.上按"Ctr1+z"键通常产生中断信号SIGSTOP。
➢2、硬件异常将产生信号。
除数为0,无效的内存访问等。这些情况通常由硬件检测到,并通知内核,然后内核产生适当的信号发送给相应的进程。
➢3、软件异常将产生信号。
当检测到某种软件条件已发生,并将其通知有关进.程时,产生号。
➢4、调用kill函数将发送信号。
注意:接收信号进程和发送信号进程的所有者必须相同,或发送信号进程的所有者必须是超级用户。
➢5、运行ki11命令将发送信号。
此程序实际. 上是使用ki1l函数来发送信号。也常用此命令终止一个失控的后台进程。一个进程收到一个信号的时候,可以用如下方法进行处理:
➢6忽略此信号
大多数信号都可以使用这种方式处理,但SIGKILL和SIGSTOP决不能被忽略,原因是它们向超级用户提供一种使进程终止的可靠方法。
➢7执行系统默认动作该进程。
对大多数信号来说,系统默认动作是用来终止
➢8自定义信号处理函数
用用户定义的信号处理函数处理该信号。

13.2 信号的基本操作

#include <sys/types. h>
#include <signal. h>
int kill (pid_t pid,int si gnum) ;

功能:
给指定进程发送信号。
参数:
pid: 详见下页
signum:信号的编号
返回值:
成功返回 0,失败返回-1。

pid的取值有4种情况:
pid>0:将信号传送给进程ID为pid的进程。
pid=0:将信号传送给当前进程所在进程组中的所有进程。
pid=-1:将信号传送给系统内所有的进程。
pid<-1:将信号传给指定进程组的所有进程。这个进程组号等于pid的绝对值。

注意:
使用kill函数发送信 号,接收信号进程和发送信号进程的所有者必须相同,或者发送信号进程的所有者是超级用户

#include <unistd. h>
unsigned int alarm (unsigned int seconds);

功能:
在seconds秒后,向调用进程发送一个SIGALRM信号,SIGALRM信号的默认动作是终止调用alarm函数的进程。
返回值:
若以前没有设置过定时器,或设置的定时器已超时,返回0;否则返回定时器剩余的秒数,并重新设定定时器。

#include <signal. h>
int raise(int signum) ;

功能:.
给调用进程本身送一个信号。
参数:
signum:信号的编号。
返回值:
成功返回0,失败返回-1。

#include <unistd. h>
int pause (void);     //无参<-个pause只能等待一个信号>

功能:
将调用 进程挂起直至捕捉到信号为止。这个函数通常用于判断信号是否已到。
返回值:
直到捕获到信 号,pause函数才返回-1,且errno被设置成EINTR。

#include <stdl1ib. h>
void abort (void);

功能:
向进程发送一个SIGABRT信号,默认情况下进程会退出。
注意:
即使SIGABRT信号被加入阻塞集,一旦进程调用了abort函数,进程也还是会被终止,且在终止前会刷新缓冲区,关文件描述符。

➢处理信号的函数主要有如下三种:
➢signal函数
➢信号集函数组
➢sigact ion函数

#include <signal.h>
typedef void (*sighandler_t) (int);
sighandler_t signal (int signum,
sighandler-t handler);

功能:
注册信号处理函数(除了SIGKILL信号、SIGSTOP信号),即确定收到信号后处理函数的入口地址。
参数:
signum:信号编号
handler的取值:
忽略该信号:SIG_IGN
执行系统默认动作:SIG_DFL
自定义信号处理函数:处理函数名
返回值:
成功:返回函数地址,该地址为此信号上一次注册的信号处理函数的地址。
失败:返回SIG_ERR
可重入函数
可重入函数是指函数可以由多个任务并发使用,而不必担心数据错误。
编写可重入函数:
不使用(返回)静态的数据、全局变量(除非用信号量互斥)。不调用动态内存分配、释放的函数。不调用任何不可重入的函数(如标准I/O函数)。
注:即使信号处理函数使用的都是可重入函数(常见的可重入函数),也要注意进入处理函数时,首先要保存errno的值,结束时,再恢复原值。因为,信号处理过程中,errno值随时可能被改变。信号的基本操作 信号的基本操作

信号集概述
一个用户进程常常需要对多个信号做出处理。为了方便对多个信号进行处理,在Linux系统中引入了信号集。信号集是用来表示多个信号的数据类型。信号的基本操作 信号的基本操作
信号集相关的操作主要有如下几个函数:

sigemptyset
sigfillset
sigismember
sigaddset
sigdelset

#include <signal.h> int sigemptyset(sigset_t *set);
功能:
初始化由set指向的信号集,清除其中所有的信号即初始化一个空信号集。
参数:
set:信号集标识的地址,以后操作此信号集,对set进行操作就可以了。
返回值:成功返回 0,失败返回 -1。

 #include <signal.h>
int sigfillset(sigset_t *set);

功能:
初始化信号集合set, 将信号集合设置为所有信号的集合。
参数:信号集标识的地址,以后操作此信号集,对set进行操作就可以了。
返回值:成功返回 0,失败返回 -1。

 #include <signal.h>
int sigismember(const sigset_t *set,intsignum);

功能:
查询signum标识的信号是否在信号集合set之中。
参数:
set:信号集标识符号的地址。
signum:信号的编号。
返回值:在信号集中返回 1,不在信号集中返回 0
错误,返回 -1

 #include <signal.h>
int sigaddset(sigset_t *set, int signum);

功能:
将信号signo加入到信号集合set之中。
参数:
set:信号集标识的地址。
signum:信号的编号。
返回值:
成功返回 0,失败返回 -1。

 #include <signal.h>
int sigdelset(sigset_t *set, int signum);

功能:
将signum所标识的信号从信号集合set中删除。
参数:
set:信号集标识的地址。
signum:信号的编号。
返回值:
成功:返回 0
失败:返回 -1

信号阻塞集(屏蔽集、掩码)
每个进程都有一个阻塞集,它用来描述哪些信号递送到该进程的时候被阻塞(在信号发生时记住它,直到进程准备好时再将信号通知进程)。所谓阻塞并不是禁止传送信号, 而是暂时信号的传送。若将被阻塞的信号从信号阻塞集中删除,且对应的信号在被阻塞时发生了,进程将会收到相应的信
号。

 #include <signal.h>
int sigprocmask(int how,
const sigset_t *set, sigset_t *oldset);

功能:
检查或修改信号阻塞集,根据how指定的方法对进程的阻塞集合进行修改,新的信号阻塞集由set指定,而原先的信号阻塞集合oldset保存。
参数:
how:信号阻塞集合的修改方法。
set:要操作的信号集地址。
oldset:保存原先信号集地址。
how:
SIG_BLOCK:向信号阻塞集合中添加set信号集
SIG_UNBLOCK:从信号阻塞集合中删除set集合
SIG_SETMASK:将信号阻塞集合设为set集合
注:若set为NULL,则不改变信号阻塞集合,函数只把当前信号阻塞集合保存到oldset中。
返回值:成功:返回 0失败:返回 -1

signal函数只能提供简单的信号安装操作。使用signal函数处理信号比较简单,只要把要处理的信号和处理函数列出即可。
signal函数主要用于前面32种不可靠(在Linux系统中不可靠的信号是指信号可能会丢失)、非实时信号的处理,并且不支持信号传递信息。
Linux提供了功能更强大的sigaction函数,此函数可以用来检查和更改信号处理操作,可以支持可靠、实时信号的处理,并且支持信号传递信息。信号的基本操作 信号的基本操作可靠、不可靠信号从UNIX系统继承过来的信号(SIGHUP~SIGSYS)都是不可靠号,不支持排队(多次发送相同的信号, 进程可能只能收到一次,可能会丢失)。
SIGRTMIN至SIGRTMAX的信号支持排队(发多少次, 就可以收到多少次, 不会丢失),故称为可靠信号。 实时、非实时信号可靠信号就是实时信号,非可靠信号就是非实时信号。信号的基本操作 信号的基本操作

实时信号(可靠信号)的优势
多个实时信号可以同时发生并传送给相应的进程。实时信号可以传参(整数或任意类型的地址)。实时信号可以保证信号传递的顺序(传递顺序和信号产生的顺序相同)。信号的基本操作 信号的基本操作

 #include <signal.h>
int sigqueue(pid_t pid, int sig,
const union sigval value);

功能:
给指定进程发送信号。
参数:
pid:进程号。
sig:信号的编号。
value:通过信号传递的参数。

信号传递的参数:

union sigval
{int sival_int;
void *sival_ptr;
};

返回值:
成功返回 0,失败返回 -1

 #include <signal.h>
int sigaction(int signum,
const struct sigaction *act,
struct sigaction *oldact);

功能:
检查或修改指定信号的设置(或同时执行这两种操作。
参数:
signum:信号编号。
act: 新的信号设置指针 。
oldact:旧的信号设置指针。
sigaction函数
如果act指针非空,则要改变指定信号的设置,如果oldact指针空,则系统将此前指定信号的设置存入oldact。
返回值:
成功:0
失败:-1

信号设置结构体:
struct sigaction
{
/旧的信号处理函数指针/
void (*sa_handler)(int signum) ;
/新的信号处理函数指针/
void (*sa_sigaction)(
int signum, siginfo_t *info, void *context);
sigset_t sa_mask;/信号阻塞集/
int sa_flags;/信号处理的方式/
};

sa_handler、sa_sigaction:
信号处理函数指针:
忽略该信号:SIG_IGN
执行系统默认动作:SIG_DFL
自定义信号处理函数:处理函数名
注:应根据情况给sa_sigaction、sa_handler两者之一赋值。
sa_mask:
信号处理函数执行期间要阻塞的信号集(但不阻塞SIGKILL、SIGSTOP)。
sa_flags:用来决定信号的行为。
不指定SA_SIGINFO:
信号的处理函数应由sa_handler设置。可完成与signal函数一样的功能。
指定SA_SIGINFO:
信号的处理函数应由sa_sigaction设置。它的功能更强大,除了获取基本的信号值外,还可以获取此信号发送者及接收者的部分信息。

 信号处理函数:

void (*sa_sigaction)(int signum,
siginfo_t *info, void *context);

参数:
signum:信号的编号。
info:记录信号发送进程信息的结构体。
context:可以赋给指向ucontext_t类型的一个对象的指针,以引用在传递信号时被中断的接收进程或线程的上下文。
进程信息结构体路径:
/usr/include/i386-linux-gnu/bits/siginfo.h
发送进程信息的结构体:siginfo_t
接收进程或线程的上下文结构体:ucontext_t

第十四章 线程概述

14.1 线程概述

每个进程都拥有自己的数据段、代码段和堆栈段,这就造成进程在进行创建、切换、撤销操作时,需要较大的系统开销。为了减少系统开销,从进程中演化出了线程。线程存在于进程中,共享进程的资源。 线程是进程中的独立控制流,由环境(包括寄存器组和程序计数器)和一系列的执行指令组成。线程概述
线程的概念
每个进程有一个地址空间、和一个控制线程。
线程概述
就像每个进程都有一个进程号一样,每个线程也有一
个线程号。
进程号在整个系统中是唯一的,但线程号不同,线程
号只在它所属的进程环境中有效。
进程号用pid_t数据类型表示,是一个非负整数。线程
号则用pthread_t数据类型来表示。
有的系统在实现pthread_t的时候,用一个结构体来表
示,所以在可移植的操作系统实现不能把它做为整数
处理。

14.2线程和进程的比较

传统意义上的进程被称为重量级进程(heavyweightprocess, HWP),从现代角度看,就是只拥有一个线程的进程。线程与进程有很多类似的性质,习惯上也称线程为轻量级进程(lightweight process, LWP)或称为迷你进程。

14.3调度

线程是CPU调度和分派的基本单位。
进程是系统中程序执行和资源分配的基本单位。

14.4拥有资源:

进程是拥有系统资源的一个独立的单位,它可以拥有自己的资源。线程自己一般不拥有资源(除了必不可少的程序计数器,一组寄存器和栈),但它可以去访问其所属进程的资源,如进程代码段,数据段以及系统资源(已打开的文件,I/O设备等)。

14.4.1并发性

不仅进程间可以并发执行,而且在一个进程中的多个线程之间也可以并发执行。

14.4.2系统开销

同一个进程中的多个线程可共享同一地址空间,因此它们之间的同步和通信的实现也变得比较容易。在进程切换时候,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置;而线程切换只需要保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作,从而能更有效地使用系统资源和提高系统的吞吐量。

14.5使用多线程的目的主要有以下几点:

14.5.1多任务程序的设计

一个程序可能要处理不同应用,要处理多种任务,如果开发不同的进程来处理,系统开销很大,数据共享,程序结构都不方便,这时可使用多线程编程方法。

14.5.2并发程序设计

一个任务可能分成不同的步骤去完成,这些不同的步骤之间可能是松散耦合,可能通过线程的互斥,同步并发完成。这样可以为不同的任务步骤建立线程。

14.5.3网络程序设计

为提高网络的利用效率,我们可能使用多线程,对每个连接用一个线程去处理。

14.5.4 数据共享

同一个进程中的不同线程共享进程的数据空间,方便不同线程间的数据共享。 在多CPU系统中,实现真正的并行。

14.6 线程的基本操作

1 pthread_create

 #include <pthread.h>
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void*),
void * arg);

功能:
创建一个线程。
参数:
thread:线程标识符地址。
attr:线程属性结构地址。
start_routine:线程函数的入口地址。
arg:传给新线程执行函数的参数。
返回值:
成功返回0,失败返回非0;

 与fork不同的是pthread_create创建的线程不与父线程在同一点开始运行,而是从指定的函数开始运行,该函数运行完后,该线程也就退出了。
 线程依赖进程存在的,如果创建线程的进程结束了,线程也就结束了。
 线程函数的程序在pthread库中,故链接时要加上参
数-lpthread。

2 pthread_join

 #include <pthread.h>
int pthread_join(pthread_t thread, void **value_ptr);

功能:
等待子线程结束,并回收子线程资源。
参数:
thread:被等待的线程号。
value_ptr:指针的地址,调用此函数后,指针指向一个存储线程完整退出状态的静态区域,可用来存储被等待线程的返回值。
返回值:
成功返回0,失败返回非0。

3 pthread_detach

 创建一个线程后应回收其资源,但使用pthread_join函数会使调用者阻塞,故Linux提供了线程分离函数:
pthread_detach。

 #include <pthread.h>
int pthread_detach(pthread_t thread);

功能:
使调用线程与当前进程分离,使其成为一个独立的线程,该线程终止时,系统将自动回收它的资源。
参数:
thread:线程ID
返回值:
成功:返回 0,失败返回非0。

4 exit

在进程中我们可以调用exit函数或_exit函数来结束进程,在一个线程中我们可以通过以下三种在不终止整个进程的情况下停止它的控制流。
线程从执行函数中返回。
线程调用pthread_exit退出线程。
线程可以被同一进程中的其它线程取消。

 #include <pthread.h>
void pthread_exit(void *value_ptr);

功能:
使调用线程退出。
参数:
value_ptr:指向线程退出状态的静态区域,在别的线程中可用pthread_join访问。一个进程中的多个线程是共享该进程的数据段,因此,通常线程退出后所占用的资源并不会释放。

5 ptread_cancel

 取消线程是指取消一个正在执行线程的操作。

#include <pthread.h>
int pthread_cancel(pthread_t thread);

功能:
取消线程。
参数:
thread:目标线程ID。
返回值:
成功返回 0,失败返回出错编号。
 pthread_cancel函数的实质是发信号给目标线程thread,使目标线程退出。
此函数只是发送终止信号给目标线程,不会等待取消目标线程执行完才返回。
然而发送成功并不意味着目标线程一定就会终止,线程被取消时,线程的取消属性会决定线程能否被取消以及何时被取消。

线程的取消状态
线程取消点
线程的取消类型
 线程的取消状态

6 pthhread_setcancelstate

在Linux系统下,线程默认可以被取消。编程时可以通过pthread_setcancelstate函数设置线程是否可以被取消。

pthread_setcancelstate(int state,
int *old_state);
state:
PTHREAD_CANCEL_DISABLE:不可以被取消、
PTHREAD_CANCEL_ENABLE:可以被取消。
old_state:

保存调用线程原来的可取消状态的内存地址。

7 pthread_testcancel

 线程的取消点
线程被取消后,该线程并不是马上终止,默认情况下线程执行到消点时才能被终止。编程时可以通过pthread_testcancel函数设置线程的取消点。
void pthread_testcancel(void);
当别的线程取消调用此函数的线程时候,被取消的线程执行到此函数时结束。
POSIX.1保证线程在调用表1、表2中的任何函数时,取消点都会出现。
线程的取消类型
线程被取消后,该线程并不是马上终止,默认情况下线程执行到消点时才能被终止。编程时可以通过pthread_setcanceltype函数设置线程是否可以立即被
取消。

pthread_setcanceltype(int type,int *oldtype);
type:
PTHREAD_CANCEL_ASYNCHRONOUS:立即取消、
PTHREAD_CANCEL_DEFERRED:不立即被取消
oldtype:

保存调用线程原来的可取消类型的内存地址。

8 pthread_cleanup_push

和进程的退出清理一样,线程也可以注册它退出时要调用的函数,这样的函数称为线程清理处理程序
(thread cleanup handler)。
注意:
线程可以建立多个清理处理程序。
处理程序在栈中,故它们的执行顺序与它们注册时的顺序相反。

 #include <pthread.h>
void pthread_cleanup_push(
void (*routine)(void*), void *arg);

功能:
将清除函数压栈。即注册清理函数。
参数:
routine:线程清理函数的指针。
arg:传给线程清理函数的参数。

 #include <pthread.h>
void pthread_cleanup_pop(int execute);

功能:
将清除函数弹栈,即删除清理函数。
参数:
execute:线程清理函数执行标志位。
非0,弹出清理函数,执行清理函数。
0,弹出清理函数,不执行清理函数
 当线程执行以下动作时会调用清理函数:
调用pthread_exit退出线程。
响应其它线程的取消请求。
用非零execute调用pthread_cleanup_pop。
无论哪种情况pthread_cleanup_pop都将删除上一 次pthread_cleanup_push调用注册的清理处理函数。
注意:
pthread_cleanup_pop、pthread_cleanup_push 必须配对使用。

14.7 同步与互斥

 同步:
两个或两个以上的线程在运行过程中协同步调,按预定的先后次序运行。
 互斥:
一个公共资源同一时刻只能被一个线程使用,多个线程不能同时使用公共资源。POSIX标准中线程同步和互斥的方法,主要有信号量和互斥锁两种方式。
 互斥锁(mutex)
mutex是一种简单的加锁的方法来控制对共享资源的访问,mutex只有两种状态,即上锁(lock)和解锁(unlock)。
在访问该资源前,首先应申请mutex,如果mutex处于unlock状态,则会申请到mutex并立即lock;如果mutex处于lock状态,则默认阻塞申请者。
unlock操作应该由lock者进行。

14.7.1线程同步与互斥 线程同步与互斥

 mutex用pthread_mutex_t数据类型表示,在使用互斥锁前,必须先对它进行初始化。
 静态分配的互斥锁:
pthread_mutex_t mutex =
PTHREAD_MUTEX_INITIALIZER;
 动态分配互斥锁:
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
在所有使用过此互斥锁的线程都不再需要使用时候,应调用pthread_mutex_destroy销毁互斥锁。

 #include <pthread.h>
int pthread_mutex_init(
pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr);

功能:
初始化一个互斥锁。
参数:
mutex:互斥锁地址。
attr:互斥锁的属性,NULL为默认的属性。
返回值:
成功返回0,失败返回非0。

 #include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);

功能:
对互斥锁上锁,若已经上锁,则调用者一直阻塞到互斥锁解锁。
参数:
mutex:互斥锁地址。
返回值:
成功返回0,失败返回非0。

 #include <pthread.h>
int pthread_mutex_trylock(
pthread_mutex_t *mutex);

功能:
对互斥锁上锁,若已经上锁,则上锁失败,函数
立即返回。
参数:
mutex:互斥锁地址。
返回值:
成功返回0,失败返回非0。

 #include <pthread.h>
int pthread_mutex_unlock(
pthread_mutex_t * mutex);

功能:
对指定的互斥锁解锁。
参数:
mutex:互斥锁地址。
返回值:
成功返回0,失败返回非0。

 #include <pthread.h>
int pthread_mutex_destroy(
pthread_mutex_t *mutex);

功能:
销毁指定的一个互斥锁。
参数:
mutex:互斥锁地址。
返回值:
成功返回0,失败返回非0。
 信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。
 进程或线程根据信号量操作结果的值判断是否对公共资源具有访问的权限,当信号量值大于0时,则可以访问,否则将阻塞。

 #include <semaphore.h>
int sem_init(sem_t *sem, int pshared,
unsigned int value);

功能:
创建一个信号量并初始化它的值。
参数:
sem:信号量地址。
pshared:决定信号量能否在几个进程间共享,目前Linux没有实现进程间共享信号量,故此值只能为0。
value:信号量的初始值。
返回值:
成功返回0,失败返回-1。

 #include <semaphore.h>
int sem_wait(sem_t *sem);

功能:
将信号量的值减1,若信号量的值小于0,此函数会引起调用者阻塞。
参数:
sem:信号量地址。
返回值:成功返回0,失败返回-1。

 #include <semaphore.h>
int sem_trywait(sem_t *sem);

功能:
将信号量的值减1,若信号量的值小于0,则对信号量的操作失败,函数立即返回。
参数:
sem:信号量地址。
返回值:成功返回0,失败返回-1。

 #include <semaphore.h>
int sem_post(sem_t *sem);

功能:
将信号量的值加1并发出信号唤醒等待线程。
参数:
sem:信号量地址。
返回值:成功返回0,失败返回-1。

 #include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);

功能:
获取sem标识的信号量的值,保存在sval中。
参数:
sem:信号量地址。
sval:保存信号量值的地址。
返回值:成功返回0,失败返回-1。

 #include <semaphore.h>
int sem_destroy(sem_t *sem);

功能:
删除sem标识的信号量。
参数:
sem:信号量地址。
返回值:成功返回0,失败返回-1。

Linux系统中消息队列,共享内存、信号和线程的基本操作使用方法相关推荐

  1. 【Linux系统编程】进程间通信--共享内存

    概述 共享内存是进程间通信中最简单的方式之一.共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针.当一个进程改变了这块地址中的内容的时 ...

  2. 在Linux系统中允许或拒绝SSH访问特定用户或组的方法

    openSSH默认配置文件有两个指令,允许和拒绝对特定用户或组的SSH访问,本文所要介绍的内容是允许或拒绝在Linux系统中对特定用户或组的SSH访问.一旦我们将用户置于限制模式,他就无法做任何事情, ...

  3. 【Linux】进程间通信 —— 匿名管道 | 命名管道 | System V | 消息队列 | 共享内存

    进程间通信 0. 进程间通信 1. 管道 1.1 匿名管道 1.1.1 匿名管道原理 1.1.2 创建匿名管道pipe 1.1.3 基于匿名管道通信的4种情况5个特点 1.2 命名管道 1.2.1 创 ...

  4. Linux系统中提示/usr/bin/ld: cannot find -lxxx错误的解决方法

    报错说明 /usr/bin/ld: cannot find -lxxx 这些讯息会随着编译不同类型的source code 而有不同的结果出来如: /usr/bin/ld: cannot find - ...

  5. linux nfs系统客户端,Linux系统中挂载共享目录NFS文件系统客户端安装与配置

    NFS服务简介      NFS是Network  File System(网络文件系统).主要功能是通过网络让不同的服务器之间可以共享文件或者目录.NFS客户端一般是应用服务器(比如web,负载均衡 ...

  6. Linux系统快速查看CPU和内存使用情况,附各参数详解

    Linux系统中查看CPU和内存使用情况,是一个运维工程师常见的事情,下面分享一下. 目 录 1.top命令 2.ps命令 3.free命令 1.top命令 top命令是Linux下常用的性能分析工具 ...

  7. 【Linux】进程间通信--systemV标准--共享内存

    文章目录 systemV 标准 共享内存通信原理 共享内存的理解 共享内存的创建--shmget函数 查看共享内存和释放共享内存方式 挂在共享内存--shmat函数 去挂在共享内存--shmdt函数 ...

  8. 【Linux Program】信号量、共享内存和消息队列

    系列文章: 文件操作 数据管理 进程和信号 POSIX 线程 进程间通信:管道 信号量共享内存和消息队列 套接字 文章目录 1. 信号量 1.1 信号量的定义 1.2 Linux 的信号量机制 1.3 ...

  9. c语言系统编程八:Linux进程间通信之消息队列

    Linux进程间通信之消息队列 一 消息队列概述 二 消息队列的特点 三 消息队列的创建和使用 3.1 获取系统唯一的key值 3.2 创建消息队列 3.3 查看消息队列和删除消息队列的shell命令 ...

最新文章

  1. 容易被误会的 Kafka 消费者属性 enable.auto.commit
  2. 理解 iOS 和 macOS 的内存管理
  3. fedora desktop
  4. 表格布局(tablelayout)
  5. 《WebForm开发系列高级篇》Item2导出EXCEL通用类(GridView,DataList,Repeater,DetailView)
  6. 【赠书】pandas创始人手把手教你利用Python进行数据分析
  7. Shell-脚本只能运行1次
  8. java的打印语句_java打印输出语句是什么?
  9. ISO IEC 27001 企业信息安全管理要求
  10. 水经注地图下载器注册机机器码过长_微图影像下载参数说明
  11. C语言程序设计实验报告——实验六
  12. 2.4G频段的无线收发芯片 SI24R1 问题汇总解答
  13. 笛卡尔坐标为什么叫Cartesian coordinate而不是Descartes coordinate?
  14. 微型计算机延时,延迟时间
  15. 小米手机使用FlutterDownloader下载安卓apk文件不能正常安装
  16. matlab模拟风场竖桥向时程,大跨度桥梁三维脉动风场的计算机模拟
  17. 西门子200SMART(七)交叉引用
  18. iamp是什么意思计算机网络,IAMP是什么意思
  19. 升职加薪/积分落户必备职称——2020年中级经济师不容错过
  20. python字典增加方法_python增加字典项的方法

热门文章

  1. fossa介绍和使用
  2. ipv6 介绍,格式定义
  3. 从开发的角度看待产品需求评审会
  4. 天猫开店流程及费用,怎么计算天猫店铺的开店成本?
  5. Joomla模板修改
  6. 一个案例告诉你Smartbi自助数据分析工具有多实用!
  7. java settitle_关于java的JFrame中的setTitle()方法
  8. python 视频转场_利用Python 进行短视频的二次创作
  9. java controller注解原理_@Controller、@RestController注解区别详解
  10. HTML自定义滚动条(仿网易邮箱滚动条)