前几天在写基于TCP的网络银行时,想到当用进程时如何共享文件而不起冲突时,我联想到了文件锁,虽然也可以用线程来完成项目,但由于,博主是初学者,还是尝试用了一下进程来完成了这个项目.
直接上干货,冲冲冲
1.进程
我们得先了解一下进程
1.每个进程都有自己独立的一块内存空间,一个进程可以有多个线程
2.父子进程,fork之后:
父子相同处: 全局变量、.data、.text、栈、堆、环境变量、用户ID、宿主目录、进程工作目录、信号处理方式
父子不同处: 1.进程ID 2.fork返回值 3.父进程ID 4.进程运行时间
父子进程并不共享这些存储空间,共享正文段(即代码段);因此子进程对变量的所做的改变并不会影响父进程。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#define PATH "a.txt"//里面谢了 hello world
int main(){
int fd = open(PATH,O_CREAT|O_RDWR,0644); //open在fork之前共享同一个文件描述符
assert(fd != -1);
char buf[128] = {};
pid_t id = fork();
assert(id != -1);
if(id == 0){ //子进程
read(fd,buf,1);
printf("我是子进程,我打印了:%s\n",buf);
}else{
sleep(1); //让子进程先输出
read(fd,buf,1);
printf("我是父进程,我打印了:%s\n",buf);
}
close(fd);
return 0;
}
我是子进程,我打印了:h
我是父进程,我打印了:e
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#define PATH "a.txt"//里面谢了 hello world
int main(){
//int fd = open(PATH,O_CREAT|O_RDWR,0644);
//assert(fd != -1);
char buf[128] = {};
pid_t id = fork();
assert(id != -1);
int fd = open(PATH,O_CREAT|O_RDWR,0644);
assert(fd != -1);
if(id == 0){ //子进程
read(fd,buf,1);
printf("我是子进程,我打印了:%s\n",buf);
close(fd);//各自关闭自己进程的文件描述符
}else{
sleep(1);
read(fd,buf,1);
printf("我是父进程,我打印了:%s\n",buf);
close(fd);//同上关闭文件描述符
}
//close(fd);
return 0;
}
我是子进程,我打印了:h
我是父进程,我打印了:h
结论:由上可知在fork之前的open打开的文件描述符共享,也就是说对同一个文件描述符进行操作时,其V-node结点会发生偏移.而在fork之后的open就相当于父子进程各自产生一个文件表,他们的文件描述符可能相同也可能不相同,要看之前各自进程是否有对文件描述进行操作.
2.进程和文件锁
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#define PATH "a.txt"//里面谢了 hello world
int w_lock(int fd){
struct flock lock = {};
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_pid = -1;
return fcntl(fd,F_SETLKW,&lock);
}
int un_lock(int fd){
struct flock lock = {};
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_pid = -1;
return fcntl(fd,F_SETLK,&lock);
}
int main(){
int fd = open(PATH,O_CREAT|O_RDWR,0644);
assert(fd != -1);
w_lock(fd);
char buf[128] = {};
pid_t id = fork();
assert(id != -1);
if(id == 0){ //子进程
read(fd,buf,1);
printf("我是子进程,我打印了:%s\n",buf);
// close(fd);
}else{
// sleep(1);
read(fd,buf,1);
printf("我是父进程,我打印了:%s\n",buf);
// close(fd);
}
sleep(1);
un_lock(fd);
getchar();
close(fd);
return 0;
}
结果为父子进程一起打印,并没有产生影响
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#define PATH "a.txt"//里面谢了 hello world
int w_lock(int fd){
struct flock lock = {};
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_pid = -1;
return fcntl(fd,F_SETLKW,&lock);
}
int un_lock(int fd){
struct flock lock = {};
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_pid = -1;
return fcntl(fd,F_SETLK,&lock);
}
int main(){
char buf[128] = {};
pid_t id = fork();
assert(id != -1);
int fd = open(PATH,O_CREAT|O_RDWR,0644);
assert(fd != -1);
w_lock(fd);
if(id == 0){ //子进程
read(fd,buf,1);
printf("我是子进程,我打印了:%s\n",buf);
// close(fd);
}else{
sleep(1);
read(fd,buf,1);
printf("我是父进程,我打印了:%s\n",buf);
// close(fd);
}
sleep(1);
un_lock(fd);
getchar();
close(fd);
return 0;
}
结果为先打印父进程,随后才打印子进程,受到了文件锁的影响.
所以我们项目在用进程时,要考虑open的顺序,我建议在fork之前,然后每次使用read和write时lseek偏移到文件开始操作.