线程\进程同步
线程同步
1>同步概念:协同步调,按预定先后顺序访问共享数据.
2>访问共享数据的所有线程都硬加锁.(不加锁就可以直接访问)
1.互斥量(互斥锁)(mutex)
1>访问共享资源后应立即解锁(保证加锁粒度最小)
2>死锁问题:1:对同一个锁加锁两次,第二次就会阻塞等待(解决办法:细心);2:两个线程同时去加锁对方已经加锁的锁时,双方均会阻塞等待.使用trylock一样(解决办法:当线程不能索取全部锁时,主动放弃所有的锁让对方加锁完成访问后释放锁)3:震荡:哲学家吃饭问题,只需一个与其他的那口子的顺序不同即可
3>实现
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
/***********************互斥量的控制函数格式:pthread_mutex_xxx********************************
*函数1:pthread_mutex_init()
*头文件:#include <pthread.h>
*格式:int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
*参数:pthread_mutex_t:结构体,用来定义一个锁(mutex:取值0(加锁)\1(解锁))e(*restrict作用:限制其他指针对mutex的地址进行修改,只能本指针完成)
*参数:const pthread_mutexattr_t *restrict attr:默认值传入NULL;
*作用:初始化一个锁(互斥量)
********************************************************
*函数2:pthread_mutex_destroy()
*头文件:#include <pthread.h>
*格式: int pthread_mutex_destroy(pthread_mutex_t *mutex);//pthread_mutex_t *mutex:传入参数,传入锁
*作用:destroy a mutex(互斥量)//与初始化对应
*返回值:If successful, the pthread_mutex_destroy() and pthread_mutex_init() functions shall return zero; otherwise, an error number shall be
*returned to indicate the error.
********************************************************
*函数3-5:pthread_mutex_lock() \pthread_mutex_unlock()\pthread_mutex_trylock()
*头文件:#include <pthread.h>
*格式: int pthread_mutex_lock(pthread_mutex_t *mutex);//作用:对线程进行阻塞加锁(如果加锁不成功,就会阻塞在此处),pthread_mutex_t *mutex:传入参数,传入锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);//作用:对线程加锁,非阻塞的方式(因此要采用轮询的方式);
int pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁
*返回值:成功-0;错误-错误号;
*****************************互斥量的控制函数格式:pthread_mutex_xxx***************************/
pthread_mutex_t mutex;//1.定义锁要放在全局变量(子线程能访问)
void * func(void* arg)
{
while(1)
{
pthread_mutex_lock(&mutex);//加锁
printf("HELLO");
sleep(1);
printf("WORLD\n");
pthread_mutex_unlock(&mutex);//解锁
sleep(1);
}
pthread_exit(NULL);//结束传出参数为空
}
//创建一个子线程,测试互斥量保证多个线程对共享数据(采用文件秒速符)访问顺序的问题
int main()
{
pthread_t tid;
pthread_mutex_init(&mutex,NULL);//2.初始化锁,后面要对应销毁,此时mutex=1;
pthread_create(&tid,NULL,func,NULL);//创建子线程
for(int i=0;i<5;i++)
{
pthread_mutex_lock(&mutex);//3.加锁
printf("hello");
sleep(1);
printf("world\n");
pthread_mutex_unlock(&mutex);//4.解锁
sleep(1);
}
pthread_cancel(tid);//杀死子线程
pthread_join(tid,NULL);
pthread_mutex_destroy(&mutex);//5.释放锁
pthread_exit(NULL);
}
运行结果:
不会出现同时大小写,按顺序访问输出文件描述符.
2.读写锁(只有一把锁)(rwlock)
1>写独占,读共享;写的优先级更高
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
/***********************读写锁的控制函数格式:pthread_rwlock_xxx********************************
*函数1:pthread_mutex_init()
*头文件:#include <pthread.h>
*格式: int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
*参数:pthread_rwlock_t :结构体,用来定义一个锁(rwlock:取值0(加锁)\1(解锁))e(*restrict作用:限制其他指针对rwlock的地址进行修改,只能本指针完成)
*参数:const pthread_rwlockattr_t *restrict attr:默认值传入NULL;
*作用:初始化一个锁(互斥量)
********************************************************
*函数2:pthread_rwlock_destroy()
*头文件:#include <pthread.h>
*格式: int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);//pthread_rwlock_t *rwlock:传入参数,传入锁
*作用:destroy a rwlock(读写锁)//与初始化对应
*返回值:If successful, the pthread_rwlock_destroy() and pthread_mutex_init() functions shall return zero; otherwise, an error number
*shall bereturned to indicate the error.
********************************************************
*函数3-5:pthread_mutex_lock() \pthread_mutex_unlock()\pthread_mutex_trylock()
*头文件:#include <pthread.h>
*格式: //加锁是区分了读和写
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//作用:对线程进行以读的方式阻塞加锁(如果加锁不成功,就会阻塞在此处),pthread_mutex_t *mutex:传入参数,传入锁名
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);//作用:对线程进以读的方式进行非阻塞加锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//作用:对线程进行以写的方式阻塞加锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);//解锁
*返回值:成功-0;错误-错误号;
*****************************读写锁的控制函数格式:pthread_rwlock_xxx***************************/
pthread_rwlock_t rwlock;//1.定义读写锁要放在全局变量(子线程能访问)
int test_num;
void * rfunc(void* arg)
{
int i=(int)arg;
usleep(i*100);
pthread_rwlock_rdlock(&rwlock);//3.加锁,保证在访问共享资源的时候按顺序执行.不会同事进行写操作
printf("%d th read thread:test_num=%d\n",i+1,test_num);
pthread_rwlock_unlock(&rwlock);//4.解锁
pthread_exit(NULL);
}
void * wfunc(void* arg)
{
int i=(int)arg;
usleep(i*100);
pthread_rwlock_rdlock(&rwlock);
test_num=10+i;
printf("%d th write thread:test_num=%d\n",i+1,test_num);
pthread_rwlock_unlock(&rwlock);
pthread_exit(NULL);
}
//主线程创建和回收子线,子线成
int main()
{
pthread_t tid[10];
int i;
pthread_rwlock_init(&rwlock,NULL);//2.初始化锁,后面要对应销毁;
//创建线程
for(i=0;i<10;i++)
{
if(i==3||i==4)
pthread_create(&tid[i],NULL,wfunc,(void *)i);//创建2个写子线程
else
pthread_create(&tid[i],NULL,rfunc,(void *)i);//创建8个读子线程
}
//回收线程
for( i=0;i<10;i++)
{
pthread_join(tid[i],NULL);
}
pthread_rwlock_destroy(&rwlock);//5.释放锁
pthread_exit(NULL);
}
//注意:都没有加入错误检查.
运行结果:
3.条件变量(cond)
1>条件变量本身不是锁,但它也可以造成程序的阻塞.通常与互斥锁配合使用.与与互斥量比较的优点就是:阻塞时就释放cpu,提高cpu效率.
2>口述生产者消费者模型
3>代码实现
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
/***********************条件变量的控制函数格式:pthread_cond_xxx********************************
*函数1:pthread_cond_init()
*头文件:#include <pthread.h>
*格式:int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
*参数:pthread_cond_t :结构体,用来定义一个条件变量(*restrict作用:限制其他指针对rwlock的地址进行修改,只能本指针完成)
*参数:const pthread_condattr_t *restrict attr:默认值传入NULL;
*作用:初始化一个条件变量
********************************************************
*函数2:pthread_cond_destroy()
*头文件:#include <pthread.h>
*格式: int pthread_cond_destroy(pthread_cond_t *cond);//pthread_cond_t *cond:传入参数,传入定义的条件变量
*作用:destroy a cond//与初始化对应
*返回值:If successful, the pthread_cond_destroy() and pthread_cond_init() functions shall return zero; otherwise, an error number shall be returned to indicate the error.
********************************************************
*函数3:pthread_cond_wait()
*头文件:#include <pthread.h>
*格式: int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
*参数:pthread_cond_t *restrict cond:定义并初始化好的条件变量
*参数:pthread_mutex_t *restrict mutex:定义并初始化好的并加锁的互斥量(锁);
*作用:1:阻塞等待条件变量cond(参数1)满足;2:释放已经掌握的互斥锁(解锁互斥量)(相当于pthread_mutex_unlock(&mutex));(1-2为原子操作)
*3:当被唤醒pthread_cond_signal() /pthread_cond_broadcast(),函数会返回时,解除阻塞(这个阻塞是因为pthread_cond_wait()函数造成的)
*并从新申请获取互斥锁(pthread_mutex_lock(&mutex))
*返回值:成功-0;错误-错误号;
********************************************************
*函数4-5:pthread_cond_signal() /pthread_cond_broadcast()
*头文件:#include <pthread.h>
*格式: int pthread_cond_signal(pthread_cond_t *cond);//传入要通知的条件变量(cond)(唤醒至少一个)
int pthread_cond_broadcast(pthread_cond_t *cond);//(唤醒全部)
*作用:唤醒pthread_cond_wait()函数
*返回值:成功-0;错误-错误号;
********************************************************
*函数6:pthread_cond_timedwait()
*头文件:#include <pthread.h>
*格式:int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
*参数:pthread_cond_t *restrict cond :定义并初始化好的条件变量
*参数:pthread_mutex_t *restrict mutex:定义并初始化好的并加锁的互斥量(锁);
*参数:const struct timespec *restrict abstime:const struct timespec-结构体,abstime:绝对时间,注意正确使用方法;
* struct timespec {
time_t tv_sec; Seconds
long tv_nsec; Nanoseconds [0 .. 999999999] (纳秒)
};
*作用:到达设定的绝对时间就解除阻塞(pthread_cond_timedwait它的阻塞).即使条件不满足
*返回值:成功-0;错误-错误号;
abstime:绝对时间定时法:
time_t cur=time(NULL);//获取系统当前时间;
struct timespec t;
t.tv_sec=cur+1(定时时间)//这里定时的是一秒,然后再把&t传进函数;
*****************************条件变量的控制函数格式:pthread_cond_xxx**************************/
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//使用宏,实现条件变量的定义和初始化(不用在调用pthread_cond_init()函数)
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//定义和初始化互斥锁
//定义一个链表
typedef struct link
{
int data;
struct link *next;
}link_t;
struct link *head=NULL;
struct link *temp=NULL;
void *producter(void *arg)
{
int num=(int)arg;
for(int i=1;i<=num;i++)
{ usleep(500);//保证cpu异主,防止一直生产,产生段错误
temp=(link_t *)malloc(sizeof( link_t));
int ret4=pthread_mutex_lock(&mutex);//加锁
if(ret4!=0)
printf("pthread_mutex_lock error:%d\n",ret4);
temp->data=i;
printf("today %dth data(%d) was producted\n",i,temp->data);
//头插法
temp->next=head;
head=temp;
int ret5=pthread_mutex_unlock(&mutex);//访问完后,解锁
if(ret5!=0)
printf("pthread_mutex_unlock error:%d\n",ret5);
int ret6=pthread_cond_signal(&cond);//唤醒pthread_cond_wait()
if(ret6!=0)
printf("pthread_cond_signal error:%d\n",ret6);
}
pthread_exit(NULL);
}
void *consumer(void *arg)
{
while(1)
{
int ret7=pthread_mutex_lock(&mutex);//加锁,一定要在pthread_cond_wait()函数之前
if(ret7!=0)
printf("pthread_mutex_lock error:%d\n",ret7);
while(head==NULL)//不能用if,被唤醒后需要再一次判断是否为空,所以要用while()
{
printf("***1\n");
int ret9=pthread_cond_wait(&cond,&mutex);//如果链表里面为空,则调用pthread_cond_wait()函数,释放本线程已经加锁的互斥锁,并阻塞(让出cpu),等待唤醒(等待生产者生产)
if(ret9!=0)
printf("pthread_cond_wait error:%d\n",ret9);
printf("***2\n");
}
temp=head;
printf("data(%d) was consumed\n",temp->data);
head=temp->next;
int ret8=pthread_mutex_unlock(&mutex);
if(ret8!=0)
printf("pthread_mutex_unlock error:%d\n",ret8);
free(temp);
temp=NULL;//养成一个好习惯,就是在调用以上两个函数后马上手动赋空,程序对空指针进行多次释放的时候,是不会出错误的。( double free or corruption错误)
}
}
/********************************************************
*使用条件变量加互斥锁实现生产者和消费者模型
********************************************************/
int main(int argc,char *argv[])
{
if(argc<2)
{
printf("cin number error.");
exit(1);
}
pthread_t pit,cit; //定义两个线程号
srand((unsigned int)time(NULL));
//创建两个线程
int ret0=pthread_create(&pit,NULL,producter,(void *)atoi(argv[1]));
if(ret0!=0)
printf("pthread_create error:%d\n",ret0);
int ret1=pthread_create(&cit,NULL,consumer,NULL);
if(ret1!=0)
printf("pthread_create error:%d\n",ret1);
//回收子线程
int ret2=pthread_join(pit,NULL);
if(ret2!=0)
printf("pthread_join error:%d\n",ret2);
while(head==NULL)
{
printf("***3\n");
pthread_cancel(cit);//注意:一定要有取消点
//pthread_cond_signal(&cond);
printf("***3\n");
break;
}
int ret3=pthread_join(cit,NULL);
if(ret3!=0)
printf("pthread_join error:%d\n",ret3);
//释放条件变量和互斥锁
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
pthread_exit(NULL);
}
4.信号量(sem,线程\进程都可以用)
1>前面的三种都是只允许一个线程访问,限制了程序的并发性,而信号量可以允许多个线程对共享数据进行访问,也能保证同步.
2>信号量是进化版的互斥锁,(互斥锁初始化后的mutex等于1,加锁后就–,等于0,解锁后++等于1;信号量初始化时可以指定值(允许多少个线程访问,当为1时,相当于互斥量),sem_wait()函数访问时,信号量大于0,则允许访问,并对信号量–,如果等于0;则阻塞等待sem_post()函数对信号量++,并唤醒sem_wait()函数)
3>条件变量共享数据使用的链表,信号量可以使用队列实现(线性队列\环形队列)
4>信号量实现生产者\消费者模型
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
#include <semaphore.h>
/***********************信号量的控制函数格式:sem_xxx********************************
*函数1:sem_init()
*头文件:#include <semaphore.h>
*格式: int sem_init(sem_t *sem, int pshared, unsigned int value);
*参数:sem_t *sem:传入参数,sem_t类型,指定信号量
*参数:int pshared:0-使用于线程中的,!=0-使用与进程;
*参数:unsigned int value:初始化信号量的值(最大允许访问的线程数)
*作用:初始化一个信号量
*注意:Link with -pthread.
*返回值:sem_init() returns 0 on success; on error, -1 is returned, and errno is set to indicate the error.
********************************************************
*函数2:sem_destroy()
*头文件:#include <semaphore.h>
*格式: int sem_destroy(sem_t *sem);// sem_t *sem:传入参数,传入定义的信号量
*作用:destroy an unnamed semaphore//与初始化对应
*返回值: sem_destroy() returns 0 on success; on error, -1 is returned, and errno qis set to indicate the error.
********************************************************
*函数3:sem_wait() \sem_trywait()\sem_timedwait()
*头文件:#include <semaphore.h>
*格式: int sem_wait(sem_t *sem);//如果sem信号量不等于零则访问共享数据,并将sem的值--,如果sem的值已经等于零,则阻塞等待(sem_post()函数将其唤醒).
int sem_trywait(sem_t *sem); //非阻塞的方式(因此要轮询调用)
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);//采用绝对时间,到达时间后如果是阻塞等待的,自动放弃阻塞.
*作用:加锁,访问共享数据
*注意:Link with -pthread.
*返回值:成功-0;错误--1;
********************************************************
*函数4:sem_post()
*头文件:#include <semaphore.h>
*格式: int sem_post(sem_t *sem);
*作用:将sem信号量的值+1,如果有线程阻塞在sem_wait()函数,也会将其解除阻塞.
*返回值:成功-0;错误--1;
*****************************信号量的控制函数格式:sem_xxx**************************/
#define NUM 5
sem_t blank_num,products_num;//1.定义两个信号量 消费的,生产的
int queue[NUM]; //全局数组实现环形队列
void *product(void * arg)
{
int i=0;
while(1)
{
sem_wait(&blank_num);//生产者访问信号量blank_num,并将blank_num--,blank_num=0时,阻塞(最大5个线程).
queue[i]=rand()%10+1;
printf("++++++++products %d was producted+++++++++++\n",queue[i]);
sem_post(&products_num);//唤醒消费者,将信号量products_num++.
i=i+1%NUM; //i循环变化
sleep(1);//速度太快会发生段错误
}
}
void *consume(void * arg)
{
int i=0;
while(1)
{
sem_wait(&products_num);//消费者访问信号量products_num,并将products_num--,products_num=0时,阻塞(最大5个线程同时访问).
printf("--------products %d was consumed with cid----------\n",queue[i]);
queue[i]=0;
sem_post(&blank_num);//唤醒消费者,将信号量blank_num++.
i=i+1%NUM; //i循环变化
sleep(1);//速度太快会发生段错误
}
}
void *consume1(void * arg)
{
int i=0;
while(1)
{
sem_wait(&products_num);//消费者访问信号量products_num,并将products_num--,products_num=0时,阻塞(最大5个线程同时访问).
printf("---------products %d was consumed with cid1----------\n",queue[i]);
queue[i]=0;
sem_post(&blank_num);//唤醒消费者,将信号量blank_num++.
i=i+1%NUM; //i循环变化
sleep(1);//速度太快会发生段错误
}
}
/********************************************************
*使用信号量实现生产者和消费者模型
********************************************************/
int main(int argc,char *argv[])
{
pthread_t pid,cid,cid1;
if(argc<2)
{
printf("cin number error.\n");
exit(1);
}
srand((unsigned int)time(NULL));
//2.信号量变量初始化
sem_init(&blank_num,0,NUM);//刚开始,空格子信号量为5,最大允许5个生产者访问;
sem_init(&products_num,0,0);//刚开始,产品数量为0;不允许消费者访问.
//创建生产和消费线程
pthread_create(&pid,NULL,product,NULL);
pthread_create(&cid,NULL,consume,NULL);
pthread_create(&cid1,NULL,consume1,NULL);
//线程回收
pthread_join(pid,NULL);
pthread_join(cid,NULL);
pthread_join(cid1,NULL);
//3释放信号量
sem_destroy(&pid);
sem_destroy(&cid);
pthread_exit(NULL);
}
//注意返回值检查;
运行结果:
进程同步
1.信号量(sem,线程\进程都可以用)这里就不累赘
2.互斥量(锁)(mutex)
1>与线程中的互斥锁唯一的差别就是在调pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);第二个参数不能传NULL;而要修改为进程锁,步骤如下:
说明:先定义pthread_mutexattr_t 结构体变量(pthread_mutexattr_t是一个结构体),然后调用三个函数.
3.文件锁
1>只能用在进程中,以为线程的fd(文件描述符共享),调用fcntl()函数没有用.线程里有读写锁可以使用.
2>读时共享,写时独占.
3>代码实现
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include <sys/wait.h>
/*******************************************************
*函数:fcntl() //借助该函数实现,操作文件的进程没有获得锁时,可以打开文件,但是没有read\write的权限
*头文件:#include<unistd.h>\#include<fcntl.h>
*格式: int fcntl(int fd, int cmd, ... /* arg */ //);//变参函数,后面的参数有cmd决定.
/*参数1:文件描述符;参数2:三个取值:F_SETLK-设置文件锁(不会阻塞 trylock), F_SETLKW-设置文件锁(会阻塞,W-lock),
*F_GETLK-获取文件锁;(三个取值都是struct flock*类型的)参数3:struct flock:
struct flock {
...
short l_type; // Type of lock: F_RDLCK,F_WRLCK, F_UNLCK (以读的方式加锁,写的方式加锁,解锁)原则:写时独享,读时共享
short l_whence; // How to interpret l_start: SEEK_SET, SEEK_CUR, SEEK_END (文件加锁的开始位置)
off_t l_start; // Starting offset for lock (相对于文件加锁开始位置偏移量)
off_t l_len; // Number of bytes to lock (文件加锁的长度)
pid_t l_pid; // PID of process blocking our lock(set by F_GETLK and F_OFD_GETLK),获取进程的pid,F_SETLK和F_SETLKW时不能用.
...
};
参数2有三个取值,且都是struct flock*类型,所以参数3传入一个struct flock结构体,来设置对应的属性.
*作用:获取\设置文件访问控制属性.
*返回值:成功-0,失败--1;
********************************************************/
void myperror(char *str[])
{
perror(str);
exit(1);
}
//使用文件锁,一个进程读,一个进程写的属性加锁测试
int main(int argc,char *argv[])
{
if(argc<2)
{
printf("cin param error");
exit(1);
}
pid_t pid;
pid=fork();
if(pid==-1)
{
myperror("fork error");
}
//子进程以读的方式加锁
if(pid==0)
{
//打开文件
int fd=open(argv[1],O_RDWR);
if(fd==-1)
{
myperror("open error");
}
//1.定义结构体
struct flock file_lock; //不能定义为指针
//2.设置加锁属性
file_lock.l_type=F_RDLCK; //读的方式加锁
file_lock.l_whence=SEEK_SET;
file_lock.l_start=0;
file_lock.l_len=0; //等于0,对全部文件加锁.
//3.加锁
sleep(1);//保证父进程先加锁,验证写时独享,读时共享
fcntl(fd,F_SETLKW,&file_lock); //加锁,解锁都调用该函数.(F_SETLKW:加锁不成功会阻塞)
printf("child process lock file %s successed.\n",argv[1]);
sleep(5);
printf("sleep 5s successed\n");
//4.设置属性
file_lock.l_type=F_UNLCK;
//5.解锁
fcntl(fd,F_SETLKW,&file_lock); //加锁,解锁都调用该函数.(第二个参数不变F_SETLKW)
printf("child process unlock file %s successed.\n",argv[1]);
close(fd);
}
//父进程以写的方式加锁
if(pid>0)
{
//打开文件
int fd1=open(argv[1],O_RDWR);
if(fd1==-1)
{
myperror("open error");
}
//1.定义结构体
struct flock file_lock1;
//2.设置加锁属性
file_lock1.l_type=F_WRLCK; //写的方式加锁
file_lock1.l_whence=SEEK_SET;
file_lock1.l_start=0;
file_lock1.l_len=0; //等于0,对全部文件加锁.
//3.加锁
int ret1=fcntl(fd1,F_SETLKW,&file_lock1);//加锁,解锁都调用该函数.(F_SETLKW:加锁不成功会阻塞)
if(ret1==-1)//此处报错 11-27
{
myperror("fcntl1 error");
}
printf("parent process lock file %s successed.\n",argv[1]);
sleep(5);
printf("sleep 5s successed\n");
//4.设置属性
file_lock1.l_type=F_UNLCK;
//5.解锁
fcntl(fd1,F_SETLKW,&file_lock1);//加锁,解锁都调用该函数.(第二个参数不变F_SETLKW)
printf("parent process unlock file %s successed.\n",argv[1]);
close(fd1);
wait(NULL); //回收子进程
}
return 0;
}
运行结果: