搜索
查看
编辑修改
首页
UNITY
NODEJS
PYTHON
AI
GIT
PHP
GO
CEF3
JAVA
HTML
CSS
搜索
weixin_40725706
这个屌丝很懒,什么也没留下!
关注作者
热门标签
jquery
HTML
CSS
PHP
ASP
PYTHON
GO
AI
C
C++
C#
PHOTOSHOP
UNITY
iOS
android
vue
xml
爬虫
SEO
LINUX
WINDOWS
JAVA
MFC
CEF3
CAD
NODEJS
GIT
Pyppeteer
article
热门文章
1
USB驱动框架_usb驱动架构
2
MyBatis(三)——SQL映射文件_sql中使用对象接收返回值,对应java bean中缺少无参构造
3
人工智能 | 自然语言处理研究报告(应用篇)_人工智能文本自然语言处理实验报告
4
Linux篇之在Centos环境下搭建Nvidia显卡驱动_centos安装nvidia驱动
5
升级Linux内核后,导致NVIDIA驱动丢失_nvidia-installer
6
JAVA像Python切片取数组_从平铺的TIFF中提取切片并存储在numpy数组中
7
阿里云 MQTT协议 AT指令 ESP8266-01S 数据上下传输_mqtt协议格式 esp8266
8
【深度学习笔记】9_5 多尺度目标检测
9
23.线程阻塞(android消息机制)_android binding.ivstop.postdelayed阻塞主线程
10
YOLOv8创新改进:SPPF创新涨点篇 | SPPELAN:SPP创新结合ELAN ,效果优于SPP、SPPF| YOLOv9_sppelan的优势
当前位置:
article
> 正文
Unix高级编程:库函数与系统调用函数区别、文件锁、进程基础_系统调用函数 锁
作者:weixin_40725706 | 2024-03-17 13:57:55
赞
踩
系统调用函数 锁
一、库函数和系统调用之间的关系和区别(文件操作函数说明)
fopen(3)
FILE *fopen(const char *path, const char *mode);
当使用fopen(3)打开文件的时候,发生了什么?
1)首先分配了一块内存空间,用于文件内容的缓冲
2)然后调用open(2)
fputc(3)
int fputc(int c, FILE *stream);
首先将数据放入fopen(3)开辟的那块内存空间里:
1)如果内存空间不满,不会立即写入到文件,当清理缓存的时候或者或者缓存满的时候,调用"write"(2)将缓存里的内容写到文件。
2)如果内存空间满的时候,调用write(2)将缓存里的数据写入到文件,然后将fputc的内容写到缓存里。
fgetc(3)
int fgetc(FILE *stream);
调用fgetc(fp)的时候,首先到fp指向的内存空间里去找一个字符:
1)如果空间里有字符,立即返回。
2)如果没有字符,调用"read(fp->_fileno, buf, len)",buf指向了fp的内存空间。这个时候将文件的内容读取到fopen(3)开辟的那块内存空间里,这个时候再从这块内存空间里将字符读取。
fclose(3)
int fclose(FILE *fp);
首先将缓存里的数据写入到文件,然后释放缓存、调用close(2)关闭文件
"缓冲文件"
"非缓冲文件"
fflush(3)
int fflush(FILE *stream);
刷新缓冲(清理缓存),原因:感觉写入文件其实在数据在缓冲,并没有实际写入,调用fflush(3)即可。
stdin
0
标准输入
stdout
1
标准输出
stderr
2
标准错误输出
printf("hhhh"); //printf占用行缓存,没满有时不输出,加\n输出,缓冲所致
1) \n //加\n,也能输出,方法1
2) fflush(1); //加标准输出,也能输出,方法2
二、文件锁(建议锁/强制锁暂不讲)
多个进程同时访问一个文件的时候,这个文件就成为"临界资源",这个时候对这个文件的访问需要同步。
同步和异步
"同步" 事情A和事情B,只有在事情A完成之后,再进行事情B,那么AB同步。
"异步" 事情A和事情B,谁先执行都可以,执行无先后顺序,那么AB异步。
为了避免在读写同一个文件的同一个区域时发生冲突,进程之间应该遵循以下规则:
1)如果一个进程"正在写",那么"其它进程既不能写也不能读"
2)如果一个进程"正在读",那么"其它进程不能写但是可以读"
为了实现同步,在这里使用对文件加锁。"建议锁"/"强制锁",两种所都有:
"读锁" (共享锁)对一整个文件或特定区域可以加多把读锁
"写锁" (互斥锁)对一整个文件或特定区域只能加一把写锁
"fcntl"(2) //使用该系统调用函数完成对文件的加锁
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
功能:操作文件描述符
参数:
"fd" open(2)的返回值,指定要操作的文件(描述符)
"cmd" 命令。对文件描述符进行操作的命令
F_SETLK 设置锁-非阻塞模式,进程遇锁立即以错误返回,返回错误
F_SETLKW 设置锁-阻塞模式,进程遇锁将等待冲突锁直到锁被释放
F_GETLK 获取锁
"..." 可变参数(需要or不需要"取决与cmd参数")
"..."=="lock"
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
(F_GETLK only) */
};
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
/*举例验证文件锁的使用,代码参见 processA.c & processB.c*/
/* processA.c */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void) {
int fd;
fd = open("aaa.txt", O_RDWR);//打开一个文件
if(fd == -1) {
perror("open");//打开失败
return 1;
}
struct flock lock; //初始化文件锁
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
int ret = fcntl(fd, F_SETLK, &lock);//设置锁
if(ret == -1) {
perror("fcntl");
return 2;
}
printf("read lock success...\n");
sleep(20);
close(fd);//关闭文件
printf("read lock release...\n");
return 0;
}
/* processB.c */
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(void) {
int fd;
fd = open("aaa.txt", O_RDWR);//打开文件
if(fd == -1) {
perror("open");//打开失败
return 1;
}
struct flock lock;//初始化文件锁
lock.l_type = F_WRLCK;//读锁可直接进行/写锁需等待A完成
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
int ret = fcntl(fd, F_SETLKW, &lock);//设置锁
if(ret == -1) {
perror("fcntl");
return 2;
}
printf("read lock success...\n");
close(fd);//关闭文件
return 0;
}
此两个程序运行A的同时,在另一个标签页运行B,B可设置读锁,但B的写锁要等待A运行完毕才会执行写锁成功。
三、文件操作杂项
"chdir"(2) //cd 命令就是调用封装的 chdir 系统函数
#include <unistd.h>
int chdir(const char *path);
功能:改变工作路径
参数:"path" 目标工作路径
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
"mkdir"(2) //mkdir 命令就是调用封装的 mkdir 系统函数
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
功能:创建一个文件夹或目录
参数:
"pathname" 要创建的文件夹名字
"mode" 新的文件夹的权限,例如 0664 或 mode & ~umask & 0777
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
"getcwd"(3) //pwd 命令就是调用封装的 getcwd 系统函数
#include <unistd.h>
char *getcwd(char *buf, size_t size);
功能:获取当前的工作路径
参数:
"buf" 将当前路径的绝对路径名字拷贝到buf指定的地址空间里
"size" 拷贝的长度
返回值:
成功 - 当前工作路径的首地址指针
失败 - 返回 NULL,errno被设置
"rmdir"(2) //rm -r 命令就是调用封装的 rmdir 系统函数
#include <unistd.h>
int rmdir(const char *pathname);
功能:删除一个目录
参数:"pathname" 要删除的文件夹名字
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
"link"(2) / "symlink"(2) //其中link可做命令行指令使用
#include <unistd.h>
int link(const char *oldpath, const char *newpath);
功能:为文件创建一个新名字 - 硬链接(非拷贝内容)
参数:
"oldpath" 原文件路径
"newpath" 新文件路径
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
"unlink"(2)
#include <unistd.h>
int unlink(const char *pathname);
功能:删除一个硬链接文件名字
参数:"pathname" 文件名
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
"rename"(2) //mv 命令就是调用封装的 rename 系统函数
#include <stdio.h>
int rename(const char *oldpath, const char *newpath);
功能:更改文件名或文件路径
参数:
"oldpath" 原文件名/路径
"newpath" 新文件名/路径
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
"chmod"(2)
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
功能:改变文件的权限
参数:
"path" 文件名
"mode" 权限属性宏
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
文件内容小结:
1)文件的管理(open/read/write/close/lseek/dup/dup2/fcntl/mmap...)
2)文件系统管理(stat/opendir/closedir/readdir/telldir...)
3)文件的权限、类型、软链接、硬链接、属主、属组都需弄清楚
四、进程的基本概念
进程和程序
"程序是静态的",是被存储在磁盘上,包含机器指令和数据的文件。
"进程是动态的",是被装载到内存中,被处理器操作的代码和数据。
"一个程序可被运行为多个进程"
进程快照
命令行:ps
PID TTY TIME CMD
2346 pts/0 00:00:00 bash
12376 pts/0 00:00:00 ps
PID:进程标识
TTY:控制终端次设备号
TIME:进程运行时间
CMD:进程启动命令
命令行:ps aux
VSZ:占用虚拟内存大小(KB)
RSS:占用物理内存大小(KB)
每个进程都有自己的"pid",都有自己的户口本"PCB"(进程控制器)。
PID:即Process Identification,"进程标识"。每个进程都有这么一个"非负整数形式的唯一编号"。任何时刻都是唯一的,但是"可以重用,进程被终止并被回收"以后,其PID就可以被其他进程所用。
PCB 是由操作系统的内核kernel来完成的,内核通过来管理PCB来对进程进行管理。
0 号进程:调度进程(交换进程swapper),系统内核的一部分
1 号进程:init进程,以超级用户运行的普通进程,永不终止
2 号进程:页守护进程,负责虚拟内存系统的分页操作
在linux系统中,用户进程是以一棵树来组织的。
"pstree"命令查看这棵树,格式:pstree
进程和进程之间的父子关系或者兄弟关系,亲缘关系。
"init进程"是"1号进程"
怎么去创建一个子进程?使用系统调用函数fork(2)来创建
"fork"(2)
#include <unistd.h>
pid_t fork(void);
功能:创建一个子进程
参数:void
返回值:
成功 - 在父进程里子进程的"pid被返回",在子进程里返回 0
失败 - 在父进程里返回 -1,子进程没有被创建,errno被设置
/*举例验证fork创建子进程,代码参见 fork.c*/
#include <stdio.h>
#include <unistd.h>
int main(void) {
pid_t pid;
pid = fork(); //此行开始下面就是2份代码在执行,父子进程先调用谁,由操作系统决定,人为无法判断
if(pid < 0) {
perror("fork");
return 1;
}
if(pid == 0) {
printf("I am child process...\n");
}
printf("I am father process...\n");
return 0;
}
子进程创建完成,子进程和父进程的执行是"异步"的。
子进程是父进程的不完全副本,子进程的数据区、BSS区、堆栈区(包括I/O流缓冲区),甚至参数和环境区都从父进程拷贝,"唯有代码区与父进程共享"。
调用fork函数前的代码只有父进程执行,fork函数成功返回后的代码父进程和子进程都会执行,受逻辑控制进入不同分支。
fork函数调用一次,在父进程和子进程中各返回一次,在父进程中返回所创建子进程的PID,而在子进程中返回0。
fork函数成功返回以后,父子进程各自独立运行,其被调度的先后顺序并不确定。
"getpid"(2)/"getppid"(2)
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void); //获取自己的pid
pid_t getppid(void); //获取自己父进程的pid
/*代码演示*/
printf("pid = %d\n", getpid());
printf("ppid = %d\n", getppid());
实例:
父-子(父)-子(父)-子
/*代码*/
#include <stdio.h>
#include <unistd.h>
void myfork(int c) {
pid_t pid;
if(c-- == 1) {
return ;
}
pid = fork();
if(pid == 0) {
printf("pid = %d\n", getpid());
printf("ppid = %d\n", getppid());
printf("----------------------\n");
myfork(c);
}
else
sleep(5);
return;
}
int main(void) {
int count = 4;
myfork(count);
return 0;
}
进程的退出:
"exit"(3) 和 return 的区别
return 只是完成函数的返回
exit 函数是进程的退出
在 main 函数中 return 返回之后,进程还没有结束
而调用exit函数的时候,进程就完全结束了
事实上,main函数里的return语句也会被编译器处理为类似对exit函数的调用,所以可以认为 return x 等价于 exit(x)。
"_exit"(2)
#include <unistd.h>
void _exit(int status);
功能:退出进程
参数:"status" 文件描述符
返回值:无返回值
在进程终止之前可以做一些处理工作
"atexit"(3)
#include <stdlib.h>
int atexit(void (*function)(void));
功能:向进程注册,进程终止之前调用的函数
参数:void (*function)(void) 进程结束时要调用的函数,无参数
返回值:
成功 - 返回 0
失败 - 返回非 0
/*举例验证,代码参见 atexit.c*/
#include <stdio.h>
#include <stdlib.h>
void handle(void) {
printf("oh , my god..!\n");
return;
}
int main(void) {
atexit(handle);
sleep(5);
return 0;//在return之后,在进程退出之前,printf
}
"on_exit"(3)
#include <stdlib.h>
int on_exit(void (*function)(int , void *), void *arg);
功能:向进程注册,进程终止之前调用的函数
参数:
void (*function)(int , void *) 进程结束时要调用的函数,两个参数
function函数里的 int 是 return 的数字传到int里
"arg" function函数里的第 2 个参数
返回值:
成功 - 返回 0
失败 - 返回非 0
/*举例验证,代码参见 on_exit.c*/
#include <stdio.h>
#include <stdlib.h>
void handle(int x, void *arg) {
printf("%d\t%s\n", x, (char *)arg);
return ;
}
int main(void) {
on_exit(handle, "tarena");
sleep(5);
return 2;// 或 exit(5)
}
声明:
本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:
https://www.wpsshop.cn/w/weixin_40725706/article/detail/256386
推荐阅读
article
vue3
+TS
axiox
接口
模块
添加
,
fast
mock
接口
访问测试...
vue3
+TS
axiox
接口
模块
添加
,
fast
mock
接口
访问测试
vue3
+TS
axiox
接口
模块
添加
,fas...
赞
踩
article
FTP
连接
常见
的
报错
信息
及诠释...
“426 Data connection closed, error decompressing data stream...
赞
踩
article
layui
数据
表格
请求传参_
Layui
table
组件的
使用
之初始化加载
数据
、
数据
刷新
表格
、传参...
背景笔者之前一直
使用
bootstrap
table
,因为当前项目中主要
使用
Layui
框架,于是也就随了 Layu...
赞
踩
article
【
Flutter
】
IOS
运行
工程
二次
启动
崩溃
问题
。...
IOS
14+设备,切后台划掉,
二次
启动
崩溃,看crash日志 一直提示第三方plugin注册
问题
。一个一个尝试注掉,发现...
赞
踩
article
选择
大
语言
模型
:2024 年
开源
LLM
入门指南_中文
开源
llm
...
开源
LLM
是免费提供的
LLM
,任何人都可以修改和定制。有了
开源
LLM
,任何个人或企业都可以将其用于自己的目的,而...
赞
踩
article
vant
请求
封装_
vue
之
axios
封装...
在
vue
项目中,和后台交互获取数据这块,我们通常使用的是
axios
库,它是基于promise的http库,可运行在浏览器...
赞
踩
article
使用
base64
,
展示
图片
_
base64
图片
展示
...
使用
base64
和img标签组合出来的是这个样子
base64,iVBO...
赞
踩
article
使用
腾讯云
服务器
搭建鸿蒙操作
系统
编译
环境_
unity3d
打的包鸿蒙
系统
能用吗...
使用
腾讯云
服务器
搭建鸿蒙操作
系统
编译
环境_
unity3d
打的包鸿蒙
系统
能用吗
unity3d
打的包鸿蒙
系统
能用吗 ...
赞
踩
article
vue3
hooks
封装
常用
请求
_
vue3
可以在
hooks
里面
调用
接口
...
当前
请求
不包含表格分页,表格分页hook
封装
请看这个。2.参数变化
hooks
自动
调用
后端
接口
。_
vue3
可以在hook...
赞
踩
article
熟悉
Android
打包
编译
的
流程
_从
android
源码到
apk
的
编译
打包
流程
...
从事
Android
高级研发,怎能不知道
Android
的
打包
流程
呢?今天就为大家讲解
Android
打包
的
流程
:Androi...
赞
踩
article
【
快捷
部署
】00
2
_
Flink
(
1.17
.
2
)...
快捷
部署
系列,本期带来的是
Flink
,快速体验、学习【
快捷
部署
】00
2
_
Flink
(
1.17
.
2
) ...
赞
踩
article
C++
经典
面试题
全集 50~
100
道 都附带有
参考答案
_
c++
面试题
目
100
及
最佳答案
...
51. 引用与指针有什么区别?答 、1) 引用必须被初始化,指针不必。2) 引用初始化以后不能被改变,指针可以改变所指的...
赞
踩
article
vue3
hooks
组件封装_
bindprops
bindevents
...
vue3
hooks
组件封装_
bindprops
bindevents
bindprops
bindevents
...
赞
踩
article
在
Vue3
中实现
发送
网络
请求
功能_
vue3
怎么
请求
curl
...
本文主要介绍在
Vue3
中实现
发送
网络
请求
功能。_
vue3
怎么
请求
curl
vue3
怎么
请求
curl
...
赞
踩
article
mybatis
-
plus
条件
查询
_
mybatis
plus
条件
查询
...
条件
查询
首先使用QueryWrapper创建一个wrapper对象存放
条件
ge:>=gt:>le:<=lt:< //创...
赞
踩
article
Android
Aidl for
hal
开发案例_
android
中 使用
aidl
实现
hal
接口
...
一次Aidl for
hal
的
实现
案例,包含bp编译、
aidl
接口
、服务端、客户端、selinux的
实现
以及一些问题的解...
赞
踩
article
mac
访问
局域网
服务器地址_
mac
访问//
172.16
.
8.12
...
在桌面里面选中 前往→连接服务器,如图:输入IP地址,点击连接就可以了_
mac
访问//
172.16
.
8.12
mac
访...
赞
踩
article
HIDL
最全
编译
流程_
hidl
-
gen
...
想了解
HIDL
介绍的可以参考《
HIDL
概述》,本篇文章主要介绍
HIDL
的详细
编译
流程及简单的客户端应用(C++跟Andr...
赞
踩
article
Ma
c
上执行命令“
brew
install
*”时需谨慎,警惕home
brew
自动更新
python3...
问题描述:准备通过pip3
install
py
en
c
hant
安装PyEn
c
hant模块,实现识别输入字符串是否为有效英...
赞
踩
article
Mybatis
if
标签
使用
总结,有图易懂...
在项目开发中,mybatis<
if
>
标签
使用
广泛,本文讲解
if
标签
的两种
使用
方式其一、
使用
<
if
>
标签
判断某一字段是否为...
赞
踩
相关标签
vue.js
前端
javascript
layui 数据表格请求传参
flutter
ios
语言模型
人工智能
自然语言处理
大数据
搜索引擎
elasticsearch
vant 请求封装
vue
腾讯云
服务器
harmonyos
Android
打包编译
flink
数据仓库
淘客科技
部署
运维