首页 > 系统相关 >Linux系统编程笔记

Linux系统编程笔记

时间:2023-07-10 16:12:53浏览次数:57  
标签:文件 函数 int 编程 笔记 char 信号 Linux 进程

系统调用

open函数

文件打开函数

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

返回值为一个文件描述符

参数列表:

  • pathname:文件的完整路径

  • flags:打开文件的模式,
    常用的模式包括:

    • O_WRONLY:只写模式
    • O_RDONLY:只读模式
    • O_RDWR:读写模式
    • O_CREAT:如果文件不存在则创建
    • O_EXCL:如果文件不存在则返回错误码
    • O_TRUNC:文件截断为0 (清空)
    • O_NONBLOCKING:设置为非阻塞状态
  • mode:文件权限,在创建文件时生效,文件最终权限为mode - umask

  • 如果没有指定权限,则默认权限为777-umaks

  • umask默认是三个八进制数字

return value:成功则返回文件描述符,失败则返回-1
open常见错误:

  • 文件不存在:此时fd为-1,errno=2
  • 权限不足:fd=-1,errno=13
  • 以写方式打开目录:fd=-1,errno=21

错误打印printf("%s\n",strerror(errno))
也可以使用perror,它会自动和errno结合,输出你设置的描述和errno对应的错误描述


read、write函数

文件的读、写操作

函数原型:
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

参数列表:

  • fd:文件描述符

  • buf:数据缓冲区

  • count:(read)缓冲区大小 (write)写入数据的大小

read返回值:

  • 成功 返回读取、写入的字节数
  • 失败 返回-1且errno设置为对应的值
  • -1 并且errno=EAGAINEWOULDBLOCK说明不是read失败,而是read在以非阻塞方式读一个设备或者网络文件,并且文件无数据
  • 0 读到文件末尾

readwrite函数通常被成为Unbuffered I/O。指的是无用户级缓冲,但不保证不适用内核缓冲区
文件的读和写都是共用的一个偏移量


文件描述符

PCB进程控制块: 一个结构体
成员包含文件描述符表
文件描述符:0、1、2……1023
新打开的文件描述符时表中可用的最小的

  • 0 - STDIN_FILENO
  • 1 - STDOUT_FILENO
  • 2 - STDERR
struct file{  
...  
偏移量  
访问权限  
打开标志  
内核缓冲区首地址  
struct operations *f_op;  
};  

阻塞、非阻塞:

阻塞是设备、网络文件的属性
产生阻塞的场景:

  • 读设备文件
  • 网络文件(常规文件无阻塞这个概念)
  • /dev/tty -> 终端文件

fcntl函数

用于文件控制

函数原型:
int fcntl(int fd, int cmd, ... /* arg */ );

参数列表:

  • fd:对应的文件描述符
  • cmd:控制指令
    • F_GETFL:获取文件状态
    • F_SETFL:设置文件状态
  • ...:相应指令对应的参数

返回值:

  • 成功,返回命令对应的返回值
  • 失败,返回-1

lseek函数

用于将文件指针移动到指定偏移量处

函数原型:
off_t lseek(int fd, off_t offset, int whence)

参数列表:

  • fd:文件描述符
  • offset:偏移量
  • whence:偏移位置,常见值有SEEK_SET SEEK_CUR SEEK_END
    • SEEK_SET:设置偏移字节
    • SEEK_CUR:偏移量设置为当前位置+偏移字节
    • SEEK_END:偏移量设置为文件大小+偏移字节

返回值:

  • 成功:起始位置偏移量
  • 失败:-1,errno设置为相应值

可以用于文件扩展,获取文件大小


文件存储

首先了解文件存储的相关概念:inode dentry 存储系统 文件系统

inode

本质上是一个结构体,存储文件的属性。比如:权限,类型,大小,时间,用户,位置……也叫做文件属性管理结构,大多数inode都存储在磁盘上


dentry

目录项,本质是结构体,重要的成员变量有两个:文件名和inode,文件内容保存在磁盘盘块中


相关系统调用

stat函数

用于获取文件属性

函数原型:
int stat(const char *pathname, struct stat *statbuf)

struct stat {
               dev_t     st_dev;         /* ID of device containing file */
               ino_t     st_ino;         /* Inode number */
               mode_t    st_mode;        /* File type and mode */
               nlink_t   st_nlink;       /* Number of hard links */
               uid_t     st_uid;         /* User ID of owner */
               gid_t     st_gid;         /* Group ID of owner */
               dev_t     st_rdev;        /* Device ID (if special file) */
               off_t     st_size;        /* Total size, in bytes */
               blksize_t st_blksize;     /* Block size for filesystem I/O */
               blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */
               struct timespec st_atim;  /* Time of last access */
               struct timespec st_mtim;  /* Time of last modification */
               struct timespec st_ctim;  /* Time of last status change */
           };

参数列表:

  • pathname:文件路径和文件名
  • statbuf:(传出参数)文件属性缓冲区指针
    返回值:
  • 成功:0
  • 失败:-1errno设置为对应的值

stat函数是默认可以穿透符号链接文件的,当查询符号链接文件信息时,默认获取链接所指向文件的文件信息。如果不想穿透,则可以使用lstat函数,这两个函数的用法是一样的。

int stat_test(const char *path) {  
	struct stat file_info;  
	int ret = lstat(path, &file_info);  
  
	if (ret != 0) {  
		perror("stat error");  
		_exit(-1);  
	}  
	printf("file size=%ld\n", file_info.st_size);  
  
  
	switch (file_info.st_mode & S_IFMT) {  
		case S_IFBLK: printf("block device\n");  
		break;  
		case S_IFCHR: printf("character device\n");  
		break;  
		case S_IFDIR: printf("directory\n");  
		break;  
		case S_IFIFO: printf("FIFO/pipe\n");  
		break;  
		case S_IFLNK: printf("symlink\n");  
		break;  
		case S_IFREG: printf("regular file\n");  
		break;  
		case S_IFSOCK: printf("socket\n");  
		break;  
		default: printf("unknown?\n");  
		break;  
	}  
return 0;  
}

link函数

用于创建目录项

函数原型:
int link(const char *oldpath, const char *newpath)
参数列表:

  • oldpath:旧目录项
  • newpath:新目录项

readlink函数

读取符号链接文件本身内容,得到链接所指向文件名

函数原型:
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz)
参数列表:

  • pathname:文件链接路径
  • buf:所指向的文件名
  • bufsiz:接收缓冲区大小

目录操作函数

对目录进行操作

函数原型:
DIR *opendir(const char *name);
struct dirent *readdir(DIR *dirp);
int closedir(DIR *dirp);
char *getcwd(char *buf, size_t size);
结构体内部:

struct dirent {
               ino_t          d_ino;       /* Inode number */
               off_t          d_off;       /* Not an offset; see below */
               unsigned short d_reclen;    /* Length of this record */
               unsigned char  d_type;      /* Type of file; not supported
                                              by all filesystem types */
               char           d_name[256]; /* Null-terminated filename */
           };

代码用例

#include <dirent.h>  
#include <unistd.h>  
#include <sys/types.h>
#include <dirent.h>
#include <string.h>  
#include <stdio.h>  
void my_ls(const char *dir) {  
//参数为空,则为当前工作目录  
	if (strlen(dir) == 0) {  
		dir = getcwd(NULL, 0);  
	}  
  
	DIR *dir_p;  
	struct dirent *pdirent;  
  
	dir_p = opendir(dir);  
	  
	if (dir_p == NULL) {  
		perror("opendir failed\n");  
		_exit(-1);  
	}  
	  
	//读取目录项  
	while (1) {  
	pdirent = readdir(dir_p);  
		if (pdirent == NULL) {  
			break;  
		}  
		if (strcmp(pdirent->d_name, ".") == 0 || strcmp(pdirent->d_name, "..") == 0) {  
		continue;  
		}
		printf("%s\t", pdirent->d_name);  
	};  
	fprintf(stdout, "\n");  
	closedir(dir_p);  
}


getcwd获取当前工作目录

函数有两种用法

//1.
char *dir = NULL;
dir = getcwd(NULL, 0);
//2.
char dir[256]={0};
getcwd(dir,sizeof(dir));

重定向

将制定的文件描述符复制一份并返回新的文件描述符

函数原型:
int dup(int oldfd);
int dup2(int oldfd, int newfd);newfd指向oldfd


进程

PCB进程控制块

  • 程序:死的,只占用磁盘空间
  • 进程:程序的一次运行过程,占用系统资源

进程控制块包含

  1. 标识符
  2. 状态(就绪,运行,挂起)
  3. 切换时需要保存和恢复的CPU寄存器
  4. 虚拟地址空间信息
  5. 控制终端信息
  6. 当前工作目录
  7. umask掩码
  8. 文件描述符表
  9. 信号相关信息
  10. 用户ID和组ID
  11. 会话和进程组
  12. 可使用的资源上限

fork

创建一个子进程

函数原型:
pid_t fork(void)

使用fork函数创建子进程后,子进程从fork函数之后开始运行,父子进程各自返回,父进程的fork函数返回子进程的pid,子进程返回0

相关的函数还有:
pid_t getpid(void)
pid_t getppid(void)

fork之后

  • 相同处
    • 全局变量
    • .data
    • .text
    • 堆栈
    • 环境变量
    • 用户ID
    • 宿主目录
    • 工作目录
    • 信号处理方式……
  • 不同处
    • 进程ID
    • fork返回值
    • 父进程ID
    • 运行时间
    • 定时器

父子进程间遵循读时共享写时复制原则,顾名思义,当需要写全局变量时,就会复制一份然后写复制品
父子进程共享打开文件描述符和mmap建立的映射区


exec函数族

fork函数执行后,父子进程都执行相同的代码,子进程使用exec函数可以执行另一程序,此时进程用户空间代码和数据完全被新的程序替代,从新的程序启动例程开始执行。exec函数并不会创建新的进程,前后进程id并不会发生改变

函数原型:
extern char **environ;

执行一个可执行文件
int execl(const char *pathname, const char *arg, ...);

借助环境变量来执行一个可执行文件
int execlp(const char *file, const char *arg, ...);

int execle(const char *pathname, const char *arg, ...);

int execv(const char *pathname, char *const argv[]);

int execvp(const char *file, char *const argv[]);

int execvpe(const char *file, char *const argv[], char *const envp[]);

函数示例:

void exec_test(void) {  
	printf("%s:%d start function [%s]\n", __FILE__, __LINE__, __FUNCTION__);  
	printf("main pid %d\n", getpid());  
	pid_t pid = fork();  
	if (pid < 0) {  
		perror("pid<0");  
		_exit(-1);  
	}  
	if (pid == 0) {  
		//argv[0]是程序本身  
		execlp("ls", "ls", "-l",  "-h", NULL);//NULL代表参数结束  
		//exec执行失败,就会执行下列代码
		perror("exec error");  
		_exit(1);  
	} else {  
		sleep(1);  
		printf("I'm parent: %d\n", getpid());  
	}  
		printf("ending\n");  
}

exec函数族一般规律:

一旦执行成功,即开始新的程序,不返回,只有失败才会返回,错误值为-1,所以再调用之后直接使用perror或者exit,无需使用判断语句

  • l(list):命令行参数列表
  • p(path):搜索file时使用path变量
  • v(vector):使用命令行参数数组
  • e(environment):使用环境变量数组,不适用进程原有的环境变量,设置新加载程序运行的环境变量

孤儿进程

父进程先于子进程结束,则子进程为孤儿进程,此时子进程的父进程变为init进程,称为init进程领养孤儿进程

僵尸进程

进程终止后,父进程尚未回收,子进程残余资源(PCB)放于内核,变为僵尸进程
就是子进程死后,父进程还在运行的状态,子进程就变为僵尸进程

wait函数

进程终止后会释放资源,但是他的PCB会保留再内核中,如果正常退出则保留退出状态,异常终止则保留导致终止的信号,此时父进程使用wait函数可以获取这些信息

函数原型:

pid_t wait(int *wstatus);

pid_t waitpid(pid_t pid, int *wstatus, int options);

int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

参数:

  • wstatus:一个传出参数,用于获取结果

返回值:

  • 成功:回收子进程的pid
  • 失败:-1

wait 只会回收结束了的子进程,如果没有结束,则会阻塞直到结束

获取到的status可以通过一系列宏函数获取到相关信息

  • WIFEXITED (status):
    若子进程正常结束则返回真(true)。 此时,宏 WEXITSTATUS(status)返回子进程的退出状态。
  • WIFSIGNALED (status)
    若通过信号杀掉子进程则返回真(true)。此时,宏WTERMSIG(status)返回导致子进程终止 的信号编号。若子进程产生内核转储文件,则宏WCOREDUMP(status)返回真值(true)SUSv3并未规范宏 WCOREDUMP(),不过大部分 UNIX 实现均支持该宏
  • WIFSTOPPED (status)
    若子进程因信号而停止,则此宏返回为真(true)。此时,宏WSTOPSIG(status)返回导致子进程停止的信号编号
  • WIFCONTINUED (status)
    若子进程收到 SIGCONT 而恢复执行,则此宏返回真值(true)。自Linux 2.6.10 之后开始支持该宏

waitpid系统调用:

  • pid:填写子进程的pid,为0则表示任意子进程
  • options参数是一个整型变量,用于指定waitpid系统调用的行为。常见的选项有WNOHANGWUNTRACED.
    • WNOHANG:如果没有子进程结束,则立即返回0。如果有子进程结束,则返回该子进程的PID,非阻塞式回收。
    • WUNTRACED:如果子进程被暂停,则返回该子进程的PID。如果子进程没有被暂停,不返回。

返回值

  • 成功:子进程pid
  • 失败:-1

进程间通信IPC

常用的有四种方式

  • 管道(最简单)
  • 信号(数据载量小)
  • 共享内存映射(只能用于有关系的进程之间)
  • 本地套接字(最稳定但复杂)
  • fifo

管道

最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递
原理:实为内核使用环形队列机制,借助内核缓冲区实现,大小为4096字节

  • 本质是一个伪文件(实为内核缓冲区)
  • 由两个文件描述符引用,一个表示读端,一个表示写端
  • 规定数据从管道写端流入,从读端流出

局限性:

  • 进程无法自写自读
  • 不可重复读取,读走后就不存在
  • 单工通信,只能单方向流动
  • 只能在有公共祖先的进程间使用

函数原型:
int pipe(int pipefd[2])

形参:

  • pipefd[2]:两个文件描述符,一个用于读,一个用于写,fd[1]用于写,fd[0]用于读

管道的读写行为:

  • 读管道:
    1. 管道中有数据:read返回实际读到的字节数
    2. 管道中无数据:
      • 写端被全部关闭,read返回0(相当于读到文件末尾)
      • 写端没有被全部关闭,read阻塞等待
  • 写管道:
    1. 读端被全部关闭:进程异常终止,也可捕捉SIGPIPE信号,使进程不终止
    2. 读端没有全部关闭:
      • 管道满:write阻塞
      • 管道未满:write写入成功,返回实际写入字节

fifo命名管道

可以用于两个没有血缘关系的进程通信,用起来和文件差不多
创建一个命名管道

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

参数:

  • pathname:管道的完整路径
  • mode:权限,与创建文件时权限一致

返回值:
与大多系统调用一样,成功返回0,失败返回-1


存储区映射I/O

又叫mmap,将一个对象或者文件映射进内存

函数原型:
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *addr, size_t length);

mmap:创建存储区映射函数
参数:

  • addr:映射内存的地址,通常使用NULL,让系统自己分配
  • length:共享内存映射区大小(≤文件或对象的大小)
  • prot:设置共享内存的读写属性。PROT_READPROT_WRITE
  • flags:标注共享内存的共享属性。MAP_SHAREDMAP_PRIVATE
  • fd:用于创建共享内存映射区的那个文件的文件描述符
  • offset:默认0,表示映射文件全部。偏移量,需要为4K的整数倍

返回值:

  • 成功:内存映射区的首地址
  • 失败:MAP_FAILED,并且设置errno

注意事项:

  • 用于创建映射区文件大小为0,指定非0大小创建映射区\(\Rightarrow\)总线错误
  • 用于创建映射区文件大小为0,指定0大小创建映射区\(\Rightarrow\)参数不合法
  • 用于创建映射区文件属性为只读,指定映射区属性为读写\(\Rightarrow\)参数不合法
  • 文件描述符在mmap创建完成后就可以关闭,后续访问可通过缓冲区映射完成
  • 映射最小单位为4K,所以offset要为4k整数倍
  • 如果映射区访问权限为私有,对内存操作只对内存有效,对磁盘上的文件无效

父子间进程通信:

  1. 父进程创建共享内存映射区。open(O_RDWR) mmap(MAP_SHARED)
  2. 指定MAP_SHARED权限
  3. fork创建子进程
  4. 一个进程读,一个进程写

无血缘关系进程间使用mmap通信:

  1. 两个进程打开同一个文件,创建映射区
  2. 指定权限为MAP_SHARED
  3. 一个进程写入,一个进程读出

操作系统提供的特殊文件

和匿名映射一样,只能用于有血缘关系的进程

  • /dev/zero:读取或者写入时,提供无限的数据流
  • /dev/null:空设备文件或者黑洞文件

信号

信号是信息的载体,系统中通信主要手段

概念

  • 未决:信号产生与递达之间状态
  • 递达:产生并送达至进程。直接被内核处理
  • 信号处理方式:执行默认处理动作、忽略、捕捉(自定义)
  • 阻塞信号集(信号屏蔽字):本质:位图。用来记录信号的屏蔽状态。一旦屏蔽的信号,在解除前一致处于未决状态
  • 未决信号集:处于未决状态的集合

信号四要素:

  1. 编号
  2. 名称
  3. 事件
  4. 默认处理动作
Linux常规信号一览表
编号 名称 意义 默认动作
1 SIGHUP 当用户退出shell时,由该shell启动的所有进程将收到这个信号 终止进程
2 SIGINT 当用户按下了<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号 终止进程
3 SIGQUIT 当用户按下<ctrl+\>组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信号 终止进程。
4 SIGILL CPU检测到某进程执行了非法指令 终止进程并产生core文件
5 SIGTRAP 该信号由断点指令或其他 trap指令产生 终止里程 并产生core文件。
6 SIGABRT 调用abort函数时产生该信号 终止进程并产生core文件。
7 SIGBUS 非法访问内存地址,包括内存对齐出错 终止进程并产生core文件。
8 SIGFPE 在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误 终止进程并产生core文件。
9 SIGKILL 无条件终止进程。本信号不能被忽略,处理和阻塞 终止进程。它向系统管理员提供了可以杀死任何进程的方法。
10 SIGUSR1 用户定义 的信号。即程序员可以在程序中定义并使用该信号 终止进程。
11 SIGSEGV 指示进程进行了无效内存访问 终止进程并产生core文件。
12 SIGUSR2 另外一个用户自定义信号,程序员可以在程序中定义并使用该信号 终止进程。
13 SIGPIPE Broken pipe向一个没有读端的管道写数据 终止进程。
14 SIGALRM 定时器超时,超时的时间 由系统调用alarm设置 终止进程。
15 SIGTERM 程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行shell命令Kill时,缺省产生这个信号 终止进程。
16 SIGSTKFLT Linux早期版本出现的信号,现仍保留向后兼容 终止进程。
17 SIGCHLD 子进程结束时,父进程会收到这个信号 忽略这个信号。
18 SIGCONT 如果进程已停止,则使其继续运行 继续/忽略。
19 SIGSTOP 停止进程的执行。信号不能被忽略,处理和阻塞 暂停进程。
20 SIGTSTP 停止终端交互进程的运行。按下<ctrl+z>组合键时发出这个信号 暂停进程。
21 SIGTTIN 后台进程读终端控制台 暂停进程。
22 SIGTTOU 该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生 暂停进程。
23 SIGURG 套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达 忽略该信号
24 SIGXCPU 进程执行时间超过了分配给该进程的CPU时间 ,系统产生该信号并发送给该进程 终止进程。
25 SIGXFSZ 超过文件的最大长度设置 终止进程。
26 SIGVTALRM 虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间 终止进程。
27 SGIPROF 类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间 终止进程。
28 SIGWINCH 窗口变化大小时发出 忽略该信号。
29 SIGIO 此信号向进程指示发出了一个异步IO事件 忽略。
30 SIGPWR 关机 终止进程。
31 SIGSYS 无效的系统调用 终止进程并产生core文件。
32 SIGRTMIN ~ (64) SIGRTMAX LINUX的实时信号,它们没有固定的含义(可以由用户自定义) 所有的实时信号的默认动作都为终止进程。
简单的信号触发函数

函数原型:
int kill(pid_t pid, int sig);

参数列表:

  • pid
    • > 0:杀死指定进程;
    • = 0:发送信号给调用kill函数的那个进程处于同一进程组的所有进程;
    • <-1:取绝对值发送给对应的进程组;
    • =-1:发送给进程有权限发送的系统中所有进程
  • sig:信号

返回值:

  • 成功:0
  • 失败:-1 errno设置为指定值

函数原型:
unsigned int alarm(unsigned int seconds);

定时器,到指定时间后发送SIGALRM信号

参数列表:

  • seconds:定时的秒数,0为取消闹钟

返回值:

  • 上次定时剩余的时长

可以使用setitimer函数设置更为精细的定时


信号集操作

相关函数原型
  1. sigset_t set 自定义信号集
  2. sigemptyset(sigset_t *set) 清空信号集
  3. sigfillset(sigset_t *set) 信号集置一
  4. sigaddset(sigset_t *set, int signum) 将一个信号添加到集合中
  5. sigdelset(sigset_t *set, int signum) 将一个信号从集合中移除
  6. sigismember(sigset_t *set, int signum) 判断一个信号是否在集合中

前5个都是成功\(\Rightarrow\) 0,失败\(\Rightarrow\)-1;
第6个在 \(\Rightarrow\) 1,不在 \(\Rightarrow\) 0


设置信号屏蔽字 sigprocmask

函数原型:

将set中的信号从屏蔽字中加入或去掉

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

参数列表:

  • how
    • SIG_BLOCK:设置阻塞
    • SIG_UNBLOCK:取消阻塞
    • SIG_SETMASK:用自定义set替换mask
  • set:用户自定义的set
  • oldset
查看未决信号集sigpending

int sigpending (sigset_t *set);
set是一个传出参数,传出的是未决信号集


信号处理 ->signal函数

设置信号处理事件

函数原型:
typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

参数列表:

  • signum:信号编号
  • handler:信号处理函数

注册信号处理函数sigaction

函数原型:
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };

使用示例

void sigaction_test(void) {  
	struct sigaction act, oldact;  
	  
	//将屏蔽字清空  
	sigemptyset(&(act.sa_mask));//设置屏蔽字,在捕捉函数执行期间有效  
	act.sa_handler = sig_catch; //注册信号处理函数  
	act.sa_flags = 0;//设置默认属性,通常为0  
	  
	int ret = sigaction(SIGINT, &act, &oldact);  
	if (ret < 0) {  
		perror("sigaction");  
		_exit(-1);  
	}  
	while (1) {  
		sleep(1);  
	}  
}

信号特性

  1. 信号捕捉函数执行期间,自动被屏蔽(sa_flags=0
  2. 正常运行时,进程只有一个信号屏蔽字(假设为A,捕捉到信号后调用处理函数,如果函数执行时间较长,这期间屏蔽信号不由A决定,而是由sa_mask指定。
  3. 阻塞的常规信号不支持排队,多次触发只会处理一次(后32个信号支持排队),阻塞期间,如果被阻塞信号多次发生,则只处理一次

守护进程

daemon进程,运行在操作系统后台,脱离控制终端。一般不与用户直接交互,周期性等待某个事件发生,不受用户登录注销影响。

创建守护进程,最关键的一步就是用setsid函数创建一个新的session,并成为session leader

创建守护进程步骤

  1. fork出子进程,让父进程终止
  2. 子进程调用setsid()创建
  3. 根据需要,改编工作目录chdir()
  4. 根据需要,设置umask文件权限掩码
  5. 根据需要,关闭/重定向文件描述符
  6. 守护进程完成业务逻辑。while()

线程

概念

线程是调度的最小单位
进程是资源分配的最小单位

优点:开销小;通信简单;提高程序并发性
缺点:库函数,不稳定;调试编写困难,GDB不支持;对信号支持不友好

线程控制原语

pthread_t pthread_self(void);

获取线程id,类似于getpid()

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

返回值描述的是线程创建成功与否

void pthread_exit(void *retval);

将线程退出,除此之外,在线程函数中使用return也可以退出线程

int pthread_join(pthread_t thread, void **retval)

阻塞线程等待回收

int pthread_detach(pthread_t thread);

线程分离函数

int pthread_cancel(pthread_t thread);

终止线程,但是要借助取消的契机,如使用pthread_testcancel函数可添加契机点

标签:文件,函数,int,编程,笔记,char,信号,Linux,进程
From: https://www.cnblogs.com/Lhh-9999/p/17541444.html

相关文章

  • 硬核!阿里2023版Spring全家桶进阶笔记流出,堪称Java跳槽神器
    最近小伙伴在我后台留言是这样的: 现在就这光景,不比以前,会个CRUD就有人要,即使大部分公司依然只需要做CRUD的事情......现在去面试,只会CRUD还要被吐槽: 面试造火箭,工作拧螺丝,就是现在互联网最真实的写照。很多程序员都是死磕八股文,以应对面试。这种情况无可厚非,但其实最重......
  • 如何安装 Arch Linux 操作系统?
    ArchLinuxInstall安装到使用Arch说明前面或多或少已经接触过Debian系列和RedHat系列相关Linux发行版,对于虚拟化软件VirtualBox如何创建虚拟机和加载ISO启动盘就不过多赘述。除非你从未使用过VirtualBox或者其他虚拟化软件。Live环境推荐资料:官方指南......
  • Arch Linux 安装完成后配置声音
    安装完ArchLinux后,虽然已经装了 alsa-utils,但是仍然可能出现无法播放声音的情况,这里记录了一种解决方案,在我的Dell上成功。如果使用alsamixer解除静音后还是无法播放声音尝试:1amixerssetMasterunmute如果出现error:amixer:Unabletofindsimpleco......
  • 007 学习笔记--约束 + 多表查询
    约束:是作用于表中字段上的规则,用于限制存储在标中的数据;其目的,是保证数据库中的数据的正确、有效和完整性;约束分类:--约束createtableifnotexistsusers( idintPRIMARYkeyauto_incrementCOMMENT'主键', nameVARCHAR(100)notnulluniqueCOMMENT'姓名',--......
  • Cesium学习笔记3——加载topojson和Geojson
    在根目录下新建bucket.css@import"../Build/CesiumUnminified/Widgets/widgets.css";@import"../Build/CesiumUnminified/Widgets/lighter.css";html{height:100%}body{background:#000;color:#eee;font-family:sans-serif;font-size:9pt;padding:0;margin:0;w......
  • Cesium学习笔记4——几何体绘制
    引用:Sandcastle-header.js<!DOCTYPEhtml><htmllang="en"><head><metacharset="utf-8"/><metahttp-equiv="X-UA-Compatible"content="IE=edge"/><metaname="......
  • Transformer学习笔记
    09Transformer之什么是注意力机制(Attention)@水导ELMo原理解析及简单上手使用@知乎ELMo可以解决多义词的词向量,基于LSTM,基础是LSTM和RNN。......
  • 「学习笔记」Lambda 表达式
    Lambda表达式因数学中的\(\lambda\)演算得名,直接对应于其中的lambda抽象.Lambda表达式能够捕获作用域中的变量的无名函数对象,我们可以将其理解为一个匿名的内联函数,可以用来替换独立函数或者函数对象,从而使代码更可读.但是从本质上来讲,Lambda表达式只是一种语......
  • 关键词:合作博弈 纳什谈判 微网 能源系统 编程语言:matlab 主题:
    关键词:合作博弈纳什谈判微网能源系统编程语言:matlab主题:基于纳什谈判理论的风–光–氢多主体能源系统合作运行方法主要内容:以可再生能源制氢为特征的能源系统将是今后能源互联网建设的重要方向之一。该文针对风–光–氢多主体能源系统的合作运行展开研究。首先,考虑主体间的......
  • 关键词:储能容量优化 储能配置 微网 编程语言:matlab 主
    关键词:储能容量优化储能配置微网编程语言:matlab主题:基于混合整数规划方法的微网电池储能容量优化配置主要内容:本代码目的为实现微电网内电池容量的优化配置,目标函数为配置过程中整体的运行成本最小或者经济效益最大化,约束条件则包括相应的运行约束以及能量平衡约束等等,最后将......