【Linux系统编程】System V信号量_什么函数用于对信号量或信号量集合进行操作-程序员宅基地

技术标签: semget  Linux环境高级编程  semop  System V信号量  信号量  semctl  

00. 目录

01. 信号量概述

信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。

编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞。PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1。

在实际应用中两个进程间通信可能会使用多个信号量,因此 System V 的信号量以集合的概念来管理,具体操作和 Posix 信号量大同小异 。

信号量集合数据结构:struct semid_ds,此数据结构中定义了整个信号量集的基本属性。

/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct semid_ds
{
    
	struct ipc_perm	sem_perm;		/* permissions .. see ipc.h */
	__kernel_time_t	sem_otime;		/* last semop time */
	__kernel_time_t	sem_ctime;		/* last change time */
	struct sem	*sem_base;		/* ptr to first semaphore in array */
	struct sem_queue *sem_pending;		/* pending operations to be processed */
	struct sem_queue **sem_pending_last;	/* last pending operation */
	struct sem_undo	*undo;			/* undo requests on this array */
	unsigned short	sem_nsems;		/* no. of semaphores in array */
};

信号量数据结构:struct sem,此数据结构中定义了信号量的基本属性。

/* One semaphore structure for each semaphore in the system. */
struct sem
{
    
	int	semval;		/* current value *信号量的值*/
	int	sempid;		/* pid of last operation *最后一个操作信号量的进程号*/
	struct list_head sem_pending; /* pending single-sop operations */
};

02. 信号量操作命令

查看信号量数组

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ipcs -s

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 

删除信号量数组

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ipcrm -s semid 

03. 信号量操作函数

3.1 创建信号量数组

int semget(key_t key, int nsems, int semflg);
功能:
	创建或打开一个信号量集合,该集合中可以包含多个信号量。
参数:
	key:进程间通信键值,通过调用 ftok() 函数得到的键值
	nsems:创建的信号量的个数。如果只是访问而不创建则可以指定该参数为 0,一旦创建了该信号量,
    就不能更改其信号量个数,只要不删除该信号量,重新调用该函数创建该键值的信号量,
    该函数只是返回以前创建的值,不会重新创建。

	semflg:标识函数的行为及信号量的权限,其取值如下:
		IPC_CREAT:创建信号量。
		IPC_EXCL:检测信号量是否存在。
		位或权限位:信号量位或权限位后可以设置信号量的访问权限,格式和 open 函数的 mode_ t
    	一样(open() 的使用请点此链接),但可执行权限未使用。
返回值:
	成功:信号量集标识符
	失败:返回 -1

3.2 信号量控制函数

int semctl(int semid, int semnum, int cmd, ...);
功能:
	对信号量集合以及集合中的信号量进行操作。
参数:
	semid:信号量集标识符。
	semnum:集合中信号量的序号,指定对哪个信号量操作, 只对几个特殊的 cmd 操作有意义。
	cmd:信号量控制类型。semctl() 函数可能有3个参数,也可能有4个参数,参数的个数由 cmd 决定。
    当有4个参数时,第4个参数为联合体:
	
    union semun{
    
	int			val;	/*信号量的值*/
	struct semid_ds *buf;	/*信号量集合信息*/
	unsigned short  *array;/*信号量值的数组*/
	struct seminfo  *__buf;/*信号量限制信息*/
};
	cmd 的取值如下:
        
	GETVAL:获取信号量的值。此时函数有3个参数。semctl() 函数的返回值即为信号量的值。
	SETVAL:设置信号量的值。此时函数有4个参数。第4个参数为联合体中的val,其值为信号量的值。 
	IPC_STAT:获取信号量集合的信息。此时函数有4个参数。第4个参数为联合体中的__buf。
	IPC_SET:设置信号量集合的信息。此时函数有4个参数。第4个参数为联合体中的__buf。
	IPC_RMID:删除信号量集。此时函数有3个参数,2个参数semnum不起作用。
	GETALL:获取所有信号量的值。此时函数有4个参数,第2个参数semnum不起作用。
            第4个参数为联合体中的array,其值为用来存放所有信号量值的数组的首地址。
	SETALL:设置所有信号量的值 。参数说明同上。
	IPC_INFO:获取信号量集合的限制信息。此时函数有4个参数,第2个参数semnum不起作用。
    		 第4个参数为联合体中的__buf。
	GETPID:获取信号的进程号,即最后操作信号量的进程。此时函数有3个参数。
		    semctl() 函数的返回值即为信号的进程号。
	GETNCNT:获取等待信号的值递增的进程数。此时函数有3个参数。semctl() 函数的返回值即为进程数。
	GETZCNT:获取等待信号的值递减的进程数。此时函数有3个参数。semctl() 函数的返回值即为进程数。
返回值:
	成功:0
	失败:-1

3.3 信号量操作函数

int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:
	操作信号量,主要进行信号量加减操作。
参数:
	semid:信号量集标识符。
	sops:操作信号量的结构体(struct sembuf)数组的首地址( 结构体定义在 sys/sem.h ),
    此结构体中的数据表明了对信号量进行的操作。
    struct sembuf{
    
        unsigned short  sem_num;	/*信号量的序号*/
        short       sem_op;		/*信号量的操作值*/
        short       sem_flg;	/*信号量的操作标识*/
    };
结构体成员使用说明如下:

sem_num:信号量集中信号量的序号
sem_op 取值如下:
sem_op > 0:信号量的值在原来的基础上加上此值。
sem_op < 0:如果信号量的值小于 semop 的绝对值,则挂起操作进程。
    	    如果信号量的值大于等于 semop 的绝对值,
            则信号量的值在原来的基础上减去 semop 的绝对值。
sem_op = 0:对信号量的值进行是否为 0 测试。若为 0 则函数立即返回,若不为 0 则阻塞调用进程。
sem_flag 取值如下: 
	IPC_NOWAIT:在对信号量的操作不能执行的情况下使函数立即返回。
	SEM_UNDO:当进程退出后,该进程对信号量进行的操作将被撤销。
nsops:操作信号量的结构体数组中元素的个数。
    
返回值:
	成功:0
	失败:-1

04. 程序示例

4.1 创建和删除信号量数组

#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <stdio.h>
 
int main(int argc, char *argv[])
{
    
	key_t key;
 
	//创建key值
	key = ftok(".", 'a');
	if(key == -1)
	{
    
		perror("ftok");
	}
	
	//查看信号量
	system("ipcs -s");
	
	int semid;
	
	//1: 创建的信号量的个数
	semid = semget(key, 1, IPC_CREAT|0666); //创建信号量
	if(semid == -1)
	{
    
		perror("semget");
	}
	
	system("ipcs -s"); //查看信号量
	
	//删去信号量
	// 0: 代表对第0个信号量进行操作
	// IPC_RMID:删除信号量集
	semctl(semid, 0, IPC_RMID);
	
	system("ipcs -s"); //查看信号量
	
	return 0;
}

执行结果:

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c 
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     


--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     
0x61320009 0          deng       666        1         


--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 

4.2 获取信号量数组相关信息

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <stdio.h>
 
/*解决编译出错的问题*/
#define IPC_INFO 3
 
int main(int argc, char *argv[])
{
    
	key_t key;
 
	//创建key值
	key = ftok(".", 'a');
	if(key == -1)
	{
    
		perror("ftok");
	}
	
	system("ipcs -s"); //查看信号量
	
	int semid;
	//1: 创建的信号量的个数
	semid = semget(key, 1, IPC_CREAT|0666);//创建信号量
	if(semid == -1)
	{
    
		perror("semget");
	}
	
	system("ipcs -s"); //查看信号量
	
	struct seminfo buf;
	/*
	//struct seminfo相关成员
	struct seminfo {
		int semmap;
		int semmni;
		int semmns;
		int semmnu;
		int semmsl;
		int semopm;
		int semume;
		int semusz;
		int semvmx;
		int semaem;
	};
	
	*/
	
	//IPC_INFO:获取信号量集合的限制信息。
	//此时函数有4个参数,第2个参数semnum不起作用。
	semctl(semid, 0, IPC_INFO, &buf);
	
	printf("buf.semmni = %d\n", buf.semmni);
	printf("buf.semmns = %d\n", buf.semmns);
	printf("buf.semmnu = %d\n", buf.semmnu);
	printf("buf.semmsl = %d\n", buf.semmsl);
	printf("buf.semopm = %d\n", buf.semopm);
	printf("buf.semume = %d\n", buf.semume);
	printf("buf.semusz = %d\n", buf.semusz);
	printf("buf.semvmx = %d\n", buf.semvmx);
	printf("buf.semaem = %d\n", buf.semaem);
 
	//删去信号量
	// 0: 代表对第0个信号量进行操作
	// IPC_RMID:删除信号量集
	semctl(semid, 0, IPC_RMID);
	
	system("ipcs -s"); //查看信号量
	
	return 0;
}

测试结果:

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c 
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     


--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     
0x61320009 32768      deng       666        1         

buf.semmni = 32000
buf.semmns = 1024000000
buf.semmnu = 1024000000
buf.semmsl = 32000
buf.semopm = 500
buf.semume = 500
buf.semusz = 20
buf.semvmx = 32767
buf.semaem = 32767

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 

4.3 获取和设置信号量数组的值

#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <stdio.h>
 
int main(int argc, char *argv[])
{
    
	key_t key;
 
	//创建key值
	key = ftok(".", 'a');
	if(key == -1)
	{
    
		perror("ftok");
	}
	
	//查看信号量
	system("ipcs -s");
	
	int semid;
	
	//1: 创建的信号量的个数
	semid = semget(key, 1, IPC_CREAT|0666); //创建信号量
	if(semid == -1)
	{
    
		perror("semget");
	}
	
	system("ipcs -s"); //查看信号量
	
	int ret;
	
	/*
	//SETVAL: 设置信号量的值。此时函数有4个参数。第4个参数为联合体中的val,其值为信号量的值。 
	union semun{
		int			val;		//信号量的值
		struct semid_ds *buf;	//信号量集合信息
		unsigned short  *array;	//信号量值的数组
		struct seminfo  *__buf;	//信号量限制信息
	};
	*/
	ret = semctl(semid, 0, SETVAL, 20);
	if(ret == -1)
	{
    
		perror("semctl");
	}
	
	//GETVAL:获取信号量的值。函数返回值即为信号量的值。
	ret = semctl(semid, 0, GETVAL);
	if(ret == -1)
	{
    
		perror("semctl");
	}
	printf("ret = %d\n", ret);
 
	// 0: 代表对第0个信号量进行操作
	// IPC_RMID:删除信号量集
	semctl(semid, 0, IPC_RMID);
	
	system("ipcs -s");
	
	return 0;
}

测试结果:

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c 
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     


--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     
0x61320009 65536      deng       666        1         

ret = 20

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 

4.4 信号量数组

#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
 
int main(int argc, char *argv[])
{
    
	key_t key;
 
	//创建key值
	key = ftok(".", 'a');
	if(key == -1)
	{
    
		perror("ftok");
	}
	
	//查看信号量
	system("ipcs -s");
	
	int semid;
	
	//2: 创建的信号量的个数
	semid = semget(key, 2, IPC_CREAT|0666); //创建信号量
	if(semid == -1)
	{
    
		perror("semget");
	}
	
	system("ipcs -s"); //查看信号量
	
	int ret;
 
	unsigned short sem_arry[2] = {
    30,20};
	
	/*
	//SETALL: 设置所有信号量的值。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的array,其值为用来存放所有信号量值的数组的首地址。
	union semun{
		int			val;		//信号量的值
		struct semid_ds *buf;	//信号量集合信息
		unsigned short  *array;	//信号量值的数组
		struct seminfo  *__buf;	//信号量限制信息
	};
	*/
	ret = semctl(semid, 0, SETALL, sem_arry);
	if(ret == -1)
	{
    
		perror("semctl");
	}
	
	
	bzero(sem_arry, sizeof(sem_arry));
	//GETALL:获取所有信号量的值。此时函数有4个参数,
	//第2个参数semnum不起作用。第4个参数为联合体中的array,
	//其值为用来存放所有信号量值的数组的首地址。
	ret = semctl(semid, 0, GETALL, sem_arry);
	if(ret == -1)
	{
    
		perror("semctl");
	}
	printf("sem_arry[0] = %d\n", sem_arry[0]);
	printf("sem_arry[1] = %d\n", sem_arry[1]);
 
	// IPC_RMID:删除信号量集
	semctl(semid, 0, IPC_RMID);
	system("ipcs -s");
	
	return 0;
}

测试结果:

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c 
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     


--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     
0x61320009 98304      deng       666        2         

sem_arry[0] = 30
sem_arry[1] = 20

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 

4.5 综合实例

//此范例使用信号量来同步共享内存的操作
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/sem.h> 
#include <sys/shm.h>
#include <unistd.h>
#include <sys/wait.h>
 
#define SHM_KEY 0x33 
#define SEM_KEY 0x44 
 
union semun 
{
     
	int val; 
	struct semid_ds *buf; 
	unsigned short *array; 
}; 
 
int P(int semid) 
{
     
	struct sembuf sb;
	/*
	//操作信号量的结构体
	struct sembuf{
		unsigned short  sem_num;//信号量的序号
		short       sem_op;		//信号量的操作值
		short       sem_flg;	//信号量的操作标识
	};	
	*/
	sb.sem_num = 0; 
	sb.sem_op = -1;
	//SEM_UNDO:当进程退出后,该进程对信号量进行的操作将被撤销。
	sb.sem_flg = SEM_UNDO; 
 
	//操作1个信号量
	if(semop(semid, &sb, 1) == -1){
     
		perror("semop"); 
		return -1; 
	} 
 
	return 0; 
} 
 
int V(int semid) 
{
     
	struct sembuf sb;
	/*
	//操作信号量的结构体
	struct sembuf{
		unsigned short  sem_num;//信号量的序号
		short       sem_op;		//信号量的操作值
		short       sem_flg;	//信号量的操作标识
	};	
	*/
	
	sb.sem_num = 0; 
	sb.sem_op = 1;
	//SEM_UNDO:当进程退出后,该进程对信号量进行的操作将被撤销。
	sb.sem_flg = SEM_UNDO; 
 
	//操作1个信号量
	if(semop(semid, &sb, 1) == -1){
     
		perror("semop"); 
		return -1; 
	} 
	
	return 0; 
} 
 
int main(int argc, char **argv) 
{
    
	pid_t pid; 
	int i, shmid, semid; 
	int *ptr = NULL; 
	union semun semopts; 
	/*
	union semun{
		int			val;		//信号量的值
		struct semid_ds *buf;	//信号量集合信息
		unsigned short  *array;	//信号量值的数组
		struct seminfo  *__buf;	//信号量限制信息
	};
	*/
 
	//创建一块共享内存, 存一个int变量
	if ((shmid = shmget(SHM_KEY, sizeof(int), IPC_CREAT | 0600)) == -1) {
     
		perror("msgget"); 
		return -1;
	} 
 
	//将共享内存映射到进程, fork后子进程可以继承映射
	ptr = (int *)shmat(shmid, NULL, 0);
	if (ptr == (int *)-1) {
     
		perror("shmat");
		return -1;
	}
	
	*ptr = 0; //赋值为0
 
	// 创建一个信号量用来同步共享内存的操作 
	if ((semid = semget(SEM_KEY, 1, IPC_CREAT | 0600)) == -1) {
     
		perror("semget"); 
		return -1;
	} 
 
	//初始化信号量  
	semopts.val = 1; 
	if (semctl(semid, 0, SETVAL, semopts) < 0) {
     
		perror("semctl"); 
		return -1;
	} 
 
	if ((pid = fork()) < 0) {
     //创建进程
		perror("fork");
		_exit(0);
	}else if (pid == 0){
     // Child
		// 子进程对共享内存加1 
		for (i = 0; i < 100000; i++) {
     
			P(semid); 
			(*ptr)++; 
			V(semid); 
			printf("child: %d\n", *ptr); 
		} 
		
	} else {
     //Parent
		// 父进程对共享内存减1 
		for (i = 0; i < 100000; i++) {
     
			P(semid); 
			(*ptr)--; 
			V(semid); 
			printf("parent: %d\n", *ptr); 
		} 
		
		//如果子进程结束,回收其资源
		wait(NULL);
		
		//如果同步成功, 共享内存的值为0 
		printf("finally: %d\n", *ptr); 
	} 
 
	return 0; 
} 

执行结果:

child: -30
child: -29
child: -28
child: -27
child: -26
child: -25
child: -24
child: -23
child: -22
child: -21
child: -20
child: -19
child: -18
child: -17
child: -16
child: -15
child: -14
child: -13
child: -12
child: -11
child: -10
child: -9
child: -8
child: -7
child: -6
child: -5
child: -4
child: -3
child: -2
child: -1
child: 0
finally: 0

05. 附录

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/dengjin20104042056/article/details/102970461

智能推荐

艾美捷Epigentek DNA样品的超声能量处理方案-程序员宅基地

文章浏览阅读15次。空化气泡的大小和相应的空化能量可以通过调整完全标度的振幅水平来操纵和数字控制。通过强调超声技术中的更高通量处理和防止样品污染,Epigentek EpiSonic超声仪可以轻松集成到现有的实验室工作流程中,并且特别适合与表观遗传学和下一代应用的兼容性。Epigentek的EpiSonic已成为一种有效的剪切设备,用于在染色质免疫沉淀技术中制备染色质样品,以及用于下一代测序平台的DNA文库制备。该装置的经济性及其多重样品的能力使其成为每个实验室拥有的经济高效的工具,而不仅仅是核心设施。

11、合宙Air模块Luat开发:通过http协议获取天气信息_合宙获取天气-程序员宅基地

文章浏览阅读4.2k次,点赞3次,收藏14次。目录点击这里查看所有博文  本系列博客,理论上适用于合宙的Air202、Air268、Air720x、Air720S以及最近发布的Air720U(我还没拿到样机,应该也能支持)。  先不管支不支持,如果你用的是合宙的模块,那都不妨一试,也许会有意外收获。  我使用的是Air720SL模块,如果在其他模块上不能用,那就是底层core固件暂时还没有支持,这里的代码是没有问题的。例程仅供参考!..._合宙获取天气

EasyMesh和802.11s对比-程序员宅基地

文章浏览阅读7.7k次,点赞2次,收藏41次。1 关于meshMesh的意思是网状物,以前读书的时候,在自动化领域有传感器自组网,zigbee、蓝牙等无线方式实现各个网络节点消息通信,通过各种算法,保证整个网络中所有节点信息能经过多跳最终传递到目的地,用于数据采集。十多年过去了,在无线路由器领域又把这个mesh概念翻炒了一下,各大品牌都推出了mesh路由器,大多数是3个为一组,实现在面积较大的住宅里,增强wifi覆盖范围,智能在多热点之间切换,提升上网体验。因为节点基本上在3个以内,所以mesh的算法不必太复杂,组网形式比较简单。各厂家都自定义了组_802.11s

线程的几种状态_线程状态-程序员宅基地

文章浏览阅读5.2k次,点赞8次,收藏21次。线程的几种状态_线程状态

stack的常见用法详解_stack函数用法-程序员宅基地

文章浏览阅读4.2w次,点赞124次,收藏688次。stack翻译为栈,是STL中实现的一个后进先出的容器。要使用 stack,应先添加头文件include<stack>,并在头文件下面加上“ using namespacestd;"1. stack的定义其定义的写法和其他STL容器相同, typename可以任意基本数据类型或容器:stack<typename> name;2. stack容器内元素的访问..._stack函数用法

2018.11.16javascript课上随笔(DOM)-程序员宅基地

文章浏览阅读71次。<li> <a href = "“#”>-</a></li><li>子节点:文本节点(回车),元素节点,文本节点。不同节点树:  节点(各种类型节点)childNodes:返回子节点的所有子节点的集合,包含任何类型、元素节点(元素类型节点):child。node.getAttribute(at...

随便推点

layui.extend的一点知识 第三方模块base 路径_layui extend-程序员宅基地

文章浏览阅读3.4k次。//config的设置是全局的layui.config({ base: '/res/js/' //假设这是你存放拓展模块的根目录}).extend({ //设定模块别名 mymod: 'mymod' //如果 mymod.js 是在根目录,也可以不用设定别名 ,mod1: 'admin/mod1' //相对于上述 base 目录的子目录}); //你也可以忽略 base 设定的根目录,直接在 extend 指定路径(主要:该功能为 layui 2.2.0 新增)layui.exten_layui extend

5G云计算:5G网络的分层思想_5g分层结构-程序员宅基地

文章浏览阅读3.2k次,点赞6次,收藏13次。分层思想分层思想分层思想-1分层思想-2分层思想-2OSI七层参考模型物理层和数据链路层物理层数据链路层网络层传输层会话层表示层应用层OSI七层模型的分层结构TCP/IP协议族的组成数据封装过程数据解封装过程PDU设备与层的对应关系各层通信分层思想分层思想-1在现实生活种,我们在喝牛奶时,未必了解他的生产过程,我们所接触的或许只是从超时购买牛奶。分层思想-2平时我们在网络时也未必知道数据的传输过程我们的所考虑的就是可以传就可以,不用管他时怎么传输的分层思想-2将复杂的流程分解为几个功能_5g分层结构

基于二值化图像转GCode的单向扫描实现-程序员宅基地

文章浏览阅读191次。在激光雕刻中,单向扫描(Unidirectional Scanning)是一种雕刻技术,其中激光头只在一个方向上移动,而不是来回移动。这种移动方式主要应用于通过激光逐行扫描图像表面的过程。具体而言,单向扫描的过程通常包括以下步骤:横向移动(X轴): 激光头沿X轴方向移动到图像的一侧。纵向移动(Y轴): 激光头沿Y轴方向开始逐行移动,刻蚀图像表面。这一过程是单向的,即在每一行上激光头只在一个方向上移动。返回横向移动: 一旦一行完成,激光头返回到图像的一侧,准备进行下一行的刻蚀。

算法随笔:强连通分量-程序员宅基地

文章浏览阅读577次。强连通:在有向图G中,如果两个点u和v是互相可达的,即从u出发可以到达v,从v出发也可以到达u,则成u和v是强连通的。强连通分量:如果一个有向图G不是强连通图,那么可以把它分成躲个子图,其中每个子图的内部是强连通的,而且这些子图已经扩展到最大,不能与子图外的任一点强连通,成这样的一个“极大连通”子图是G的一个强连通分量(SCC)。强连通分量的一些性质:(1)一个点必须有出度和入度,才会与其他点强连通。(2)把一个SCC从图中挖掉,不影响其他点的强连通性。_强连通分量

Django(2)|templates模板+静态资源目录static_django templates-程序员宅基地

文章浏览阅读3.9k次,点赞5次,收藏18次。在做web开发,要给用户提供一个页面,页面包括静态页面+数据,两者结合起来就是完整的可视化的页面,django的模板系统支持这种功能,首先需要写一个静态页面,然后通过python的模板语法将数据渲染上去。1.创建一个templates目录2.配置。_django templates

linux下的GPU测试软件,Ubuntu等Linux系统显卡性能测试软件 Unigine 3D-程序员宅基地

文章浏览阅读1.7k次。Ubuntu等Linux系统显卡性能测试软件 Unigine 3DUbuntu Intel显卡驱动安装,请参考:ATI和NVIDIA显卡请在软件和更新中的附加驱动中安装。 这里推荐: 运行后,F9就可评分,已测试显卡有K2000 2GB 900+分,GT330m 1GB 340+ 分,GT620 1GB 340+ 分,四代i5核显340+ 分,还有写博客的小盒子100+ 分。relaybot@re...

推荐文章

热门文章

相关标签