01. Linux系统编程入门

news/2025/3/18 8:01:39/文章来源:https://www.cnblogs.com/lang77/p/18778152

入门系统编程,首先理解一下

  • 基本的系统调用和库函数的区别
  • 一切皆文件的思想,都是通过文件描述符来进行操作
  • strace命令

文件读写系统调用

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main (void) 
{int fd;fd = open("hello", O_RDWR|O_CREAT, 0666); // 0666 (oct) = 110 110 110 (binary) = rw-rw-rw- (文件权限) //所有者(User)| 同组 Group | 其他人 Other的权限:可读 (r),可写 (w),不可执行 (-)if (fd == -1){printf("open file failed!\n");return -1;}char string[20] = "hello world!\n";write (fd, string, 14);fsync (fd);  // 同步刷新到磁盘中char *buf = (char*)malloc (20);memset(buf, 0, 20);lseek(fd, 0, SEEK_SET); // SEEK_SET:文件开始 | SEEK_CUR:文件当前位置 | SEEK_END:文件末尾read(fd, buf, 14);printf("%s", buf);free(buf);close(fd);return 0;
}

复制文件

将源文件读出来后写到另一个文件

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> #define BUFFERSIZE 4096int main(int argc, char *argv[])
{if (argc != 3){printf("Usage: copy src dest\n");return 1;}int srcfd = open(argv[1], O_RDONLY);  // 以只读的方式打开文件if (srcfd == -1){perror("open srcfd failed");return 1;}int dstfd = open(argv[2], O_WRONLY|O_CREAT, 0666);  // 以只写的方式,不存在时创建的情况打开文件if (dstfd == -1) {perror("open destfd failed");return 1;}int len = 0;char buffer[BUFFERSIZE] = {0};// 读写都会返回读写的长度,需要对长度进行检查while ((len = read(srcfd, buffer, BUFFERSIZE)) > 0){if (write(dstfd, buffer, len) != len) // 对写操作进行检查{perror("Write error");return 2;}}// 读操作如果返回 len<0 就是出现了问题if (len < 0){perror("read error");return 3;}close(srcfd);close(dstfd);return 0;
}

文件系统调用参数选项

文件系统调用参数选项


以上是文件的系统调用,然后我们来理解一下文件的C标准库

文件C标准库读写文件

#include <stdio.h>
#include <stdlib.h>struct student
{char name[10];int age;float score;
} stu[2];int main (void)
{for (int i = 0; i < 2; i ++) {printf("please input name age score:\n");scanf("%s %d %f", stu[i].name, &stu[i].age, &stu[i].score);}FILE *fp;if ((fp = fopen("hello.dat", "w+")) == NULL){printf("fopen failed!\n");return -1;}fwrite(&stu, sizeof(struct student), 2, fp);if (ferror(fp) != 0)  // 检测文件流fp是否发生了错误{printf("fwrite failed!\n");clearerr(fp);return -1;}fflush(fp);rewind(fp); // 等价于 fseek(fp, 0, SEEK_SET)struct student *buf = (struct student*)malloc (2*sizeof(struct student));if (ferror(fp) != 0){printf("fread failed!\n");clearerr(fp);return -1;}printf("姓名\t年龄\t分数\t\n");for (int i = 0; i < 2; i ++)printf("%s\t%d\t%f\n", buf[i].name, buf[i].age, buf[i].score);fclose(fp);free(buf);buf = NULL;return 0;
}

以上有几个点:

  1. FILE *: 是 C 语言标准库中的 文件指针,用于管理 文件流。它指向一个 FILE 结构体,该结构体维护了:
    • 文件描述符
    • 缓冲区
    • 当前读/写位置
    • 状态(EOF、错误等)
    • 因为是一个结构体有进一步的包装,所以判断异常要使用ferror(fp) != 0
  2. size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
    • 用于向文件写入二进制数据
    • prt:指向要写入的数据的指针
    • size:每个元素的大小
    • nmemb:要写入的元素大小
    • stream:目标文件流指针

系统调用和C标准库

所以对于系统调用和C标准库,它的不同就比如说在linux下对文件的系统调用是open()等等,然后有c标准库,它使用了FILE *,然后fopen来打开,实际上底层还是使用的open()

只是对这个方法的有一层封装,让其更实用一点,而且FILE *本身有缓存区,FILE * 结构有缓冲区(buffering),这意味着 fread()fwrite() 可能不会每次都直接调用 read()write(),而是先写到缓冲区,等缓冲区满了才调用系统调用,这样能减少系统调用次数,提高性能,而且也提供了更多方便的方法

另一个原因就是因为说如果是不同的平台比如说win下他对文件的系统调用可能不是open(),如果使用c标准库,你只用 fopen() 而不用管 Linux、Windows、macOS 具体的系统调用,它们会自动适配不同的操作系统。,从而实现跨平台性,这也是很重要的

系统调用与库函数

系统调用虚拟文件系统

对linux就是使用虚拟文件系统(VFS), 一个 抽象层 统一了文件系统接口,让应用程序和内核不需要关心底层使用的是什么文件系统,在内核中提供了统一的文件操作接口

另外,系统调用的流程是:

  1. 软中断:X86下int 0x80;ARM架构下 SWI软中断指令
  2. 寄存器保存相关参数:参数、系统调用号
  3. 进入内核态,执行内核特权指令代码函数
  4. 返回值保存到寄存器
  5. 返回到用户态、系统调用结束

strace命令的使用

当你想要知道你的程序调用的方法,花费时间时,可以使用strace来观察

-e 的作用就是指定显示,这里就是指定显示open和close的调用
<0.000089> 就是 -T 的作用显示系统调用花费的时间

$ strace -T -e open,close ./a.out 
close(3)                                = 0 <0.000089>  
close(3)                                = 0 <0.000113>
close(3)                                = 0 <0.000086>
open hello.dat failed!
+++ exited with 255 +++

-tt 就是在前面显示具体的时间戳 07:42:19.519597 这种

$ strace -tt -T -e open,close ./a.out 
07:42:19.519597 close(3)                = 0 <0.000137>
07:42:19.524255 close(3)                = 0 <0.000112>
07:42:19.528934 close(3)                = 0 <0.000452>
open hello.dat failed!
07:42:19.532642 +++ exited with 255 +++

-o log 作用就是将输出写到 log 文件中

$ strace -tt -T -e open,close -o log ./a.out 
open hello.dat failed!
lqy@lqy:~/projects/system_prog/00. 入门篇/08.strace$ ls
08.strace.c  a.out  log  stract命令使用.md  test.dat

明明指定了看open()的系统调用,为什么没有?

  • 不显示open的系统调用是因为在现代 Linux 版本(glibc 2.26+)open() 可能被 openat() 取代,所以 strace -e open 可能不会捕获
$ strace -tt -T -e open,openat,close ./a.out 
07:49:51.845942 openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 <0.000249>
07:49:51.849882 close(3)                = 0 <0.000096>
07:49:51.850700 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 <0.000136>
07:49:51.854783 close(3)                = 0 <0.000254>
07:49:51.858475 openat(AT_FDCWD, "test.dat", O_RDONLY|O_CREAT, 051010) = 3 <0.000129> 
// 这里open返回的 = 3 就是说这个打开后给的文件描述符是 3
// 打开失败就是返回的文件描述符是 -1
07:49:51.859134 openat(AT_FDCWD, "hello.dat", O_WRONLY) = -1 ENOENT (没有那个文件或目录) <0.000114>
07:49:51.860171 close(3)                = 0 <0.000116>
open hello.dat failed!
07:49:51.862608 +++ exited with 255 +++

显示错误信息 errno

系统编程错误一般通过函数的返回值表示,执行成功,返回0或正确值,执行失败,返回-1,并把系统全局变量errno赋值,指示具体错误,一般返回值只知道是否执行成功,而更具体的错误由errno指出,使用perror可以输出具体错误原因

全局变量errno由操作系统维护:当系统调用或调动库函数出错时,会重置该值

int main (void)
{int fd;fd = open("hello.dat", O_WRONLY);if (fd == -1){// 会输出 open hello.dat failed!: No such file or directoryopen hello.dat failed: // 输出了用户定义的部分后,后面会加上erron执行的错误perror ("open hello.dat failed!");return -1;}close(fd);return 0;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/900755.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Macbook pro 打开pgAmin报错

当我们安装完postgresql,打开自带的pgAdmin时会报如下错误,这时候我需要去单独下载一个版本pgAdmin重新安装 下载地址:https://www.pgadmin.org/download/pgadmin-4-macos/

读DAMA数据管理知识体系指南23数据集成概念(上)

读DAMA数据管理知识体系指南23数据集成概念(上)1. 数据集成和互操作 1.1. 数据集成和互操作(DII)描述了数据在不同数据存储、应用程序和组织这三者内部和之间进行移动和整合的相关过程 1.2. 数据集成是将数据整合成物理的或虚拟的一致格式 1.3. 数据互操作是多个系统之间进行…

02. log WriteBatch 的结构和编码

在这样的情况之下,我就想来捋一下,这个代码的逻辑 首先从不同的模块说起吧include/leveldb : 这里面存储了要暴露给外部的API,这里面的结构,从使用者来说会比较熟悉,就是通过这里面的结构,实现它的功能,对不同的组件会有一个直观的定义 db : 这里面是对应的实现的类,不…

01. 非阻塞的Skiplist

首先学习LevelDB当中比较独立的一部分,当然的,读源码的话,一个很好的入门的感觉就是先从一个独立的组件模块开始,一个比较容易的开始,SkipList 然后跳表的基本概念什么的我不太想要去过多的赘述,就像二叉树那样希望能得到log(N)的性能,而又利用概率算法更好实现,可以看…

ROCm技术小结与回顾(下)

示例3–V_MFMA_F64_4x4x4F64 考虑V_MFMA_F64_4x4x4F64指令,它计算大小为44的四个独立矩阵块的MFMA。执行的操作是 ,其中 , , 和 都是大小为44元素的矩阵,N=0,1,2,3。下面的两张图显示了 1)输入参数A和B的四个分量的大小和形状,如图4-18所示。 2)分量映射到波阵面所拥有…

ROCm技术小结与回顾(上)

ROCm技术小结与回顾 在这一部分中,首先检查了Kernel 5在各种AMD GPU和问题大小上的性能,并注意到当网格超过一定大小阈值时,性能似乎会急剧下降。通过实验确定,LLC的大小是大型xy平面问题性能的限制因素。提出了两种不同的解决方法来规避缓存大小的问题,这两种方法都只需要…

有限差分法——拉普拉斯第4部分

有限差分法——拉普拉斯第4部分 提出了拉普拉斯算子有限差分法的HIP实现,并应用了四种不同的优化。在这些代码修改过程中,观察到由于全局内存的总取数减少,性能得到了逐步提高。然后,应用了进一步的优化,以在512512512上达到预期的性能目标MI250X GPU的单个GCD上的512个点…

推荐几本书1《AI芯片开发核心技术详解》、2《智能汽车传感器:原理设计应用》、3《TVM编译器原理与实践》、4《LLVM编译器原理与实践》,谢谢

4本书推荐《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》由清华大学出版社资深编辑赵佳霓老师策划编辑的新书《AI芯片开发核心技术详解》已经出版,京东、淘宝天猫、当当等网上,相应陆陆续续可以购买。该…

WebKit Inside: CSS 的匹配原理

WebKit Inside: CSS 的匹配原理相关文章WebKit Inside: CSS 样式表的解析 WebKit Inside: CSS 样式表的匹配时机 WebKit Inside: Acitvie 样式表 当WebView解析完所有外部与内联样式表,就要进入到CSS样式表的匹配阶段。 1 相关类图 WebKit中参与CSS样式表匹配的主要类如下图所…

助记词-公私钥-子私钥派生-钱包地址原理及实现

0x01.简介 现在各种DEX、钱包插件中的钱包导入及创建,大部分是通过助记词来备份的; 助记词是明文私钥的一种表现形式,最早由BIP39提出,为了帮助用户记住复杂的私钥; 一组助记词可以生成各个链上的公私钥,进而可以算出钱包地址;掌握了助记词,就代表掌握了该组助记词上的…

AI 代理的未来是事件驱动的

AI 代理即将彻底改变企业运营,它们具备自主解决问题的能力、适应性工作流以及可扩展性。但真正的挑战并不是构建更好的模型。 代理需要访问数据、工具,并且能够在不同系统之间共享信息,其输出还需要能被多个服务(包括其他代理)使用。这不是一个 AI 问题,而是一个基础设施…

树莓派 3B + Bookworm:mjpg-streamer 正确安装全流程(原创)

在树莓派 OS Bookworm 版本上安装 mjpg-streamer 并非像旧版本一样简单,许多网上的教程已经过时,甚至存在错误。我在尝试过程中遇到了多个问题,例如依赖库缺失、编译失败等,但最终成功解决并搭建了 远程视频流监控系统。本教程基于 树莓派 3B,整理了一套 完整、可复现 的 …