• 首页
  • 搜索
  • 工具
  • 分类
  • 日志
  • 友链
  • 图片

It's Geek KingYoungy

KEEP CHALLENGE

分类 系统编程基础 下的文章

系统编程基础

终端编程详解

2025-03-30 浏览量 166 暂无评论

终端编程详解


1. tcgetattr() 与 tcsetattr()

功能:获取或设置终端的属性(包括输入/输出模式、控制字符定义等)。

#include <termios.h>
int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);

参数说明:
• fd:终端设备的文件描述符(如 /dev/tty)。
• termios_p:指向 termios 结构体的指针,用于存储或传递终端属性。
• optional_actions(仅 tcsetattr):决定何时应用新设置,可选宏:
• TCSANOW:立即生效。
• TCSADRAIN:等待当前输出完成后再生效。
• TCSAFLUSH:清空输入/输出缓冲区后生效。

termios 结构体成员:

struct termios {
    tcflag_t c_iflag;   // 输入模式标志(如输入奇偶校验、回车处理)  
    tcflag_t c_oflag;   // 输出模式标志(如输出映射、延迟处理)  
    tcflag_t c_cflag;   // 控制模式标志(如波特率、数据位)  
    tcflag_t c_lflag;   // 本地模式标志(如回显、信号处理)  
    cc_t c_cc[NCCS];    // 控制字符数组(如中断、停止字符)  
};

返回值:
• 成功时返回 0,失败返回 -1 并设置 errno。


2. ioctl()

功能:执行设备相关的控制操作(如获取终端窗口大小、设置串口参数等)。

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ... /* void *arg */);

参数说明:
• fd:终端设备的文件描述符。
• request:控制请求码,与终端相关的常见宏:
• TIOCGWINSZ:获取终端窗口大小(arg 指向 winsize 结构体)。
• TIOCSWINSZ:设置终端窗口大小。
• TCGETS:等效于 tcgetattr。
• TCSETS:等效于 tcsetattr。
• arg:可变参数,通常为指向数据结构的指针。

winsize 结构体成员:

struct winsize {
    unsigned short ws_row;    // 终端窗口行数  
    unsigned short ws_col;    // 终端窗口列数  
    unsigned short ws_xpixel; // 水平像素(通常未使用)  
    unsigned short ws_ypixel; // 垂直像素(通常未使用)  
};

返回值:
• 成功时返回 0,失败返回 -1 并设置 errno。


3. cfsetispeed() 与 cfsetospeed()

功能:设置终端的输入/输出波特率。

#include <termios.h>
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);

参数说明:
• termios_p:指向 termios 结构体的指针。
• speed:波特率宏,如:
• B0:挂断线路。
• B9600:9600 波特。
• B115200:115200 波特。
• B4000000:自定义高速波特率(部分系统支持)。

返回值:
• 成功返回 0,失败返回 -1。


4. tcsendbreak()

功能:在串行线上发送 BREAK 信号(用于重置设备)。

#include <termios.h>
int tcsendbreak(int fd, int duration);

参数说明:
• fd:终端设备的文件描述符。
• duration:BREAK 持续时间(若为 0,则持续至少 0.25 秒)。

返回值:
• 成功返回 0,失败返回 -1 并设置 errno。


5. tcdrain()

功能:阻塞进程,直到所有输出数据传递到终端。

#include <termios.h>
int tcdrain(int fd);

参数说明:
• fd:终端设备的文件描述符。

返回值:
• 成功返回 0,失败返回 -1 并设置 errno。


6. tcflush()

功能:刷新终端的输入/输出队列。

#include <termios.h>
int tcflush(int fd, int queue_selector);

参数说明:
• fd:终端设备的文件描述符。
• queue_selector:刷新目标队列的宏:
• TCIFLUSH:刷新输入队列。
• TCOFLUSH:刷新输出队列。
• TCIOFLUSH:刷新输入和输出队列。

返回值:
• 成功返回 0,失败返回 -1 并设置 errno。


7. tcflow()

功能:暂停或恢复终端与计算机之间的数据传输。

#include <termios.h>
int tcflow(int fd, int action);

参数说明:
• fd:终端设备的文件描述符。
• action:控制操作的宏:
• TCOOFF:挂起输出。
• TCOON:恢复输出。
• TCIOFF:发送 STOP 字符(暂停输入)。
• TCION:发送 START 字符(恢复输入)。

返回值:
• 成功返回 0,失败返回 -1 并设置 errno。


8. tcgetpgrp() 与 tcsetpgrp()

功能:获取或设置终端的前台进程组ID。

#include <termios.h>
pid_t tcgetpgrp(int fd);
int tcsetpgrp(int fd, pid_t pgrp);

参数说明:
• fd:终端设备的文件描述符。
• pgrp:要设置的进程组ID(仅 tcsetpgrp 需要)。

返回值:
• tcgetpgrp 返回前台进程组ID。
• tcsetpgrp 成功返回 0,失败返回 -1。


9. tcgetsid()

功能:获取终端的会话ID。

#include <termios.h>
pid_t tcgetsid(int fd);

参数说明:
• fd:终端设备的文件描述符。

返回值:
• 成功返回会话ID,失败返回 (pid_t)-1 并设置 errno。


系统调用的底层实现

上述库函数最终通过系统调用与内核交互。以 ioctl 为例,其底层通过 int 0x80(x86)或 syscall(x86-64)触发中断,参数通过寄存器传递:
• eax:系统调用号(如 SYS_ioctl)。
• ebx:文件描述符 fd。
• ecx:请求码 request。
• edx:可变参数指针 arg。


参考资料:

- 阅读全文 -
系统编程基础

Linux Daemon进程开发指南:从创建到日志管理

2025-03-28 浏览量 155 暂无评论

Linux Daemon进程开发指南:从创建到日志管理

一、Daemon进程创建流程

1. 核心步骤解析

Linux守护进程需遵循UNIX规范脱离终端控制,以下是标准创建流程:

1.1 fork() - 进程复制

功能:创建子进程,父进程退出实现后台化

#include <unistd.h>
pid_t fork(void);

参数:无
返回值:
• 成功:父进程返回子进程PID,子进程返回0
• 失败:返回-1,设置errno

1.2 setsid() - 会话控制

功能:创建新会话并脱离终端

#include <unistd.h>
pid_t setsid(void);

返回值:
• 成功:返回新会话ID
• 失败:返回-1,设置errno

1.3 二次fork() - 非会话组长

必要性:防止重新获取控制终端(部分系统要求)

1.4 文件操作

umask(0); // 重置文件权限掩码
chdir("/"); // 切换工作目录到根节点
close(STDIN_FILENO); // 关闭标准IO
dup2(open("/dev/null", O_RDWR), STDIN_FILENO); // 重定向到空设备

二、SIGHUP信号处理机制

1. 信号处理器设置

1.1 sigaction() - 高级信号处理

功能:注册信号处理函数

#include <signal.h>
int sigaction(int signum, 
             const struct sigaction *act,
             struct sigaction *oldact);

参数解析:
• signum:信号编号(如SIGHUP=1)
• act:新动作描述结构体

struct sigaction {
  void     (*sa_handler)(int); // 处理函数指针
  sigset_t sa_mask;    // 执行期间阻塞的信号集
  int      sa_flags;   // 标志位:SA_RESTART|SA_NOCLDSTOP等
  void     (*sa_sigaction)(int, siginfo_t *, void *); // 替代handler
};

• oldact:原动作存储指针

返回值:
• 成功:0
• 失败:-1,设置errno

2. SIGHUP典型应用场景

• 配置文件热加载(如nginx -s reload)
• 日志文件轮转(配合logrotate)
• 服务状态刷新


三、Syslog日志系统API详解

syslog本身是一个daemon进程,本地的进程可以通过syslog()系统调用与其建立Unix Domain Socket进行通信,远程的可以使用Internet Domain Socket与其通信。它会将收到的内容根据设施(facility)与重要性等级(level)组合成的优先级(priotity)来选择相应的行为来处理日志。

1. 核心函数组

1.1 openlog() - 初始化日志连接

#include <syslog.h>
void openlog(const char *ident, int option, int facility);

参数说明:
• ident:程序标识字符串(常为程序名)
• option:控制标志组合(位或):

LOG_CONS    // 无法连接时写入控制台
LOG_NDELAY  // 立即打开连接
LOG_PID     // 记录进程ID
LOG_PERROR  // 同时输出到stderr

• facility:日志分类标识:

LOG_AUTH     // 安全/认证消息
LOG_DAEMON   // 系统守护进程
LOG_LOCAL0~7 // 自定义分类

1.2 syslog() - 日志消息生成

#include <syslog.h>
void syslog(int priority, const char *format, ...);

参数解析:
• priority:级别宏与facility的组合
级别常量:

LOG_EMERG   // 系统不可用
LOG_ALERT   // 立即处理
LOG_CRIT    // 严重错误
LOG_ERR     // 一般错误
LOG_WARNING // 警告
LOG_NOTICE  // 正常但重要
LOG_INFO    // 常规信息
LOG_DEBUG   // 调试信息

• format:类似printf的格式化字符串
• ...:可变参数列表

1.3 closelog() - 关闭日志连接

#include <syslog.h>
void closelog(void);

四、Daemon开发实践建议

  1. 资源管理:需显式关闭非必要文件描述符,防止句柄泄漏
  2. 错误处理:所有系统调用需检查返回值,结合errno输出诊断信息
  3. 信号屏蔽:正确处理SIGTERM等终止信号,实现优雅退出
  4. 日志轮转:通过SIGHUP实现日志文件重打开(示例):
void log_rotate(int sig) {
    fclose(logfile);
    logfile = fopen(path, "a+"); // 重新打开日志文件
    syslog(LOG_NOTICE, "Log rotated by signal %d", sig);
}
  1. 权限控制:建议以非root用户运行,通过setuid()降权

五、Syslog高级配置

1. 远程日志配置(/etc/rsyslog.conf)

# 启用UDP监听
module(load="imudp")
input(type="imudp" port="514")

# 启用TCP+TLS
module(load="imtcp")
input(type="imtcp" port="6514" StreamDriver.Name="gtls")

2. 日志过滤规则

# 仅记录ssh登录失败
if $programname == 'sshd' and $msg contains 'Failed' then /var/log/ssh_fail.log

通过合理运用daemon创建规范、信号处理机制和syslog系统,开发者可以构建出稳定可靠的后台服务。建议结合systemd等现代初始化系统进行服务管理,并定期审计日志安全配置。

- 阅读全文 -
系统编程基础

C语言结构体/共用体的赋值限制

2025-03-27 浏览量 140 暂无评论

C语言结构体/共用体的赋值限制

在C语言中,我们可以在定义结构体或共用体(联合体)变量时直接使用初始化列表进行赋值,例如:

struct Point {
    int x;
    int y;
};

struct Point p1 = {1, 2}; // 合法:初始化时赋值

但如果我们尝试在后续代码中使用类似的语法进行赋值,编译器会报错:

struct Point p2;
p2 = {3, 4}; // 错误:不能这样赋值!

为什么C语言允许在初始化时赋值,却不允许在后续赋值时使用相同的语法?这涉及C语言的设计哲学、历史背景和底层实现。本文将深入探讨这一特性的原因,并介绍如何绕过这一限制。


1. C语言的初始化与赋值的区别

在C语言中,初始化(Initialization)和赋值(Assignment)是两个不同的概念:

  • 初始化:在变量定义时赋予初始值,由编译器在程序加载或进入作用域时处理。
  • 赋值:在变量已经定义后修改其值,属于运行时的操作。

C语言对初始化更加宽松,允许使用{...}语法,但对赋值则更加严格,不允许直接使用结构化常量。


2. 为什么不允许结构化常量赋值?

(1) 语言设计哲学:简单性

C语言的设计目标是保持简洁和高效。在早期,编译器需要在有限的内存和计算资源下工作,因此:

  • 初始化在编译阶段就可以确定,编译器可以直接写入数据段(.data或.rodata)。
  • 赋值需要在运行时处理,如果允许p = {1, 2},编译器必须生成额外的代码来解析并逐成员赋值,这会增加复杂性。

(2) 语法一致性

C语言的数组也有类似的限制:

int arr[3] = {1, 2, 3}; // 合法
arr = {4, 5, 6};         // 非法

结构体和数组的初始化方式保持一致,避免引入特殊规则。

(3) 避免歧义

如果允许p = {1, 2},可能会与以下情况冲突:

  • 结构体赋值
  • 复合字面量(C99引入)
  • 类型转换表达式

C语言选择用不同的语法区分初始化和赋值,减少歧义。

(4) 历史原因

早期的C编译器(如K&R C)不支持结构化赋值,ANSI C(C89)保持了这一限制以兼容旧代码。虽然C99引入了复合字面量(Compound Literals)来改善这个问题,但直接赋值仍然不允许。


3. 如何绕过这一限制?

虽然不能直接使用p = {1, 2},但有几种替代方案:

(1) 逐成员赋值

struct Point p;
p.x = 1;
p.y = 2;

最直接的方式,适用于简单结构体。

(2) 使用复合字面量(C99)

struct Point p;
p = (struct Point){1, 2}; // 合法,C99引入

这种方式在运行时构造一个临时结构体并赋值。

(3) 使用memcpy

struct Point tmp = {1, 2};
memcpy(&p, &tmp, sizeof(p));

适用于需要批量拷贝的情况。

(4) 使用函数封装

void setPoint(struct Point *p, int x, int y) {
    p->x = x;
    p->y = y;
}

setPoint(&p, 1, 2);

提高代码可读性和复用性。


4. C++的改进

C++11引入了统一初始化语法(Uniform Initialization),允许在赋值时使用{}:

struct Point { int x, y; };
Point p;
p = {1, 2}; // C++合法,C仍然不允许

如果项目允许,可以考虑用C++替代C,以获得更灵活的语法。


5. 总结

情况C语言C++
初始化时赋值 T var = { ... }✅ 允许✅ 允许
后续赋值 var = { ... }❌ 不允许(C89/C99)
✅ 可用复合字面量 (T){ ... }(C99)
✅ 允许
统一初始化 T var{ ... }❌ 不允许✅ 允许(C++11)

C语言限制结构化常量赋值的主要原因是:

  1. 保持语言简洁,避免运行时解析{...}的额外开销。
  2. 语法一致性,与数组行为保持一致。
  3. 历史兼容性,早期C编译器不支持该特性。

虽然这一限制可能显得不够灵活,但我们可以通过逐成员赋值、复合字面量(C99)、memcpy或函数封装来绕过它。如果项目允许,C++提供了更现代化的初始化方式。

希望本文能帮助你理解C语言的这一特性!如果你有更好的方法或见解,欢迎在评论区讨论。 🚀

- 阅读全文 -
多进程/多线程并发编程,系统编程基础

文件锁技术详解:flock与fcntl系统调用

2025-03-25 浏览量 134 暂无评论

文件锁技术详解:flock与fcntl系统调用


一、flock系统调用

1.1 功能描述

flock()提供文件级全局锁机制,可对整个文件施加共享锁或互斥锁。该锁为建议性锁(需主动检查),常用于进程间同步文件访问,但不保证强制排他性。该锁由内核维护,存储在内核的打开文件句柄表中。因此只有该文件句柄没有任何进程的文件描述符指向时,才会删除该锁。该锁与其文件句柄共同被删除。

1.2 函数声明

#include <sys/file.h>
int flock(int fd, int operation);

1.3 参数详解

参数类型说明
fdint文件描述符,需通过open()或fileno(FILE*)获取
operationint锁操作类型,支持以下宏组合:
- LOCK_SH:共享锁(允许多进程并发读)
- LOCK_EX:互斥锁(仅允许单进程读写)
- LOCK_UN:释放锁
- LOCK_NB:非阻塞模式(与上述宏用 `` 组合,锁冲突时立即返回)

1.4 返回值

• 成功:返回0
• 失败:返回-1,错误码存于errno,常见错误:
• EAGAIN:非阻塞模式下锁冲突
• EBADF:无效文件描述符
• EINTR:被信号中断


二、fcntl系统调用

2.1 功能描述

fcntl()称为POSIX记录锁支持,记录级细粒度锁,可对文件指定区域加锁。提供强制锁与建议锁两种模式(依赖文件系统支持),具备更精细的控制能力。直接由文件系统维护,因此父子进程即使共享文件描述符,确不共享锁。但是任何进程调用close关闭文件描述符,该锁就被删除(内核所为)。

2.2 函数声明

#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, struct flock *lock);

2.3 参数详解

参数类型说明
fdint文件描述符
cmdint锁操作命令:
- F_GETLK:检测锁状态(不实际加锁)
- F_SETLK:非阻塞式设置/释放锁
- F_SETLKW:阻塞式设置锁(等待锁释放)
lockstruct flock*描述锁类型与范围的指针,结构体成员如下:
struct flock {                                                      
   short l_type;   // 锁类型:F_RDLCK/F_WRLCK/F_UNLCK              
   short l_whence; // 基准位置:SEEK_SET/SEEK_CUR/SEEK_END         
   off_t l_start;  // 偏移量(相对于l_whence)                     
   off_t l_len;    // 锁定字节数(0表示至文件尾)                  
   pid_t l_pid;    // 持有锁的进程ID(F_GETLK时填充)             
};                                                                  

2.4 返回值

• 成功:根据cmd不同返回不同:
• F_GETLK:非负值(锁信息存于lock结构)
• F_SETLK/F_SETLKW:0
• 失败:返回-1,常见错误:
• EACCES/EAGAIN:锁冲突(非阻塞模式)
• EDEADLK:死锁风险(仅限F_SETLKW)
• EINVAL:无效参数


三、关键特性对比

特性flockfcntl
锁粒度整个文件文件区域(字节级)
锁类型共享/互斥共享/互斥/释放
阻塞控制支持LOCK_NB非阻塞通过F_SETLK/F_SETLKW区分
锁继承子进程不继承锁子进程继承锁
锁检测需显式调用检查内置锁状态查询(F_GETLK)
跨进程兼容性与fcntl锁互斥与flock锁互斥
自动放锁关闭一个文件的所有文件描述符关闭一个文件的任意文件描述符

四、应用场景建议

• 简单同步:优先使用flock,代码简洁且适用于全文件锁定
• 高性能并发:选择fcntl,利用区域锁减少竞争
• 强制锁需求:需结合文件系统配置(如mount -o mand)
• 单实例daemon:在/var/run下创建“进程号.pid”文件,并持有该文件的POSIX写锁。一旦有另一个进程运行同一个程序,发现取得不了锁后就知道已经有一个实例在运行了。一般该文件先用truncate或fturncate截断为长度0,然后写入进程的pid,最后在程序结束的时候删除(remove或unlink)该文件防止占用文件系统资源。

- 阅读全文 -
  1. 1
  2. 2
  3. 3
  4. 4

浏览量 : 5865

© 2025 It's Geek KingYoungy. Power By Typecho . Theme by Shiyi

浙ICP备2025160639号  |  浙公网安备33020502001222号

This is just a placeholder img.