函数fork:
#include
pid_t fork(void);
说明:
程序示例:
pid_t pid;
if ((pid = fork()) < 0) {
perror(“fork”);
return -1;
}
else if (pid == 0) {
printf(“child process : my pid is %d\n”, getpid());
}
else {
printf(“parent process : my pid is %d\n”, getpid());
}
程序通过fork()之后的主进程创建了一个新子进程,而实际上新的子进程执行的代码是fork()之后的代码,相当于fork()之后创建的新子进程将其后的代码都复制过来进行执行,所以需要fork()的返回值(也就是进程pid)来区分父进程和子进程的运行;
父进程和子进程的关系:
函数exit/_exit():
#include
#include
void exit(int status);
void _exit(int status);
void _Exit(int status);
说明:
exit函数结束进程刷新(流)缓冲区示例:
int main(void) {
printf(“this process will exit”);
exit(0);
printf(“never be displayed”);
}
编译执行之后输出为:
this process will be exit
进程回收函数wait():
#include
pid_t wait(int *status);
说明:
进程回收和结束之间的关系:
子进程通过exit / _exit / return 返回某个值(0-255),父进程调用wait(&status) 回收,前面提到,如果子进程结束,但父进程不去回收,子进程就会成为僵尸进程被系统挂起;
进程回收函数wait()使用示例:
int status;
pid_t pid;
if ((pid = fork()) < 0) {
perror(“fork”);
exit(-1);
}
else if (pid == 0) {
sleep(1);
exit(2);
}
else {
wait(&status);
printf(“%x\n”, WEXITSTATUS(status));
}
进程回收函数waitpid():
#include
pid_t waitpid(pid_t pid, int *status, int option);
说明:
实现一个进程链,父进程->子进程->孙进程->重孙进程->重重孙进程
#include
#include
#include
int main(){
pid_t pid;
printf("I am A,mypid=%d\n",getpid());
pid=fork();//创建子进程B
if(pid==0){
printf("I am B,mypid=%d\n",getpid());
pid_t pid1=fork();//创建孙子进程C
if(pid1==0){
printf("I am C,mypid=%d\n",getpid());
pid_t pid2=fork();//创建重孙进程D
if(pid2==0){
printf("I am D,mypid=%d\n",getpid());
pid_t pid3=fork();//创建重重孙进程E
if(pid3==0){
printf("I am E,mypid=%d\n",getpid());
}
}
}
}
return 0;
}
exec函数族
execl / execlp 函数:
#include
int execl(const char *path, const char *arg, …);
int execlp(const char *file, const char *arg, …);
使用示例:
if (execl(“/bin/ls”, “ls”, “-a”, “-l”, “/etc”, NULL) < 0) {
perror(“execl”);
}
if (execlp(“ls”, “ls”, “-a”, “-l”, “/etc”, NULL) < 0) {
perror(“execlp”);
}
*最后一个参数必须用空指针NULL结束
execv / execvp 函数:
#include
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
使用示例:
char *arg[] = {“ls”, “-a”, “-l”, “/etc”, NULL};
if (execv(“/bin/ls”, arg) < 0) {
perror(“execv”);
}
if (execvp(“ls”, arg) < 0) {
perror(“execvp”);
}
*execv / execvp 函数相较于execl / execlp 函数,就相当于把后续的参数输入封装成了字符串数组形式
system 函数:
#include
int system(const char *command);
*system 函数里面直接加命令参数即可
守护进程(Daemon Process)是Linux三种进程类型之一;
是 Linux 中的后台服务进程;
是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件;
进程组(Process Group): 进程集合,每个进程组有一个组长(Leader),其进程 ID 就是该进程组 ID。
会话(Session): 进程组集合,每个会话有一个组长,其进程 ID 就是该会话组 ID。
控制终端(Controlling Terminal):每个会话可以有一个单独的控制终端,与控制终端连接的 Leader 就是控制进程(Controlling Process)。
创建子进程,父进程退出
if (fork() > 0) {
exit(0);
}
子进程创建新会话
if (setsid() < 0) {
exit(-1);
}
更改当前工作目录
chdir(“/”);
chdir(“/tmp”);
重设文件权限掩码
if (umask(0) < 0) {
exit(-1);
}
关闭打开的文件描述符
int i;
for(i=0; i<3; i++) {
close(i);
}
setsid函数:
pid_t setsid(void);
成功:返回调用进程的会话ID;失败:-1,设置errno。
调用了setsid函数的进程,既是新的会长,也是新的组长
getsid函数:
pid_t getsid(pid_t pid)
成功:返回调用进程的会话ID;失败:-1,设置errno
getpgid函数:
pid_t getpgid(pid_t pid);
获取进程组id
gcc 编译时加上-g参数:
gcc -g hello.c -o hello
然后gdb进行调试:
gdb hello
在gdb界面中输入start进行单步调试,run是全部运行;
调试多进程方法:
set follow-fork-mode child 设置GDB调试子进程
set follow-fork-mode parent 设置GDB调试父进程
set detach-on-fork on/off 设置GDB跟踪调试单个进程或多个
说明:
info inferiors 显示GDB调试的进程
inferiors 进程序号(1,2,3…) 切换GDB调试的进程
进程和线程的对比:
线程特点:
一个进程中的多个线程共享以下资源:可执行的指令、静态数据、进程中打开的文件描述符、当前工作目录、用户ID、用户组ID
每个线程私有的资源包括:线程ID (TID)、PC(程序计数器)和相关寄存器、堆栈、错误号 (errno)、优先级、执行状态和属性
pthread线程库中提供了如下基本操作:
同步和互斥机制:
pthread_create()函数
#include
int pthread_create(pthread_t *thread, const
pthread_attr_t *attr, void *(*routine)(void *), void *arg);
*使用pthread库时,如果出现error: ld链接错误,需要在编译过程中加上-lpthread进行动态链接
注意:主线程退出,其创建的子线程也会退出;而线程创建需要时间,如果主进程先行退出,那么线程就无法执行
pthread_exit()函数
#include
void pthread_exit(void *retval);
获取线程id:
#include
pthread_t pthread_self(void);
pthread_join()函数
#include
int pthread_join(pthread_t thread, void **retval);
对于一个默认属性的线程 A 来说,线程占用的资源并不会因为执行结束而得到释放
为了防止在多线程程序中回收线程时,产生某个线程未结束导致整个多线程回收任务阻塞的问题,引入了线程分离了方法
线程分离:指定该状态,线程主动与主控线程断开关系。线程结束后(不会产生僵尸线程),也就不用通过pthread_join()回收资源
方法一、pthead_detach()函数
#include
int pthread_detach(pthread_t thread);
方法二、创建线程前,先设置线程属性为分离
pthread_attr_t attr; /*通过线程属性来设置游离态(分离态)*/
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
*如果不设置线程属性,默认属性是需要进行线程回收的,线程回收部分有提到
pthread_cancel()函数
#include
int pthread_cancel(pthread_t thread);
注意,线程的取消必须要求线程具有取消点,取消点主要是**阻塞系统的调用,**比如sleep()等过程;
如果没有取消点,则可以使用pthread_testcancel()函数创造一个取消点;
void pthread_testcancel(void);
将子线程任务进行部分取消,可以使用pthread_setcancelstate()函数;
int pthread_setcancelstate(int state, int *oldstate);
state参数:
一般就在子线程函数相应流程前进行:
//延迟五秒后线程可以被取消
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
sleep(5);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
设置取消类型,pthread_setcanceltype()函数
int pthread_setcanceltype(int type, int *oldtype);
type参数:
当线程申请了堆空间,又发生异常退出时,如果没有得到清理就会产生内存泄漏;关于线程的清理主要是两个函数:
void pthread_cleanup_push(void (*routine) (void *), void *arg);
void pthread_cleanup_pop(int execute);
这两个一般成对使用;routine函数执行条件:
注意:
1、必须成对使用,即使pthread_cleanup_pop不会被执行到也必须写上,否则编译错误。2、pthread_cleanup_pop()被执行且参数为0,pthread_cleanup_push回调函数routine不会被执行.
3、pthread_cleanup_push 和pthread_cleanup_pop可以写多对,routine执行顺序正好相反
4、线程内的return 可以结束线程,也可以给pthread_join返回值,但不能触发pthread_cleanup_push里面的回调函数,所以我们结束线程尽量使用pthread_exit退出线程。
线程互斥的一些基本概念:
临界资源:一次只允许一个任务(进程、线程)访问的共享资源
临界区:访问临界资源的代码
互斥机制:mutex互斥锁,任务访问临界资源前申请锁,访问完后释放锁
pthread_mutex_init()函数(动态初始化)
#include
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t * attr);
man 函数出现 No manual entry for pthread_mutex_xxx解决办法:
apt-get install manpages-posix-dev
PTHREAD_MUTEX_INITIALIZER宏定义(静态初始化):
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
锁的申请:
#include
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
锁的释放:
#include
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
/*
说明:
开启两个线程分别加锁去对一个文件进行写入信息
如果不加锁会导致写入错乱
*/
#include
#include
#include
#include
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
FILE *fp;
void *func2(void *arg){
pthread_detach(pthread_self());
printf("This func2 thread\n");
char str[]="I write func2 line\n";
char c;
int i=0;
while(1){
pthread_mutex_lock(&mutex);
while(i<strlen(str))
{
c = str[i];
fputc(c,fp);
usleep(1);
i++;
}
pthread_mutex_unlock(&mutex);
i=0;
usleep(1);
}
pthread_exit("func2 exit");
}
void *func(void *arg){
pthread_detach(pthread_self());
printf("This is func1 thread\n");
char str[]="You read func1 thread\n";
char c;
int i=0;
while(1){
pthread_mutex_lock(&mutex);
while(i<strlen(str))
{
c = str[i];
fputc(c,fp);
i++;
usleep(1);
}
pthread_mutex_unlock(&mutex);
i=0;
usleep(1);
}
pthread_exit("func1 exit");
}
int main(){
pthread_t tid,tid2;
void *retv;
int i;
fp = fopen("1.txt","a+");
if(fp==NULL){
perror("fopen");
return 0;
}
pthread_create(&tid,NULL,func,NULL);
pthread_create(&tid2,NULL,func2,NULL);
while(1){
sleep(1);
}
}
必要性:提高线程执行效率
特性:
写者:写者使用写锁,如果当前没有读者,也没有其他写者,写者立即获得写锁;否则写者将等待,直到没有读者和写者。
读者:读者使用读锁,如果当前没有写者,读者立即获得读锁;否则读者等待,直到没有写者。
注意:
初始化一个读写锁 pthread_rwlock_init
读锁定读写锁 pthread_rwlock_rdlock
非阻塞读锁定 pthread_rwlock_tryrdlock
写锁定读写锁 pthread_rwlock_wrlock
非阻塞写锁定 pthread_rwlock_trywrlock
解锁读写锁 pthread_rwlock_unlock
释放读写锁 pthread_rwlock_destroy
避免方法:
应用场景:线程条件变量是为了解决生产者与消费者问题,是线程同步的一种手段;
必要性:为了实现等待某个资源,让线程进行休眠,从而提高运行效率;
函数:
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
使用步骤:
1、初始化:
静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //初始化互斥量
或使用动态初始化
pthread_cond_init(&cond);
2、生产资源线程
pthread_mutex_lock(&mutex);
//开始生产资源
pthread_cond_sigal(&cond); //通知一个消费线程
或者
pthread_cond_broadcast(&cond); //广播通知多个消费线程
pthread_mutex_unlock(&mutex);
3、消费者线程
pthread_mutex_lock(&mutex);
while (如果没有资源){ //防止惊群效应
pthread_cond_wait(&cond, &mutex);
}
有资源了,消费资源
pthread_mutex_unlock(&mutex);
注意:
应用示例:
#include
#include
#include
#include
pthread_cond_t hasTaxi=PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
struct taxi{
struct taxi *next;
int num;
};
struct taxi *Head=NULL;
//生产资源,创建一个taxi资源
void *taxiarv(void *arg){
printf("taxi arrived thread\n");
pthread_detach(pthread_self());
struct taxi *tx;
int i=1;
while(1){
tx = malloc(sizeof(struct taxi));
tx->num = i++;
printf("taxi %d comming\n",tx->num);
pthread_mutex_lock(&lock);
tx->next = Head;
Head = tx;
pthread_cond_signal(&hasTaxi);
pthread_mutex_unlock(&lock);
sleep(1);
}
pthread_exit(0);
}
//消费资源,消耗一个taxi资源
void *takeTaxi(void *arg){
printf("take taxi thread\n");
pthread_detach(pthread_self());
struct taxi *tx;
while(1){
pthread_mutex_lock(&lock);
while(Head==NULL)
{
pthread_cond_wait(&hasTaxi,&lock);
}
tx = Head;
Head=tx->next;
printf("%d,Take taxi %d\n",(int)arg,tx->num);
free(tx);
pthread_mutex_unlock(&lock);
}
pthread_exit(0);
}
int main(){
pthread_t tid1,tid2,tid3;
pthread_create(&tid1,NULL,taxiarv,NULL);
pthread_create(&tid2,NULL,takeTaxi,(void*)1);
pthread_create(&tid2,NULL,takeTaxi,(void*)2);
pthread_create(&tid2,NULL,takeTaxi,(void*)3);
while(1) {
sleep(1);
}
}
线程池的概念:是一种可以循环的完成任务的一组线程的集合
为什么需要线程池:
我们平时创建一个线程,完成某一个任务,等待线程的退出。但当需要创建大量的线程时,假设T1为创建线程时间,T2为在线程任务执行时间,T3为线程销毁时间,当 T1+T3 > T2,这时候就不划算了,使用线程池可以降低频繁创建和销毁线程所带来的开销,任务处理时间比较短的时候这个好处非常显著。
线程池的基本结构:
线程池的实现步骤:
创建线程池的基本结构
任务队列链表
线程池结构体
线程池的初始化
pool_init()
{
创建一个线程池结构
实现任务队列互斥锁和条件变量的初始化
创建n个工作线程
}
线程池添加任务
pool_add_task
{
判断是否有空闲的工作线程
给任务队列添加一个节点
给工作线程发送信号newtask
}
实现工作线程
workThread
{
while(1){
等待newtask任务信号
从任务队列中删除节点
执行任务
}
}
线程池的销毁
pool_destory
{
删除任务队列链表所有节点,释放空间
删除所有的互斥锁条件变量
删除线程池,释放空间
}
进程间通信的方式:
无名管道(pipe)
有名管道(fifo)
信号(signal)
共享内存(mmap)
套接字(socket)
无名管道的特点:
无名管道的创建:
#include
int pipe(int pfd[2]);
#include
#include
#include
int main(){
int pfd[2];
int i;
int re;
char buf[40]={0};
pid_t pid;
re = pipe(pfd);
if(re<0){
perror("pipe");
return 0;
}
printf("%d,%d\n",pfd[0],pfd[1]);
for(i=0;i<2;i++){
pid = fork();
if(pid<0){
perror("fork");
return 0;
}else if(pid>0){
}else{
break;
}
}
if(i==2){
close(pfd[1]);
while(1){
memset(buf,0,40);
re=read(pfd[0],buf,40);
if(re>0){
printf("%s\n",buf);
}
}
return 0;
}
if(i==1){
close(pfd[0]);
while(1){
strcpy(buf,"this is 2 process");
write(pfd[1],buf,strlen(buf));
usleep(930000);
}
return 0;
}
if(i==0){
close(pfd[0]);
while(1){
strcpy(buf,"this is 1 process");
write(pfd[1],buf,strlen(buf));
sleep(1);
}
return 0;
}
}
无名管道特性:
有名管道特点
有名管道的创建
#include
#include
int mkfifo(const char *filename, mode_t mode);
open管道文件的参数说明与注意事项
open(const char *path, O_RDONLY);//只读阻塞打开
open(const char *path, O_RDONLY | O_NONBLOCK);//只读非阻塞打开
open(const char *path, O_WRONLY);//只写阻塞打开
open(const char *path, O_WRONLY | O_NONBLOCK);//只写非阻塞打开
注意事项
概念
优点
共享内存是一种用户空间和内核空间的高效交互方式
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
功能:创建共享内存映射
返回值:成功返回创建的映射区首地址,失败返回MAP_FAILED( ((void *) -1) ),设置errno值
参数说明:
注意事项:
创建映射区的过程中,隐含着一次对映射文件的读操作,将文件内容读取到映射区
当MAP_SHARED时,要求:映射区的权限应 <=文件打开的权限(出于对映射区的保护),如果不满足报非法参数(Invalid argument)错误
当MAP_PRIVATE时候,mmap中的权限是对内存的限制,只需要文件有读权限即可,操作只在内存有效,不会写到物理磁盘,且不能在进程间共享
映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭
用于映射的文件大小必须>0,当映射文件大小为0时,指定非0大小创建映射区,访问映射地址会报总线错误,指定0大小创建映射区,报非法参数错误(Invalid argument)
文件偏移量必须为0或者4K的整数倍(不是会报非法参数Invalid argument错误)
映射大小可以大于文件大小,但只能访问文件page的内存地址,否则报总线错误 ,超出映射的内存大小报段错误
mmap创建映射区出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作
int munmap(void *addr, size_t length);
返回值:成功返回0,失败返回-1,并设置errno值
参数说明:
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(){
void *addr;
addr = mmap(NULL,2048, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
if(addr == MAP_FAILED){
perror("mmap");
return 0;
}
pid_t pid;
pid = fork();
if(pid<0){
perror("fork");
return 0;
}
else if(pid>0){
memcpy(addr,"1234567890",10);
wait(NULL);
}else {
sleep(1);
printf("read father val=%s\n",(char *)addr);
}
munmap(addr,2048);
}
概念:
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式;
Linux内核通过信号通知用户进程,不同的信号类型代表不同的事件;
Linux对早期的unix信号机制进行了扩展,而进程对信号有不同的相应方式:
信号的产生:
常用信号列举及说明:
kill/killall
#include
#include
int kill(pid_t pid, int sig);
int raise(int sig); //给自己发信号
int alarm(unsigned int seconds);
int pause(void);
useconds_t ualarm(useconds_t usecs, useconds_t interval);
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
struct itimerval {
struct timeval it_interval; // 闹钟触发周期
struct timeval it_value; // 闹钟触发时间
};
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
实现信号捕捉的步骤:
signal函数:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
参考示例:
#include
#include
#include
#include
#include
#include
typedef void (*sighandler_t)(int);
sighandler_t oldact;
void handle(int sig){
printf("I cath the SIGINT \n");
//执行之后还原原先的信号处理函数行为
signal(SIGINT,oldact);
}
int main(){
//接收到SIGINT信号之后执行handle,并将原先的信号处理函数oldact接收方便还原
oldact = signal(SIGINT,handle);
while(1){
sleep(1);
}
}
sigaction函数:
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
*Linux系统更建议使用sigaction函数,因为在不同unix版本中,signal函数表现可能不一致
使用sigaction和setitimer进行定时器函数的实现示例:
#include
#include
#include
#include
#include
#include
#include
typedef void (*sighandler_t)(int);
sighandler_t oldact;
void handle(int sig){
printf("second timer \n");
}
int main(){
struct sigaction act;
act.sa_handler = handle;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
struct itimerval timevalue;
//程序开始后5s发送SIGALRM信号,之后每隔一秒再发送一次
timevalue.it_interval.tv_sec = 1;
timevalue.it_interval.tv_usec = 0;
timevalue.it_value.tv_sec = 5;
timevalue.it_value.tv_usec = 0;
setitimer(ITIMER_REAL,&timevalue, NULL);
sigaction(SIGALRM,&act,NULL);
while(1){
}
}
使用SIGCHLD信号实现回收子进程
SIGCHLD的产生条件
示例程序:
#include
#include
#include
#include
#include
void handle(int sig){
//子进程结束时的SIGCHLD信号收到之后执行wait回收子进程
//可以方式wait阻塞父进程
wait(NULL);
printf("Get sig =%d\n",sig);
}
int main(){
pid_t pid;
struct sigaction act;
act.sa_handler = handle;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
pid = fork();
if(pid>0){
//wait(NULL);
//父进程运行并等待子进程结束时的SIGCHLD信号
sigaction(SIGCHLD,&act,NULL);
while(1){
printf("this is father process\n");
sleep(1);
}
}else if(pid==0){
sleep(5);
exit(0);
}
}
有时候不希望在接到信号时就立即停止当前执行,去处理信号,同时也不希望忽略该信号,而是延时一段时间去调用信号处理函数。这种情况可以通过阻塞信号实现
信号的阻塞:
信号的”阻塞“是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生
#include
int sigprocmask( int how, const sigset_t *restrict set, sigset_t *restrict oset );
示例程序:
#include
#include
#include
#include
void handle(int sig){
printf("I get sig=%d\n",sig);
}
int main(){
struct sigaction act;
act.sa_handler = handle;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT,&act,NULL);
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGINT);
sigprocmask(SIG_BLOCK,&set,NULL);
sleep(5);
sigprocmask(SIG_UNBLOCK,&set,NULL);
while(1){
sleep(1);
}
}
pause函数
int pause(void);
进程一直阻塞,直到被信号中断,返回值:-1 并设置errno为EINTR
sigsuspend函数
int sigsuspend(const sigset_t *sigmask);
功能:将进程的屏蔽字替换为由参数sigmask给出的信号集,然后挂起进程的执行
概念和特点:
使用步骤:
#include
#include
int msgget(key_t key, int msgflg);
程序示例:
……
int main() {
int msgid;
key_t key;
if ((key = ftok(“.”, ‘q’)) == -1) {
perror(“ftok”);
exit(-1);
}
if ((msgid = msgget(key, IPC_CREAT|0666)) < 0) {
perror(“msgget”);
exit(-1);
}
……
return 0;
}
#include
#include
int msgsnd(int msgid, const void *msgp, size_t size,int msgflg);
示例:
typedef struct{
long msg_type;
char buf[128];
}msgT;
注意:
#include
#include
int msgrcv(int msgid, void *msgp, size_t size, long msgtype, int msgflg);
msgid 消息队列id
msgp 消息缓冲区地址
size 指定接收的消息长度
msgtype 指定接收的消息类型:
msgtype=0:收到的第一条消息,任意类型
msgtype>0:收到的第一条 msg_type类型的消息
msgtype<0:接收类型等于或者小于msgtype绝对值的第一个消息
例如msgtype=-4,只接受类型是1、2、3、4的消息
msgflg 标志位:
#include
#include
int msgctl(int msgid, int cmd, struct msqid_ds *buf);
消息队列参考代码:
#include
#include
#include
#include
typedef struct{
long msg_type;
char buf[128];
}msgT;
#define MSGLEN (sizeof(msgT)-sizeof(long))
int main(){
key_t key;
int msgid;
int ret;
msgT msg;
key = ftok(".",100);
if(key<0){
perror("ftok");
return 0;
}
msgid = msgget(key,IPC_CREAT|0666);
if(msgid<0){
perror("msgget");
return 0;
}
msg.msg_type = 1;
strcpy(msg.buf,"this msg type 1");
ret = msgsnd(msgid,&msg,MSGLEN,0);
if(ret<0){
perror("msgsnd");
return 0;
}
msg.msg_type = 2;
strcpy(msg.buf,"this msg type 2");
ret = msgsnd(msgid,&msg,MSGLEN,0);
if(ret<0){
perror("msgsnd");
return 0;
}
msg.msg_type = 3;
strcpy(msg.buf,"this msg type 3");
ret = msgsnd(msgid,&msg,MSGLEN,0);
if(ret<0){
perror("msgsnd");
return 0;
}
msg.msg_type = 4;
strcpy(msg.buf,"this msg type 4");
ret = msgsnd(msgid,&msg,MSGLEN,0);
if(ret<0){
perror("msgsnd");
return 0;
}
msg.msg_type = 5;
strcpy(msg.buf,"this msg type 5");
ret = msgsnd(msgid,&msg,MSGLEN,0);
if(ret<0){
perror("msgsnd");
return 0;
}
}
#include
#include
#include
#include
typedef struct{
long msg_type;
char buf[128];
}msgT;
#define MSGLEN (sizeof(msgT)-sizeof(long))
int main(){
int msgid;
key_t key;
msgT msg;
int ret;
key = ftok(".",100);
if(key<0){
perror("ftok");
return 0;
}
msgid = msgget(key,IPC_CREAT|0666);
if(msgid<0){
perror("msgget");
return 0;
}
int count=0;
while(1){
ret = msgrcv(msgid,&msg,MSGLEN,0,0);
if(ret<0){
perror("msgrcv");
return 0;
}
count++;
if(count>3){
break;
}
printf("receiv msg type=%d,buf=%s\n",(int)msg.msg_type,msg.buf);
}
ret = msgctl(msgid,IPC_RMID,NULL);
if(ret<0){
perror("msgctl");
return 0;
}
}
信号量代表某一类资源,其值表示系统中该资源的数量;信号量是一个受保护的变量,只能通过三种操作来访问:
P(S) 含义如下:
if (信号量的值大于0) {
申请资源的任务继续运行;
信号量的值减一;
}
else { 申请资源的任务阻塞;}
V(S) 含义如下:
信号量的值加一;
if (有任务在等待资源) {
唤醒等待的任务,让其继续运行
}
信号灯种类:
有名信号灯的打开:
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);
有名信号灯的关闭:
int sem_close(sem_t *sem);
有名信号灯的删除:
int sem_unlink(const char* name);
无名信号灯初始化:
int sem_init(sem_t *sem, int shared, unsigned int value);
无名信号灯销毁:
int sem_destroy(sem_t* sem);
信号灯P操作:
int sem_wait(sem_t *sem);
获取资源,如果信号量为0,表示这时没有相应资源空闲,那么调用线程就将挂起,直到有空闲资源可以获取
信号灯V操作:
int sem_post(sem_t *sem);
释放资源,如果没有线程阻塞在该sem上,表示没有线程等待该资源,这时该函数就对信号量的值进行增1操作,表示同类资源多增加了一个。如果至少有一个线程阻塞在该sem上,表示有线程等待资源,信号量为0,这时该函数保持信号量为0不变,并使某个阻塞在该sem上的线程从sem_wait函数中返回
*注意:编译posix信号灯需要加pthread动态库
System V 信号灯使用:
int semget(key_t key, int nsems, int semflg);
int semop ( int semid, struct sembuf *opsptr, size_t nops);
struct sembuf {
short sem_num; // 要操作的信号灯的编号
short sem_op; // 1 : 释放资源,V操作
// -1 : 分配资源,P操作
short sem_flg; // 0(阻塞),IPC_NOWAIT, SEM_UNDO
};//对某一个信号灯的操作,如果同时对多个操作,则需要定义这种结构体数组
int semctl ( int semid, int semnum, int cmd…/*union semun arg*/);