《linux系统编程手册》笔记

/sbin/init 进程之父。进程号总为1。

环境列表。每个进程都有,其中每个元素都由一个名称和对应值组成。
shell中创建环境变量export myvar = 'hello world'。比如shell自身就定义了一些环境变量: HOME, PATH等。

信号-软中断。 信号在产生至送达进程期间一直处于挂起状态。当对应进程获得调度时,处于挂起状态的信号会被同时送达。

进程组。shell任务控制功能允许用户同时执行并操作多条命令或管道,会将管道内的所有进程置于一个新进程组或任务中。

/proc文件系统。是一种虚拟文件系统,有目录和文件,提供一个指向内核数据结构的接口。可以通过proc/PID形式命名的目录(PID为进程ID)查看进程信息。

三. 系统编程概念

linux中系统调用服务例程的命名为sys_xxx()形式。xxx()就是系统调用。使用任何系统调用,比如在应用程序有一句execve(path, argv, envp),会先跳到C语言函数库的对应外壳函数文件中,此处是execve.c文件,找到execve(path, argv, envp),执行后会先int 0x80产生中断,进入内核态,查找对应系统调用例程(call sys_call_table[__NR_execve]),找到sys_execve()例程,执行后返回。

“调用系统调用xyz()”意味着“调用外壳函数,you外壳函数去调用系统调用xyz()”。

全局变量errno在errno.h中,包含对各种错误编号定义的常量。当系统调用失败时,会将errno置为正值。

fd = open(path,flags,mode);
if (fd == -1){
	if(errno == EINTR)
		printf(stderr, "open was intertupted by a signal");
	else{ 
		/*other errors*/
	}
}




perror()函数。会打印msg字符串,并自动打印errno的内容。

#include <stdio.h>
void perror(const char *msg);

**/proc文件系统**

虚拟的,内核在进程访问此类信息时动态创建 /proc/pid_number/代表某个具体的进程 /proc/pid_number/status /proc/pid_number/fd_number 符号链接,指向由进程打开的文件



监控文件事件


inotify API:

创建inotify实例
int inotify_init(void);


创建一个监控项,包含路径何掩码。
int inotify_add_watch(int fd, const char   
 *pathname,uint32_t mask);  
 //mask针对path定义了欲监听的事件
 //返回一个监控描述符对应该监控项


删除某个监控项
int inotify_rm_watch(int fd, uint32_t wd);
//成功返回0,失败返回-1
//wd是add成功后返回的监控描述符。
//为wd生成IN_IGNORED事件



inotify事件列表
书p313

read()调用会阻塞, 有事件发生时返回到指定缓冲区。含有多个inotify_event结构体。

struct inotify_event{
	int wd;  //wd就是添加的监控项
	uint32_t mask;
	uint32_t cookie; //关联事件,重命名时用到
	uint32_t len;  // name字段长度
	char name[]; // 受监控目录内发生事件的文件名。若受监控的就是文件,name为空。
};

多个inotify事件之间有填充字段。部分是name的值。
单个inotify事件的长度为sizeof(struct inotify_event)+len

执行流程:

  1. inotify_init()
  2. inotify_add_watch()
  3. 进入循环,用read(fd,buf,len)从inotify不断读取至buf,buf由多个struct inotify_events结构体构成。

信号

进程接收信号后的默认行为:
  1. core:进程产生转储文件,并结束进程。
  2. term: 进程终止
  3. cont: 信号恢复了一个已停止的进程
  4. ignore:进程忽略了信号

pending:信号产生后会被稍后传递给某一进程。在产生和到达期间,信号处于等待(pending)状态。

安装处理器程序:通知内核应当去调用某一处理器的行为

信号已处理(handled),已捕获(caught):调用信号处理器程序以响应传递来的信号。

SIGCHLD信号:程的子进程终止时,内核会向父进程发送该信号。
SIGCONT信号:进程收到该信号会继续运行。
SIGHUP信号:终端断开时,会被发送给终端控制进程。还可用于守护进程(init,ubusd),守护进程收到该信号后,会进行重新初始化,以通过kill进程来手动向守护进程发送该信号。
SIGINT:在终端输入ctrl+c产生中断,终端把该信号输入给前台进程组,默认行为是终止进程。
SIGIO:特定的fd(如终端和套接字)发生IO事件时产生。
SIGSYS:进程的系统调用有误会产生该信号。
SIGTERM:标准的终止进程信号。会先清理临时文件释放资源,再杀掉进程。
SIGKILL:只有当SIGTERM无效时再使用这个信号,不会进行清理工作。

改变信号处理函数:signal()
原型:
#include<signal.h>
void (*signal(int sig, void (*new_handler)(int)))(int);
注意这里对signal函数运行后的返回值做声明。里层的new_handler是一个函数指针,是signal函数的第二个参数,
sig是信号编号,这里传进去,是希望改变它的信号处理函数为new_handler。
然后外层,同样定义一个函数指针,是将signal()调用的结果定义为一个函数指针,指向一个无返回值,接受一个整型参数的函数。

signal()返回的是old_handler,是一个指针,指向是sig的旧信号处理函数。
signal()调用返回SIG_ERR

简化:
typedef void (*handler_t)(int); //handler_t是种新的类型,被他声明的变量是:一个函数指针,指向无返回值,接收一个               整型参数的函数。

被简化为:
handler_t signal(int sig, handler_t handler);

使用实例:
下面演示暂时使用新的信号处理函数的过程。

void (*old_handler)(int);
old_handler = signal(SIGINT, new_handler);
if (old_handler == SIG_ERR)
	errExit("signal");

/*
此时干其他的事情,中途如果有SIGINT进来,会调用new_handler处理
*/

if(signal(SIGINT, old_handler) == SIG_ERR) //换回旧的信号处理函数
	errExit("signal");

handler参数值可以用下面的宏:
SIG_DFL:将信号处理  函数重置为默认值
SIG_IGN:忽略该信号。

例程,为SIGINT信号安装一个信号处理器:

#include<signal.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
static void 
new_handler(int sig)
{    
	printf("signal interrupt happen");
}
int main()
{    
	int idx;    
	if(signal(SIGINT, new_handler)==SIG_ERR)    
	{    
		perror("interupt");         
		exit(EXIT_FAILURE);    
	}    
	for(idx = 0;;idx++)    
	{        
		printf("idx = %d", idx);        
		sleep(3);    
	}        
	return 0;
}

上图的程序给中断信号分配了处理器函数,程序在for里循环,一旦有中断信号产生就调用信号处理函数。

为多个信号添加同一个处理器。处理器会进行信号判断。

#include<signal.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
static void 
new_handler(int sig)
{   
	if(sig == SIGINT)    
	{           
		printf("signal interrupt happen");        
		return;    
	}    
	else if (sig == SIGTERM)    
	{       printf("signal term, i will close the program now");       						   
		exit(EXIT_SUCCESS);    
	}    
}
int main()
{    
	int idx;    
	if(signal(SIGINT, new_handler)==SIG_ERR)    
	{    
		perror("interupt");         
		exit(EXIT_FAILURE);    
	}    
	if(signal(SIGTERM, new_handler)==SIG_ERR)    //添加第二个信号
	{    
		perror("term");         
		exit(EXIT_FAILURE);    
	}    
	while (1)    
	{        
		sleep(3);    
	}           
	return 0;
}

发送信号:kill()

原型:int kill(pid_t pid, int sig);
  • pid
    pid > 0,表示向进程号为 pid 的进程发信号
    pid = 0,表示向同组进程发信号(有权限才行)
    pid < -1,向进程组 |pid||pid| 发信号(有权限才行)
    pid = -1,向所有进程发信号(有权限才行,早期的 POSIX 并未定义此种情况)

*sig
sig 为 0 ,通常用来测试是否有权限向进程发信号。

4188号进程发送SIGINT中断信号。

int main(int argc, char *argv[])
{
	int pid = 4188, sig = SIGINT;    
 	s = kill(pid, sig);    
 	if (s == 0)    
 	{       
 		exit(EXIT_SUCCESS);    
 	}    
 	else    
 	{        
 		if(s == ESRCH)            
 			printf("no such process");        
 		else if(s == EPERM)            
 			printf("no permition");        
 		else        
 		{            
 			perror("kill");            
 			exit(EXIT_FAILURE);        
 		}            
 	}    
 	return 0;
 }


进程或线程调用raise向自己发送信号。

int raise(int sig); //返回负数则调用失败

显示信号描述

char *strsignal(int sig);   :返回一个指针,指向描述信号信息的字符串。
void psignal(int sig, const char *msg);   :先打印msg再显示信号信息。

信号集
信号集是一个表示多个信号的数据结构。

数据类型为:sigset_t。set就是’集’的意思。

#include <signal.h>
 int sigemptyset(sigset_t *set); //初始化一个未包含任何成员的信号集
 int sigfillset(sigset_t *set);  //初始化一个包含所有信号的信号集
 int sigaddset(sigset_t *set, int sig); //向信号集添加一个信号
 int sigdelset(sigset_t *set, int sig); //从信号集删除一个信号
以上函数返回0表示成功,返回-1表示失败。



信号掩码(阻塞信号传递)

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
返回0表示成功,返回-1失败。

作用是给将set信号集添加至信号掩码或从信号掩码接触。在信号掩码内的信号集们都会被阻塞。
how --  SIG_BLOCK  将set添加至信号掩码。
    --  SIG_UNBLOCK 将set从信号掩码解除
    --  SIG_SETMASK  将信号掩码赋值为set
oldset -- 保存之前的信号掩码

当set参数为空时,how参数将被忽略,此时就可以用来获取当前信号掩码  

每个进程都有各自的信号掩码。
下面例程演示:暂时阻塞中断信号,以让不能被中断的程序执行完,然后接触对中断信号的阻塞。

int main(int argc, char *argv[])
{
	_sigset_t set, prevmask;    
	sigempty(&set);    //初始化信号集
	sigaddset(&set, SIGINT);    //添加将被阻塞的信号
	if(sigprocmask(SIG_BLOCK, &set, &prevmask) == -1)  //将信号集加入阻塞掩码
							   //信号集的信号都将被阻塞    
	{        
		perror("sigprocmask");        
		exit(EXIT_FAILURE);    
	}
    /*
    运行不应该被中断的代码 
    */
    
	if(sigprocmask(SIG_SETMASK, &prevmask, NULL) == -1)    //恢复旧的阻塞掩码
     	{        
    		perror("sigprocmask");        
    		exit(EXIT_FAILURE);    
    	}
    	return 0;

接收进程收到的同一种信号数量远小于发送进程发送的数量。

sender
关键用到kill(pid, sig)来向制定进程发送消息
在这里插入图片描述

receiver
先为每个信号绑定处理器。
再阻塞所有信号并进入睡眠,睡醒后读出睡眠期间被阻塞的信号。
然后unblock所有信号,进入while循环,等待SIGINT到来,到来后会触发对应的处理器。
在这里插入图片描述
在这里插入图片描述

sigaction
是signal的优化版

原型:

int sigaction(int sig, const struct sigaction *act. const struct sigaction *oldact);

struct sigaction{
	void (*sa_handler)(int);  //处理器函数
	sigset_t sa_mask;	//当处理器函数被调用时,sa_mask信号集内的信号都会被阻塞。
	int sa_flags;
};

pause
原型:
int pause(void);
永远返回-1,并且把errno设为EINTER。
调用后进程会暂停执行,直到处理器函数被调用,即信号到达,会产生中断,结束pause的执行。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值