最近在学习Linux系统编程,记录一下学习过程中写过的程序,编程语言为C++,系统为Ubuntu 20.04.2 LTS
文章目录
- 文件复制:模拟实现cp命令
- 递归遍历目录:模拟实现ls -R命令
- fork创建子进程
- pipe()管道实现进程间通信
- mmap()建立映射区
- 信号集练习
- sigaction()注册信号捕捉函数
- signal注册信号捕捉函数
- 借助信号捕捉回收子进程
- 创建守护进程
- thread_create()创建线程
- 循环创建多个线程
- pthread_join()等待回收线程
- pthread_detach()设置线程分离
- 线程属性设置线程分离
- 互斥锁/互斥量mutex练习
- 条件变量 生产者消费者模型
- 信号量 生产者消费者模型
1.文件复制:模拟实现cp命令
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-09 21:00:22
* @ Modified time: 2021-07-09 21:43:44
* @ Description: 通过linux下的read和write系统调用,模拟实现cp命令,完成文件复制功能
*/
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <fcntl.h>
using namespace std;
int main(int argc, char* argv[]){
if(argc!=3){ //检查参数个数是否为3个,若不是,则显示警告后,退出程序
cout<<"参数非法,请重新输入!\n";
exit(1);
}
int fd1 = open(argv[1],O_RDONLY); //打开待读取文件
if(fd1==-1){ //打开失败
perror("read error"); //显示错误信息
exit(1);
}
int fd2 = open(argv[2],O_WRONLY|O_TRUNC|O_CREAT,0644); //打开待写入文件,不存在则创建
if(fd2==-1){ //打开或创建失败
perror("write error"); //显示错误信息
exit(1);
}
char buf[1024];
int val = 0; //保存read函数返回值,返回值为0说明已读之文件末尾
while((val = read(fd1,buf,1024))!=0){
write(fd2,buf,val); //写入文件
}
cout<<"文件复制完毕,"<<argv[2]<<"已生成!\n";
close(fd1); //关闭文件
close(fd2);
return 0;
}
运行结果
2.递归遍历目录:模拟实现ls -R命令
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-12 13:11:40
* @ Modified time: 2021-07-12 14:25:34
* @ Description: 模拟实现ls -R命令,完成递归遍历目录
*/
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#include <cstring>
#include <stdio.h>
using namespace std;
void isFile(const string& name);
void read_dir(const string dir){
DIR* dp = opendir(dir.c_str()); //打开目录
if(dp == nullptr){ //失败
perror("opendir error");
return ;
}
struct dirent *sdp;
while((sdp = readdir(dp)) != nullptr){ //读取目录
//文件名为“.”或“..”,则跳过
if(strcmp(sdp->d_name, ".") == 0 || strcmp(sdp->d_name, "..") == 0){
continue;
}
//拼接文件名
string f_name = string(sdp->d_name,strlen(sdp->d_name));
string path = dir + "/" + f_name;
isFile(path); //递归调用isFile()
}
closedir(dp);
return ;
}
void isFile(const string& name){
struct stat sbf;
//获取文件属性,判断文件类型(文件或文件目录)
int ret = stat(name.c_str(), &sbf);
if(ret == -1){ //失败
perror("stat error");
return ;
}
if(S_ISDIR(sbf.st_mode)){ //目录
read_dir(name);
}
//普通文件
printf("file_size: %-10ld",sbf.st_size); //输出文件大小,左对齐,占10位
cout << "file_name: " << name <<"\n"; //文件名称
return ;
}
int main(int argc, char* argv[]){
//未输入参数,则默认当前目录,已输入参数,则使用输入的参数
string name = (argc==1) ? "." : argv[1];
isFile(name);
return 0;
}
运行结果
3.fork创建子进程
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-13 09:40:24
* @ Modified time: 2021-07-13 09:53:10
* @ Description: 使用fork()创建子进程
*/
#include <iostream>
#include <cstdlib>
#include <unistd.h>
using namespace std;
int main(int argc, char* argv[]){
cout<<"before fork - 1 ---\n";
cout<<"before fork - 2 ---\n";
cout<<"before fork - 3 ---\n";
cout<<"before fork - 4 ---\n";
pid_t pid = fork();
if(pid == -1){
perror("Fork error");
exit(1);
}
else if(pid==0){
cout<<"Child process was created!\n";
cout<<"my pid: "<<getpid();
cout<<" my parent pid: "<<getppid()<<"\n";
}
else if(pid > 0){
cout<<"This is parent process. \nMy child is "<<pid;
cout<<" my pid: "<<getpid();
cout<<" my parent pid: "<<getppid()<<"\n";
}
cout<<"This is the end of file.\n";
return 0;
}
运行结果
4.pipe()管道实现进程间通信
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-15 14:26:32
* @ Modified time: 2021-07-15 14:44:06
* @ Description : pipe()管道练习
*/
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>
using namespace std;
int main(int argc, char* argv[]){
int fd[2];
int ret = pipe(fd);
if(ret == -1){
perror("pipe error");
}
pid_t pid = fork();
if(pid > 0){ //父进程
close(fd[0]); //关闭管道的读端
string str = "Hello pipe!\n";
write(fd[1],str.c_str(),str.size()); //向管道写数据
close(fd[1]);
wait(nullptr); //回收子进程
}
else if(pid == 0){ //子进程
close(fd[1]); //关闭管道的写端
char buf[1024];
int size = read(fd[0],buf,sizeof(buf)); //从管道读数据
write(STDOUT_FILENO,buf,size); //将读到的数据输出到屏幕
close(fd[0]);
}
return 0;
}
运行结果
5.mmap()建立映射区
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-15 20:51:09
* @ Modified time: 2021-07-16 15:27:17
* @ Description : mmap建立映射区
*/
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
using namespace std;
int main(int argc, char* argv[]){
//打开或创建测试文件
int fd = open("test.txt",O_RDWR | O_CREAT | O_TRUNC, 0644);
if(fd == -1){
perror("file open error");
exit(1);
}
int num = unlink("test.txt"); //删除测试文件
if(num == -1){
perror("unlink error");
exit(1);
}
ftruncate(fd,50); //扩展文件大小
int len = lseek(fd,0,SEEK_END); //获取文件长度
cout<<len<<"\n";
//建立映射区
char *p = (char*)mmap(nullptr,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(p==MAP_FAILED){
perror("mmap error"); //失败,退出程序
exit(1);
}
close(fd); //关闭文件
strcpy(p,"this is a test for mmap()"); //向文件写操作
printf("-----%s-----\n",p);
int ret = munmap(p,len); //释放映射区
if(ret == -1){ //错误
perror("munmap error");
exit(1);
}
return 0;
}
运行结果
6.信号集练习
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-16 21:40:55
* @ Modified time: 2021-07-16 21:54:28
* @ Description : 信号集练习
*/
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
using namespace std;
void print_set(sigset_t *set){
for(int i=1; i<32; ++i){
if(sigismember(set,i)){
putchar('1');
}
else{
putchar('0');
}
}
printf("\n");
}
int main(int argc, char* argv[]){
sigset_t set,oldset,pedset;
int ret = 0;
sigemptyset(&set);
sigaddset(&set,SIGINT);
ret = sigprocmask(SIG_BLOCK,&set,&oldset);
if(ret == -1){
perror("sigprocmask error");
exit(1);
}
while(true){
ret = sigpending(&pedset);
if(ret == -1){
perror("sigpending error");
exit(1);
}
print_set(&pedset);
sleep(1);
}
return 0;
}
运行结果
7.sigaction()注册信号捕捉函数
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-17 20:34:45
* @ Modified time: 2021-07-17 20:57:12
* @ Description : sigaction()注册信号捕捉函数
*/
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
using namespace std;
void sig_catch(int sigNo){ //回调函数
cout<<"\nCatch you! sigNo: "<<sigNo<<"\n";
return ;
}
int main(int argc, char* argv[]){
struct sigaction act,oldact;
act.sa_handler = sig_catch; //设置回调函数
sigemptyset(&(act.sa_mask)); //清空sa_mask屏蔽字
act.sa_flags = 0; //默认值
int ret = sigaction(SIGINT,&act,&oldact); //注册信号捕捉函数
if(ret == -1){
perror("sigaction error");
exit(1);
}
ret = sigaction(SIGQUIT,&act,&oldact); //注册信号捕捉函数
while(true);
return 0;
}
运行结果
8.signal注册信号捕捉函数
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-17 20:38:09
* @ Modified time: 2021-07-17 20:42:10
* @ Description : signal注册信号捕捉函数
*/
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
using namespace std;
void sig_catch(int sigNo){
cout<<"\nCatch you! sigNo: "<<sigNo<<"\n";
return ;
}
int main(int argc, char* argv[]){
signal(SIGINT,sig_catch);
while(true);
return 0;
}
运行结果
9.借助信号捕捉回收子进程
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-17 21:02:18
* @ Modified Time: 2021-07-17 21:27:45
* @ Description : 借助信号捕捉回收子进程
*/
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>
using namespace std;
void catch_child(int sigNo){
pid_t wpid;
while((wpid = wait(nullptr)) != -1){ //循环回收,防止僵尸进程出现
cout<<"------catch child,id = "<<wpid<<"\n";
}
return ;
}
int main(int argc, char* argv[]){
pid_t pid;
int i;
for(i=0;i<5;++i){
if((pid = fork())==0){
break;
}
}
if(5==i){ //父进程
cout<<"I am parent, pid = "<<getpid()<<"\n";
struct sigaction act;
act.sa_handler = catch_child;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGCHLD,&act,nullptr);
while(true);
}else{ //子进程
cout<<"I am child, pid = "<<getpid()<<"\n";
}
return 0;
}
运行结果
10.创建守护进程
创建守护进程步骤
- fork创建子进程,父进程退出。所有工作在子进程中进行,形式上脱离了控制终端;
- 在子进程中创建新会话,setsid();
- 根据需要,改变工作目录,chdir(),防止占用可卸载的文件系统;
- 根据需要,重设文件权限掩码,umask();
- 根据需要,关闭/重定向文件描述符;
- 守护进程业务逻辑
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-17 22:51:06
* @ Modified Time: 2021-07-17 23:27:46
* @ Description : 创建守护进程
*/
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
using namespace std;
void sys_error(string str){ //错误处理
perror(str.c_str());
exit(1);
}
int main(int argc, char* argv[]){
pid_t pid;
pid = fork();
if(pid>0){ //父进程终止
exit(0);
}
//子进程
pid = setsid(); //创建新会话
if(pid == -1){
sys_error("setsid error");
}
int ret = chdir("/home/wangyusong"); //改变工作目录位置
if(ret == -1){
sys_error("chdir error");
}
umask(0022); //改变文件访问权限掩码
close(STDIN_FILENO); //关闭文件描述符0
int fd = open("/dev/null",O_RDWR); //fd->0
if(fd == -1){
sys_error("open error");
}
dup2(fd,STDOUT_FILENO); //重定向stdout和stderr
dup2(fd,STDERR_FILENO);
while(true); //模拟守护进程业务
return 0;
}
运行结果
11.thread_create()创建线程
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-18 19:15:09
* @ Modified Time: 2021-07-18 19:31:14
* @ Description : pthread_create()创建线程
*/
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <pthread.h>
using namespace std;
void* func(void *arg){
printf("thread pid = %d, tid = %lu\n",getpid(),pthread_self()); //pthread_self()获取线程id
return nullptr;
}
int main(int argc, char* argv[]){
printf("main pid = %d, tid = %lu\n",getpid(),pthread_self());
pthread_t tid;
int ret = pthread_create(&tid,nullptr,func,nullptr); //创建线程
if(ret != 0){ //错误
perror("pthread_create error");
exit(1);
}
sleep(1); //给新创建的线程预留运行时间
return 0;
}
运行结果
12.循环创建多个线程
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-18 19:31:39
* @ Modified Time: 2021-07-19 16:42:23
* @ Description : 循环创建多个线程
*/
#include <iostream>
#include <cstdlib>
#include <unistd.h>
using namespace std;
void* func(void *arg){
long i = reinterpret_cast<long>(arg); //void*转换成int会报错(cast from 'void*' to 'int' loses precision)
sleep(i);
printf("-----I'm %ld(th) thread, pid = %d, tid = %lu\n", i+1, getpid(),pthread_self());
return nullptr;
}
int main(int argc, char* argv[]){
int i, ret;
pthread_t tid;
printf("-----main pid = %d, tid = %lu\n",getpid(),pthread_self());
for(i=0;i<5;++i){ //循环创建线程
ret = pthread_create(&tid,nullptr,func,reinterpret_cast<void*>(i));
if(ret != 0){ //错误
perror("pthread_create error");
exit(1);
}
}
sleep(i);
return 0;
}
运行结果
13.pthread_join()等待回收线程
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-18 20:49:19
* @ Modified Time: 2021-07-19 16:47:15
* @ Description : pthread_join()等待回收线程
*/
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <pthread.h>
using namespace std;
struct thrd{
int var;
string str;
};
void* func(void* arg){
return (void*)("hello pthread!");
}
int main(int argc, char* argv[]){
pthread_t tid;
struct thrd * reval;
int ret = pthread_create(&tid,nullptr,func,nullptr);
if(ret != 0){
perror("pthread_create error");
exit(1);
}
char *str;
ret = pthread_join(tid,(void**)&str);
if(ret != 0){
perror("pthread_join error");
exit(1);
}
cout<<"child thread exit with msg = \""<< str <<"\"\n";
pthread_exit(0);
}
运行结果
14.pthread_detach()设置线程分离
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-18 22:22:00
* @ Modified Time: 2021-07-18 22:32:11
* @ Description : pthread_detach()设置线程分离
*/
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <pthread.h>
using namespace std;
void* func(void*){
printf("thread pid = %d, tid = %lu\n",getpid(),pthread_self());
return nullptr;
}
int main(int argc, char* argv[]){
pthread_t tid;
int ret = pthread_create(&tid,nullptr,func,nullptr); //创建线程
if(ret != 0){ //错误
fprintf(stderr,"pthread_create error:%s\n",strerror(ret));
exit(1);
}
ret = pthread_detach(tid); //设置线程分离后,线程终止时会自动清理pcb,无需回收
if(ret != 0){
fprintf(stderr,"pthread_detach error:%s",strerror(ret));
exit(1);
}
sleep(1);
ret = pthread_join(tid,nullptr);
//线程分离后,回收线程时tid无效,错误信息为Invalid argument
if(ret != 0){
fprintf(stderr,"pthread_join error:%s\n",strerror(ret));
exit(1);
}
printf("main pid = %d, tid = %lu\n",getpid(),pthread_self());
pthread_exit((void*)0);
}
运行结果
15.线程属性设置线程分离
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-19 14:00:18
* @ Modified Time: 2021-07-19 14:54:54
* @ Description : 线程属性设置线程分离
*/
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <pthread.h>
using namespace std;
void* func(void *arg){
printf("\nthread pid = %d, tid = %lu\n",getpid(),pthread_self()); //pthread_self()获取线程id
return nullptr;
}
int main(int argc, char* argv[]){
pthread_attr_t attr;
pthread_t tid;
int ret = pthread_attr_init(&attr);
if(ret != 0){
fprintf(stderr,"pthread_attr_init error:%s",strerror(ret));
exit(1);
}
//设置线程属性为分离属性
ret = pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
if(ret != 0){
fprintf(stderr,"pthread_attr_setdetachstate error:%s",strerror(ret));
exit(1);
}
//第二个参数:nullptr --> &attr
ret = pthread_create(&tid,&attr,func,nullptr);
if(ret != 0){
fprintf(stderr,"pthread_create error:%s",strerror(ret));
exit(1);
}
ret = pthread_attr_destroy(&attr);
if(ret != 0){
fprintf(stderr,"pthread_attr_destory error:%s",strerror(ret));
exit(1);
}
//线程属性为分离属性,回收失败(pthread_join error:Invalid argument)
ret = pthread_join(tid,nullptr);
if(ret != 0){
fprintf(stderr,"pthread_join error:%s",strerror(ret));
exit(1);
}
printf("\nmail pid = %d, tid = %lu\n",getpid(),pthread_self());
pthread_exit(nullptr);
}
运行结果
16.互斥锁/互斥量mutex练习
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-19 15:00:10
* @ Modified Time: 2021-07-19 15:22:07
* @ Description : 互斥锁/互斥量mutex练习
*/
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
using namespace std;
pthread_mutex_t mutex; //定义互斥锁
void* func(void*){
while(true){
pthread_mutex_lock(&mutex); //加锁
cout<<"hello ";
sleep(rand()%3);
cout<<"world\n";
pthread_mutex_unlock(&mutex); //解锁
sleep(rand()%3);
}
}
int main(int argc, char* argv[]){
pthread_t tid;
srand(time(nullptr));
int ret = pthread_mutex_init(&mutex,nullptr); //初始化互斥锁
if(ret != 0){
fprintf(stderr,"pthread_mutex_init error:%s\n",strerror(ret));
exit(1);
}
ret = pthread_create(&tid,nullptr,func,nullptr); //创建线程
if(ret != 0){
fprintf(stderr,"pthread_create error:%s",strerror(ret));
exit(1);
}
while(true){
pthread_mutex_lock(&mutex); //加锁
cout<<"HELLO ";
sleep(rand()%3);
cout<<"WORLD\n";
pthread_mutex_unlock(&mutex); //解锁
sleep(rand()%3);
}
ret = pthread_mutex_destroy(&mutex); //销毁互斥锁
if(ret != 0){
fprintf(stderr,"pthread_mutex_destory error:%s\n",strerror(ret));
exit(1);
}
pthread_exit(nullptr);
}
运行结果
17.条件变量 生产者消费者模型
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-19 18:13:13
* @ Modified Time: 2021-07-20 12:24:52
* @ Description : 条件变量 生产者消费者模型
*/
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <time.h>
#include <pthread.h>
using namespace std;
//链表作为共享数据,需要被互斥保护
struct msg{
int num;
struct msg *next;
};
struct msg *head;
//静态初始化一个条件变量和一个互斥量
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void* consumer(void*){ //消费者函数
struct msg *mp;
while(true){
pthread_mutex_lock(&lock);
while(head == nullptr){ //无数据
pthread_cond_wait(&has_product,&lock);
}
mp = head;
head = mp->next;
pthread_mutex_unlock(&lock);
printf("--consumer----%d\n",mp->num);
free(mp);
sleep(rand()%2);
}
}
void* producer(void*){ //生产者函数
struct msg *mp;
while(true){
mp = (msg*)malloc(sizeof(msg)); //生成结点
mp->num = rand()%1000+1; //随即生成数据
printf("--produce----%d\n",mp->num);
pthread_mutex_lock(&lock);
mp->next = head; //写公共区域
head = mp;
pthread_mutex_unlock(&lock);
//唤醒阻塞在条件变量has_product上的线程
pthread_cond_signal(&has_product);
sleep(2);
}
}
int main(int argc, char* argv[]){
srand(time(nullptr));
pthread_t con_id,pro_id;
//创建消费者线程
int ret = pthread_create(&con_id,nullptr,consumer,nullptr);
if(ret != 0){
fprintf(stderr,"consumer pthread_create error:%s\n",strerror(ret));
exit(1);
}
//创建生产者线程
ret = pthread_create(&pro_id,nullptr,producer,nullptr);
if(ret != 0){
fprintf(stderr,"producer pthread_create error:%s\n",strerror(ret));
exit(1);
}
//设置线程分离
pthread_detach(con_id);
pthread_detach(pro_id);
//主线程
pthread_exit(nullptr);
}
运行结果
18.信号量 生产者消费者模型
源代码
/**
* @ Author: WangYusong
* @ E-Mail: admin@wangyusong.cn
* @ Create Time : 2021-07-19 20:38:46
* @ Modified Time: 2021-07-19 21:02:26
* @ Description : 信号量 生产者消费者模型
*/
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#include <pthread.h>
#include <semaphore.h>
using namespace std;
const int num = 5;
int queue[num];
sem_t blank_num,product_num; //空格信号量、产品信号量
void* producer(void*){
int i=0;
while(true){
sem_wait(&blank_num);
queue[i] = rand()%1000 + 1; //产生数据
printf("---produce--%d\n",queue[i]);
sem_post(&product_num);
i = (i+1) % num;
sleep(2);
}
}
void* consumer(void*){
int i=0;
while(true){
sem_wait(&product_num);
printf("---consume--%d\n",queue[i]);
queue[i]=0; //消费数据
sem_post(&blank_num);
i = (i+1) % num;
sleep(2);
}
}
int main(int argc, char* argv[]){
pthread_t pid,cid;
sem_init(&blank_num,0,num); //初始化空格信号量为num
sem_init(&product_num,0,0); //初始化产品信号量为0
int ret = pthread_create(&pid,nullptr,producer,nullptr);
if(ret != 0){
fprintf(stderr,"producer pthread_create error:%s\n",strerror(ret));
exit(1);
}
ret = pthread_create(&cid,nullptr,consumer,nullptr);
if(ret != 0){
fprintf(stderr,"consumer pthread_create error:%s\n",strerror(ret));
exit(1);
}
//设置线程分离
pthread_detach(pid);
pthread_detach(cid);
//回收信号量
sem_destroy(&blank_num);
sem_destroy(&product_num);
//主线程
pthread_exit(nullptr);
}
运行结果
THE END.