LIUNX学习

LV5进程,线程和进程间通讯

5.1进程

1.进程和程序

程序:存放在磁盘上的指令和程序的有序集合,是静态的
进程:执行一次程序所需要分配资源的总称,是动态的
包括了进程的创建,调度,执行和衰亡等过程

2.进程的组成,类型及状态

进程的组成
在这里插入图片描述
代码段:用于存放执行程序所需要的代码,机器码
数据段:用于存放已经初始化的全局变量
BSS段:用于存放未初始化的全局变量
堆: 用于存放用户动态分配的内存,即用malloc分配的内存
栈: 用于存放局部变量,函数的地址,参数等,但不含static声明的变量

3进程常用命令

命令格式作用示例
psps -elf|grep 进程名查看指定进程的信息ps -elf|grep demo1
toptop动态查看进程信息top
/proc /proc查看进程详细信息/proc
nicenice [-n NI值]按用户指定优先级运行程序,NI值0-19,值越大优先级越低nice [-n 15]
renicerenice [优先级] pid号改变正在运行中进程的优先级renice [15] 1234
jobsjobs查看后台进程jobs
bgbg 索引号将进程挂起到后台运行bg 1
fgfg 索引号将后台进程放到前台运行fg 1
./进程名 &./进程名 &将程序转为后台程序运行./demo1 &

4进程的创建

#include <unistd.h>
pid_t fork(void);
/*
返回值:
成功:父进程返回子进程ID号
	  子进程返回0
失败:返回EOF
*/

辨析点:
1.fork函数作用:复制fork和其后内容给子进程执行
父进程:父进程执行完fork函数后会继续执行剩余程序
子进程:子进程复制fork函数和其以下的程序来执行
具体见demo1程序
2.返回值:
父进程fork后获取的为子进程的ID号
子进程fork后获取的ID值为0
可以通过fork的返回值来区分父子进程,执行不同程序代码
3.父子进程的执行顺序是由内核来进程调度的
4.父子进程有独立的地址空间,两者不会相互干扰
5.假设父进程先结束,则子进程会变为孤儿进程变init函数收养,并且程序变为后台程序
子进程先结束,父进程为及时回收,则子进程变为僵尸进程

见demo2程序

//demo1 进程的创建
/*
程序目的:
1.测试fork函数的作用
2.测试父子进程的pid返回值
3.实现父子进程如何实现不同代码
*/
#include <stdio.h>
#include <unistd.h>
int main(void)
{
	pid_t pid;
	printf("before fork\r\n");
	pid=fork();
	printf("after fork\r\n");
	if(pid>0)
	{
		//父进程
		printf("father process id=%d\r\n",pid);
	}else if(pid==0)
	{
		//子进程
		printf("child process id=%d\r\n",pid);
	}else
	{
		perror("fork");
		return 0;
	}
	printf("process create finish\r\n");
}

在这里插入图片描述
程序运行结果可以看出:
1.”before fork“字符串打印了一次,”after fork“和”process create finish“字符串打印了两次,足以说明
fork函数是将其以下的函数复制了一份给子进程执行
2.pid的返回值:父进程的返回值为1960,子进程的返回值为0,足以验证上述的返回值的现象

5进程的退出

#include <stdlib.h>
#include <unistd.h>
void exit(int status);
void _exit(int status);
void _Exit(int status);
//结束当前进程并且返回状态值

注意点:
1.exit函数返回会刷新流缓存区,其余不会刷新
2.exit和return函数的区别
mian函数中调用return 函数相当于隐式调用exit函数返回某个值
其他函数中调用return函数不会去调用exit函数

6 进程的回收

#include <sys/wait.h>
pid_t wait(int *status);
/*
成功 返回回收子进程的进程号
失败 返回EOF
可调用的宏
WIFEXITED(status)    判断进程是否正常结束
WEXITSTATUS(status)  判断子进程返回值
WIFSIGNALED(status)  判断子进程是否被信号结束
WTERMSIG(status)     判断结束子进程信号类型
*/
pid_t waitpid(pid_t pid,int *status,int option);
/*
成功返回指定回收子进程的pid或0,失败返回EOF
pid可以指定回收哪个子进程或任意子进程
pid>0 只等待ID等于pid的子进程结束(常用)
pid=0等待同一个进程组中的任意子进程,若子进程进入别的进程组,则不予理财
pid<-1等待一个指定进程组中的任意子进程,进程组ID等于pid的绝对值
status保存子进程返回值和结束方式的地址
option指定回收方式 0(阻塞)或WNOHANG(不等待)
*/

注意点:
1.若子进程未结束,wait函数处于阻塞状态
2.多子进程回收,哪个先结束哪个先回收
3.status存放子进程的返回值和结束方式的地址,若为NULL表示直接释放进程PCB进程控制块

//demo2 进程的退出及回收
/*
程序目的:
1.实现进程的退出和回收功能
2.验证父子进程退出的特性
3.使用ps命令查看对应的进程信息
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
	pid_t pid;
	printf("before fork\r\n");
	pid=fork();
	printf("after fork\r\n");
	if(pid>0)
	{
		//父进程
		printf("father process id=%d\r\n",pid);
		sleep(5);
		#if 0
			exit(0);
		#else
			while(1)
			{
				sleep(1);
			}
		#endif
	}else if(pid==0)
	{
		//子进程
		printf("child process id=%d\r\n",pid);
		#if 0
		while(1)
		{
		sleep(1);
		}
		#else
		sleep(5);
		exit(0);
		#endif
	}else
	{
		perror("fork");
		return 0;
	}
	printf("process create finish\r\n");
}
/*
编译:gcc -o demo2 demo2.c
执行:./demo2
查看进程:ps -elf|grep demo2
*/

执行结果:
实验1:父子进程中 #if 1
输入完ps命令后可以看出生成了两个进程
父进程pid号为1982
子进程pid号为1983
在这里插入图片描述
等待5s后父进程退出,再次输入ps命令查看进程信息
可以看出子进程PID=1983,其父进程ID变为了1002
在这里插入图片描述
再次输入ps -elf查看进程信息可以得到init进程的id号为1002,即证明了父进程结束后,子进程被init函数收养
此外5s延时结束后,程序退出了执行,但查看进程信息是发现子进程仍在运行,即子进程变为了后台进程
在这里插入图片描述
实验2:父子进程#if 0
等待5s后输入ps命令查看进程
可以看出子进程pid=2045后被加上[demo3],即表示为僵尸进程
在这里插入图片描述

5.2 exec函数族

1.exec函数族作用及函数

作用:实现程序的替换,让父子进程实现不同的程序.

#include <unistd.h>
int execl(const char *path,const char *arg,.....);
int execlp(const char *file,const char *arg,....);
/*
返回值:
成功:执行替换的程序
失败:返回EOF
参数:
path:执行程序的名称,包含路径
arg :所传递的参数
file:执行的程序,在path系统目录下查找
*/

注意点:
1.arg参数中,第0个参数必须写(未使用到),最后一个参数必须为NULL

//demo3 exec函数族使用
/*
程序目的:
1.使用exec实现程序替换
*/
//exec1.c
#include <stdio.h>
#include <unistd.h>
int main()
{
	printf("this is exec test!!\r\n");
}
//gcc -o exec1 exec1.c
//demo2.c
#include <stdio.h>
#include <unistd.h>
int main(void)
{
	printf("before exec/r/n");
	execlp("./exec1","0","NULL");
	printf("after exec/r/n");
}
//gcc -o demo demo1.c

执行结果:
可以看出执行结果替换了exec1的程序,且execlp函数下方的printf没有被执行

2.守护进程

守护进程:生存周期长,独立于中断并且周期性的执行某种任务或等待处理某种事件
特点:后台进程,孤儿进程,可避免被终端信息打扰
命令行格式:nohup xxx &
相关函数:

//设置进程的会话组ID
pid_t setsid(void);
/*
成功:返回调用进程的会话ID
失败:返回-1,并设置errno
备注:调用了setsid函数的进程,即是新的会长,也是新的组长
*/
pid_t getsid(pid_t pid);
/*
成功:返回进程ID
失败:返回-1
备注:获取会话ID
*/
pid_t getpid(void);
/*
成功:返回进程ID
失败:返回-1
备注:获取进程ID
*/
pid_t getpgid(pid_t pid);
/*
成功:返回进程组ID
失败:返回-1
备注:获取进程组ID
*/

3.守护进程的创建

//STEP1:创建子进程,父进程退出
if(fork()>0)
{
	exit(0);
}
//备注:子进程变孤儿进程,被init进程收养,变为后台进程
//STEP2:子进程设置为新会话组组长
if(setsid(pid)<0)
{
	perror("setsid");
	exit(0);
}
//备注:子进程当家作主
//STEP3:更改当前目录
chdir("/"); 
chdir("/tmp");
//备注:更改到根目录或者临时目录 确保程序的目录不会被删除
//STEP4:重新设置文件权限
if(umask(0)<0)
{
	perror("umask");
	exit(0);
}
//STEP5:关闭打开的文件描述符
for(i=0;i<3;i++)
{
	close(i);
}
//备注:关闭从父进程继承的标准文件描述符
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
	pid_t pid;
	pid=fork();
	if(pid>0)
	{
		exit(0);
	}else if(pid<0)
	{
		perror("fork");
		exit(0);
	}
	printf("before pid=%d,sid=%d,pgid=%d\r\n",getpid(),getsid(getpid()),getpgid(getpid()));
	if(setsid()<0)
	{
		perror("setsid");
		exit(0);
	}
	printf("after pid=%d,sid=%d,pgid=%d\r\n",getpid(),getsid(getpid()),getpgid(getpid()));
	chdir("/");
	if(umask(0)<0)
	{
		perror("umask");
		exit(0);
	}
	close(0);
	close(1);
	close(2);
}

在这里插入图片描述

4.gcc调试多进程

//相关命令
//启动GDB
gcc -o -g demo demo.c
gdb demo
//执行程序
run   //单步执行
start //一次性执行
n     //下一步
//设置选择分支
set follow-fork-mode child   //选择为子分支
set follow-fork-mode parent //选择为父分支
set detach-on-fork on/off   //设置调试的分支 ON只调试父或子 OFF两个都调试,一个调试一个暂停
//查看调试进程
info inferiors 进程号       //切换调试进程


5.3 线程

1进程和线程

进程:有独立的栈空间,每个进程都参与内核调度
线程:共享栈空间
LINUX不加以区分线程和进程

2.相关函数

#include <pthread.h>
//创建线程
int pthread_create(pthread_t *thread,pthread_atrr_t *atrr,void *(*rountine)(void *),void *arg);
/*
成功,返回0
失败,分会EOF
参数:
thread: 线程对象
atrr:   线程属性,默认属性一般填写NULL
routine:执行的函数指针
arg:    函数指针参数
*/
//线程退出
void pthread_exit(void *retval);
/*
结束当前进程
参数:
retval:返回的参数,一般被pthread_join获取
注意点:线程参数的传参方式:按值传递和按址传递
*/
//线程号的查看
pthread_t pthread_self(void);
/*
查看线程id号
线程命令行查看命令 ps -eLf|grep 线程名
*/
//线程的回收
int pthread_join(pthread_t thread,void **retval);
/*
成功,返回0
失败,返回EOF
参数:
thread:回收的线程对象
revtal:回收的参数值
注意点:
1.线程结束不意味着线程资源得到释放,需要调用回收函数
2.函数为阻塞函数,指定线程未结束则继续等待
3.线程回收只能一个一个回收
*/
//线程的取消
void pthread_cancle(pthread_t thread);
/*
需要有取消点才能取消线程
取消点:系统阻塞调用 sleep,死循环等
无取消点可设置取消点
*/
//设置取消点
void pthread_testcancel(void);
int pthread_seycancelstate(int state,int oldstate);
/*
参数:
state:		PTHREAD_CANCEL_ENABLE   可取消
	   		PTHREAD_CANCEL_DISABLE  不可取消
oldstate:	返回原来状态
*/
int pthread_setcancel(int type,int *oldtype);
/*
参数1:PTHREAD_CANCEL_DEFERRED     到取消点取消
	  PTHREAD_CANCEL_ASYNCHRONOUS 目标线程立即取消
参数2:返回原来状态
*/
//线程清理
void pthread_cleanup_push(void *rounte(void *),void *arg));
void pthread_cleanup_pop(int execute);
/*
作用:对线程中申请的变量进行释放操作
参数
execute: 0表示为不执行回调函数
		 1表示为执行回调函数
执行函数的条件:
1.pthread_cancel函数取消掉
2.执行了pthread_exit函数
3.非0参数
注意点:
1,对线程申请的变量进行释放
2.push和pop函数必须成对使用,不然编译会报错
*/

3.辨析点

/*
线程创建的参数传递方式:
1.按值传递
void *func(void *arg)
{
	printf("agr=%d\r\n",*(int *)arg);
}
int mian()
{
	thread_t thread;
	int arg=5;
	pthread_create(thread,NULL,func,(void *)&arg);

}
2.按址传递
void *func(void *arg)
{
	printf("agr=%d\r\n",(int *)arg);
}
int mian()
{
	thread_t thread;
	int arg=5;
	pthread_create(thread,NULL,func,(void *)arg);
}
//注意点:按值传递时会报警告需要注意所传参数和指针大小相同
*/
/*
线程退出和回收的方法:
1.主线程调用pthread_join()函数实现
2.子线程中调用pthread_detach(pthread_self());
3.主线程在创建线程时,设置线程属性
pthread_attr_t atrr;
pthread_attr_init(&atrr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
pthread_create(&tid,&atrr,func,NULL);
*/

4示例代码

//demo1 线程的创建
/*
程序目的:
1.创建线程,线程的退出及回收
2.线程得参数传递方式
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_tid1(void *arg)
{
	pthread_detach(pthread_self());
	printf("thread1 printf=%d\r\n",*(int *)arg);
	thread_exit("thread11 exit!!\r\n");
}
void *thread_tid2(void *arg)
{
	pthread_detach(pthread_self());
	printf("thread1 printf=%d\r\n",(int *)arg);
	thread_exit("thread12 exit!!\r\n");
}
int mian()
{
	pthread_t tid;
	void *reval1,*reval2;
	int arg=5;
	//址传递
	pthread_create(&tid,NULL,thread_tid1,&(void *)arg);
	//值传递
	pthread_create(&tid,NULL,thread_tid2,(void *)arg);
	sleep(5);
	pthread_join(&reval1);
	pthread_join(&reval2);
}

实验结果为:5s后程序自动退出

在这里插入图片描述

//demo2 线程得取消
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *func(void *arg){
    printf("This is child thread\n");
    //设置取消点不取消线程
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);    
    sleep(5);
    //设置取消点
    pthread_testcancel();
    //设置到取消点取消线程
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
    //取消点
    while(1){
        sleep(1);
    } 
    pthread_exit("thread return");
}
int main(){
    pthread_t tid;
    void *retv;
    int i;
    pthread_create(&tid,NULL,func,NULL);
    sleep(1);
    pthread_cancel(tid);
    pthread_join(tid,&retv);
    while(1){    
        sleep(1);
    } 
 
}

//demo3 线程的回收
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
 
void cleanup(void *arg){
    printf("cleanup,arg=%s\n",(char*)arg);
}
void cleanup2(void* arg){
    printf("cleanup2,arg=%s\n",(char*)arg);
}
 
void *func(void *arg){
    printf("This is child thread\n");
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
    pthread_cleanup_push(cleanup,"abcd");
    pthread_cleanup_push(cleanup2,"efgh");
    //while(1)
    {
        sleep(1);
         
    }
    while(1){
        printf("sleep\n");
        sleep(1);
    }
    pthread_exit("thread return");
    pthread_cleanup_pop(1);
    pthread_cleanup_pop(1);
    sleep(10);
    pthread_exit("thread return");
}
int main(){
    pthread_t tid;
    void *retv;
    int i;
    pthread_create(&tid,NULL,func,NULL);
    sleep(1);
    pthread_join(tid,&retv);
    printf("thread ret=%s\n",(char*)retv);
    while(1){    
        sleep(1);
    } 
 
}

5.5 信号量

1.互斥信号量

临界资源 :只允许一个任务或线程访问的资源
临界区 :访问临界资源的代码
互斥机制 :访问临界资源前申请锁,访问资源后释放锁

2.函数

//初始化
#include  <pthread.h>
//动态方法
int  pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *  attr);
//静态方法
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
/*
返回值:成功返回0,失败返回错误代码
参数:
	mutex:指向初始化的互斥锁对象
	attr :互斥锁属性,NULL标识缺省属性
*/
//互斥锁销毁
int pthread_mutex_destroy(pthread_mutex_t *mutex)
//申请锁
int  pthread_mutex_lock(pthread_mutex_t *mutex);
int  pthread_mutex_trylock(pthread_mutex_t *mutex);
/*
返回值:成功返回0,失败返回错误代码
mutex  指向要初始化的互斥锁对象
pthread_mutex_lock 如果无法获得锁,任务阻塞
pthread_mutex_trylock 如果无法获得锁,返回EBUSY而不是挂起等待
*/
//释放锁
int  pthread_mutex_unlock(pthread_mutex_t *mutex);
/*
返回值:成功返回0,失败返回错误代码
mutex  指向要初始化的互斥锁对象
执行完临界区要及时释放锁
*/
//初始化一个读写锁   
pthread_rwlock_init()
//读锁定读写锁       
pthread_rwlock_rdlock()
//非阻塞读锁定     
pthread_rwlock_tryrdlock()
//写锁定读写锁       
pthread_rwlock_wrlock()
//非阻塞写锁定      
pthread_rwlock_trywrlock()
//解锁读写锁        
pthread_rwlock_unlock()
//释放读写锁       
pthread_rwlock_destroy()
*/

3.读写锁特性

写锁:写者尝试使用写锁,若当前无任何读者和写着,则立马获取锁,
不然等待直到无读者或其他写者
读锁:读者尝试使用读锁,若当前无读者,则立马获得读锁,不然直到等待无读者
同一时刻,可以有多个读者获得锁,只能有一个写者获得锁

4.死锁现象

线程1占据锁1
线程2占据锁2
线程1和线程均向获得彼此的锁----死锁现象,程序阻塞
解决方法:尽量采用一把锁或调整锁的顺序

5.条件变量使用

//生产资源不可共有,生产资源的时间可以是随机的函数
//等待资源
int pthread_cond_wait()
//等待资源超时退出
int pthread_cond_timedwait();
//等到资源信号释放资源
int pthread_cond_signal();
//广播等待资源信号释放资源
int pthread_cond_broadcast()

6.条件变量使用步骤

//SETP1:信号量初始化
pthread_cond_t cond=PTHREAD_COND_INITIALIZE;
pthread_mutex_t mutex=PTHREAD)_MUTEX_INITIALIZE;
pthread_cond_init(&cond);
//STEP2:生产者线程资源
pthread_mutex_lock(&mutex);
pthread_cond_sigal(&cond);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
//STEP3:消费资源线程
pthread_mutex_lock(&mutex);
while(没有资源)
{
	pthread_cond_wait(&cond,&mutex);
}
pthread_mutex_unlock(&mutex);

7.线程池

一个线程的池子,可以循环的完成任务的一组线程集合
特点:
我们平时创建一个线程,完成某一个任务,等待线程的退出。但当需要创建大量的线程时,假设
T1为创建线程时间,T2为在线程任务执行时间,T3为线程销毁时间,当T1+T3 > T2,这时候就不划算了,使用线程池可以降低频繁创建和销毁线程所带来的开销,任务处理时间比较短的时候这个好处非常显著
结构:线程池的基本结构:
1.任务队列,
存储需要处理的任务,由工作线程来处理这些任务
2.线程池工作线程,
它是任务队列任务的消费者,等待新任务的信号
实现步骤:
STEP1
创建线程池的基本结构:
任务队列链表
typedef struct Task;
线程池结构体
typedef struct ThreadPool;
STEP2.
线程池的初始化:
pool_init()
{
创建一个线程池结构
实现任务队列互斥锁和条件变量的初始化
创建n个工作线程
}
STEP3
线程池添加任务
pool_add_task
{
判断是否有空闲的工作线程
给任务队列添加一个节点
给工作线程发送信号newtask
}
STEP4
实现工作线程
workThread
{
while(1){
等待newtask任务信号
从任务队列中删除节点
执行任务
}
}
STEP5
线程池的销毁
pool_destory
{
删除任务队列链表所有节点,释放空间
删除所有的互斥锁条件变量
删除线程池,释放空间
}

8.程序示例

//demo1 互斥量使用
/*
两个线程同时写入文件
*/
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
 //静态声明信号量
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);
    } 
}
//demo2:读写锁使用
/*
四个线程,两个线程读,两个线程写
*/
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
pthread_rwlock_t rwlock;
FILE *fp;
void * read_func(void *arg){
    pthread_detach(pthread_self());
    printf("read thread\n");
    char buf[32]={0};
    while(1){
        pthread_rwlock_rdlock(&rwlock);
        while(fgets(buf,32,fp)!=NULL){
            printf("%d,rd=%s\n",(int)arg,buf);
            usleep(1000);
        }
        pthread_rwlock_unlock(&rwlock);
        sleep(1);
    }
}
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_rwlock_wrlock(&rwlock);
        while(i<strlen(str))
        {
            c = str[i];
            fputc(c,fp);
            usleep(1);
            i++;
        }
        pthread_rwlock_unlock(&rwlock);
        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_rwlock_wrlock(&rwlock);
        while(i<strlen(str))
        {
            c = str[i];
            fputc(c,fp);
            i++;
            usleep(1);
        }
        pthread_rwlock_unlock(&rwlock);
        i=0;
        usleep(1);
    }
    pthread_exit("func1 exit");
}
int main(){
    pthread_t tid1,tid2,tid3,tid4;
    void *retv;
    int i;
    fp = fopen("1.txt","a+");
    if(fp==NULL){
        perror("fopen");
        return 0;
    }
    pthread_rwlock_init(&rwlock,NULL);
    pthread_create(&tid1,NULL,read_func,1);
    pthread_create(&tid2,NULL,read_func,2);
    pthread_create(&tid3,NULL,func,NULL);
    pthread_create(&tid4,NULL,func2,NULL);
    while(1){    
        sleep(1);
    } 
}
//demo3 条件变量使用
/*
两个线程,一个线程生产taxi资源
        一个线程消费taxi资源
*/
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
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;
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_cond_broadcast(&hasTaxi);
        pthread_mutex_unlock(&lock);
        sleep(1);
    }
    pthread_exit(0);
}
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);
//    sleep(5);
    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);
 
    }
}
//demo4 简单线程池使用
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define POOL_NUM 10
typedef struct Task{
    void *(*func)(void *arg);
    void *arg;
    struct Task *next;
}Task;
typedef struct ThreadPool{
    pthread_mutex_t taskLock;
    pthread_cond_t newTask;
    pthread_t tid[POOL_NUM];
    Task *queue_head;
    int busywork;
}ThreadPool;
ThreadPool *pool;
void *workThread(void *arg){
    while(1){
        pthread_mutex_lock(&pool->taskLock);
        pthread_cond_wait(&pool->newTask,&pool->taskLock);
        Task *ptask = pool->queue_head;
        pool->queue_head = pool->queue_head->next;
        pthread_mutex_unlock(&pool->taskLock);
        ptask->func(ptask->arg);
        pool->busywork--;
    }
}
void *realwork(void *arg){
    printf("Finish work %d\n",(int)arg);
}
void pool_add_task(int arg){
    Task *newTask;
    pthread_mutex_lock(&pool->taskLock);
    while(pool->busywork>=POOL_NUM){
        pthread_mutex_unlock(&pool->taskLock);
        usleep(10000);
        pthread_mutex_lock(&pool->taskLock);
    }
    pthread_mutex_unlock(&pool->taskLock);
    newTask = malloc(sizeof(Task));
    newTask->func =  realwork;
    newTask->arg = arg;
    pthread_mutex_lock(&pool->taskLock);
    Task *member = pool->queue_head;
    if(member==NULL){
        pool->queue_head = newTask;
    }else{
       while(member->next!=NULL){
            member=member->next;
       }
       member->next = newTask;
    }
    pool->busywork++;
    pthread_cond_signal(&pool->newTask);
    pthread_mutex_unlock(&pool->taskLock);
}
void pool_init(){
    pool = malloc(sizeof(ThreadPool));
    pthread_mutex_init(&pool->taskLock,NULL);
    pthread_cond_init(&pool->newTask,NULL);
    pool->queue_head = NULL;
    pool->busywork=0;
    for(int i=0;i<POOL_NUM;i++){
        pthread_create(&pool->tid[i],NULL,workThread,NULL);
    }
}
void pool_destory(){
    Task *head;
    while(pool->queue_head!=NULL){
        head = pool->queue_head;
        pool->queue_head = pool->queue_head->next;
        free(head);
    }
    pthread_mutex_destroy(&pool->taskLock);
    pthread_cond_destroy(&pool->newTask);
    free(pool);
}
int main(){
   pool_init();
   sleep(20);
   for(int i=1;i<=20;i++){
       pool_add_task(i);
   }
   sleep(5);
   pool_destory();
}

5.6 进程间通信

1.通讯类型

无名管道
有名管道
信号
共享内存
套接字

2.无名管道
int pipe(int pfd[2]); 
/*
成功:0;失败:-1,设置errno
参数:   
pfd[0] 读管道    pfd[1] 写管道
*/

注意点:
1.只能用于亲缘关系的进程间通信(父子进程,兄弟进程)
2.管道通信是单工的,一端读,一端写(程序实现设计好)
3. 数据自己读不能自己写
4. 管道可以用于大于2个进程共享
5.管道写端被全部关闭,read返回0 (好像读到文件结尾)
6.写端没有全部被关闭,read阻塞等待(不久的将来可能有数据递达,此时会让出cpu)
7.写管道
管道读端全部被关闭, 进程异常终止(也可使用捕捉SIGPIPE信号,使进程不终止)
管道读端没有全部关闭
管道已满,write阻塞。(管道大小64K)
管道未满,write将数据写入,并返回实际写入的字节数。

3.有名管道
#include  <unistd.h>
#include <fcntl.h>
int mkfifo(const char *filename, mode_t mode);
/*
成功时返回0,失败时返回EOF
path  创建的管道文件路径
mode 管道文件的权限,如0666
*/
//打开文件
open(const char *path, O_RDONLY);             //1
open(const char *path, O_RDONLY | O_NONBLOCK);//2
open(const char *path, O_WRONLY);             //3
open(const char *path, O_WRONLY | O_NONBLOCK);//4

注意点:
1.程序不能以O_RDWR(读写)模式打开FIFO文件进行读写操作,管道是单工的即单方向传输
2.O_NONBLOCK表示为该函数为非阻塞函数,不加参数默认为阻塞函数
3.如果有多个进程写同一个管道,使用O_WRONLY方式打开管道,如果写入的数据长度小于等于
PIPE_BUF(4K),那么或者写入全部字节,或者一个字节都不写入,系统就可以确保数据决不
会交错在一起。

4.内存映射

指定一块内存区域映射磁盘文件,使得进程可以像访问内存一样访问文件,不必使用read和write函数
函数

//创建共享内存
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);	
/*
addr:设置映射的内存地址,一般设置为NULL让系统自动选择
length:必须>0,映射地址空间的字节数,可从被映射文件开头offset个字节算起
prot:指定共享内存的访问权限,可选参数
PROT_READ(可读), PROT_WRITE(可写), PROT_EXEC(可执行), PROT_NONE(不可访问)
flags:MAP_SHARED(共享的) MAP_PRIVATE(私有的), MAP_FIXED(不推荐使用),MAP_ANONYMOUS(匿名映射,用于血缘关系进程间通信)
fd;表示要映射的文件句柄。如果匿名映射写-1
offset:表示映射文件的偏移量,一般设置为 0 表示从文件头部开始映射。
*/
//内存释放
int munmap(void *addr, size_t length);
/*
返回值:成功返回0,失败返回-1,并设置errno值。
函数参数:
addr:调用mmap函数成功返回的映射区首地址
length:映射区大小(即:mmap函数的第二个参数)
*/

注意点:
映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭。
用于映射的文件大小必须>0
文件偏移量必须为0或者4K的整数倍
一定要检查返回值确保共享内存建立成功后在操作

5.System V共享内存
//使用步骤:
//1.创建/打开共享内存
int shmget(key_t key, int size, int shmflg);
/*
   key值可以通过调用ftok函数实现
   key_t  ftok(const char *path,  int id);
   path    指定文件名
   id      指定id号,范围0-255
   size:  内存大小
   shmflg:IPC_CREAT|0666
   返回值:失败返回-1
*/
//2.映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
void  *shmat(int shmid, const void *shmaddr, int shmflg);
/*
	参数2填写NULL,表示为自动映射
	参数3一般写0,表示为可读写
*/
//3 读写共享内存
//往shmat函数的返回地址中填写数据
//4 撤销共享内存映射
int  shmdt(void *shmaddr);
//撤销后不可访问
//5 删除共享内存对象
shmctl(shmid, IPC_RMID, NULL);
6.示例代码
//demo1 无名管道
/*
父进程写数据,子进程读数据
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(){
    int pfd[2];
    int re;
    char buf[20]={0};
    pid_t pid;
    re = pipe(pfd);
    if(re<0){
        perror("pipe");
        return 0;
    }
    printf("%d,%d\n",pfd[0],pfd[1]);
    pid = fork();
    if(pid<0){
        perror("fork");
        return 0;
    }else if(pid>0){
        //close(pfd[0]);
        while(1){
            strcpy(buf,"hhahahahah");
            write(pfd[1],buf,strlen(buf));
            sleep(1);
        }
    }else{
        close(pfd[1]);        
       while(1){
            re=read(pfd[0],buf,20);
            if(re>0){
                printf("read pipe=%s\n",buf);
            }    
        }
    }
}
//demo2 有名管道
//fifow.c 进程写数据功能
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
int main(){
    int re;
    int fd;
    char buf[32];
    re = mkfifo("/myfifo",0666);
    if(re<0){
        perror("mkfifo");
        //return 0;
    }
    fd = open("/myfifo",O_WRONLY|O_NONBLOCK);
    if(fd<0){
        perror("open");
        return 0;
    }
    printf("after open\n");
    while(1){
        fgets(buf,32,stdin);
        write(fd,buf,strlen(buf));
    }
}
//fifor.c 进程读数据功能
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h.
int main(){
    int re;
    int fd;
    char buf[32];
/*    re = mkfifo("/myfifo",0666);
    if(re<0){
        perror("mkfifo");
        return 0;
    }
    */
    fd = open("/myfifo",O_RDONLY);
    if(fd<0){
        perror("open");
        return 0;
    }
    printf("after open\n");
    while(1){
         
        re=read(fd,buf,32);
        if(re>0){
            printf("read fifo=%s\n",buf);
        }else if(re==0){
            exit(0);
        }
    }
}
//执行程序前,需要先创建文件myfifo
//demo3 内存映射通讯
//shmw.c 进程写数据
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/shm.h>
#include <string.h>
 
int main(){
    key_t key;
    int shmid;
    char *buf;
    key = ftok("keytest",100);
    if(key<0){
        perror("ftok");
        return 0;
    }
    printf("key=%x\n",key); 
    shmid = shmget(key,512,IPC_CREAT|0666);
    if(shmid<0){
        perror("shmget");
        return 0;
    }
    printf("shmid=%d\n",shmid);
    buf = shmat(shmid,NULL,0);
    if(buf<0){
        perror("shmat");
        return 0;
    }    
    strcpy(buf,"hello world");
}
//shmr.c 进程读数据
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>
int main(){
    key_t key;
    int shmid;
    char *buf;
    key = ftok("keytest",100);
    if(key<0){
        perror("ftok");
        return 0;
    }
    printf("key=%x\n",key);
    shmid = shmget(key,512,0666);
    if(shmid<0){
        perror("shmget");
        return 0;
    }    
    printf("shmid=%d\n",shmid);
    buf = shmat(shmid,NULL,0);
    if(buf<0){
        perror("shmat");
        return 0;
    }    
//    strcpy(buf,"hello world");
    printf("share mem=%s\n",buf);
    while(1){
        sleep(1);
    }    
    shmdt(buf);
    shmctl(shmid, IPC_RMID, NULL);
//    printf("detach mem=%s\n",buf);
 
//demo4 system V共享内存
//mmapw.c
#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
 
int main(){
     
    void *addr;
    int fd;
    fd =open("test",O_RDWR);
    if(fd<0){
        perror("open");
        return 0;
    }
    int len = lseek(fd,0,SEEK_END);    
    addr = mmap(NULL,2048, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if(addr == MAP_FAILED){
        perror("mmap");
        return 0;
    }
    close(fd);
    int i=0;
    while(i<2048){
        memcpy((addr+i),"a",1);
        i++;
        sleep(1);
    }    
//    printf("read=%s\n",(char*)(addr));
}
//mmap_r
#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
    void *addr;
    int fd;
    fd =open("test",O_RDWR);
    if(fd<0){
        perror("open");
        return 0;
    }
    int len = lseek(fd,0,SEEK_END);    
    addr = mmap(NULL,2048, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if(addr == MAP_FAILED){
        perror("mmap");
        return 0;
    }
    close(fd);
//    memcpy((addr),"99999999999999",15);
    while(1){
        printf("read=%s\n",(char*)(addr));
        sleep(1);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值