1. socket
- SOCK_STREAM
- SOCK_DGRAM
- SOCKRAW
PF: protocol family
AF_: address family 与PF_*是一一对应的1.1. sock地址
AF_LOCAL AF_UNIX AF_FILE: 都是一回事, 进程间通信#include <sys/socket.h>
AF_INET:
AF_INET6://设置socket地址 int bind (int socket, struct sockaddr *addr, socklen t length) //读取socket地址 int getsockname (int socket, struct sockaddr *addr, socklen t *length-ptr)
1.2. 接口名称, 比如eth0
#include <net/if.h> //接口名到index unsigned int if_nametoindex (const char *ifname) //index到接口名 char * if_indextoname (unsigned int ifindex, char *ifname) //获取全部接口 struct if_nameindex * if_nameindex (void) //释放if_nameindex分配的内存 void if_freenameindex (struct if nameindex *ptr)
1.3. local socket, 用于进程间通信 PF_LOCAL PF_UNIX PF_FILE
local socket又称为unix或file socket, socket地址就是个文件名, 一般放在/tmp下面 当一个local socket关闭时, 也需要delete这个文件 用struct sockaddr_un表示地址 举例:
#include <stddef.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
int
make_named_socket(const char *filename)
{
struct sockaddr_un name;
int sock;
size_t size;
/* Create the socket. */
sock = socket(PF_LOCAL, SOCK_DGRAM, 0);
if (sock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
/* Bind a name to the socket. */
name.sun_family = AF_LOCAL;
strncpy(name.sun_path, filename, sizeof(name.sun_path));
name.sun_path[sizeof(name.sun_path) - 1] = ’\0’;
/* The size of the address is
the offset of the start of the filename,
plus its length (not including the terminating null byte).
Alternatively you can just do:
size = SUN LEN (&name);
*/
size = (offsetof(struct sockaddr_un, sun_path)
+ strlen(name.sun_path));
if (bind(sock, (struct sockaddr *) &name, size) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
return sock;
}
1.4. 网络socket PF_INET PF_INET6
这个socket用struct sockaddr_in或struct sockaddr_in6表示地址 ip地址用struct in_addr表示
#include <arpa/inet.h>
//将192.168.1.1转换成32bit的ip地址
int inet_aton (const char *name, struct in addr *addr)
//相反
char * inet_ntoa (struct in addr addr)
1.4.1. 主机名称, 比如alpha.gnu.org
#include <netdb.h>
//用struct hostent表示一个host
struct hostent * gethostbyname (const char *name)
struct hostent * gethostbyaddr (const void *addr, socklen t length, int format)
1.4.2. 端口和服务
#include <netinet/in.h>
#include <netdb.h>
//每个服务对应struct servent
struct servent * getservbyname (const char *name, const char *proto)
struct servent * getservbyport (int port, const char *proto)
1.4.3. 网络字节序
sin_port和sin_addr必须是网络字节序
#include <netinet/in.h>
uint16_t htons (uint16 t hostshort)
uint16_t ntohs (uint16 t netshort)
uint32_t htonl (uint32 t hostlong)
uint32_t ntohl (uint32 t netlong)
1.4.4. 举例
举例1:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
int
make_socket(uint16_t port)
{
int sock;
struct sockaddr_in name;
/* Create the socket. */
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
/* Give the socket a name. */
name.sin_family = AF_INET;
name.sin_port = htons(port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
return sock;
}
举例2:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void
init_sockaddr(struct sockaddr_in *name,
const char *hostname,
uint16_t port)
{
struct hostent *hostinfo;
name->sin_family = AF_INET;
name->sin_port = htons(port);
hostinfo = gethostbyname(hostname);
if (hostinfo == NULL) {
fprintf(stderr, "Unknown host %s.\n", hostname);
exit(EXIT_FAILURE);
}
name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
}
1.5. socket接口
#include <sys/socket.h>
//open
int socket (int namespace, int style, int protocol)
/*close
**可以直接close文件描述符, 此时colse会尝试发送未完成的data
**也可以int shutdown (int socket, int how), how可以指定
**0 拒绝接受data
**1 停止发送
**2 收发都停
*/
//创建一对socket
int socketpair (int namespace, int style, int protocol, int filedes[2])
//client连接, 阻塞等待server回应. 除非设了nonblocking模式
int connect (int socket, struct sockaddr *addr, socklen t length)
//server端
int listen (int socket, int n)
int accept (int socket, struct sockaddr *addr, socklen t *length_ptr) //addr是对端的地址
//获取对端地址, 前提是这个socket已经connect
int getpeername (int socket, struct sockaddr *addr, socklen t *length-ptr)
1.6. socket读写
可以用read和write, 但使用send和recv能获得更多的控制. 其实, write就相当于send的flags为0 不管是write还是send, 向已经损坏的socket写会得到SIGPIPE
ssize_t send (int socket, const void *buffer, size t size, int flags)
ssize_t recv (int socket, void *buffer, size t size, int flags)
flags:
- MSG_OOB: out of band data
- MSG_PEEK: 用而不取
- MSG_DONTROUTE
1.7. stream client举例
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define PORT 5555
#define MESSAGE "Yow!!! Are we having fun yet?!?"
#define SERVERHOST "www.gnu.org"
write_to_server(int filedes)
{
int nbytes;
nbytes = write(filedes, MESSAGE, strlen(MESSAGE) + 1);
if (nbytes < 0) {
perror("write");
exit(EXIT_FAILURE);
}
}
int
main(void)
{
extern void init_sockaddr(struct sockaddr_in * name,
const char *hostname,
uint16_t port);
int sock;
struct sockaddr_in servername;
/* Create the socket. */
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket (client)");
exit(EXIT_FAILURE);
}
/* Connect to the server. */
init_sockaddr(&servername, SERVERHOST, PORT);
if (0 > connect(sock,
(struct sockaddr *) &servername,
sizeof(servername))) {
perror("connect (client)");
exit(EXIT_FAILURE);
}
/* Send data to the server. */
write_to_server(sock);
close(sock);
exit(EXIT_SUCCESS);
}
1.8. stream server举例
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define PORT 5555
#define MAXMSG 512
int
read_from_client(int filedes)
{
char buffer[MAXMSG];
int nbytes;
nbytes = read(filedes, buffer, MAXMSG);
if (nbytes < 0) {
/* Read error. */
perror("read");
exit(EXIT_FAILURE);
} else if (nbytes == 0)
/* End-of-file. */
return -1;
else {
/* Data read. */
fprintf(stderr, "Server: got message: ‘%s’\n", buffer);
return 0;
}
}
int
main(void)
{
extern int make_socket(uint16_t port);
int sock;
fd_set active_fd_set, read_fd_set;
int i;
struct sockaddr_in clientname;
size_t size;
/* Create the socket and set it up to accept connections. */
sock = make_socket(PORT);
if (listen(sock, 1) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
/* Initialize the set of active sockets. */
FD_ZERO(&active_fd_set);
FD_SET(sock, &active_fd_set);
while (1) {
/* Block until input arrives on one or more active sockets. */
read_fd_set = active_fd_set;
if (select(FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0) {
perror("select");
exit(EXIT_FAILURE);
}
/* Service all the sockets with input pending. */
for (i = 0; i < FD_SETSIZE; ++i)
if (FD_ISSET(i, &read_fd_set)) {
if (i == sock) {
/* Connection request on original socket. */
int new;
size = sizeof(clientname);
new = accept(sock,
(struct sockaddr *) &clientname,
&size);
if (new < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
fprintf(stderr,
"Server: connect from host %s, port %hd.\n",
inet_ntoa(clientname.sin_addr),
ntohs(clientname.sin_port));
FD_SET(new, &active_fd_set);
} else {
/* Data arriving on an already-connected socket. */
if (read_from_client(i) < 0) {
close(i);
FD_CLR(i, &active_fd_set);
}
}
}
}
}
1.9. oob数据
OOB数据通过send和recv传递, 使用flag MSG_OOB, 拥有高优先级, 不会和普通数据一起排队. OOB会触发信号SIGURG 发送OOB时, 会在普通数据流内加个mark, 表示"即将完成"OOB数据 举例:
int
discard_until_mark(int socket)
{
while (1) {
/* This is not an arbitrary limit; any size will do. */
char buffer[1024];
int atmark, success;
/* If we have reached the mark, return. */
success = ioctl(socket, SIOCATMARK, &atmark);
if (success < 0)
perror("ioctl");
if (atmark)
return;
/* Otherwise, read a bunch of ordinary data and discard it.
This is guaranteed not to read past the mark
if it starts before the mark. */
success = read(socket, buffer, sizeof buffer);
if (success < 0)
perror("read");
}
}
举例:
struct buffer {
char *buf;
int size;
struct buffer *next;
};
/* Read the out-of-band data from SOCKET and return it
as a ‘struct buffer’, which records the address of the data
and its size.
It may be necessary to read some ordinary data
in order to make room for the out-of-band data.
If so, the ordinary data are saved as a chain of buffers
found in the ‘next’ field of the value. */
struct buffer *
read_oob(int socket)
{
struct buffer *tail = 0;
struct buffer *list = 0;
while (1) {
/* This is an arbitrary limit.
Does anyone know how to do this without a limit? */
#define BUF_SZ 1024
char *buf = (char *) xmalloc(BUF_SZ);
int success;
int atmark;
/* Try again to read the out-of-band data. */
success = recv(socket, buf, BUF_SZ, MSG_OOB);
if (success >= 0) {
/* We got it, so return it. */
struct buffer *link
= (struct buffer *) xmalloc(sizeof(struct buffer));
link->buf = buf;
link->size = success;
link->next = list;
return link;
}
/* If we fail, see if we are at the mark. */
success = ioctl(socket, SIOCATMARK, &atmark);
if (success < 0)
perror("ioctl");
if (atmark) {
/* At the mark; skipping past more ordinary data cannot help.
So just wait a while. */
sleep(1);
continue;
}
/* Otherwise, read a bunch of ordinary data and save it.
This is guaranteed not to read past the mark
if it starts before the mark. */
success = read(socket, buf, BUF_SZ);
if (success < 0)
perror("read");
/* Save this data in the buffer list. */
{
struct buffer *link
= (struct buffer *) xmalloc(sizeof(struct buffer));
link->buf = buf;
link->size = success;
/* Add the new link to the end of the list. */
if (tail)
tail->next = link;
else
list = link;
tail = link;
}
}
}
1.10. datagram
//发到数据addr
ssize_t sendto (int socket, const void *buffer, size t size, int flags, struct sockaddr *addr, socklen t length)
//接收数据, 源地址保存在*addr
ssize_t recvfrom (int socket, void *buffer, size t size, int flags, struct sockaddr *addr, socklen t *length-ptr)
举例: local+datagram 运行在同一个主机上, 进程间通信
server
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SERVER "/tmp/serversocket"
#define MAXMSG 512
int
main(void)
{
int sock;
char message[MAXMSG];
struct sockaddr_un name;
size_t size;
int nbytes;
/* Remove the filename first, it’s ok if the call fails */
unlink(SERVER);
/* Make the socket, then loop endlessly. */
sock = make_named_socket(SERVER);
while (1) {
/* Wait for a datagram. */
size = sizeof(name);
nbytes = recvfrom(sock, message, MAXMSG, 0,
(struct sockaddr *) & name, &size);
if (nbytes < 0) {
perror("recfrom (server)");
exit(EXIT_FAILURE);
}
/* Give a diagnostic message. */
fprintf(stderr, "Server: got message: %s\n", message);
/* Bounce the message back to the sender. */
nbytes = sendto(sock, message, nbytes, 0,
(struct sockaddr *) & name, size);
if (nbytes < 0) {
perror("sendto (server)");
exit(EXIT_FAILURE);
}
}
}
client
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SERVER "/tmp/serversocket"
#define CLIENT "/tmp/mysocket"
#define MAXMSG 512
#define MESSAGE "Yow!!! Are we having fun yet?!?"
int
main(void)
{
extern int make_named_socket(const char *name);
int sock;
char message[MAXMSG];
struct sockaddr_un name;
size_t size;
int nbytes;
/* Make the socket. */
sock = make_named_socket(CLIENT);
/* Initialize the server socket address. */
name.sun_family = AF_LOCAL;
strcpy(name.sun_path, SERVER);
size = strlen(name.sun_path) + sizeof(name.sun_family);
/* Send the datagram. */
nbytes = sendto(sock, MESSAGE, strlen(MESSAGE) + 1, 0,
(struct sockaddr *) & name, size);
if (nbytes < 0) {
perror("sendto (client)");
exit(EXIT_FAILURE);
}
/* Wait for a reply. */
nbytes = recvfrom(sock, message, MAXMSG, 0, NULL, 0);
if (nbytes < 0) {
perror("recfrom (client)");
exit(EXIT_FAILURE);
}
/* Print a diagnostic message. */
fprintf(stderr, "Client: got message: %s\n", message);
/* Clean up. */
remove(CLIENT);
close(sock);
}
1.11. inetd守护进程
inetd根据配置文件/etc/inetd.conf来监听端口, 发现连接请求后, 就启动一个新的server进程, 运行配置文件制定的程序. 这个程序的标准输入输出已被重定向到socket. 这个配置文件格式: service style protocol wait username program arguments service来源于/etc/services. 比如, ftp stream tcp nowait root /libexec/ftpd ftpd talk dgram udp wait root /libexec/talkd talkd
1.12. socket配置
int getsockopt (int socket, int level, int optname, void *optval, socklen t *optlen-ptr)
int setsockopt (int socket, int level, int optname, const void *optval, socklen t optlen)
其中, level就是SOL_SOCKET 而optname可以是:
- SO_DEBUG: 打开socket调试信息
- SO_REUSEADDR: 使能两个socket同时使用一个端口
- SO_KEEPALIVE: 使能周期发消息检测对端是否broken
- SO_DONTROUTE: 报文不经过通常的routing过程, 直接发送到interface
- SO_BROADCAST: datagram是否被广播
- SO_OOBINLINE: OOB被放到普通数据中
- SO_SNDBUF: 设置发送buffer大小
- SO_RCVBUF: 设置接收buffer大小
- SO_STYLE: 用于获取协议类型
- SO_ERROR: 获取socket错误信息
2. terminal
//是否tty
int isatty (int filedes)
char * ttyname (int filedes)
tty的buffer在内核里, 而不是普通IObuffer
2.1. terminal的模式
#include <termios.h>
struct termios
tcflag_t c_iflag //输入
tcflag_t c_oflag //输出
tcflag_t c_cflag //控制
tcflag_t c_lflag //local
cc_t c_cc[NCCS]
2.2. 对terminal的改动是对dev来说是全局的
共享同一个terminal的其他进程也会改变.
int tcgetattr (int filedes, struct termios *termios-p)
int tcsetattr (int filedes, int when, const struct termios *termios-p)
举例:
int
set_istrip(int desc, int value)
{
struct termios settings;
int result;
result = tcgetattr(desc, &settings);
if (result < 0) {
perror("error in tcgetattr");
return 0;
}
settings.c_iflag &= ~ISTRIP;
if (value)
settings.c_iflag |= ISTRIP;
result = tcsetattr(desc, TCSANOW, &settings);
if (result < 0) {
perror("error in tcsetattr");
return 0;
}
return 1;
}
2.3. 输入输出和控制属性, 见man tcsetattr
2.4. local属性, ICANON ECHO* ISIG(ctrl+c, ctrl+\, ctrl+z)
//速率
int cfsetspeed (struct termios *termios-p, speed t speed)
//raw模式
void cfmakeraw (struct termios *termios-p)
//相当于
termios-p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
|INLCR|IGNCR|ICRNL|IXON);
termios-p->c_oflag &= ~OPOST;
termios-p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
termios-p->c_cflag &= ~(CSIZE|PARENB);
termios-p->c_cflag |= CS8;
举例: raw模式, 不回显
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
/* Use this variable to remember original terminal attributes. */
struct termios saved_attributes;
void
reset_input_mode(void)
{
tcsetattr(STDIN_FILENO, TCSANOW, &saved_attributes);
}
void
set_input_mode(void)
{
struct termios tattr;
char *name;
/* Make sure stdin is a terminal. */
if (!isatty(STDIN_FILENO)) {
fprintf(stderr, "Not a terminal.\n");
exit(EXIT_FAILURE);
}
/* Save the terminal attributes so we can restore them later. */
tcgetattr(STDIN_FILENO, &saved_attributes);
atexit(reset_input_mode);
/* Set the funny terminal modes. */
tcgetattr(STDIN_FILENO, &tattr);
tattr.c_lflag &= ~(ICANON | ECHO); /* Clear ICANON and ECHO. */
tattr.c_cc[VMIN] = 1;
tattr.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr);
}
main(void)
{
char c;
set_input_mode();
while (1) {
read(STDIN_FILENO, &c, 1);
if (c == ’\004’) /* C-d */
break;
else
putchar(c);
}
return EXIT_SUCCESS;
}
2.5. 虚拟终端, <-->master side <--> slave side<-->, 一对一
#include <stdlib.h>
//获取一个新的pty(master), 也可以直接open("/dev/ptmx",O_RDWR), 每次open都会返回一个独立的PTM, 并且在/dev/pts里生成一个相应的pys
int getpt (void)
//设置slave pty的权限
int grantpt (int filedes)
//在open slave之前必须先unlock
int unlockpt (int filedes)
//返回与master相关的slave的pts的文件名
char * ptsname (int filedes)
举例:
int
open_pty_pair(int *amaster, int *aslave)
{
int master, slave;
char *name;
master = getpt();
if (master < 0)
return 0;
if (grantpt(master) < 0 || unlockpt(master) < 0)
goto close_master;
name = ptsname(master);
if (name == NULL)
goto close_master;
slave = open(name, O_RDWR);
if (slave == -1)
goto close_master;
if (isastream(slave)) {
if (ioctl(slave, I_PUSH, "ptem") < 0
|| ioctl(slave, I_PUSH, "ldterm") < 0)
goto close_slave;
}
*amaster = master;
*aslave = slave;
return 1;
close_slave:
close(slave);
close_master:
close(master);
return 0;
}
static int ptym_open (int *p_master, int *p_aux, char *p_slave_name)
{
char *ptsnam;
*p_master=open("/dev/ptmx",O_RDWR);
if(*p_master < 0)
return 0;
ptsnam=ptsname(*p_master);
if(ptsnam==NULL)
{
close(*p_master);
*p_master=-1;
return 0;
}
grantpt(*p_master);
unlockpt(*p_master);
*p_aux = open(ptsnam, O_RDWR);
if (*p_aux < 0)
return 0;
strcpy(p_slave_name,ptsnam);
return 1;
}
2.6. BSD方式打开pty
#include <pty.h>
int openpty (int *amaster, int *aslave, char *name, const struct termios *termp, const struct winsize *winp)
/*fork一个子进程, 使用刚open的slave pts作为子进程的控制终端;
**和fork类似, 返回0表示在子进程; 在父进程返回fork后的进程号. -1为错误
*/
int forkpty (int *amaster, char *name, const struct termios *termp, const struct winsize *winp)
3. syslog
syslogd进程会处理syslog, 与之对应的是unix socket文件/dev/log, 配置文件是/etc/syslog.conf
3.1. log的处理方式:
- 控制台
- 给某人发邮件
- 写到log文件
- 传递给另外的进程
- 丢弃
3.2. syslogd会处理
- 网络log. 通过监听syslog UDP port
- 内核消息, Klogd传递给syslogd.
- 本地log. 通过/dev/log
3.3. syslogd的两个参数, 用一个数字来同时表示facility/priority
- facility: mail subsystem, FTP server,
- priority: debug, informational, warning, critical
3.4. syslog接口
#include <syslog.h> //open, 选项有LOG_PERROR LOG_CONS LOG_PID, facility默认是LOG_USER void openlog (const char *ident, int option, int facility) //close void closelog (void) /*log接口, 可以不openlog 直接调用 **第一个参数是个参数对, 用宏LOG_MAKEPRI(LOG_USER, LOG_WARNING)可以生成 */ void syslog (int facility_priority, const char *format, . . . )
- facility有:
- LOG_USER LOG_MAIL LOG_DAEMON LOG_FTP 等等
- priority有: 如果只填priority, 则用默认的facility
- LOG_EMERG LOG_ALERT LOG_CRIT LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG
举例:
再举例:用setlogmask()过滤掉debug和info, 这些log不会真正到syslog#include <syslog.h> syslog (LOG_MAKEPRI(LOG_LOCAL1, LOG_ERROR), "Unable to make network connection to %s. Error=%m", host);
#include <syslog.h> setlogmask (LOG_UPTO (LOG_NOTICE)); openlog ("exampleprog", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); syslog (LOG_NOTICE, "Program started by User %d", getuid ()); syslog (LOG_INFO, "A tree falls in a forest"); closelog ();
- LOG_EMERG LOG_ALERT LOG_CRIT LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG
举例:
4. 数学
//伪随机数
#include <stdlib.h>
int rand (void)
void srand (unsigned int seed)
5. 日期与时间
#include <time.h>
//间隔
double difftime (time t time1, time t time0)
//进程独占的时间, 不包括阻塞时间
#include <time.h>
clock_t clock (void)
举例:
clock_t start, end;
double cpu_time_used;
start = clock();
... /* Do the work. */
end = clock();
cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
//进程的处理器时间
#include <sys/times.h>
clock_t times (struct tms *buffer)
//用到的结构体
struct tms //单位是clock
clock_t tms_utime //进程用户态处理器时间
clock_t tms_stime //进程内核态处理器时间
clock_t tms_cutime //进程和子进程的用户态时间, 不包括还没结束的子进程
clock_t tms_cstime //进程和子进程的内核态时间, 不包括还没结束的子进程
struct timeval
long int tv_sec
long int tv_usec
time_t //其实就是long int, 从1970年开始算起的seconds
//返回简单日历时间, 可以传入NULL
time_t time (time t *result)
//设置时间, 推荐用settimeofday
int stime (const time t *newtime)
//精确到微妙的日历时间, 从epoch(1970)算起
int gettimeofday (struct timeval *tp, struct timezone *tzp)
int settimeofday (const struct timeval *tp, const struct timezone *tzp)
//平滑的调整时间
int adjtime (const struct timeval *delta, struct timeval *olddelta)
//年月日小时分秒时间(broken-down time)
struct tm
int tm_sec
int tm_min
int tm_hour
int tm_mday
int tm_mon
int tm_year
int tm_wday //0-6
int tm_yday //0-365
const char *tm_zone
struct tm * localtime (const time t *time)
//UTC/GMT时间
struct tm * gmtime (const time t *time)
//从broken-down时间到简单时间
time_t mktime (struct tm *brokentime)
//转换为时间字符串
char * asctime (const struct tm *brokentime)
char * ctime (const time t *time)
5.1. 格式化时间字符串
//从时间到字符串
size_t strftime (char *s, size t size, const char *template, const struct tm *brokentime)
//从字符串到时间
char * strptime (const char *s, const char *fmt, struct tm *tp)
举例:
#include <time.h>
#include <stdio.h>
#define SIZE 256
int
main(void)
{
char buffer[SIZE];
time_t curtime;
struct tm *loctime;
/* Get the current time. */
curtime = time(NULL);
/* Convert it to local time representation. */
loctime = localtime(&curtime);
/* Print out the date and time in the standard format. */
fputs(asctime(loctime), stdout);
/* Print it out in a nice format. */
strftime(buffer, SIZE, "Today is %A, %B %d.\n", loctime);
fputs(buffer, stdout);
strftime(buffer, SIZE, "The time is %I:%M %p.\n", loctime);
fputs(buffer, stdout);
return 0;
}
5.2. 定时器
struct itimerval
struct timeval it_interval //周期
struct timeval it_value //单次
/*which 对应三种:ITIMER_REAL, ITIMER_VIRTUAL, or ITIMER_PROF
**分别对应三种信号: SIGALRM, SIGVTALRM, SIGPROF
**在用sigaction注册信号处理函数时, SA_RESTART被用来表示被打断的系统调用自动重试.
**如果希望定时器可以打断阻塞中的系统调用, 则不要设置SA_RESTART标记
*/
int setitimer (int which, const struct itimerval *new, struct itimerval *old)
int getitimer (int which, struct itimerval *old)
//alarm是简化版的ITIMER_REAL, 传入0表示取消这个定时器
unsigned int alarm (unsigned int seconds)
alarm相当于:
unsigned int
alarm(unsigned int seconds)
{
struct itimerval old, new;
new.it_interval.tv_usec = 0;
new.it_interval.tv_sec = 0;
new.it_value.tv_usec = 0;
new.it_value.tv_sec = (long int) seconds;
if (setitimer(ITIMER_REAL, &new, &old) < 0)
return 0;
else
return old.it_value.tv_sec;
}
5.3. sleep
#include <unistd.h>
unsigned int sleep (unsigned int seconds)
注意sleep可以被信号打断而提前返回, 如果不想被打断, 可以用不包含任何fd的select(), 传入超时时间. 在GNU系统中, sleep和SIGALRM可以同时存在, 因为sleep不使用SIGALRM.
5.4. 纳秒睡眠
#include <time.h>
int nanosleep (const struct timespec *requested_time, struct timespec *remaining)
6. 系统资源
6.1. 资源统计
#include <sys/resource.h>
int getrusage (int processes, struct rusage *rusage)
struct rusage
struct timeval ru_utime //用户态时间
struct timeval ru_stime //内核态时间
long int ru_maxrss //最大run time内存, kb
long int ru_ixrss //和其他进程共享的代码段, kb
long int ru_idrss //不共享的数据段, kb
long int ru_isrss //不共享的栈空间, kb
long int ru_minflt //非io页fault
long int ru_majflt //io页fault
long int ru_nswap //被换出内存的时间
long int ru_inblock //读硬盘次数
long int ru_oublock //写硬盘次数
long int ru_msgsnd //ipc发送次数
long int ru_msgrcv //ipc接收次数
long int ru_nsignals //被signal的次数
long int ru_nvcsw //进程主动让出cpu次数
long int ru_nivcs //进程被动让出cpu次数
6.2. 限制, 两种
- current limit: 进程可自己修改的limit
- maximum limit: 只有超级用户才能改的limit
int getrlimit (int resource, struct rlimit *rlp) int setrlimit (int resource, const struct rlimit *rlp)
6.3. ulimit
#include <ulimit.h>
long int ulimit (int cmd, . . . )
6.4. 实时进程优先级
实时进程0-99, 对同一个优先级的几个实时进程的调度分为:
- FIFO: 先到先执行, 必须主动让出CPU(sched_yield)
- RR: round robin, 同级时间片轮转. 这里的时间片不是从父进程继承而来, 在linux下面, 比普通进程的时间片小得多(150ms)
6.5. 进程调度
#include <sched.h> //SCHED_OTHER SCHED_FIFO SCHED_RR int sched_setscheduler (pid t pid, int policy, const struct sched param *param) int sched_getscheduler (pid t pid) /*我估计这个文档里的0优先级是普通优先级(100-139), 而大于0的优先级都是实时进程的优先级(0-99) **后面提到的动态优先级是对这里的0号优先级说的 */ //设置绝对优先级 int sched_setparam (pid t pid, const struct sched param *param) //获取绝对优先级 int sched_getparam (pid t pid, struct sched param *param) //最小 最大的允许优先级 int sched_get_priority_min (int policy) int sched_get_priority_max (int policy) //RR进程时间片 int sched_rr_get_interval (pid t pid, struct timespec *interval) //主动让出cpu int sched_yield (void)
6.6. nice值
- +19: 优先级最低, 时间片大约10ms
- -20: 优先级最高, 时间片大约400ms
一般进程能够提高nice, 但不能降低nice
#include <sys/resource.h> //获取nice值, class = PRIO_PROCESS, PRIO_PGRP, PRIO_USER(同一user下的所有进程) int getpriority (int class, int id) //设置nice值 int setpriority (int class, int id, int niceval) //提高nice int nice (int increment) //相当于: int nice (int increment) { int result, old = getpriority (PRIO_PROCESS, 0); result = setpriority (PRIO_PROCESS, 0, old + increment); if (result != -1) return old + increment; else return -1; }
6.7. 绑定cpus
#include <sched.h>
结构体cpu_set_t, 表示cpuset
void CPU_ZERO (cpu set t *set)
void CPU_SET (int cpu, cpu set t *set)
void CPU_CLR (int cpu, cpu set t *set)
int CPU_ISSET (int cpu, const cpu set t *set)
//获取进程cpu mask
int sched_getaffinity (pid t pid, size t cpusetsize, cpu set t *cpuset)
//设置
int sched_setaffinity (pid t pid, size t cpusetsize, const cpu set t *cpuset)
6.8. 可用内存页数目
#include <unistd.h>
//获取当前进程page size
int getpagesize (void)
//获取系统物理页数, 但这些页不一定全能用
sysconf (_SC_PHYS_PAGES)
//获取当前进程当时可用的物理页, 不会影响其他进程
sysconf (_SC_AVPHYS_PAGES)
//也可用GNU的函数
#include <sys/sysinfo.h>
long int get_phys_pages (void)
long int get_avphys_pages (void)
6.9. cpu个数
//全部的cpu个数
sysconf (_SC_NPROCESSORS_CONF)
//online的cpu个数, 即可用的cpu个数
sysconf (_SC_NPROCESSORS_ONLN)
//也可用GNU的函数
#include <sys/sysinfo.h>
int get_nprocs_conf (void)
int get_nprocs (void)
6.10. 当前系统负载, 1 5 15分钟平均负载
#include <stdlib.h>
int getloadavg (double loadavg[], int nelem)