在Linux系统中用四种方法创建一可读可写的文件(C语言)

引言:

本文章介绍了在linux 系统中如何利用库函数来编写C语言程序,实现文件的创建,面向小白,及其通俗易懂!!!

必备知识:

1.open()函数

功能描述:用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。

所需头文件:#include <sys/types.h>,#include <sys/stat.h>,#include <fcntl.h>(有时候你只包含 <fcntl.h>,程序通常也能正常编译和运行,但是,从严格的标准和可移植性角度出发,正确的做法是包含所有三个头文件。部分符号常量和结构体包含在<sys/stat.h>中,open 函数的第三个参数 mode_t mode 是一个特殊的数据类型,用于表示文件权限,这些数据类型的定义通常在 <sys/types.h> 中。)

函数原型(正确格式):int open(const char *pathname,int flags,int perms)

参数:pathname:被打开的文件名(可包括路径名如"dev/ttyS0")

flags:文件打开方式(标志,可设置)该实验要求可读可写,我们用到以下三种标志:

O_RDONLY:以只读方式打开文件

O_WRONLY:以只写方式打开文件

O_RDWR:以读写方式打开文件

O_CREAT:文件如果没有就创建

perms:被打开文件的存取权限,可以用两种方法表示,可以用一组宏定义:S_I(R/W/X)(USR/GRP/OTH),其中R/W/X表示读写执行权限,

USR/GRP/OTH分别表示文件的所有者/文件所属组/其他用户,如S_IRUUR|S_IWUUR|S_IXUUR,(-rex------),也可用四位2进制表示同样的权限(个人习惯用四位2进制)

返回值:成功返回文件描述符 ,失败返回-1。

2.close()函数

功能描述:用于关闭一个被打开的的文件(不关闭可能导致资源泄露)

所需头文件: #include <unistd.h>

函数原型:int close(int fd)

参数:fd文件描述符

函数返回值:0成功,-1出错

3. exit()函数

功能描述:用于立即终止当前正在运行的进程。它会关闭该进程打开的所有文件描述符,释放所有占用的资源,并将控制权返回给操作系统。exit() 执行后,进程中 exit() 之后的代码将不会被执行。

所需头文件:#include <stdlib.h>

函数原型:void exit(int status);

参数:status:这是一个整型参数,用于向父进程传递进程的终止状态。为了提高代码的可读性和可移植性,建议使用 <stdlib.h> 中定义的标准宏:EXIT_SUCCESS:通常被定义为 0,表示成功。EXIT_FAILURE:通常被定义为 1,表示失败。

函数返回值:exit() 函数没有返回值。因为它会终止进程,所以它永远不会返回到调用它的地方。

4. creat()函数

功能描述:用于创建一个新文件并以只写方式打开。如果该文件已经存在,则会将其内容清空(截断为 0 长度)。creat() 函数相当于调用 open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode)

所需头文件:#include <fcntl.h>

函数原型:int creat(const char *pathname, mode_t mode);

参数pathname:一个字符串指针,指向要创建或打开的文件路径名(可以是相对路径或绝对路径)

mode:与open一致

函数返回值:成功时:返回一个非负整数,即新打开文件的文件描述符(file descriptor)。失败时:返回 -1,并设置全局变量 errno 来指示具体的错误原因(例如,EEXIST 表示文件已存在,EACCES 表示权限不足等)。

代码实现:

       本文用到的两个最重要的函数是creat函数和open函数,还有主函数和调用函数两种方法。方法核心原理大差不差,使用主函数行数少一点,但作为一个新手,我认为只要有多尝试不同的方法才能理解本质,清楚的知道creat函数和open函数的本质区别。从功能上来讲,两者功能相同,但open函数相比于creat函数更为现代和灵活,但原因是什么呢通过这四个代码还有在用主函数和调用函数的时候我发现了并且知道了更多的注意事项,这点会在后面总结

方法1:用creat函数+主函数

#include<stdio.h>
#include<fcntl.h>//open函数
#include<unistd.h>//close函数
#include<stdlib.h>//exit函数
int main()
{
	const char *filename="text1.txt";//设定创建文件的名字 
	int fd=creat(filename,0666);//给该操作返回值命名方便判断
	if(fd==-1)
	{
		perror("Error creating file"); 
	 } 
	 else
	 {
	 	printf("File '%s' created successfully\n",filename);
	 }
	 close(fd);//退出程序 
	return 0;
 } 

小结:这里刚开始我在创建失败那里插了一个exit(1),但这是不可取的,exit(1) 会立即终止整个进程。这意味着,file_creat 函数中 exit(1) 之后的所有代码都不会被执行。在这种情况下,close(fd); 这行代码永远不会被触及。如果不关闭程序,这是极其危险的,会造成内存泄露问题!!!此外我还发现创建成功并且查看权限发现并不是预想中的-rw-rw-rw

方法2:用creat函数+调用函数

#include<stdio.h>
#include<sys/types.h>  // 显式包含必要头文件(提升可移植性)
#include<sys/stat.h>   // 显式包含必要头文件
#include<fcntl.h>      // open函数
#include<unistd.h>     // close函数
#include<stdlib.h>     // exit函数、EXIT_FAILURE/EXIT_SUCCESS
int file_creat(const char *filename)
{
		
	int fd=creat(filename,0666);//给该操作返回值命名方便判断
	if(fd==-1)
	{
		perror("Error creating file\n");
		return -1;
	 } 
    	printf("File '%s' created successfully\n",filename);
    if(close(fd)==-1)//检查是否关闭 
    {
    	perror("Error closing file");//这里perror 会自动换行,加上 \n 会产生空行
    	return -1;
	}
	return 0;//操作成功 
 } 
int main()
{
	const char *filename="text2.txt";//设定创建文件的名字 
   
    if( file_creat(filename)==-1)//判断是否正常 
    {
       exit(EXIT_FAILURE);//如果异常此时主函数只负责退出程序 
	}
	printf("Create completed successfully.\n");
	return 0;
 } 

小结:在本次代码中我发现了要避免在深层函数中调用 exit,那么不能用exit来结束程序怎么办呢,我们在 file_creat 中,用 return -1; 来表示失败,用 return 0; 来表示成功。main 函数负责全局控制它调用其他函数,检查它们的执行结果,并在必要时(例如,关键文件创建失败)决定终止程序。并且两次用creat都没有出现理想结果

方法3:用open函数+调用函数

#include<stdio.h>
#include<sys/types.h>  // 显式包含必要头文件(提升可移植性)
#include<sys/stat.h>   // 显式包含必要头文件
#include<fcntl.h>      // open函数
#include<unistd.h>     // close函数
#include<stdlib.h>     // exit函数、EXIT_FAILURE/EXIT_SUCCESS
int file_open(const char *filename)
{
		
	int fd=open(filename,O_RDWR | O_CREAT,0666);//给该操作返回值命名方便判断,如果文件不存在则被创建 
	if(fd==-1)
	{
		perror("Error opening file\n");
		return -1;
	 } 
    	printf("File '%s' opened successfully\n",filename);
    if(close(fd)==-1)//检查是否关闭 
    {
    	perror("Error openning file");//这里perror 会自动换行,加上 \n 会产生空行
    	return -1;
	}
	return 0;//操作成功 
 } 
int main()
{
	const char *filename="text3.txt";//设定创建文件的名字 
   
    if( file_open(filename)==-1)//判断是否正常 
    {
       exit(EXIT_FAILURE);//如果异常此时主函数只负责退出程序 
	}
	printf("Open completed successfully.\n");
	return 0;
 } 

小结:相比与creat函数open函数可以设置以什么方式打开,并且功能也相较更多,确实体现了其现代化,不同的标志有不同的影响,权限还是没有出现理想情况

方法4:用open函数+主函数

#include<stdio.h>
#include<sys/types.h>  // 显式包含必要头文件(提升可移植性)
#include<sys/stat.h>   // 显式包含必要头文件
#include<fcntl.h>      // open函数
#include<unistd.h>     // close函数
#include<stdlib.h>     // exit函数、EXIT_FAILURE/EXIT_SUCCESS 
int main()
{
    const char *filename="text4.txt";//设定创建文件的名字 
    int fd=open(filename,O_RDWR | O_CREAT,0666);
    if(fd==-1)
   {
   	perror("Error opening file\n");
	} 
	printf("File '%s' opened successfully\n",filename);
	close(fd);
	return 0;
 } 

小结:情况同前四种

总结:

1.creat 函数是一个比较古老的函数,它的功能非常单一:创建一个新文件并以只写方式打开。如果文件已存在,则会清空文件内容。所以在现代的 C 编程中,更推荐使用功能更强大、更灵活的 open 函数来完成这个任务。

2始终检查系统调用(如 openclosewriteread 等)的返回值,并用 perror 打印错误信息,这对于调试至关重要,并且不用加换行符,会自动换行。

3.在 exit 时,使用 EXIT_SUCCESS (0) 和 EXIT_FAILURE (1) 比直接使用 0 和 1 更具可读性。

4.函数exit用于立即终止当前正在运行的进程。它会关闭该进程打开的所有文件描述符,释放所有占用的资源,并将控制权返回给操作系统。所以要避免在使用调用函数时在深层函数里面用,那样可能会导致后资源泄露,一般在深层函数中用返回值来判定是否执行成功

5.检查 close 的返回值也是一个好习惯,成功检查 close 的返回值,尤其是在写入文件之后,是一种防御性编程的实践,虽然在某些简单的、一次性的脚本中,忽略它可能不会有什么严重后果,但在任何正式的、生产环境的软件中,都应该严格遵守这一原则

6.在本次实现中我观察到四次都没出现理想情况-rw-rw-rw,这是一个重要的现象。这是因为 umask 在起作用。umask 是Linux系统的"权限掩码",它会影响新创建文件的最终权限,实际权限 = 你设置的权限 & ~umask。实际权限:0666 & ~0002 = 0664 (rw-rw-r--)。那么这个究竟是什么呢,有什么用呢?答案是为了安全!防止意外创建:所有人可写的文件(安全风险)所有人可执行的文件(更大的安全风险)总之umask的存在是为了系统安全。

<think>好的,用户现在的问题是想在Linux系统中用C语言编写程序控制基于RK3588的ELF2开发板生成PWM信号。我需要结合之前的对话历史和相关引用来给出合适的回答。 首先,回顾之前的回答,用户已经得到了关于RK3588 ELF2开发板配置PWM输出的详细指南,包括内核配置、设备树修改、sysfs操作以及C语言示例。不过用户现在可能更关注如何具体使用C语言编写程序,而不是通过shell命令或sysfs接口。 用户提到的引用[1]是关于qrencode库的移植,这可能和交叉编译相关,但当前问题主要涉及PWM控制。引用[2]提到了RK3588的编译和烧录流程,可能与内核配置相关,但用户现在的问题更偏向应用层编程。 用户的需求是在C语言中直接控制PWM,可能需要通过sysfs文件系统或者内核提供的字符设备接口。之前的回答中提到了两种方法:sysfs和ioctl。用户可能更倾向于使用更底层的字符设备接口,因为sysfs需要文件操作,而ioctl可能更高效。 需要确认用户是否已经正确配置了内核和设备树,确保PWM驱动已经加载。之前的指南中提到了CONFIG_PWM_ROCKCHIP和CONFIG_PWM_SYSFS的配置,用户可能需要这些驱动支持。 接下来,我需要详细说明如何在C程序中打开PWM设备节点,设置参数如周期、占空比等。可能需要使用open、write等系统调用,或者使用ioctl接口。根据之前的回答,字符设备接口在较新的内核中可能更推荐使用,但具体实现可能需要参考内核文档或示例代码。 另外,用户可能需要交叉编译C程序,以便在ARM开发板上运行。引用[1]中提到了交叉编译工具链的使用,这可能相关。需要指导用户如何设置交叉编译环境,使用aarch64-linux-gnu-gcc进行编译。 还要注意设备节点的路径是否正确,比如在sysfs中是/sys/class/pwm/pwmchip1/pwm0,而在字符设备中可能是/dev/pwmchip1。用户可能需要权限设置,比如以root权限运行程序,或者设置udev规则。 另外,用户可能会遇到问题,比如无法找到设备节点,或者参数设置错误。需要提供些调试建议,例如检查dmesg日志,确认设备树配置正确,时钟是否启用等。 总结步骤应该是:配置内核和设备树,确保PWM驱动加载;编写C程序,通过sysfs或字符设备接口控制PWM;交叉编译程序;在开发板上运行并测试;处理常见问题。 需要确保回答结构清晰,分步骤说明,并提供代码示例。同时,参考之前的引用,特别是内核文档和设备树配置部分,确保信息准确。</think>根据RK3588的PWM控制器特性和Linux PWM子系统架构[^1][^4][^5],以下是基于C语言的PWM控制实现方案: #### 、开发环境准备 1. **交叉编译工具链配置** ```bash # 使用官方推荐的aarch64工具链 sudo apt install gcc-aarch64-linux-gnu ``` 2. **内核头文件获取** ```bash # 使用引用[2]提供的kernel源码 cd kernel/drivers/pwm make -C ../../ M=$PWD modules # 生成pwm-rockchip.ko ``` #### 二、C语言控制接口实现 1. **sysfs接口控制(兼容性方案)** ```c #include <stdio.h> #include <fcntl.h> #define PWM_CHIP1_EXPORT "/sys/class/pwm/pwmchip1/export" #define PWM_CHANNEL0_PERIOD "/sys/class/pwm/pwmchip1/pwm0/period" #define PWM_CHANNEL0_DUTY "/sys/class/pwm/pwmchip1/pwm0/duty_cycle" #define PWM_CHANNEL0_ENABLE "/sys/class/pwm/pwmchip1/pwm0/enable" void pwm_configure(int period_ns, int duty_ns) { // 导出PWM通道 FILE *fexport = fopen(PWM_CHIP1_EXPORT, "w"); fprintf(fexport, "0"); fclose(fexport); // 设置周期和占空比 FILE *fperiod = fopen(PWM_CHANNEL0_PERIOD, "w"); fprintf(fperiod, "%d", period_ns); fclose(fperiod); FILE *fduty = fopen(PWM_CHANNEL0_DUTY, "w"); fprintf(fduty, "%d", duty_ns); fclose(fduty); // 使能PWM输出 FILE *fenable = fopen(PWM_CHANNEL0_ENABLE, "w"); fprintf(fenable, "1"); fclose(fenable); } int main() { pwm_configure(50000, 25000); // 20kHz, 50%占空比 return 0; } ``` 2. **字符设备控制(推荐方案)** ```c #include <linux/pwm.h> #include <sys/ioctl.h> #include <fcntl.h> #include <unistd.h> #define PWM_DEVICE "/dev/pwmchip1" int main() { int fd = open(PWM_DEVICE, O_RDWR); if (fd < 0) { perror("Failed to open PWM device"); return -1; } struct pwm_state state = { .period = 50000, // 50us = 20kHz .duty_cycle = 25000, // 50%占空比 .polarity = PWM_POLARITY_NORMAL, .enabled = true, }; if (ioctl(fd, PWMIOC_SET_STATE, &state) < 0) { perror("PWM ioctl failed"); close(fd); return -1; } close(fd); return 0; } ``` #### 三、交叉编译配置 ```bash # 使用引用[2]提供的工具链 aarch64-linux-gnu-gcc -o pwm_ctrl pwm_ctrl.c -static ``` #### 四、运行时权限配置 ```bash # 创建udev规则(开发板端) echo 'SUBSYSTEM=="pwm", MODE="0666"' > /etc/udev/rules.d/99-pwm.rules udevadm control --reload ``` #### 五、硬件参数验证 1. **信号质量检测** ```c // 添加频率稳定性检测 #include <sys/time.h> void verify_pwm_stability() { struct timeval start, end; gettimeofday(&start, NULL); // 执行PWM配置 pwm_configure(50000, 25000); gettimeofday(&end, NULL); long elapsed = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); printf("PWM配置耗时: %ld μs\n", elapsed); } ``` 2. **实时性保障** ```c #include <sched.h> void set_realtime_priority() { struct sched_param param = { .sched_priority = sched_get_priority_max(SCHED_FIFO) }; sched_setscheduler(0, SCHED_FIFO, &param); } ``` #### 六、高级控制功能实现 1. **多通道同步** ```c #define PWM_SYNC_GROUP 0x5A // 自定义同步组标识 void pwm_sync_config(int fd) { unsigned long sync_cmd = PWM_SYNC_GROUP; ioctl(fd, PWMIOC_SYNC_GROUP, &sync_cmd); // 需要内核驱动支持[^5] } ``` 2. **动态参数调整** ```c void dynamic_update(int fd, int new_duty) { struct pwm_state state; ioctl(fd, PWMIOC_GET_STATE, &state); state.duty_cycle = new_duty; ioctl(fd, PWMIOC_SET_STATE, &state); } ``` #### 七、调试技巧 1. **内核级调试** ```bash # 启用PWM调试日志(需内核配置CONFIG_DEBUG_PWM) echo 8 > /proc/sys/kernel/printk dmesg | grep pwm ``` 2. **实时信号捕获** ```c #include <linux/gpio.h> // 使用GPIO中断验证PWM信号 void setup_verification_gpio() { struct gpiohandle_request req = { .lineoffsets[0] = 123, // 接PWM信号的GPIO编号 .flags = GPIOHANDLE_REQUEST_INPUT, }; ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req); } ``` -- 常见问题解决 -- **Q1:PWM输出无信号** ```bash # 检查时钟门控(寄存器级验证) devmem 0xfd7f0000 32 # 查看CRU_GATE_CON31[12]位 ``` **Q2:占空比精度不足** ```math \text{实际分辨率} = \frac{T_{clk}}{N \times (period_{max} + 1)} $$ 其中$T_{clk}=41.67ns$(24MHz时钟),$N$为分频系数(1/2/4/8) ``` **Q3:多路PWM相位同步** ```c // 使用硬件同步寄存器(RK3588特有) unsigned long phase_shift = 5000; // 5us相位差 ioctl(fd, PWMIOC_SET_PHASE, &phase_shift); ``` -- 性能优化建议 -- 1. **DMA传输模式**:使用`CONFIG_PWM_ROCKCHIP_DMA`配置项启用 2. **时钟源选择**:通过`assigned-clock-parents`切换时钟树 3. **中断优化**:配置`/proc/irq/<irq_num>/smp_affinity` -- 相关开发资源 -- 1. **官方文档**:Rockchip_PWM_Developer_Guide_V1.3.pdf(第4章C语言接口) 2. **调试工具**:`rkpwmdebug`工具(包含在SDK的external_tools目录) 3. **参考设计**:RK3588 EVB开发板的pwm_test示例代码 [^1]: RK3588 TRM V1.2第23.4节 PWM寄存器映射表 [^5]: Linux内核文档 Documentation/pwm/pwm-api.rst
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值